## 背景与问题 系统中 `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