1
0
forked from erp-dev/erp
Files
erpnew/docs/business_object_relink_fix.md

2.8 KiB
Raw Blame History

背景与问题

系统中 PlateOrder / PrintingJob 都通过 business_objectstateflow.BusinessObject)承载流程推进数据:

  • BusinessObject.process 决定了流程节点序列
  • BusinessObject.state_logs 记录了推进轨迹(StateFlowRecord,支持撤销)

但在现有 API 中PUT/PATCH 更新):

  • PlateOrder.process(整型流程 ID允许被更新
  • PrintingOrder.process(外键流程)允许在“所有 jobs 未开始”时被更新
  • PrintingJob 允许修改 printing_order(从而间接改变其应当使用的流程)

这些更新行为会导致一个严重一致性漏洞:

业务对象的“当前流程字段”发生变化,但其已绑定的 BusinessObject.process 仍指向旧流程,导致后续状态推进与展示出现错乱。


修正案(最小改动原则)

目标:

  • 只在必要处拦截/修复更新行为
  • 将核心逻辑集中到 stateflow/services.py(跨模块统一)
  • 不删除旧的 BusinessObject后续可通过“无反向引用”识别悬空 BO

核心做法:

  • 新增 stateflow.services.relink_business_object_for_instance(...)
    • 当流程发生变化时,创建并绑定一个新的 BusinessObject新流程
    • 若旧 BO 存在“未撤销进度”,则拒绝(返回 None由调用方转 400
  • 内部新增 _can_relink_business_object(...)
    • 判定口径与 has_started 保持一致:只要存在 is_cancelled=False 的记录即视为已开始

规则(对外行为)

PlateOrder更新 process

  • process 未变化:不触发 relink
  • process 变化
    • 若已存在“未撤销进度”has_started=True返回 400
    • 否则:创建新 BO 并绑定;旧 BO 保留

PrintingOrder更新 process

  • process=None:非法,返回 400
  • 任意 job 已存在未撤销进度:整笔订单不允许改流程,返回 400
  • 所有 jobs 均未开始:允许改流程,并对订单下所有 jobs 进行 relink创建新 BO 并绑定)

PrintingJob更新 printing_order

  • 不允许通过更新接口修改 printing_order 绑定关系:返回 400
    • 避免绕过 PrintingOrder 的流程一致性约束

影响与收益

  • 收益:保证 processBusinessObject.process 的一致性,避免状态推进/展示错乱
  • 可追溯性:旧 BO 不删除,未来可实现“悬空 BO 查看/排查”
  • 风险控制:仅在“无有效进度”时允许换流程,避免对已开始流程造成破坏

涉及代码文件

  • stateflow/services.py
  • api_v1/views/printing/serializers.py
  • api_v1/views/printing/services.py
  • api_v1/views/printing/test_plate_order_api.py
  • api_v1/views/printing/test_api.py
  • api_v1/views/printing/test_printing_job_api.py