企业微信消息回调怎么对接?重复推送和已读别搞混
适合人群:正在做企微客服、SCRM、聚合会话的后端同学 说明:下文按「调用 + 回调」双通道思路整理,字段名以你实际对接文档为准。
前言
很多人联调时卡在同一类问题上:
回调能收到,但消息重复入库
已读、撤回被当成新消息
文本还行,图片/文件处理到一半就乱了
本文把 消息回调的分流和去重 说清楚,方便你一次搭稳。
- 先认信封,再看内容
回调入口通常是一个公网 HTTP 接口。常见结构类似:
{"uuid":"实例标识","type":"事件大类","json":{"msgtype":"文本或其他","referid":0}}建议处理顺序固定成三步:
校验 uuid,确认是哪个在线账号
按 type 做大类路由(登录 / 联系人 / 群 / 消息)
消息类再按 msgtype 细分,并做幂等
- type 大类:别用一个巨大 if
async functiondispatch(uuid,type,json){if(isLoginEvent(type))returnhandleLogin(uuid,json)if(isContactEvent(type))returnhandleContact(uuid,json)if(isRoomEvent(type))returnhandleRoom(uuid,json)if(isMessageEvent(type))returnhandleMessage(uuid,json)returnsaveUnknown(uuid,type,json)// 先落日志,别直接 500}原则:
登录态、联系人、群、消息 分处理器
未知 type 先存原始报文,避免对方重试把你打挂
3. msgtype:按类型处理,不要一刀切入库
类型 建议
文本
直接进会话表 / 推坐席
图片/文件
先存元数据,异步下载
语音/视频
注意时长与转码任务
撤回/系统提醒
更新状态,不当新会话
文件类还要区分来源(外部文件 vs 企微内部/CDN),下载接口可能不同,别写死成一套。
- referid:已读不是新消息
联调里常见约定(以文档为准):
referid = 0:多为原消息
referid ≠ 0:多为衍生事件(例如已读相关)
错误写法:
// 危险:每条回调都 insert
db.insertMessage(json)
更稳妥:
if(Number(json.referid||0)===0){awaitupsertOriginalMessage(json)}else{awaitapplyDerivedEvent(json)// 已读/状态更新}- 幂等:用唯一键挡住重复推送
至少选一个稳定键:
uuid + msgid
或 uuid + type + 关键去重字段
推荐两层存储:
原始事件表:可重复,保真,便于重放
业务消息表:唯一索引,保证逻辑上不重复
回调入口尽量快速返回成功,再异步处理,降低对方超时重推概率。
最小可运行骨架
/callback
├─ 快速 ACK
├─ 写 events_raw
├─ 按 type 路由
└─ 消息类:msgtype + referid + 幂等
本地可用内网穿透验证公网可达;上线后重点盯:回调失败率、重复率、处理耗时。和双通道怎么配合
回调只解决「收事件」。完整业务还要:
主动调用:发消息、建群、客户相关动作
回调下发:消息与状态回流
两边都用同一个实例标识(常见为 uuid),日志里同时打调用与推送,排查会轻松很多。
模块摘要与场景说明见项目门户;完整接口与示例可在体验环境查阅:
体验环境:https://www.wecomkit.cn/
小结
消息回调想稳,抓住三件事:
type 分流
msgtype 分类处理
referid + 幂等
先把这三层搭对,再扩媒体类型和业务规则,会少走很多回头路。