钉钉审批与低代码双向同步:回调事件与状态机设计
结论先行:审批双向同步的本质不是"打通接口",而是让钉钉侧的审批状态机与低代码侧的业务单据状态机保持最终一致。落地只盯 3 件事:订阅审批实例回调、用业务单号加事件类型做幂等键、用一张状态映射表把平台状态翻译成业务状态。回调处理应在 5 秒内返回,重活丢给队列异步做。
审批入口同时开在钉钉与低代码系统,会带来一个隐蔽的问题:两边都认为自己知道最新状态。财务在钉钉点了同意,采购专员又在低代码里改了金额,以谁为准?本文从状态所有权、回调订阅、状态机映射、幂等与对账四个层面给出答案。
一、先定主数据:谁的状态说了算
第一件事不是写代码,而是明确状态所有权。常见有三种模式:
- 钉钉为主:审批在钉钉发起,低代码系统只做只读同步与下游触发,冲突几乎不会发生;
- 低代码为主:单据在业务系统内创建,再调用钉钉审批实例接口发起,钉钉只承担审批动作,业务字段的回写以低代码为准;
- 双入口:两边都能发起,单据上必须增加
source(来源)与version(版本号),冲突时比较版本号与更新时间,后写入者生效,并记录一条冲突供人工复核。
双入口最容易踩的坑,是同一张业务单在两边各生成一条主记录。正确做法是用业务单号做关联键,钉钉实例 ID 只作为外部引用,不承担主键职责。
二、回调事件订阅:先解密,再验签,最后才处理
钉钉开放平台的事件订阅有两条通道:HTTP 回调与 Stream 模式(长连接)。前者需要公网可达的回调地址并配置加密密钥;后者适合无公网入口的自托管部署。两种通道的报文都加密,处理顺序必须是先解密、再校验来源、最后落业务,顺序颠倒会放过伪造事件。
审批实例事件通常覆盖发起、完成、终止等节点,回调携带实例 ID、审批结果与操作人信息。事件名、字段与加密参数的具体命名,以钉钉开放平台官方文档为准;写死字段名是常见的返工原因。
工程上有三个硬约束:
- 回调接口必须快速返回成功响应,业务处理异步化,否则平台会判定超时并重推;
- 回调不含完整表单数据,需凭实例 ID 再调详情接口补全;
- 回调地址变更、证书过期、302 跳转都会导致解密失败,需要单独记一类日志。
三、状态机映射表:只允许单向推进
两个系统的状态集合并不对等,把钉钉状态直接存进业务表会出事。推荐单独维护一张映射表:
| 钉钉侧状态 | 业务单据状态 | 允许的后续动作 |
|---|---|---|
| 审批中 | 处理中 | 表单锁定为只读、占用预算额度 |
| 审批完成且同意 | 已通过 | 生成下游单据、释放占用、通知发起人 |
| 审批完成但拒绝 | 已驳回 | 释放占用、记录驳回意见 |
| 已终止或发起人撤销 | 已撤回 | 作废下游单据、回滚库存 |
钉钉审批实例一般以 PROCESSING、FINISHED、TERMINATED 这类英文枚举返回状态,审批结果再区分同意与拒绝,具体枚举值与字段名以官方文档为准。
关键规则只有一条:状态只前进、不回退。把允许的迁移写成白名单,"处理中"只能到"已通过/已驳回/已撤回",其余一律拒绝并告警。这同时解决了乱序——旧事件晚到也改不回去。
四、幂等与乱序:两道防线
回调重复推送是常态。重试、网络抖动、人工补发都会让同一事件到达两次,防线分两层:
- 幂等表:以"业务单号 + 事件类型 + 审批节点标识"建联合唯一索引,插入成功才允许继续处理,重复事件在插入阶段就被拦下;
- 业务侧二次校验:生成类动作(如生成采购单、扣减库存)额外增加"是否已生成"标记字段,避免幂等表被误清理后重复执行。
乱序的处理更依赖状态机本身。事件时间戳不保证单调,不要用它判断"最新状态",让状态机只接受前进方向的迁移即可。
回调会丢是必须承认的现实。行业通行做法是加一条对账任务:每 30 分钟扫描"处理中且超过 24 小时未更新"的单据,主动查审批详情比对。它同时承担补偿职责,是双向同步里最不能省的一块。
五、可观测性:三类日志与三个指标
排查同步问题时最耗时的是"不知道断在哪一环"。固定输出三类日志:接收日志(原始报文与请求 ID)、解密验签日志、处理结果日志(状态迁移前后值)。核心指标只留三个:回调到达率、处理时延、对账差异数。
人员映射也是高频故障点。回调里的操作人是钉钉 userid,需映射到内部账号,可调用 /topapi/v2/user/get 取姓名与部门,具体字段以官方文档为准。映射失败时降级为"未知操作人"并告警,不要丢弃事件——丢一条审批结果就要人工补单。
FAQs
Q1:回调地址必须是公网域名吗?
A:不是。HTTP 回调需要公网可达且可信的地址;没有公网入口时可改用 Stream 模式长连接订阅,内网自托管更合适。
Q2:回调重复推送会不会重复生成下游单据?
A:概率很低但必须防。用业务单号与事件类型建唯一索引的幂等表,重复事件在插入阶段被拦截;生成类动作再加一次"是否已生成"校验。
Q3:钉钉侧撤销审批后,低代码里已生成的单据怎么处理?
A:把"已撤回"定义为终态,触发补偿动作:作废或红冲下游单据、回滚库存与预算占用,并写入审计日志。绝不做物理删除,历史链路要可追溯,否则财务对账时会出现无源之差。
延伸阅读
- 审批流程设计——多级审批、加签与代理的通用设计方法
- 多级审批流设计实战:条件路由、加签、代理与撤回——审批链路本身的配置细节
- 低代码工作流怎么推钉钉群机器人?Webhook 签名与消息卡片——审批结果如何推送到群里,形成消息闭环
