1
0
forked from erp-dev/erp
Files
erpnew/docs/printing_external_records_sync_2026-03-20.md
2026-03-20 20:00:12 +08:00

6.9 KiB
Raw Blame History

外部 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

同步主规则

  1. BianHaoID 对本次拉到的 records 分组
  2. 每个分组 upsert 一条 PrintingOrder
  3. 分组内每条 record 按 ID upsert 一条 PrintingJob
  4. 仅当本地全部写入成功后,才推进外部 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 原样保存
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 原样保存
BeiZhu 滚筒注意事项 PrintingOrder.rolling_warn 原样保存
KdRiQi 下单日期 PrintingOrder.outgoing_date 按 ISO 时间解析并保存
分组首条原始数据 外部来源快照 PrintingOrder.external_raw 保存 order 级别的原始数据,便于追溯

补充说明:

  • 外部没有可靠的 “面料” 字段,因此 PrintingOrder.fabric 本次同步固定写空字符串 ""
  • BianHaoKDRiQiFidJ 等当前未单独属性化的字段,保留在 external_raw
  • BianHaoKD 当前样例值类似 "1.27",不按日期强解析

二、单条 record -> PrintingJob

外部字段 含义 本系统字段 规则
ID 明细顺序 ID PrintingJob.original_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.nameexternal_product_name / external_raw 表达
单条原始数据 外部来源快照 PrintingJob.external_raw 保存 job 级别完整原始 record

员工绑定规则

  1. 读取外部 CaoZY
  2. 在同步用户所属商户下按 Employee.name 精确匹配
  3. 若匹配到的 Employee.sys_user 存在:
    • PrintingOrder.created_by = employee.sys_user
    • PrintingOrder.external_employee_name = ""
  4. 若未匹配到,或员工未绑定系统用户:
    • PrintingOrder.created_by = settings.PRINTING_EXTERNAL_SYNC_USER_ID
    • PrintingOrder.external_employee_name = 外部原文

客户绑定规则

  1. 读取 customer.KhName
  2. 在同步用户所属商户下按 Customer.name 精确匹配
  3. 取第一条结果作为 PrintingOrder.customer
  4. 同时保留:
    • external_customer_id
    • external_customer_name

保护规则:

  • 若本地找不到客户,则本次同步失败,不推进外部 cursor
  • 原因:PrintingOrder.customer 在业务上不可空,且当前未授权把客户兜底到固定客户

产品绑定与图片规则

  1. 读取 YanSe
  2. 在同步用户所属商户下按 Product.name 精确匹配
  3. 匹配成功:
    • PrintingJob.product = 匹配到的产品
    • 不请求外部图片 API
    • PrintingJob.external_product_name = ""
  4. 匹配失败:
    • 先创建本地 Product
    • 再调用外部图片 API
      • GET /api/v1/image?name_b64=<base64url(YanSe)>
    • 将返回的 base64 图片内容写入 Product.image
    • 由 Django storage 自动上传到七牛
    • 成功后 PrintingJob.product = 新建产品
    • PrintingJob.external_product_name = ""
  5. 若产品创建或图片拉取/上传失败:
    • 该条外部 record.ID 记为失败
    • 该 record 不创建 PrintingJob
    • 本批次其它成功 records 继续处理
    • cursor 仍然推进

外部接口与游标策略

外部配置写入 settings.py,不经 .env

  • PRINTING_EXTERNAL_RECORDS_BASE_URL
  • PRINTING_EXTERNAL_RECORDS_AUTHORIZATION
  • PRINTING_EXTERNAL_SYNC_USER_ID
  • PRINTING_EXTERNAL_SYNC_PRODUCT_CATEGORY_ID

游标策略:

  1. 拉取 records 时显式使用 update_cursor=false
  2. 本地成功 records / 失败 records 都处理完成后,调用 /api/v1/cursor/set 推进到 last_record_id
  3. 失败 records 需单独落库,便于后续按外部 record.ID 补偿

调度策略

Celery Beat 每 5 分钟触发一次。

单次请求固定:

  • limit=100
  • 不传范围参数

即依赖外部 cursor 做增量拉取。

本次新增字段计划

PrintingOrder

  • external_order_id
  • external_customer_id
  • external_customer_name
  • external_employee_name
  • external_raw

PrintingJob

  • external_product_name
  • external_raw

API v1 失败记录

  • PrintingExternalSyncFailure
    • external_record_id
    • external_order_id
    • product_name
    • error
    • raw

已接受的业务风险

  1. BianHaoID 是否跨外部系统唯一,不由本系统负责纠偏
  2. 客户按名称取第一条,存在误绑风险,但这是本期接受的业务策略
  3. 产品若本地不存在,则依赖“创建产品 + 拉图上传”流程;该流程失败时只影响对应 record.ID