钉钉通讯录增量同步到 MySQL:单个策略的实战配置与踩坑复盘
这个策略解决什么问题(场景与价值)
某制造企业把钉钉作为全员唯一身份源,但人事、流程、报表系统还在 MySQL 自建表里。员工转岗、离职、改名每天都在发生,审批流却常因为「发起人已离职」中断。核心矛盾只有一个:钉钉通讯录变更,必须在当天落到 MySQL 的用户表里。本文聚焦的就是其中一条具体动作——「user-钉钉获取通讯录-修改」,也就是按 userid 把变更后的用户字段同步写回 MySQL。
数据流向与字段映射(源 → 中间层 → 目标)
源端是钉钉开放平台 topapi/v2/user/get,POST 调用,入参只关心一个用户维度的 userid,固定回 zh_CN;目标端是 MySQL 的 execute 接口,本质是一条预编译的 UPDATE,带 main_sql 占位符。两端之间的字段对照如下:
| 业务含义 | 钉钉侧字段(响应) | MySQL 落库字段 | 备注 |
|---|---|---|---|
| 钉钉用户主键 | userid | userid | UPDATE WHERE 条件,作为去重主键 |
| 工号 | job_number | job_number | 异动高频字段 |
| 姓名 | name | name | 同上 |
| 职位 | title | title | 允许为空 |
| 统一身份标识 | unionid | unionid | 跨系统打通的关键 |
| 所属部门列表 | dept_id_list | dept_id_list | 数组型,落 JSON 字符串 |
| 部门内是否负责人 | leader_in_dept | leader_in_dept | 审批节点判断依赖 |
| 删除标记 | del_flag | del_flag | 离职软删,靠源端字段回写 |
这条策略本身只做「按 userid 写回」,不做新增和删除分流,因此 UPDATE 的 WHERE 条件必须严格落到 userid,否则极易把整张表刷掉——这是后面会重点复盘的一条。
在轻易云上如何配置
在轻易云数据集成平台(Qeasy)上,这条策略属于典型的「源拉取 + 目标执行」型 WebAPI 同步。配置要点我按现场习惯拆成四块:
-
源端配置(钉钉):
metadata.api填topapi/v2/user/get,effect 选QUERY。请求体里userid走变量注入,由上游策略或调度器给出;language固定zh_CN;dep_strategy指向「按部门拉取通讯录」那条上游策略的 ID,作为上游依赖。id用userid,开启autoFillResponse自动铺出返回字段,避免每次新增响应都要手动建模型。 -
目标端配置(MySQL):effect 选
EXECUTE,metadata.api固定为execute。request里只放一个main_params对象作为入参占位,真正的 SQL 全部塞到otherRequest的main_sql里,采用命名占位符:userid、:name这种写法。idCheck保持开启,平台会自动校验源端id与 SQL WHERE 条件中的userid一致,防止错配。 -
调度设置:
crontab设成22,52 23 * * *,也就是每天 23:22 与 23:52 各跑一次。放在深夜是因为钉钉通讯录高频变更集中在白天工作时段,深夜落库窗口长、冲突少。 -
依赖与顺序:本策略
depends_on指向「获取部门列表」策略,先有部门,后有部门下的人员,顺序才稳。轻易云里直接用sequence: B+ 依赖配置表达即可。
实施步骤
我们把这套链路分三段推进,每段都有明确的退出标准:
阶段一:增量起点搭建。 先在轻易云里把源端的 userid 入参与上游策略的输出做变量绑定,跑通单条 userid 的拉取与回写。退出标准:任意挑 3 个真实用户,数据库里 updated_at 与「姓名/职位」字段都能正确变化。
阶段二:批量触发。 在上游「部门策略」完成后,把本策略的入参 userid 改成「由上游输出批量传入」,从而实现「先按部门拿到用户清单,再逐个 userid 回写」。这一步在轻易云里通常用「表头表体分阶段」的方式承接:上游是表头(部门),本策略是表体(部门下每个用户)。退出标准:全量 5000 人左右一次跑完,耗时在分钟级,无主键冲突。
阶段三:调度频率打磨。 从「每天一次」逐步调到 22,52 23 * * *,观察钉钉 API 限流与 MySQL 慢日志。如果出现 429,就在轻易云的策略上加重试与退避;如果 MySQL 出现行锁,就把两次执行错开 30 分钟,而不是简单去掉一次调度。
踩坑复盘
这几点是客户现场反复翻车过的:
- 典型错误一:UPDATE 没带 WHERE。 第一次部署时,有人把
main_sql写成了update hzero_platform.dingtalk_user set ...不带where userid=:userid。结果一次跑批把全表刷成同一个用户。稳妥做法:SQL 里强制保留命名占位符 WHERE,并在轻易云端开启idCheck,让平台帮你兜底。 - 典型错误二:dept_id_list 用 VARCHAR 接数组。 钉钉返回的是数组,直接拼字符串会让 JSON 解析失败。稳妥做法:在中间层把它序列化成 JSON 字符串落库,读取端反序列化;轻易云的字段映射里加一个表达式转换即可。
- 典型错误三:调度冲突。 同一时间点「部门策略」和「用户策略」并发跑,会出现「部门还没写完,用户已经在回写」的空指针。稳妥做法:用
depends_on显式声明依赖,而非靠人工约定顺序。 - 典型错误四:编码映射散落各处。 钉钉的
dept_id与 MySQL 自建部门 ID 不一致,每次都在 SQL 里硬写转换,改起来很痛。稳妥做法:把编码映射集中放到轻易云的「映射表」里,源端和目标端都引用同一份,改一处即可。 - 典型错误五:增量与全量混跑。 上线初期同时开了「全量回刷」和「增量调度」,导致同一行被多次写,日志爆炸。稳妥做法:增量与全量分两条策略,全量只在初始化阶段跑一次,跑完立即停用。
适用场景与不适用场景
适用:钉钉作为组织架构唯一源,且业务侧使用 MySQL 自建用户表、需要按 userid 精确回写变更的场景,典型如审批流、报表用户维度的对齐。
不适用:需要全量替换(非按 userid 回写)、或者需要把变更以事件流方式推送到多个下游的场景——那种更适合「拉取全部 + 主数据分发」型策略,而不是本文这种单条 UPDATE 型策略。