forked from erp-dev/erp
2.8 KiB
2.8 KiB
背景与问题
系统中 PlateOrder / PrintingJob 都通过 business_object(stateflow.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的流程一致性约束
- 避免绕过
影响与收益
- 收益:保证
process与BusinessObject.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