1. 引言:为什么需要企业微信二次开发?
企业微信作为腾讯推出的企业级办公平台,已深度融入众多组织的日常运营。然而,标准功能往往难以完全匹配企业独特的业务流程、数据整合与自动化需求。二次开发便成为连接企业微信标准化能力与个性化业务场景的关键桥梁。

通过二次开发,企业能够:
- 打通信息孤岛:将企业微信与内部的 ERP、CRM、OA 等系统无缝对接。
- 自动化业务流程:例如自动创建审批单、同步客户信息、触发任务提醒。
- 定制化功能体验:构建专属的应用页面、机器人、小程序,提升员工与客户的使用效率。
- 强化数据价值:汇聚散落在聊天、审批、汇报中的数据,形成可分析的业务洞察。
本文将系统介绍企业微信二次开发的核心概念、技术选型、实战步骤与最佳实践,助你快速构建可靠的企业级集成应用。
2. 开发前准备:账号、权限与工具
2.1 获取企业微信管理后台权限
- 登录企业微信管理后台。
- 确保拥有"开发者权限"或超级管理员身份。
- 记录以下关键信息(后续开发必备):
- CorpID:企业唯一标识,在"我的企业" > "企业信息"中查看。
- Secret:每个应用独立的密钥,用于获取访问令牌。务必妥善保管,避免泄露。
2.2 创建自建应用
- 进入"应用管理" > "自建",点击"创建应用"。
- 填写应用名称、Logo、描述等信息,并指定可见范围(部门或成员)。
- 创建成功后,记录 AgentId 和 Secret。
2.3 开发环境与工具准备
- 后端语言:推荐使用 Java(Spring Boot)、Python(Flask/Django)、Node.js 等,企业微信官方提供了多语言 SDK。
- HTTP 工具:Postman 或 curl,用于调试 API。
- 内网穿透工具(开发调试必备) :由于企业微信回调地址要求公网可访问,开发阶段可使用 ngrok 、localtunnel 或 钉钉内网穿透 将本地服务暴露到公网。
- 代码管理:Git。
3. 核心 API 与能力概览
企业微信开放了丰富的 API,主要分为以下几类:
| API 类别 | 主要能力 | 典型应用场景 |
|---|---|---|
| 身份验证 | 获取访问令牌 (access_token) | 所有 API 调用的前提 |
| 消息推送 | 发送文本、图文、卡片、文件等消息到会话 | 通知、告警、流程提醒 |
| 通讯录管理 | 成员、部门、标签的增删改查 | 同步组织架构,自动化权限分配 |
| 应用管理 | 菜单配置、工作台展示、JSSDK 使用 | 定制应用界面,网页授权登录 |
| 客户联系 | 外部联系人管理、群聊管理、客户标签 | SCRM(社交客户关系管理) |
| 素材与文件 | 上传临时/永久素材,下载文件 | 发送图片、视频、文件消息 |
| OA 数据 | 审批、打卡、汇报等数据的获取与推送 | 集成内部审批流,数据分析 |
关键概念:access_token
- 调用绝大多数 API 都需要携带
access_token参数。 - 通过
GET https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET获取。 - 重要 :
access_token有效期为 2 小时,且调用频率有限制。务必在服务端缓存并定时刷新,避免重复获取。
4. 实战一:发送应用消息(告警机器人)
让我们实现一个最常见的场景:通过企业微信应用向指定成员或部门发送文本告警消息。
4.1 获取 access_token
首先,实现一个获取并缓存 token 的服务。
java
// Spring Boot 示例:TokenService.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.TimeUnit;
@Service
public class TokenService {
@Value("${wechat-work.corpId}")
private String corpId;
@Value("${wechat-work.appSecret}")
private String appSecret;
private String accessToken;
private long tokenExpireTime;
public String getAccessToken() {
if (accessToken == null || System.currentTimeMillis() > tokenExpireTime) {
refreshToken();
}
return accessToken;
}
private void refreshToken() {
String url = String.format("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s",
corpId, appSecret);
RestTemplate restTemplate = new RestTemplate();
Map<String, Object> response = restTemplate.getForObject(url, Map.class);
if (response != null && "0".equals(response.get("errcode").toString())) {
this.accessToken = (String) response.get("access_token");
// 提前 5 分钟过期,避免临界点调用失败
this.tokenExpireTime = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(7200 - 300);
} else {
throw new RuntimeException("获取企业微信 access_token 失败: " + response);
}
}
}
4.2 发送文本消息
调用发送消息接口,支持按用户 ID、部门 ID 或标签 ID 发送。
java
// MessageService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.*;
@Service
public class MessageService {
@Autowired
private TokenService tokenService;
public void sendTextMessage(String content, List<String> toUser, List<String> toParty, List<String> toTag) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + tokenService.getAccessToken();
Map<String, Object> message = new HashMap<>();
message.put("touser", String.join("|", toUser)); // 如:"zhangsan|lisi"
message.put("toparty", String.join("|", toParty));
message.put("totag", String.join("|", toTag));
message.put("msgtype", "text");
message.put("agentid", 1000002); // 替换为你的应用 AgentId
Map<String, String> textContent = new HashMap<>();
textContent.put("content", content);
message.put("text", textContent);
// 发送请求
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Map<String, Object>> request = new HttpEntity<>(message, headers);
Map<String, Object> response = restTemplate.postForObject(url, request, Map.class);
if (!"0".equals(response.get("errcode").toString())) {
throw new RuntimeException("发送消息失败: " + response);
}
}
}
调用示例:服务器 CPU 使用率超过 90% 时触发告警。
java
messageService.sendTextMessage(
"【系统告警】服务器 192.168.1.10 CPU 使用率已达 95%,请及时处理!",
Arrays.asList("zhangsan", "lisi"), // 接收人
Collections.emptyList(),
Collections.emptyList()
);
5. 实战二:接收与处理用户消息(回调配置)
许多交互场景需要接收用户发送给应用的消息,例如关键词回复、指令处理。这需要配置接收消息服务器。
5.1 服务器配置流程
- 准备公网可访问的 URL :如
https://your-domain.com/wechat/callback。 - 在管理后台验证 URL :
- 进入应用详情页 > "接收消息" > "设置 API 接收"。
- 填写 URL、Token(自定义,用于生成签名)、EncodingAESKey(自动生成或手动填写)。
- 企业微信会向该 URL 发送一个 GET 请求进行验证,你需要按照官方文档正确响应
echostr参数。
- 验证通过后,用户向应用发送的消息、点击菜单等事件都会以 POST 请求推送到该 URL。
5.2 消息解密与处理示例(Python Flask)
企业微信推送的消息是加密的,需要使用官方提供的加解密库(如 wechatpy)进行处理。
python
# app.py
from flask import Flask, request, jsonify
from wechatpy.enterprise import parse_message, create_reply
from wechatpy.enterprise.crypto import WeChatCrypto
from wechatpy.exceptions import InvalidSignatureException
app = Flask(__name__)
TOKEN = 'your_token' # 后台设置的 Token
AES_KEY = 'your_aes_key' # 后台设置的 EncodingAESKey
CORP_ID = 'your_corp_id' # 企业 CorpID
crypto = WeChatCrypto(TOKEN, AES_KEY, CORP_ID)
@app.route('/wechat/callback', methods=['GET', 'POST'])
def wechat_callback():
if request.method == 'GET':
# 验证 URL
signature = request.args.get('msg_signature', '')
timestamp = request.args.get('timestamp', '')
nonce = request.args.get('nonce', '')
echostr = request.args.get('echostr', '')
try:
echostr = crypto.check_signature(signature, timestamp, nonce, echostr)
return echostr
except InvalidSignatureException:
return 'Invalid Signature', 403
elif request.method == 'POST':
# 处理消息
signature = request.args.get('msg_signature', '')
timestamp = request.args.get('timestamp', '')
nonce = request.args.get('nonce', '')
data = request.data
try:
# 解密消息
decrypted_data = crypto.decrypt_message(data, signature, timestamp, nonce)
msg = parse_message(decrypted_data)
# 根据消息类型处理
if msg.type == 'text':
# 示例:关键词回复
if msg.content == '帮助':
reply = create_reply('请输入:\n1. 查询工单\n2. 联系客服', msg)
else:
reply = create_reply(f'您说:{msg.content}', msg)
# 加密回复
encrypted_reply = crypto.encrypt_message(reply.render(), nonce, timestamp)
return encrypted_reply
else:
return 'success' # 其他类型消息暂不处理,但需返回 success 避免重试
except Exception as e:
app.logger.error(f'处理消息失败: {e}')
return 'error', 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
6. 实战三:集成网页授权登录(JSSDK)
在企业微信内打开的自建应用网页,可以获取员工身份信息,实现免登。
6.1 配置可信域名
- 进入应用详情页 > "网页授权及 JS-SDK" > "设置可信域名"。
- 将你的应用前端页面部署的域名(如
h5.your-company.com)添加进去。 - 下载校验文件,放置到该域名的根目录下,确保可通过
https://h5.your-company.com/校验文件名.txt访问。
6.2 前端获取用户身份代码示例
html
<!DOCTYPE html>
<html>
<head>
<script src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
</head>
<body>
<div id="user-info">正在获取用户信息...</div>
<script>
// 1. 通过后端接口获取 config 注入所需的参数(需后端调用企业微信 API 生成)
fetch('/api/wechat/js-config?url=' + encodeURIComponent(window.location.href.split('#')[0]))
.then(res => res.json())
.then(configData => {
// 2. 配置 JSSDK
wx.config({
beta: true, // 必须这么写,否则在 iOS 上可能无法正常使用
debug: false, // 开启调试模式,调用的所有 api 的返回值会在客户端 alert 出来
appId: configData.corpId, // 企业微信的 corpID
timestamp: configData.timestamp, // 生成签名的时间戳
nonceStr: configData.nonceStr, // 生成签名的随机串
signature: configData.signature, // 签名
jsApiList: ['agentConfig'] // 需要使用的 JS 接口列表
});
wx.ready(function() {
// 3. 通过 agentConfig 注入应用的权限
wx.agentConfig({
corpid: configData.corpId,
agentid: configData.agentId,
timestamp: configData.timestamp,
nonceStr: configData.nonceStr,
signature: configData.signature,
jsApiList: ['getContext'], // 需要使用的 JS 接口
success: function(res) {
// 4. 获取用户上下文(内部用户)
wx.invoke('getContext', {}, function(ctxRes) {
if(ctxRes.err_msg == "getContext:ok") {
const userId = ctxRes.userId; // 当前用户的 userid
document.getElementById('user-info').innerHTML = `欢迎你,员工 ${userId}`;
// 可将 userId 发送到后端,获取更详细的员工信息
}
});
},
fail: function(err) {
console.error('agentConfig fail:', err);
}
});
});
});
</script>
</body>
</html>
7. 常见问题与排错指南
7.1 API 调用返回错误码
- 40001 :
access_token无效或过期 → 检查 Secret 是否正确,并确保 token 已缓存刷新。 - 40014 :不合法的
access_token→ 同上,也可能是网络问题导致 token 不完整。 - 41001 :缺少
access_token参数 → 检查请求 URL 或参数拼接。 - 60020 :非法的部门列表 → 检查
toparty参数中的部门 ID 是否存在。 - 81013:需要可信域名 → 调用 JSSDK 或网页授权前,未配置可信域名或配置有误。
7.2 回调 URL 验证失败
- 检查服务器代码是否正确响应了
GET请求并返回了echostr。 - 确认 Token、EncodingAESKey 与后台设置完全一致(包括大小写)。
- 使用企业微信官方提供的加密解密调试工具进行本地验证。
7.3 消息发送成功但用户未收到
- 检查应用"可见范围"是否包含了目标用户。
- 确认用户是否已在企业微信中"启用"了该应用(在手机端工作台找到应用并进入一次)。
- 检查消息内容是否触发了企业微信的安全过滤规则(如包含敏感词、链接等)。
8. 进阶与最佳实践
8.1 异步与重试机制
- 消息发送:对于非实时性要求极高的消息(如报表推送),建议采用异步队列(如 RabbitMQ、Kafka)发送,避免阻塞主业务。
- 回调处理 :处理用户消息或事件回调时,应快速响应
success,然后将具体业务逻辑放入后台任务执行,避免超时(企业微信要求 5 秒内响应)。
8.2 安全与权限管控
- Secret 管理:切勿将 Secret 硬编码在客户端或公开仓库。使用配置中心或环境变量管理。
- IP 白名单:在管理后台配置"企业可信 IP",限制只有公司出口 IP 才能调用关键 API。
- 权限最小化:为不同应用分配仅满足其功能所需的 API 权限范围。
8.3 监控与日志
- 记录所有 API 调用的请求与响应,便于排查问题。
- 监控
access_token的获取频率,异常增高可能意味着泄露或程序 bug。 - 对消息发送失败、回调处理异常等关键节点设置告警。
9. 总结
企业微信二次开发是一个将标准化平台能力与个性化业务需求深度融合的过程。从简单的消息推送到复杂的业务流程集成,其核心在于对 API 的熟练运用、对回调机制的理解以及对安全规范的遵守。
启动你的第一个项目:建议从"发送告警消息"开始,逐步扩展到"接收用户指令"和"网页授权登录",最终构建出能够显著提升组织效率的智能办公应用。
资源推荐: