兄弟们,做IT这么多年,你是不是也踩过这个坑:公司上了企业微信,审批流程都在企微里跑,但自研OA那边一堆历史数据、定制表单,两边“各玩各的”——员工提交个请假,OA不知道,HR统计考勤还得手动翻;OA里审批通过了一个报销,企微那边状态还是“审批中”,财务核对时直接晕菜。
这种“信息孤岛”的痛,懂的都懂。今天咱就聊聊怎么用Webhook这个“传令兵”,把企微审批和自研OA捏合到一起,实现双向同步。不讲大道理,拿真实场景说话。
Webhook是啥?说白了就是个“主动报信的”
先打个比方:以前你让两个系统同步数据,就像你每隔5分钟派个人去对方门口喊一嗓子:“喂,你有新审批没?”——这叫“轮询”,费工费时还容易错过。而Webhook呢,就像对方门口装了个门铃,一旦有新审批,立马按门铃通知你:“嘿,有张请假单,数据在这!”你只需要把“收门铃”的程序写好了就行。
企微的审批Webhook就是这种机制:当审批流程有状态变化(比如提交、通过、驳回、撤销),企微会主动往你配置的URL地址推送一条JSON数据。你自研OA那边写个接口,接住这条数据,解析内容,然后更新自己数据库里的审批状态,就完事了。
双向同步?其实就两个“门铃”互相按
很多人一听“双向”就头大,觉得要写一堆复杂的逻辑。其实思路很简单:各管各的入口,互相通知。
场景一:员工在企微提交请假申请 → 自动同步到OA
- 企业微信后台 → 审批应用 → 回调配置,填上你的OA接口地址(比如
http://your-oa.com/webhook/wecom-callback)。 - 当张三在企微提交“请假3天”,企微立刻把申请单号、申请人、假期类型、时间、原因等字段,包装成XML或JSON,POST到你给的地址。
- 你的OA接口收到后,解析数据,在OA里创建一条对应的请假记录,状态标记为“企微审批中”。这里注意:企微推送的消息里有一个
MsgType字段,event类型表示事件通知,里面有ApprovalInfo。
场景二:HR在OA里修改了审批结果 → 同步回企微
这个稍微多一步:企微提供了一个“审批开放接口”,你可以调用它的/cgi-bin/oa/set_approval_info接口来更新状态。举个例子:HR在OA里把张三的请假驳回了(因为年假余额不足),你的OA业务代码在更新本地状态的同时,调用这个接口,把sp_no(申请单号)和status(比如2表示已驳回,3表示已完成)传过去,企微那边就会自动更新审批详情页的显示。
注意:企微的修改接口有权限限制,需要先拿到access_token,而且只能修改“审批结果”,不能修改表单数据本身,这点要提前跟业务方讲清楚。
避坑指南:几个老司机经验
重试机制得加上:网络抖动或者服务器重启时,企微推送可能会失败。企微默认会重试3次(间隔15秒、30秒、60秒),你的接口要设计成幂等的——也就是同一个回调消息重复接收也不会造成数据重复。比如用申请单号做唯一键,先查后插。
签名验证别偷懒:企微推送时会带一个
MsgSignature参数,是用你的Token、时间戳、随机数以及消息体拼接后SHA1加密的。你一定要验这个签名,否则互联网上随便一个请求都能往你接口写数据,风险极大。回包要正确:企微规定,你收到推送后必须在5秒内返回
<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>,否则企微会认为推送失败。很多新手卡在这,接口处理逻辑太慢,超时了,企微反复重试造成日志炸了。自研OA的状态机要兜底:有些场景企微推送可能延迟或丢失(虽然概率低),建议加一个定时任务,每天凌晨同步一次两边所有“审批中”的单据,对账修正,这叫“最终一致性”,不用追求实时100%无缝。
来一个完整的“请假同步”例子
假设你的自研OA接口是Python写的,用Flask框架。配置企微回调URL为http://yourdomain.com/wechat/callback,Token设为myToken123。
import hashlib
import xml.etree.ElementTree as ET
from flask import Flask, request, make_response
app = Flask(__name__)
@app.route('/wechat/callback', methods=['POST'])
def wecom_callback():
# 1. 签名验证(简化,实际要对比签名)
msg_signature = request.args.get('msg_signature')
timestamp = request.args.get('timestamp')
nonce = request.args.get('nonce')
# 验签代码省略...
# 2. 解析XML
xml_data = request.data
root = ET.fromstring(xml_data)
sp_no = root.find('.//SpNo').text # 申请单号
apply_time = root.find('.//ApplyTime').text
applicant = root.find('.//ApplicantUserName').text
# 3. 根据事件类型处理
event = root.find('.//Event').text
if event == 'open_approval_change': # 审批状态变化
status = root.find('.//ApprovalInfo/Status').text
# Status: 1=审批中, 2=已通过, 3=已驳回,4=已撤销
# 更新OA数据库...
# 4. 回包
return '<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>'
而反向同步(OA→企微)的代码更简单,就是调接口:
import requests
def sync_to_wecom(sp_no, status):
# 先获取access_token(需缓存)
token = get_wecom_token()
url = f"https://qyapi.weixin.qq.com/cgi-bin/oa/set_approval_info?access_token={token}"
data = {
"sp_no": sp_no,
"status": status # 2:通过 3:驳回
}
resp = requests.post(url, json=data)
return resp.json()
总结一下
Webhook同步就像两个系统之间彼此留了个“门铃”和“门牌号”:企微审批状态变了,按一下你的门铃(推给你);你OA里改了审批结果,再按一下企微的门铃(调接口)。只要把两边接口写好,重试、安全、幂等处理好,基本就能睡个安稳觉了。
当然,每个公司OA的定制程度天差地别,有的用Java、有的用.NET,表单字段千奇百怪。如果你觉得自研太费劲,或者想看看成熟的企业级方案怎么做,更多方案可访问 itfangan.com,上面有完整的对接模板和避坑手册,直接拿来改改就能用。
老哥们,咱干技术的就是要把复杂的事情变简单。Webhook这东西,用熟了就能解决很多系统孤岛问题,不止是审批,消息推送、数据同步都能用。以后有机会再聊。