Odoo16 微信公众号模块开发示例

Odoo16 微信公众号模块开发示例

本模块基于 aiohttp + asyncio 进行异步微信公众号接口开发, 仅实现了部分 API 仅供学习参考,更完善的同步接口请参考:wechatpy 或 werobot,可用来替代 模块中的 wechat client。

业务需求

  1. 小程序中需要用户先关注公众号后才能进一步操作
  2. 通过公众号给用户发送通知

功能设计

方案:

同一用户在小程序与公众号中的 openid 是不同的,通过微信 UnionId 机制, 将小程序及公众号绑定到同一开放平台(开放平台需认证缴费),把已有公众号用户同步到后台,并处理关注及取消关注事件,即在后台中同步所有关注用户,并通过 UnionId 判断用户是否已关注,未关注时小程序端展示公众号中已添加的关注引导文章。

功能点:

后端:

  • 基于 aiohttp + asyncio 开发公众号 Api 接口
  • 公众号基本配置管理
  • 关注用户信息管理
  • 批量同步公众号关注用户
  • 单个用户信息同步
  • 微信服务配置回调API
  • 关注及取消关注时同步用户信息
  • 发送微信模板消息
  • 查询用户是否关注接口

小程序端:

  • 调用接口判断用户是否已关注
  • 未关注时跳转webview打开公众号中关注引导文章

公众号客户端

  • api/get_user_info/_api.apy 依赖 request.py 封装具体请求参数,解析返回结果
  • client.py client 实例,注入配置参数,依赖 api/* 实现各公众号 API,缓存 access_token 在api间重用,进行异常处理,记录日志
  • request.py 封装 aiohttp 客户端请求。
# client.py
class WxClient(object):
    '''
    基于 aiohttp 的微信公众号异步客户端
    使用示例:
        wx_client = client_instance(wx_appid, wx_secret)
        loop = wx_client.loop
        ret = loop.run_until_complete(self.async_get_user_info(self.openid, wx_client))
    '''

    def __init__(self, app_id, secret):
        self._app_id = app_id
        self._secret = secret
        # access_token 缓存
        self._token_store = TokenStore(secret) 
        # aiohttp 共用异步 http client + 事件循环
        self._session, self._loop = init_session() 
    
    @handle_exception(bool)
    async def get_user_info(self, openid: str) -> get_user_info.WxUserInfo:
        token = await self.latest_token()
        return await get_user_info.request(self._session, token, openid)

def client_instance(app_id, secret):
	'''公众号客户端实例'''
	global client
	if not client:
	    client = WxClient(app_id, secret)
	return client
# request.py 封装 aiohttp 客户端请求, 初始化 session 、event_loop
def create_session():
    conn = aiohttp.TCPConnector(ssl=False)
    return aiohttp.ClientSession(connector=conn)

def init_session():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    session = create_session()
    return session, loop

def check_response_error(data, error_code=0, error_msg_key='errmsg'):
    if code := int(data.get('errcode', 0)) != error_code:
        raise ServerError(code, data[error_msg_key])

async def get_response(session, url, params=None, timeout=10, response_callback=None):
    async with async_timeout.timeout(timeout):
        async with session.get(url, params=params) as response:
            if response_callback:
                return await response_callback(response)
            else:
                result = await response.json()
                check_response_error(result)
                return result

知识点:通过 odoo.tools.config['data_dir'] 获取 odoo 数据存储目录,以存储自定义接口日志。

公众号配置

在这里插入图片描述- 公众号配置管理模型

class WxConfig(models.Model):
    _name = 'wx.config'
    _description = u'公众号配置'
    
    name = fields.Char('名称')
    wx_appid = fields.Char('AppId')
    wx_secret = fields.Char('AppSecret')
    wx_url = fields.Char('URL', readonly=True, compute='_compute_wx_url', help='复制到公众号官方后台')
    wx_token = fields.Char('Token', default=_generate_token)
    wx_aeskey = fields.Char('EncodingAESKey', default='')
    reply = fields.Char(string='关注回复', default='欢迎关注!')
...

知识点:限制模型仅单条记录、展示form视图。

  1. 通过 data.xml 创建记录
<odoo>
    <data noupdate="1">
        <record id="wx_config_data_1" model="wx.config">
            <field name="name">公众号配置</field>
            <field name="wx_appid">xxxxxxx</field>
        </record>
    </data>
</odoo>
  1. 视图中 views.xml 创建窗口动作,view_mode 为 form
    <record id="list_wx_config_action" model="ir.actions.act_window">
        <field name="name">公众号设置</field>
        <field name="res_model">wx.config</field>
        <field name="view_mode">form</field>
        <field name="res_id" ref="wx_config_data_1"/>
    </record>
  1. 将模型权限设置为1100,仅读取修改权限
  2. 模型中添加模型方法读取该唯一记录:
    @api.model
    def get_config(self):
        return self.env.ref('odooer_wechat.wx_config_data_1')

批量用户同步

  1. 自定义列表视图,添加同步用户按钮

在这里插入图片描述
定义组件模板,添加同步按钮:

<xpath expr="//div[hasclass('o_list_buttons')]" position="inside">
    <button type="button" class="btn btn-secondary o_button_download_import_tmpl"
        t-on-click.stop.prevent="onClickSyncBtn">
        同步用户
    </button>
</xpath>

组件js, 定义按钮处理逻辑及组件名称 sync_wx_user_tree:

export class SyncUserController extends ListController {
    setup() {
        super.setup();
        this.orm = useService('orm');
    }
    async onClickSyncBtn() {
        const action = await this.orm.call(
            "wx.user",
            "action_sync_users"
        );
        window.location.reload();
        // this.actionService.doAction(action)
    }
}

export const SyncUserListView = {
    ...listView,
    Controller: SyncUserController,
    buttonTemplate: "SyncUser.ListView.Buttons",
};

registry.category("views").add("sync_wx_user_tree", SyncUserListView);

__manifest__.py 注册自定义组件源码:.js .xml .css

'assets': {
    'web.assets_backend': [
        'odooer_wechat/static/src/components/**/*',
    ]
},

通过 js_class="sync_wx_user_tree" 指定使用列表自定义组件。

<record id="view_wx_user_list" model="ir.ui.view">
     <field name="name">wx.user.list</field>
     <field name="model">wx.user</field>
     <field name="arch" type="xml">
          <tree js_class="sync_wx_user_tree">
     .......
  1. 向前端页面推送消息
    在这里插入图片描述
	 self.env['bus.bus']._sendone(self.env.user.partner_id, 'simple_notification', {
	     'title': '同步关注用户信息',
	     'message': '开始同步公众号关注者信息',
	     'warning': True
	 })
  1. 在线程中处理长时间任务防止界面阻塞等待
	thread = threading.Thread(target=_task)
	thread.start() # 启动线程执行任务
	def _task():
		with self.env.registry.cursor() as cr: # 保持使用该游标创建新的环境变量
		    env = api.Environment(cr, uid, {})

事件处理

https://**/wx_handler 复制到微信公众号后台服务配置URL,并启用配置。在 controller 中处理接收到的各种事件及消息。

    @http.route('/wx_handler', type='http', auth='none', methods=['GET', 'POST'], csrf=False)
    def handle(self, **kwargs):
        # 其他...
        ret = ''
        if msg.type in ['text', 'image', 'voice', 'video', 'location', 'link']:
            ret = input_handle(request, msg)
        elif msg.type == 'event':
            if msg.event == 'subscribe':
                ret = subscribe(request, msg)
            elif msg.event == 'unsubscribe':
                ret = unsubscribe(request, msg)

模块源码

未严格测试,请勿在生产环境直接使用

点击查看 https://apps.odoo.com/apps/modules/16.0/odooer_wechat/

其他参考

  • Odoo13 公众号模块:https://apps.odoo.com/apps/modules/13.0/oejia_wx2/
  • aiohttp 官网: https://docs.aiohttp.org/en/stable/client.html
  • asyncio官网: https://docs.python.org/3/library/asyncio.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/33419.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

pdf文档多页内插入统一图片

常用来添加公司logo、签名、印章等等 概括来说就是插入同一个图片&#xff0c;然后复制在每一页&#xff08;自动&#xff09; 用的是福昕pdf阅读器 首先打开pdf&#xff1a; 点击图像标注功能&#xff1a; 在弹出窗口中选择浏览&#xff0c;点击需要插入的图片&#xff08…

在个人电脑上部署ChatGLM2-6B中文对话大模型

简介 ChatGLM2-6B 是清华大学开源的一款支持中英双语的对话语言模型。经过了 1.4T 中英标识符的预训练与人类偏好对齐训练&#xff0c;具有62 亿参数的 ChatGLM2-6B 已经能生成相当符合人类偏好的回答。结合模型量化技术&#xff0c;用户可以在消费级的显卡上进行本地部署&…

计算机网络—网络层

文章目录 网络层服务虚电路网络数据报网络 IPv4IP数据报IP数据报分片 IP编址&#xff08;IPv4&#xff09;有类IP地址IP子网划分子网掩码 无类IP地址&#xff08;CIDR&#xff09;DHCPNATICMP协议 路由算法链路状态路由算法距离向量路由算法不同子网之间的路由算法学习RIP协议O…

【深度学习】3-3 神经网络的学习- 导数梯度

导数 导数就是表示某个瞬间的变化量&#xff0c;式子如下&#xff1a; 式子的左边&#xff0c;表示f(x)关于x的导数&#xff0c;即f(x)相对于x的变化程度。式子表示的导数的含义是&#xff0c;x的“微小变化”将导致函数f(x)的值在多大程度上发生变化。其中&#xff0c;表示…

了解一下EPC模式和它的优势

目录 什么是EPCEPC的优势有哪些&#xff1f;BT、BOT、EPC分别是什么模式&#xff1f;总结 什么是EPC EPC是Engineering&#xff08;工程&#xff09;&#xff1a;代表设计、采购和施工总承包。Procurement&#xff08;采购&#xff09;&#xff1a;代表采购和物资管理。Constru…

各牌浏览器设置地址栏显示完整URL

有时候&#xff0c;我们在浏览器的地址栏输入URL后&#xff0c;需要查看完整的URL路径&#xff0c;比如想看到是http协议还是https协议。 目前大多数浏览器都直接将协议头隐藏&#xff0c;需要复制出地址&#xff0c;或者点击地址栏才能看到&#xff0c;比较麻烦。 浏览器支持通…

Docker 部署 jar 项目

文章目录 1、上传jar包2、新建 Dockerfile 文件3、新建 deploy.sh 脚本&#xff08;创建并运行&#xff09;4、新建 upgrade.sh 脚本&#xff08;更新&#xff09; 1、上传jar包 2、新建 Dockerfile 文件 添加jar包及修改端口 # 基础镜像 FROM java:8 # 添加jar包 ADD servic…

【STM32】基于stm32的阿里云智能家居

摘 要 智能家居是一种通过物联网将家里的各种电器设备连接在一起&#xff0c;并由中心控制器统一管理的信息系统。系统的核心是各类家居信息的采集与处理。阿里云能够提供云端的数据存储和分析功能&#xff0c;可以作为智能家居中心控制器的重要平台。 本文主要研究了基于阿里云…

WPF 控件设置透明度的方法

方法一&#xff1a;通过 Opacity 属性设置背景色透明度。范围从0-1&#xff0c;0表示完全透明&#xff0c;看不见。 通过 Opacity 属性去改变控件透明度 会影响子控件的透明度&#xff0c;是因为Opacity属性是在UIElement 类(以及Brush基类)中定义&#xff0c;所有元素都具有该…

Unity VR 开发教程:Meta Quest 一体机开发 (二)混合现实 MR 透视 Passthrough 环境配置

文章目录 &#x1f4d5;教程说明&#x1f4d5;配置透视的串流调试功能&#x1f4d5;第一步&#xff1a;设置 OVRManager&#x1f4d5;第二步&#xff1a;添加 OVRPassthroughLayer 脚本&#x1f4d5;第三步&#xff1a;在场景中添加虚拟物体&#x1f4d5;第四步&#xff1a;删除…

系统架构设计师 5:软件工程

一、软件工程 1 软件过程模型 软件要经历从需求分析、软件设计、软件开发、运行维护&#xff0c;直至被淘汰这样的全过程&#xff0c;这个全过程称为软件的生命周期。 为了使软件生命周期中的各项任务能够有序地按照规程进行&#xff0c;需要一定的工作模型对各项任务给予规…

JavaScript进阶----《getter 和 setter 是什么》

前言&#xff1a; 这两个属性在学习前端的时候看到过&#xff0c;但是由于项目中没有用到过&#xff0c;所以一直没有细致的了解。今天 review 同事代码的时候&#xff0c;遇到了这个写法&#xff0c;看了半天也不知道如何处理。再不学习真的以后连别人的代码都不知道什么意思了…

spring Cloud使用Skywalking搭建笔记

skywalking支持dubbo&#xff0c;SpringCloud&#xff0c;SpringBoot集成&#xff0c;代码无侵入&#xff0c;通信方式采用GRPC&#xff0c;性能较好&#xff0c;实现方式是java探针&#xff0c;支持告警&#xff0c;支持JVM监控&#xff0c;支持全局调用统计等等&#xff0c;功…

uniapp 中 引入vant组件 和 vant 报错Unclosed bracket 的问题解决

在uniapp 中引入vant组件&#xff0c;遇到一个报错&#xff0c;所以在此记录一下完整过程 一、引入vant组件 方式一&#xff1a;前往 GitHub官网 Vant 下载压缩文件&#xff0c;获取下载中的dist 文件 方式二&#xff1a;通过npm install 方式引入 npm i vant/weapp -S --pr…

Android Studio 找不到 uploadArchives 入口

在4.2之前版本的 Android Studio 中想要module 打包arr&#xff0c;上传Maven 我们只需要 在对应module的build.gradle文件顶部添加 apply plugin: maven然后每一次修改记得要修改版本号&#xff0c;相同版本号提交失败&#xff0c;是不会覆盖的 defaultConfig {......versi…

2022年12月份青少年软件编程Python等级考试试卷六级真题(含答案)

一、单选题(共25题&#xff0c;共50分) 1.数据文件“abc.txt”中包含若干个英文单词&#xff0c;如图所示&#xff1a; 读取文件“abc.txt”中数据的Python程序段如下&#xff1a; file abc.txt word_b [] for word in open(file):if word[0:1] a and len(word)>4:wo…

工具系列之wireshark使用说明

简介 工具下载&#xff1a; https://www.wireshark.org/官方FAQ: https://www.wireshark.org/faq.html 过滤器设置 通常情况下&#xff0c;将.pcap 数据拖拽至 wireshark中即可打开。通过&#xff1a; 导航栏–》分析 --> 显示过滤器 即可找到对应的筛选器&#xff0c;筛…

优维低代码实践:数据加工/转化详解

优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。 优维…

自然语言处理(概念)

1、 RNN模型简介 1.2传统RNN模型 2、LSTM模型 3、GRU模型 5、注意力机制 6、人名分类器 7 、BERT 8、Transformer 的结构是什么样子的&#xff1f; 各个子模块有什么作用&#xff1f; 8.1 Encoder模块 8.2 Decoder模块 8.3 Transformer 结构中的Decoder端具体输入是什么&#…

期末复习【网络安全】

期末复习【网络安全】 前言推荐期末复习重点第1章 引言1.1 计算机安全概念 21.2 OSI安全体系结构 61.3 安全攻击 71.3.1 被动攻击1.3.2 主动攻击 第2章 对称加密和消息机密性2.1 对称加密原理 232.1.3 Feistel密码结构 25 2.2 对称分组加密算法 272.2.1 数据加密标准2.2.2 三重…