企业微信自建应用开发实战:OAuth2.0授权 + 通讯录 + 消息推送
前言企业在使用企业微信的过程中经常会遇到标准功能无法满足需求的情况——比如要把企业微信和内部ERP系统对接或者基于企微的消息能力做业务流程自动化。这个时候就需要通过企业微信的Web API来开发自建应用。这篇文章讲一个完整的实战场景从注册企业微信自建应用开始到拿到授权token到读取通讯录数据最后发送一条消息推送给指定员工。全流程可跑通有代码有注释适合有一点开发基础的工程师参考。【配图位置】前置准备1. 开通企业微信开发者权限首先你的企业微信管理员需要在管理后台 → 应用管理中创建一个自建应用获取以下信息corpId企业ID在管理后台 → 我的企业页面可以看到agentId应用AgentId创建应用后获得corpSecret应用密钥创建应用后生成妥善保管不要泄露2. 配置应用可信域名在应用详情 → 网页授权及JS-SDK中配置可信域名。这个域名必须 ICP 备案且使用 HTTPS。3. 安装开发依赖本文使用 Python 作为开发语言主要依赖# requirements.txt requests2.28.0第一步获取Access Token企业微信的所有API调用都需要携带access_token有效期为7200秒2小时。代码实现import requests import time class WeComClient: 企业微信 API 客户端 def __init__(self, corp_id: str, corp_secret: str): self.corp_id corp_id self.corp_secret corp_secret self._token None self._token_expires_at 0 def get_token(self) - str: 获取 Access Token带缓存逻辑 企业微信 access_token 有效期为 7200 秒需要在过期前刷新 # 如果缓存的 token 还有效直接返回 if self._token and time.time() self._token_expires_at: return self._token url https://qyapi.weixin.qq.com/cgi-bin/gettoken params { corpid: self.corp_id, corpsecret: self.corp_secret } resp requests.get(url, paramsparams) data resp.json() if data.get(errcode, 0) ! 0: raise Exception(f获取 token 失败: {data}) self._token data[access_token] # 提前 5 分钟刷新留出缓冲时间 self._token_expires_at time.time() data.get(expires_in, 7200) - 300 return self._token def call_api(self, method: str, path: str, **kwargs) - dict: 统一 API 调用入口自动携带 access_token token self.get_token() url fhttps://qyapi.weixin.qq.com/cgi-bin{path} params {access_token: token} resp requests.request(method, url, paramsparams, **kwargs) result resp.json() if result.get(errcode, 0) ! 0: # 42001 token 过期需要刷新后重试 if result.get(errcode) 42001: self._token None return self.call_api(method, path, **kwargs) raise Exception(fAPI 调用失败: {result}) return result使用示例# 初始化客户端 client WeComClient( corp_idww1234567890abcdef, corp_secretYOUR_CORP_SECRET_HERE ) # 获取 token token client.get_token() print(fToken 获取成功: {token[:20]}...)参考文档腾讯企业微信官方文档 - 获取access_token【配图位置】第二步OAuth2.0 网页授权获取用户信息如果你的应用需要获取企业成员的身份信息比如做单点登录需要使用企业微信的OAuth2.0网页授权流程。授权流程概述用户访问你的应用页面跳转到企业微信授权页用户同意授权企业微信回调到你的服务器带上code参数服务端用code换取userid成员UserID用userid调用通讯录接口获取用户详细信息生成授权跳转链接def generate_oauth_url(self, redirect_uri: str, state: str ) - str: 生成企业微信 OAuth2.0 授权跳转链接 参数: redirect_uri: 授权成功后的回调地址需 URL 编码且域名必须在可信域名列表中 state: 随机 state 参数用于防止 CSRF 攻击 base_url https://open.weixin.qq.com/connect/oauth2/authorize params { appid: self.corp_id, redirect_uri: redirect_uri, response_type: code, scope: snsapi_privateinfo, # 可获取成员详细信息 state: state } query .join(f{k}{requests.utils.quote(v)} for k, v in params.items()) # 企业微信要求在 URL 末尾拼接 #wechat_redirect return f{base_url}?{query}#wechat_redirect用 Code 换取用户信息def get_user_info_by_code(self, code: str) - dict: 通过授权码 code 获取成员信息 文档: https://developer.work.weixin.qq.com/document/path/91080 return self.call_api(GET, /auth/getuserinfo, params{code: code}) def get_user_detail(self, userid: str) - dict: 获取成员详细信息需要 snsapi_privateinfo 授权 scope 文档: https://developer.work.weixin.qq.com/document/path/96028 return self.call_api(POST, /auth/getuserdetail, json{ user_ticket: userid # 注意这里需要先通过 getuserinfo 获取 user_ticket })Flask 完整示例from flask import Flask, request, redirect, session app Flask(__name__) app.secret_key your-secret-key-here app.route(/login) def login(): 登录入口 callback_url request.url_root callback state os.urandom(16).hex() # 生成随机 state session[oauth_state] state auth_url client.generate_oauth_url(callback_url, state) return redirect(auth_url) app.route(/callback) def callback(): 授权回调 code request.args.get(code) state request.args.get(state) # 校验 state防止 CSRF if state ! session.get(oauth_state): return State 校验失败, 400 # 用 code 获取用户信息 user_info client.get_user_info_by_code(code) session[userid] user_info.get(UserId) return f登录成功用户ID: {user_info.get(UserId)}注意使用snsapi_privateinfo授权 scope需要在管理后台配置可获取的字段姓名、手机号、邮箱、部门等。【配图位置】第三步读取通讯录数据企业微信的通讯录接口支持读取成员列表、部门列表获取成员详情等。常用接口def get_department_list(self, id: int None) - dict: 获取部门列表 文档: https://developer.work.weixin.qq.com/document/path/90208 params {} if id is not None: params[id] id return self.call_api(GET, /department/list, paramsparams) def get_user_list(self, department_id: int, fetch_child: bool True) - dict: 获取部门下成员列表 文档: https://developer.work.weixin.qq.com/document/path/90200 params { department_id: department_id, fetch_child: 1 if fetch_child else 0, simple_user_info: 0 # 0完整信息1简化信息 } return self.call_api(GET, /user/list, paramsparams) def get_user(self, userid: str) - dict: 获取成员详情 文档: https://developer.work.weixin.qq.com/document/path/90196 return self.call_api(GET, /user/get, params{userid: userid})导出企业通讯录示例def export_org_chart(): 导出企业通讯录按部门层级整理 # 1. 获取所有部门 departments client.get_department_list() dept_map {d[id]: d[name] for d in departments.get(department, [])} # 2. 遍历每个部门获取成员 all_users [] for dept_id in dept_map.keys(): users client.get_user_list(dept_id) for user in users.get(userlist, []): user[department_name] dept_map.get(user.get(department, [None])[0], 未知部门) all_users.append(user) # 3. 整理成你需要的格式如导出为 CSV/Excel for user in all_users: print(f{user[department_name]} | {user[name]} | {user[mobile]} | {user[email]}) return all_users第四步发送消息推送企业微信支持多种消息类型文本、图片、语音、视频、文件、文本卡片、markdown等。发送应用消息def send_text_message(self, userids: list, content: str, agent_id: int None) - dict: 发送文本消息给指定成员 文档: https://developer.work.weixin.qq.com/document/path/90236 payload { touser: |.join(userids), # 接收人多个用 | 分隔 msgtype: text, agentid: agent_id or self.agent_id, text: {content: content}, safe: 0 # 是否保密消息保密消息不可转发 } return self.call_api(POST, /message/send, jsonpayload) def send_markdown_message(self, userids: list, content: str, agent_id: int None) - dict: 发送 Markdown 格式消息支持加粗、颜色、换行等 Markdown 支持的格式有限详见官方文档 payload { touser: |.join(userids), msgtype: markdown, agentid: agent_id or self.agent_id, markdown: {content: content} } return self.call_api(POST, /message/send, jsonpayload)应用消息示例# 发送文本消息 result client.send_text_message( userids[zhangsan, lisi], content【系统通知】本周五下午3点召开Q2季度复盘会请相关部门负责人准时参加。, agent_id1000001 ) print(f发送结果: {result}) # 发送 Markdown 消息更美观支持格式 md_content **Q2 销售数据概览** 请各部门负责人查收 - 华东区: 完成任务率 **102%** ✅ - 华南区: 完成任务率 **98%** ⚠️ - 华北区: 完成任务率 **110%** ✅ 详情请登录 CRM 系统查看。 result client.send_markdown_message( userids[sales_manager_01], contentmd_content )参考文档腾讯企业微信官方文档 - 发送应用消息【配图位置】进阶消息回调与事件订阅企业微信支持配置消息回调 URL当成员触发特定事件如关注/取消关注应用、点击菜单、提交审批等时腾讯服务器会POST事件数据到你的服务器。接收消息回调Flask示例from wechatpy.crypto import WeChatCrypto from wechatpy.exceptions import InvalidSignatureException # 在管理后台设置应用的 EncodingAESKey 和 Token crypto WeChatCrypto( tokenYOUR_TOKEN, encoding_aes_keyYOUR_AES_KEY, corp_idYOUR_CORP_ID ) app.route(/wecom/callback, methods[POST]) def wecom_callback(): 接收企业微信事件回调 signature request.args.get(msg_signature) timestamp request.args.get(timestamp) nonce request.args.get(nonce) body request.get_data() try: # 验签并解密消息 decrypted_msg crypto.decrypt(body, signature, timestamp, nonce) # 解析 XML 消息 import xml.etree.ElementTree as ET root ET.fromstring(decrypted_msg) msg_type root.findtext(MsgType) event root.findtext(Event) if msg_type event: if event subscribe: # 用户订阅/关注应用 user_id root.findtext(FromUserName) print(f用户 {user_id} 关注了应用) elif event enter_agent: # 用户进入应用 user_id root.findtext(FromUserName) print(f用户 {user_id} 进入了应用) return success except InvalidSignatureException: return signature error, 403完整项目结构一个可复用的企业微信自建应用项目结构wecom-app/ ├── config.py # 配置corp_id, corp_secret, agent_id ├── client.py # WeComClient SDK 封装 ├── app.py # Flask Web 应用入口 ├── handlers/ │ ├── oauth.py # OAuth 授权处理 │ ├── address_list.py # 通讯录接口封装 │ └── message.py # 消息推送封装 ├── requirements.txt └── README.md常见问题排查错误码含义排查方向40014不合法的 access_tokentoken 是否正确生成是否已过期40013不合法的 corp_idcorp_id 格式是否正确40003不合法的 UserIDUserID 是否正确企业内是否包含该成员30006应用无权限管理后台是否开通了对应接口权限41001缺少 access_token调用接口时是否正确携带 token结语企业微信的自建应用能力给企业数字化集成提供了很大的灵活性。从基础的通讯录读取、消息推送到OAuth单点登录、审批回调基本覆盖了企业内部的常用集成场景。需要注意的是生产环境中的接口调用一定要做好错误处理特别是token过期重试、频率限制处理以及敏感信息corpSecret等的安全存储建议使用环境变量或密钥管理服务不要硬编码。开发过程中遇到具体问题建议直接查阅腾讯企业微信开发者文档文档质量较高大部分场景都能找到答案。上海华万通信科技有限公司专注企业数字化工具选型与集成腾讯系产品授权服务商。