Real-Time Inventory Sync in Practice: Pulling Stock Details from Wangdiantong into MySQL
What This Strategy Solves (Scenario & Value)
A retail customer runs Wangdiantong as its e-commerce fulfillment hub, while a MySQL data warehouse feeds BI and reconciliation. Operations asks questions like "how much can we still sell right now" or "which warehouse is about to run out" — exporting data manually is not realistic. We use the Qeasy Data Integration Platform to land Wangdiantong inventory details into MySQL on a 13-minute cadence, providing near-real-time data to BI while centralizing the mapping rules for warehouse codes and SKU codes in the platform to prevent data drift between systems.
Data Flow & Field Mapping
The pipeline is a single-direction landing: Wangdiantong Qimen (wdt.stock.query) as the query source → Qeasy as the middle layer for time windowing, pagination, and mapping → MySQL target table jry_wdt_stock_query written via REPLACE INTO for idempotency.
Key field mapping (excerpt, source → target):
| Meaning | Source (Wangdiantong) | Target (MySQL) | Notes |
|---|---|---|---|
| Incremental start | start_time | n/a | Bound to {{LAST_SYNC_TIME}} |
| Incremental end | end_time | n/a | Bound to {{CURRENT_TIME}} |
| Page size | page_size | n/a | Use {{PAGINATION_PAGE_SIZE}}, default 40, suggest 50-80 |
| Unique key | rec_id | rec_id | idCheck must be enabled for dedup |
| SKU code | spec_no | spec_no | Business key, combined with warehouse_no as composite unique |
| Stock qty | stock_num | stock_num | Pass-through |
| Available qty | avaliable_num | avaliable_num | Source spelling is avaliable, keep it |
The source start_time / end_time can be left empty, but in production you must bind them to time variables, otherwise every run becomes a full pull.
How to Configure in Qeasy
Source (Wangdiantong query)
- API:
wdt.stock.query, POST; effect = QUERY. - Bind
start_timetoLAST_SYNC_TIMEandend_timetoCURRENT_TIME; enable idCheck on the id field. - Put
page_sizein otherRequest as{{PAGINATION_PAGE_SIZE}}; the platform auto-paginates. - warehouse_no and spec_no can be passed through for single-warehouse or single-SKU testing.
Target (MySQL write)
- API:
execute, effect = EXECUTE. - Use
REPLACE INTO jry_wdt_stock_query (...)with named parameters(:field). Never concatenate strings to avoid injection. - Include
rec_idin the column list so REPLACE can dedup by primary key. - With effect = EXECUTE, the platform treats lastInsertId as success; no further query is needed.
Platform-level conventions common among Qeasy customers: centralize code mapping in the platform's mapping table for cross-strategy reuse; phase header and line items into separate strategies; keep incremental and full loads on parallel tracks — production runs incremental on crontab, while a one-off full load covers the initial backfill and any post-incident recovery.
Implementation Steps
- Prepare the mapping table: build warehouse code and SKU code mappings in Qeasy, do an offline reconciliation first, then go live.
- Smoke test: set page_size = 1 and pin start_time/end_time to a 5-minute window, confirm a single record lands correctly.
- Incremental seed: initialize
LAST_SYNC_TIMEto 24 hours before go-live and run a backfill to flush historical stock into MySQL. - Full load trigger: use a one-off script to top up all stock before go-live, then reset
LAST_SYNC_TIMEto the go-live moment to avoid overlap with the incremental window. - Schedule cadence: source crontab
*/13 * * * *, target3-59/13 * * * *. The 3-minute offset gives the source fetch + middle layer processing enough headroom. - Monitoring & alerting: track "rows written this run", "retry count", and "is LAST_SYNC_TIME advancing". Alert on any anomaly.
Pitfalls Recap
- Time window not bound: a typical mistake is leaving
start_timeempty, which causes a full pull every run and burns the API quota within hours. The safe approach is to explicitly bindLAST_SYNC_TIMEandCURRENT_TIME, and validate the variables in the platform. - Page size set too large: bumping page_size to 100 makes the source API time out, and the task retries forever. Start with 50 for a week, observe the success rate, then tune.
- REPLACE INTO field spelling: the source field is
avaliable_num(missing an 'l'), and the target table keeps that spelling. Do not "helpfully" rename it toavailable, otherwise the column will be empty. - Both crontabs hit at the same minute: source pulls at :00, target writes at :00, the middle layer has no time to process, and data piles up. Offsetting by 3-5 minutes is the safe play.
- idCheck not enabled: if
rec_idrepeats within the same window, REPLACE handles it, but during pagination some pages may be skipped. With idCheck on, the platform dedups and re-pulls as needed.
When It Fits — and When It Doesn't
Fits: e-commerce fulfillment scenarios where Wangdiantong stock needs to be landed into MySQL/warehouse at high frequency for BI, reconciliation, and stockout alerting; SKU and warehouse dimensions in the hundreds to tens of thousands. Doesn't fit: sub-second real-time inventory (this strategy runs at ~13-minute granularity at best); complex multi-ERP aggregation that requires pre-aggregation before landing — in those cases split into multiple strategies.
Reference Material (Desensitized)
Strategy metadata excerpt:
{
"source": {
"platform": "Wangdiantong Qimen",
"api": "wdt.stock.query",
"effect": "QUERY",
"crontab": "*/13 * * * *"
},
"target": {
"platform": "MySQL",
"api": "execute",
"effect": "EXECUTE",
"table": "jry_wdt_stock_query",
"crontab": "3-59/13 * * * *",
"main_sql": "REPLACE INTO jry_wdt_stock_query (rec_id, spec_no, warehouse_no, stock_num, avaliable_num, ...) VALUES (:rec_id, :spec_no, :warehouse_no, :stock_num, :avaliable_num, ...)"
}
}
(All tenant names, platform IDs, and table names above are taken from the raw configuration JSON; replace with the customer's own environment values when implementing.)