# 生产报工追溯方案 ## 一、追溯目标 | 方向 | 链路 | |------|------| | **正向**(订单 -> 产出)| 销售来源 -> 工单 -> 任务 -> 报工 -> 产出/消耗/质检 -> 库存事务 -> 批次库存 | | **反向**(产出 -> 订单)| 库存事务/批次 -> 产出/消耗单 -> 报工 -> 任务 -> 工单 -> 客户/来源 | | **横向**(按物料/批次)| 物料/批次 -> 所有进出事务 -> 相关报工/工单 | ## 二、现有追溯资产(链路已通的部分) 现有的 ID 链已经构成一张追溯网,**正常流程下追溯是完整的**: ``` mes_pro_work_order (工单) └─> mes_pro_task (任务, work_order_id) [待报工台账入口] └─> mes_pro_feedback (报工, task_id + work_order_id) ← 双绑定,工单侧兜底 ├─> mes_wm_product_produce / _line (产出, feedback_id) │ ├─> mes_wm_transaction (IN, biz_type=WM_PRODUCT_PRODUCE, biz_id=produce.id) │ └─> mes_qc_ipqc (source_doc_id=feedback_id, source_line_id=produce_line_id) │ └─> mes_qc_defect_record (qc_id) └─> mes_wm_item_consume / _line / _detail (消耗, feedback_id) └─> mes_wm_transaction (OUT, biz_type=WM_ITEM_CONSUME, biz_id=consume.id) ``` 关键点: - `mes_wm_transaction` 记录**每一笔**库存进出(扣料 OUT / 入库 IN),带 `biz_type` + `biz_id` + `biz_line_id` + `batch_id` + `transaction_time` + `related_transaction_id`。这是最细粒度的流水,批次/物料级追溯靠它。 - feedback **同时存 `task_id` 和 `work_order_id`**,任一链路出问题都能从另一侧绕回。 - 已审批 feedback 不可删(仅草稿可删),已回写数量的审计痕迹保留。 - `BaseDO` 使用 `@TableLogic` 软删除,`deleteById` 实为软删(`deleted=1`),数据行保留,取证时可绕过过滤查询。 ## 三、风险点与对应方案 ### 风险1:删任务导致软删引用(UX 追溯断链)⭐ 最实际 - **现状**:`MesProTaskServiceImpl.deleteTask` 只校验任务非终态,不查报工。报工回写不改任务状态,故已被报工的任务仍是 PREPARE、可删。软删后 `feedback.task_id` 指向 `deleted=1` 的任务,正常查询看不到,点击追溯断。 - **方案**:`deleteTask` 加校验——有报工记录的任务禁止删除。 ```java // feedbackMapper 按 task_id 查数量 > 0 则抛 PRO_TASK_HAS_FEEDBACK ``` - **成本**:极小(一个 count + 一个错误码)。**建议立即做。** ### 风险2:taskId 弱绑定(无 DB 外键,DO 注释 `TODO 待关联`) - **现状**:靠 `validateFeedbackData` 业务校验保证 task 与工单/工位/路线/工序/物料一致;DB 层无 FK。当前数据 0 条 null task_id。 - **方案**: - **不加硬 FK**(软删除 + 跨模块会冲突,yudao 体系惯例不用 FK)。 - 给 `mes_pro_feedback.task_id`、`work_order_id` **加普通索引**,提升追溯查询性能。 - feedback 详情查询时确保能带出 task 基本信息(即使任务被软删也提示"任务已删除"而非报错)。 - **成本**:小。**建议立即做(加索引)。** ### 风险3:台账看不到在途报工(盲区,非断链) - **现状**:台账用 `task.produced_quantity`(只含已审批 status=4)。草稿/审批中/待检验报工未回写,台账"待报工数量"偏大,与实际在途不符。 - **方案**:任务/台账增加在途报工量字段,口径明确: - `approvedQuantity` = 该任务下 status=4 的 feedbackQuantity 之和(= producedQuantity,冗余展示) - `inTransitQuantity` = 该任务下 status IN (0,2,3) 的 feedbackQuantity 之和 - `pendingQuantity` = 排产数量 − approvedQuantity(台账口径不变,"待报工"指尚未报) - `reportableQuantity` = 排产 − approved − inTransit(还可报的量) - **成本**:中(一个聚合查询)。**建议一期做。** ### 风险4:无报工冲销/撤销(纠错缺口)⭐ 最大缺口 - **现状**:报工审批后不可撤销、无反向冲销单、`feedbackQuantity` 校验 >0 不允许负数。数量填错只能改 DB。 - **方案**:新增**报工冲销流程**: - feedback 表加 `source_feedback_id`(冲销关联原报工)+ 状态加 `REVERSED(已冲销)` - 冲销时创建一条冲销 feedback(数量为正,但标记为冲销类型),生成**反向库存事务**(`related_transaction_id` 配对原事务),任务/工单数量**反向回写**(减) - 原 feedback 保留(审计痕迹),状态置 `REVERSED` - 权限独立(`mes:pro-feedback:reverse`) - **成本**:大(新流程、反向事务、状态机)。**建议二期。** ### 风险5:producedQuantity 无变更历史 - **现状**:`setSql increment` 累加,无独立流水。要还原某时刻值得按 feedback 审批时间累加。 - **方案**: - **A(推荐,轻量)**:不建表,靠 feedback 记录重建(feedback 本身就是流水,按 `update_time` 排序累加即可还原)。在任务详情加"报工历史"tab 展示。 - B(重):新建 `mes_pro_task_progress_log` 每次回写记一条。更严谨但维护成本高。 - **成本**:A 小。**建议一期做(加报工历史 tab)。** ## 四、分期实施建议 | 期 | 内容 | 成本 | 收益 | |----|------|------|------| | **一期·堵漏** | 风险1 删任务校验 + 风险2 加索引 + 风险3 在途报工量 + 风险5 报工历史 tab | 小~中 | 堵住断链、补齐盲区,追溯基本完整 | | **二期·纠错** | 风险4 报工冲销流程 | 大 | 闭环纠错,审计完整 | | **三期·增强**(可选) | 批次级正反向追溯接口;任务进度流水表(B 方案) | 中 | 精细追溯 | ## 五、推荐的追溯查询接口(一期可顺手加) | 接口 | 入参 | 返回 | 用途 | |------|------|------|------| | `GET /mes/pro/feedback/trace/{id}` | 报工 ID | 报工 + 产出/消耗/质检 全链 | 按报工单追溯 | | `GET /mes/pro/task/feedback-list?taskId=` | 任务 ID | 该任务所有报工(含在途) | 任务 -> 报工明细 | | `GET /mes/wm/transaction/trace-by-item?itemId=&batchId=` | 物料/批次 | 相关事务 + 来源单据 | 物料/批次反向追溯 | 前两个是一期就该有的基本追溯能力(台账点进去能展开明细);第三个可三期。 ## 六、一期落地后的追溯能力(预期) 做完一期后,从"待报工台账"出发的完整追溯路径: ``` 待报工台账(任务行:排产/已审批/在途/待报工/可报量) │ 点击任务 ▼ 任务详情 + 报工历史 tab(按时间排列所有报工,含在途状态) │ 点击某条报工 ▼ 报工追溯页(报工单 + 关联产出/消耗/质检 + 库存事务流水) │ 点击库存事务 ▼ 批次库存变动明细(该批次所有进出) ``` 且因风险1已堵,任务不会被误删导致断链;因风险2加索引,大表下追溯查询不卡。 ## 七、结论 - 正常流程下,现有 ID 链已构成完整追溯网,**追溯本身是通的**。 - 主要隐患是"删任务不校验报工"造成的软删断链,**一期立即修**。 - 台账的"在途报工"盲区和"报工历史"是一期补齐的重点。 - 报工冲销是最大的缺口,但工程量大,建议一期稳定后再上二期。 ## 附:涉及文件清单(一期预估) | 改动 | 文件 | |------|------| | 删任务校验 | `MesProTaskServiceImpl.deleteTask`、`MesProFeedbackMapper`(加 countByTaskId)、`ErrorCodeConstants`(加 PRO_TASK_HAS_FEEDBACK) | | 加索引 | DDL:`mes_pro_feedback` 加 `idx_task_id`、`idx_work_order_id` | | 在途报工量 | `MesProTaskRespVO` 加字段、`MesProTaskMapper`/Service 加聚合查询、`buildTaskRespVOList` 填充 | | 报工历史 tab | `MesProFeedbackController` 加 `list-by-task` 接口、`MesProFeedbackMapper` 加 selectListByTaskId | | 追溯接口 | `MesProFeedbackController` 加 `/trace/{id}`,组装产出/消耗/质检 | | 前端文档 | `docs/feedback_traceability_*` 系列文档 |