Qeasy Cloud
Get Started

Syncing DingTalk Contacts Incrementally into MySQL: A Practical Guide for One Integration Strategy

· 系统管理员· Integration Solutions· 9 views· 4 min read
MySQLDingTalkWebAPIIncremental Sync组织架构轻易云

What This Strategy Solves (Scenario and Value)

At one manufacturer, DingTalk is the single source of identity for all employees, while HR, workflow, and reporting systems still read from a self-managed MySQL table. Staff transfers, resignations, and name changes happen every day, yet approval flows routinely break because "the initiator is no longer with the company." The core problem is straightforward: changes in the DingTalk directory must land in MySQL the same day. This guide focuses on a single concrete action—"Fetch DingTalk Contacts — Modify"—which writes updated user fields back into MySQL by userid.

Data Flow and Field Mapping (Source → Middle Layer → Target)

The source side is the DingTalk open platform endpoint topapi/v2/user/get, invoked via POST. The input only needs one dimension—the target userid—with language fixed at zh_CN. The target side is MySQL's execute endpoint, which is essentially a parameterized UPDATE with a main_sql placeholder. The mapping between the two sides is:

Business MeaningDingTalk Field (Response)MySQL ColumnNotes
DingTalk user primary keyuseriduseridUPDATE WHERE clause; de-duplication key
Job numberjob_numberjob_numberFrequently changing field
NamenamenameSame as above
TitletitletitleNullable
Unified identityunionidunionidCritical for cross-system linkage
Department listdept_id_listdept_id_listArray type, stored as JSON string
Department leader flagleader_in_deptleader_in_deptDrives approval node logic
Deletion flagdel_flagdel_flagSoft delete via source field

This strategy only does "write-back by userid"—no split between insert and delete. The UPDATE's WHERE clause must be strictly bound to userid, otherwise the entire table can be wiped out in one run. That is the first pitfall we revisit later.

How to Configure It on Qeasy

On the Qeasy data integration platform, this strategy is a typical "source pull + target execute" WebAPI sync. We break the configuration into four parts based on field experience:

  1. Source configuration (DingTalk): Set metadata.api to topapi/v2/user/get and effect to QUERY. The userid parameter is injected as a variable from an upstream strategy or scheduler; language is fixed at zh_CN; dep_strategy points to the ID of the "Fetch DingTalk Departments" upstream strategy, declaring the dependency. Use userid as the id field and enable autoFillResponse so the response schema is auto-populated and you do not have to rebuild the model every time a new field appears.

  2. Target configuration (MySQL): Set effect to EXECUTE and metadata.api to execute. The request block only carries a single main_params object as the input placeholder; the actual SQL lives in otherRequest under main_sql, written with named placeholders like :userid and :name. Keep idCheck enabled so the platform automatically verifies that the source id matches the userid in the SQL WHERE clause, preventing mismatches.

  3. Scheduling: Set crontab to 22,52 23 * * *, meaning two runs per day at 23:22 and 23:52. Late-night timing is chosen because DingTalk directory churn is concentrated during business hours; the night window gives the longest writeback runway with the least contention.

  4. Dependencies and sequence: This strategy's depends_on points to the "Fetch Departments" strategy—departments first, then people under them. In Qeasy, this is expressed by sequence: B together with the explicit dependency declaration.

Implementation Steps

We split the rollout into three stages, each with a clear exit criterion:

Stage 1: Build the incremental starting point. In Qeasy, wire the source-side userid parameter to the upstream strategy's output and get a single-user pull + writeback working. Exit criterion: pick any 3 real users and confirm updated_at plus name/title actually change in the database.

Stage 2: Batch triggering. Once the upstream "Departments" strategy is in place, switch this strategy's userid input to "batch-passed from upstream output," so the flow becomes "get the user list per department, then write back per userid." On Qeasy this is typically handled with the "header / body phased" pattern: upstream is the header (department), this strategy is the body (every user under it). Exit criterion: a full run of ~5,000 users finishes in minutes with no primary-key conflicts.

Stage 3: Tune the schedule. Move from "once per day" toward 22,52 23 * * *, watching DingTalk API rate limits and MySQL slow logs. On 429s, add retry and backoff on the Qeasy strategy itself; on row locks, stagger the two executions by 30 minutes rather than simply removing one of them.

Pitfalls We Hit in the Field

These are the recurring failure modes we have seen on customer sites:

  • Pitfall #1: UPDATE without a WHERE clause. During the first deployment, the main_sql was written as update hzero_platform.dingtalk_user set ... without where userid=:userid. One batch run rewrote the whole table into a single user. The safe approach: keep the named-placeholder WHERE clause in SQL and enable idCheck on the Qeasy side so the platform has your back.
  • Pitfall #2: Receiving an array into a VARCHAR column. DingTalk returns dept_id_list as an array; concatenating it directly as a string breaks JSON parsing downstream. The safe approach: serialize it to a JSON string in the middle layer, and deserialize on read; an expression transform in Qeasy's field mapping handles it.
  • Pitfall #3: Schedule collisions. Running the "Departments" strategy and the "Users" strategy at the same time causes "users being written before their departments exist," producing null pointers. The safe approach: declare the dependency explicitly with depends_on, instead of relying on human convention.
  • Pitfall #4: Scattered code mappings. DingTalk's dept_id does not match the self-managed department ID in MySQL, so every team hardcodes the conversion inside SQL—painful to maintain. The safe approach: centralize the mapping in Qeasy's "mapping table," and let both source and target reference the same one; one change, everywhere.
  • Pitfall #5: Mixing full and incremental runs. During early rollout, "full backfill" and "incremental schedule" were enabled together, causing the same row to be written multiple times and exploding the logs. The safe approach: keep full and incremental as two separate strategies; run full only during initial bootstrap and disable it immediately after.

When This Strategy Applies — and When It Does Not

Applies: DingTalk is the single source of org structure, and the business side relies on a self-managed MySQL user table that needs precise userid-based writeback. Typical examples include approval workflows and reporting users staying aligned with HR.

Does not apply: Cases that need full replacement (not userid-keyed writeback), or where changes must be distributed as an event stream to multiple downstream systems. Those call for a "pull-all + master-data fan-out" pattern, not the single-row UPDATE strategy described here.

Original content. Please credit the source when reposting: https://www.qeasy.cloud/insights/solutions/strat-mysql-dingtalk-4900-user-mom-db1c92ab

Comments