Practical Tutorial: Syncing Kingdee Cloud Customer Master Data to MySQL
What This Strategy Solves
In one of our projects, a retail company kept all membership and store settlement objects in Kingdee Cloud, while its downstream business middle platform, self-service reporting, and CRM were built on MySQL. Whenever a new customer was added, cross-system reconciliation fell apart: settlement objects were missing names or organizations, the marketing side had to maintain data manually, and within three months the two systems diverged by more than 8%. We needed a pipeline that pushed new customers from Kingdee Cloud into MySQL in near real time. That is exactly what the 'CRM-Kingdee Customer Sync - New' strategy is for. It handles only the 'create' action—no updates—and fits naturally as the first segment of the master data backbone.
Data Flow and Field Mapping
The flow is: Kingdee Cloud (source) → Qeasy Data Integration Platform (middleware) → MySQL (target).
The source side pulls customer bills through the WebAPI executeBillQuery. Key fields include FCUSTID, FNumber (code), FName (name), FCreateOrgId_FNumber (creating org), FUseOrgId_FNumber (using org), and FDescription. The middleware renames fields, converts types, and handles nulls before writing them as named parameters into the wk_wodtop_customer table on MySQL.
| Source (Kingdee Cloud) | Target (MySQL) | Handling Notes |
|---|---|---|
| FCUSTID | data_id | Strictly preserved as primary key |
| FNumber | customer_code | Centrally managed code mapping |
| FName | customer_name | Direct mapping |
| FCreateOrgId.FNumber | creating_org | Organization code foreign key |
| FUseOrgId.FNumber | using_org | Organization code foreign key |
| FDescription | abbreviation | Description → short name |
| Custom fields | customer_category / customer_group etc. | Many-to-one lookup |
| Date fields | establishment_date / freeze_date / disable_date | Empty string must be NULL |
'Centrally managed code mapping' is a common pattern among Qeasy customers: organization, customer category, and currency codes are kept in a single mapping table. When the source side changes, you fix it in one place instead of editing every strategy.
How to Configure It in Qeasy
In the Qeasy Data Integration Platform, open the strategy configuration page, create a new strategy, pick Kingdee Cloud as the source and MySQL as the target. On the source side, choose executeBillQuery and declare the request fields such as FNumber, FCUSTID, FName, FCreateOrgId_FNumber, and FUseOrgId_FNumber, then enable autoFillResponse. On the target side, choose SQL-EXECUTE, paste the main_sql template, and declare every :-prefixed named parameter inside the main_params sub-structure.
For scheduling, the source uses */5 * * * * and the target uses */6 * * * *, so the target lands one minute after the source. This one-minute gap protects the target from reading a half-written batch. 'Header and body staged separately' is another common pattern—since this strategy only reads the customer header and not any line items, splitting is not required, but we still recommend keeping the _head suffix in the strategy name so that future extensions stay clean.
Implementation Steps
- Define the incremental starting point: On first go-live, treat all customer bills created after a known cutoff as the starting point. After that, use the max
FCUSTIDor last-modified timestamp as the watermark. - Trigger the full backfill: Run a one-time full backfill for historical customers, then immediately switch to incremental mode so the full run does not overwrite later additions.
- Set the schedule frequency: Source every 5 minutes, target every 6 minutes, leaving a one-minute buffer in between.
- Add validation: Enforce a unique key
(data_id, company_id)on MySQL, and route failed writes to a dead-letter queue. - Observation period: Watch the new daily insert count for 7 days and verify it matches the
executeBillQueryresult on the source side.
'Incremental and full-volume dual-track' is a typical Qeasy customer pattern: full volume for backfilling, incremental for catching up, each running in its own time window without interference.
Pitfalls and Lessons
- Empty-string dates break writes: Kingdee Cloud returns an empty string
''when a date is blank. MySQLDATEcolumns reject empty strings, so wrap dates withNULLIF(:establishment_date,''). The SQL template above already does this—reuse it when you write new strategies. - Inconsistent organization codes: When
FCreateOrgId_FNumberandFUseOrgId_FNumberfollow different naming rules across books, foreign keys on MySQL will fail to match. Agree on a single organization code dictionary up front. - Special characters in customer names: Some names contain single quotes or backslashes. Concatenating them into SQL causes errors. Always use parameterized binding—never string concatenation.
- First full backfill times out: With a large customer base, one full pull will not finish in time. The safe approach is to split by
FCUSTIDranges, 2000 rows per batch. - Duplicate primary keys: If a customer is re-written or re-approved on the source side, it can re-enter the incremental queue. The MySQL side must be idempotent with
INSERT IGNOREorON DUPLICATE KEY UPDATE.
When to Use and When Not to Use
Use it when Kingdee Cloud is the source of customer master data and a downstream MySQL is used for BI reporting, self-service analytics, or a lightweight CRM read, with a customer count under a few hundred thousand. Do not use it when you need bidirectional sync, when you must write updates back to Kingdee Cloud, or when the master data source lives somewhere else—in those cases, design a separate strategy.