forked from erp-dev/erp
7.6 KiB
7.6 KiB
外部 records 同步到 PrintingOrder / PrintingJob 设计
日期:2026-03-20
目标
将外部服务 GET http://43.139.183.222:18080/api/v1/records 返回的增量 records,
按每 5 分钟一次的频率同步到本系统:
- 一组相同
BianHaoID的 records -> 1 条printing.PrintingOrder - 每条 record -> 1 条
printing.PrintingJob
本设计文档用于固化本次实现的字段映射和兜底规则,后续若业务修正,以此为追溯基线。
外部 records 样例判断
外部 records 不是“订单主表”,而是“印花明细表”。
判断依据:
- 同一个
BianHaoID下会出现多条 record - 每条 record 含数量、尺寸、单位、产品名等明细信息
- 因此单条 record 更接近本系统的
PrintingJob
同步主规则
- 按
BianHaoID对本次拉到的 records 分组 - 每个分组 upsert 一条
PrintingOrder - 分组内每条 record 按
BianHaoID + YanSe覆盖 upsert 一条PrintingJob - 仅当本地全部写入成功后,才推进外部 cursor
字段映射
一、外部分组 -> PrintingOrder
| 外部字段 | 含义 | 本系统字段 | 规则 |
|---|---|---|---|
BianHaoID |
订单编号 | PrintingOrder.external_order_id |
分组键;原样保存 |
customer.KhName |
客户名 | PrintingOrder.customer |
按本商户 Customer.name 精确匹配,取第一条 |
KhID |
外部客户 ID | PrintingOrder.external_customer_id |
原样保存 |
customer.KhName |
外部客户名 | PrintingOrder.external_customer_name |
原样保存 |
HpName |
布料名 | PrintingOrder.fabric |
原样保存到订单面料字段 |
area |
地区/区域 | PrintingOrder.area |
原样保存;取不到时回退为空字符串 "" |
CaoZY |
外部操作员/业务员 | PrintingOrder.created_by / PrintingOrder.external_employee_name |
若能匹配到内部员工且员工绑定了系统用户,则 created_by 取该用户,external_employee_name 置空;否则 created_by 取固定同步用户,external_employee_name 保留原文 |
SHDZ |
布料来源 + 工艺 | PrintingOrder.fabric_source / PrintingOrder.craft |
以空白字符 split;第 1 段为 fabric_source,剩余重新拼接为 craft |
SeHao |
幅宽 | PrintingOrder.width |
原样保存 |
MeoA |
曲线 | PrintingOrder.curve |
原样保存 |
FidJ |
电脑位置 | PrintingOrder.position |
取分组首条 record 的原始值 |
BeiZhu |
滚筒注意事项 | PrintingOrder.rolling_warn |
原样保存 |
KdRiQi |
下单日期 | PrintingOrder.outgoing_date |
按 ISO 时间解析并保存 |
| 分组首条原始数据 | 外部来源快照 | PrintingOrder.external_raw |
保存 order 级别的原始数据,便于追溯 |
补充说明:
- 外部
HpName已作为布料名使用,直接写入PrintingOrder.fabric - 外部
area直接写入PrintingOrder.area;若外部未返回或为空,则统一写入空字符串 BianHaoKD、RiQi等当前未单独属性化的字段,保留在external_raw中BianHaoKD当前样例值类似"1.27",不按日期强解析
二、单条 record -> PrintingJob
| 外部字段 | 含义 | 本系统字段 | 规则 |
|---|---|---|---|
ID |
明细顺序 ID | PrintingJob.original_id |
保存最近一次覆盖该任务的外部明细 ID,便于追溯 |
YanSe |
外部产品名 | PrintingJob.product / PrintingJob.external_product_name |
若能按本商户 Product.name 精确匹配,直接绑定该产品且不再拉图;若本地不存在,则先创建产品、拉图并上传到 storage,成功后绑定该产品 |
ShuLiang |
下单数量 | PrintingJob.quantity |
按整数解析;当前预期外部值为整数字符串或 10.00 这类可安全转整数的值 |
JiJiaDW |
单位 | PrintingJob.unit |
去掉前导 / 后保存,例如 /段 -> 段 |
ShuLiangZ |
一段尺寸 | PrintingJob.size |
原样保存 |
BeiZhuC |
件数 | PrintingJob.pieces |
提取其中的整数,例如 10件 -> 10 |
YanSe |
外部产品名 | PrintingJob.description |
本次不单独映射到 description;原始值由 product.name 或 external_product_name / external_raw 表达 |
| 单条原始数据 | 外部来源快照 | PrintingJob.external_raw |
保存 job 级别完整原始 record |
补充说明:
PrintingJob的覆盖唯一性按PrintingOrder.external_order_id + YanSe处理- 若同一外部订单下再次出现相同
YanSe,则更新已有PrintingJob,不再新增 - 更新时
original_id会覆盖为最新外部 record 的ID
员工绑定规则
- 读取外部
CaoZY - 在同步用户所属商户下按
Employee.name精确匹配 - 若匹配到的
Employee.sys_user存在:PrintingOrder.created_by = employee.sys_userPrintingOrder.external_employee_name = ""
- 若未匹配到,或员工未绑定系统用户:
PrintingOrder.created_by = settings.PRINTING_EXTERNAL_SYNC_USER_IDPrintingOrder.external_employee_name = 外部原文
客户绑定规则
- 读取
customer.KhName - 在同步用户所属商户下按
Customer.name精确匹配 - 取第一条结果作为
PrintingOrder.customer - 同时保留:
external_customer_idexternal_customer_name
保护规则:
- 若本地找不到客户,则本次同步失败,不推进外部 cursor
- 原因:
PrintingOrder.customer在业务上不可空,且当前未授权把客户兜底到固定客户
产品绑定与图片规则
- 读取
YanSe - 在同步用户所属商户下按
Product.name精确匹配 - 匹配成功:
PrintingJob.product = 匹配到的产品- 不请求外部图片 API
PrintingJob.external_product_name = ""
- 匹配失败:
- 先创建本地
Product - 再调用外部图片 API:
GET /api/v1/image?name_b64=<base64url(YanSe)>
- 将返回的 base64 图片内容写入
Product.image - 由 Django storage 自动上传到七牛
- 成功后
PrintingJob.product = 新建产品 PrintingJob.external_product_name = ""
- 先创建本地
- 若产品创建或图片拉取/上传失败:
- 该条外部
record.ID记为失败 - 该 record 不创建
PrintingJob - 本批次其它成功 records 继续处理
- cursor 仍然推进
- 该条外部
外部接口与游标策略
外部配置写入 settings.py,不经 .env:
PRINTING_EXTERNAL_RECORDS_BASE_URLPRINTING_EXTERNAL_RECORDS_AUTHORIZATIONPRINTING_EXTERNAL_SYNC_USER_IDPRINTING_EXTERNAL_SYNC_PRODUCT_CATEGORY_ID
游标策略:
- 拉取 records 时显式使用
update_cursor=false - 本地成功 records / 失败 records 都处理完成后,调用
/api/v1/cursor/set推进到last_record_id - 失败 records 需单独落库,便于后续按外部
record.ID补偿
调度策略
Celery Beat 每 5 分钟触发一次。
单次请求固定:
limit=100- 不传范围参数
即依赖外部 cursor 做增量拉取。
本次新增字段计划
PrintingOrder
external_order_idexternal_customer_idexternal_customer_nameexternal_employee_nameexternal_raw
PrintingJob
external_product_nameexternal_raw
API v1 失败记录
PrintingExternalSyncFailureexternal_record_idexternal_order_idproduct_nameerrorraw
已接受的业务风险
BianHaoID是否跨外部系统唯一,不由本系统负责纠偏- 客户按名称取第一条,存在误绑风险,但这是本期接受的业务策略
- 产品若本地不存在,则依赖“创建产品 + 拉图上传”流程;该流程失败时只影响对应
record.ID