forked from erp-dev/erp
fux: mdy_plate_order sync pk conflict
This commit is contained in:
169
docs/mdy_plate_order_sync.md
Normal file
169
docs/mdy_plate_order_sync.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# MDY(明道云)PlateOrder 同步:任务 / Command / 数据查询说明
|
||||
|
||||
## 这份文档解决什么问题
|
||||
|
||||
当你忘记:
|
||||
|
||||
- PlateOrder(开版数据表)是否有定时同步?
|
||||
- 手动怎么跑同步?
|
||||
- 同步的数据落到哪里、怎么查?
|
||||
|
||||
直接看本文档即可。
|
||||
|
||||
---
|
||||
|
||||
## 一、同步“是什么”:从明道云到哪里?
|
||||
|
||||
### 1) 同步目标表(本地暂存)
|
||||
|
||||
同步会把明道云“开版数据表”的行写入本地暂存表:
|
||||
|
||||
- 模型:`api_v1.models.MDYPlateOrderStaging`
|
||||
- 字段:
|
||||
- `mdy_rowid`:明道云 rowid(唯一)
|
||||
- `raw`:整行原始数据(JSON)
|
||||
- `related`:可选,跨表关联数据(list[dict],已做碾平)
|
||||
- `ctime/utime`:明道云系统时间字段(用于排序/增量)
|
||||
|
||||
### 2) 增量游标(checkpoint)
|
||||
|
||||
同步进度记录在:
|
||||
|
||||
- 模型:`api_v1.models.DataSync`
|
||||
- 表名枚举:`DataSync.TableName.PLATE_ORDER`
|
||||
|
||||
Command 默认会读取/写入 checkpoint;可以通过参数关闭。
|
||||
|
||||
---
|
||||
|
||||
## 二、有没有“定时任务”(Celery Beat)?
|
||||
|
||||
结论:**当前没有把 PlateOrder 同步加入 `CELERY_BEAT_SCHEDULE`**。
|
||||
|
||||
目前 `flower/settings.py` 里看到的 mdy 定时任务只有:
|
||||
|
||||
- `mdy_product_sync`:每 10 分钟(`api_v1.tasks.sync_mdy_products`)
|
||||
- `mdy_customer_sync`:每 5 分钟(`api_v1.tasks.sync_mdy_customers`)
|
||||
|
||||
但代码里确实存在 PlateOrder 的 Celery task(见下文),只是 **未配置到 beat schedule**。
|
||||
|
||||
---
|
||||
|
||||
## 三、如何手动运行(推荐:management command)
|
||||
|
||||
### Command:`sync_mdy_plate_orders`
|
||||
|
||||
入口文件:
|
||||
|
||||
- `api_v1/management/commands/sync_mdy_plate_orders.py`
|
||||
|
||||
最常用用法:
|
||||
|
||||
- 默认增量同步(使用 DataSync checkpoint;默认抓关联表数据):
|
||||
- `uv run python manage.py sync_mdy_plate_orders`
|
||||
|
||||
常用参数:
|
||||
|
||||
- `--page-size 100`:每页拉取条数(默认 100)
|
||||
- `--max-pages 2`:本次最多处理多少页(不是最大页码)
|
||||
- `--max-records 200`:本次最多处理多少条
|
||||
- `--with-related / --without-related`:是否抓取跨表关联数据(默认 with-related)
|
||||
- `--max-related-per-type 5`:每种关联表最多拉多少条 rowid
|
||||
- `--request-interval-seconds 0.02`:每次明道云请求之间最小间隔(限流;0.02≈50qps)
|
||||
- `--use-checkpoint / --skip-checkpoint`:是否读取 DataSync 游标(默认 use)
|
||||
- `--update-checkpoint / --skip-checkpoint-write`:是否写入 DataSync 记录(默认写)
|
||||
- `--sort-direction asc|desc`:按 ctime 升/降序抓取(默认 asc)
|
||||
|
||||
示例:
|
||||
|
||||
- 只拉主表、不拉关联(更快):
|
||||
- `uv run python manage.py sync_mdy_plate_orders --without-related`
|
||||
- 从头全量跑一段(不读 checkpoint,且跑完不写 checkpoint):
|
||||
- `uv run python manage.py sync_mdy_plate_orders --skip-checkpoint --skip-checkpoint-write`
|
||||
- 控制单次任务规模(避免跑太久):
|
||||
- `uv run python manage.py sync_mdy_plate_orders --page-size 100 --max-pages 3 --max-records 250`
|
||||
|
||||
---
|
||||
|
||||
## 四、如何通过 Celery 运行(可选)
|
||||
|
||||
### Celery Task:`api_v1.tasks.sync_mdy_plate_orders`
|
||||
|
||||
位置:
|
||||
|
||||
- `api_v1/tasks.py`(task 名:`sync_mdy_plate_orders`)
|
||||
|
||||
说明:
|
||||
|
||||
- 该 task 内部调用 `api_v1.mdy_plate_order_sync.sync_mdy_plate_orders_to_staging`
|
||||
- 该 task **目前不在 beat schedule**,但可以手动触发
|
||||
|
||||
两种触发方式(任选其一):
|
||||
|
||||
1) 通过 celery call(适合已有 worker 环境):
|
||||
- `uv run celery -A flower call api_v1.tasks.sync_mdy_plate_orders --kwargs='{"page_size":100,"max_pages":2,"with_related":true,"request_interval_seconds":0.02}'`
|
||||
|
||||
2) 通过 Django shell(适合本地临时验证):
|
||||
- `uv run python manage.py shell -c "from api_v1.tasks import sync_mdy_plate_orders; r=sync_mdy_plate_orders.delay(page_size=100,max_pages=2); print(r.id)"`
|
||||
|
||||
> 如果你确实需要“定时同步 PlateOrder”,可以把该 task 加进 `CELERY_BEAT_SCHEDULE`;但这属于策略选择(数据量/频率/接口限流),当前项目默认没开。
|
||||
|
||||
---
|
||||
|
||||
## 五、同步后如何查询(API)
|
||||
|
||||
### 暂存查询接口(只读)
|
||||
|
||||
文档:
|
||||
|
||||
- `docs/api_v1_mdy_plate_order_staging.md`
|
||||
|
||||
接口:
|
||||
|
||||
- List:`GET /api/v1/mdy-plate-order-staging/?limit=20&offset=0`
|
||||
- Detail:`GET /api/v1/mdy-plate-order-staging/{id}/`
|
||||
|
||||
说明:
|
||||
|
||||
- 支持 LimitOffset 分页
|
||||
- 支持按“内部字段名”的 query param 过滤(对前端隐藏 JSONField 细节)
|
||||
|
||||
---
|
||||
|
||||
## 六、相关实现入口(定位用)
|
||||
|
||||
- 同步逻辑(写入暂存+checkpoint):`api_v1/mdy_plate_order_sync.py`
|
||||
- 拉取明道云 rows:`flower/utils/mingdaoyun/fetch.py`(`fetch_plate_orders_from_mingdaoyun`)
|
||||
- 明道云客户端:`flower/utils/mingdaoyun/client.py`(当前 appKey/sign 仍是硬编码)
|
||||
- 暂存查询 API:`api_v1/views/mingdaoyun/plate_order_staging.py`
|
||||
|
||||
---
|
||||
|
||||
## 七、常见报错:`duplicate key value violates ... api_mdy_plate_order_staging_pkey`
|
||||
|
||||
### 这到底是哪种“唯一性冲突”?
|
||||
|
||||
`api_mdy_plate_order_staging_pkey` 是 **主键(id)** 的约束名,不是 `mdy_rowid` 的 unique。
|
||||
|
||||
这类报错通常意味着:**PostgreSQL 自增序列(sequence)落后于当前表内的最大 id**(常见于 restore/copy/import 后)。
|
||||
|
||||
### 当前代码如何处理
|
||||
|
||||
`sync_mdy_plate_orders_to_staging` 在写入暂存表时,如果遇到该主键冲突,会:
|
||||
|
||||
- 自动重置该表的 id sequence 到 `MAX(id)`(使下一次插入从 `MAX(id)+1` 开始)
|
||||
- 然后对当前 row 再重试一次写入
|
||||
|
||||
对应实现:`api_v1/mdy_plate_order_sync.py` 的 `_reset_mdy_plate_order_staging_id_sequence()`。
|
||||
|
||||
### 手动修复(可选)
|
||||
|
||||
如果你想一次性手动修复,可在 PostgreSQL 执行(把 sequence 名替换为实际值):
|
||||
|
||||
```sql
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('api_mdy_plate_order_staging', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM api_mdy_plate_order_staging), 1),
|
||||
true
|
||||
);
|
||||
```
|
||||
Reference in New Issue
Block a user