状态:**已执行(2026-09-21)**。主数据侧(18 条工艺路线)与订单侧 82 张生产订单及全部工单均已改造完成并通过推导校验。
本文档记录改造的原因、推导机制、执行内容、最终结果与校验方法,供后续排查与前端联调参考。
改造前 18 条工艺路线每条**只有 1 道工序**,且 18 条路线同名、工序序列完全相同、BOM 配方也完全相同。原因是两处推导机制把同层多工序去重合并,同时造数据时按同一模板复制。
两个目标:
这两段逻辑决定了路线与 BOM 要怎么摆,改错会导致"在前端保存一次路线就被打回原形"。
代码位置
| 场景 | 方法 |
|---|---|
| 主工艺路线保存 | TechnologyRoutingServiceImpl.syncRoutingOperationsByBom() |
| 生产订单级路线 | ProductionBomStructureServiceImpl.buildDesiredRoutingOperationList() |
| 去重键 | 两处的 buildBomOperationDedupKey(),均为 operationId + "#" + 产出产品规格id + "#" + parentId |
两条推导路径的顺序规则不同
where bom_id=? and operation_id is not null ORDER BY id DESC,按 id 倒序迭代,dragSort 从 1 递增。buildOperationListPostOrderWithDepth)。同一父节点下的子节点按 id ASC 排序。由此得出的硬约束
validateProcessConsistency 与 handleBomProcessChange 都会提示"同一层级的工序必须一致")。is_quality=1。因为报工时会按"是否末道工序"生成 inspect_type=2 或 1 的质检单,而 quality_test_standard 里只有 type 0(原材料)和 type 2(出厂检验),没有 type 1(过程检验),非末道质检工序会生成无标准的检验单。BOM 的层级含义:某一行 operation_id 表示"产出它父节点产品"的那道工序;unit_quantity 是"每产出 1 单位父产品需要多少该行产品";demanded_quantity = unit_quantity × 父节点需求量,根节点需求量 = 订单数量。
按产品性质把 18 条路线分成 3 档,工序数 3 / 4 / 5 不等:
| 档位 | 适用产品 | 工序序列 | 路线 id | 路线数 |
|---|---|---|---|---|
| CLK 熟料类 | 硫铝酸钙熟料(product_model 444 / 448) | 原料破碎研磨(42) → 成品包装(46) → 出厂检验(47) | 10、14 | 2 |
| STD 标准膨胀剂 | CSA/UEA/HCSA/HEA 等粉剂膨胀剂 | 42 → 粉剂原料复配(43) → 46 → 47 | 1–5、11–13 | 8 |
| FN 复合功能型外加剂 | 复合功能型外加剂 | 42 → 43 → 成品复合掺配(49) → 46 → 47 | 6–9、15–18 | 8 |
路线 description 也跟着分档,不再全部同名:
熟料破碎粉磨与包装工艺路线粉剂类膨胀剂常规工艺路线复合功能型外加剂工艺路线| 对象 | 表 | 变更 |
|---|---|---|
| 工序 | technology_operation |
id 42/43 改为非质检(is_quality=0, is_production=1, type=1)并补备注;新增 id 46 成品包装(0/1/1)、id 47 出厂检验(1/0/0) |
| 工序 | technology_operation |
新增 id 49 成品复合掺配(编号 GX00000049,is_quality=0, is_production=1, type=1,create_user=287,dept_id=100)——仅 FN 档使用 |
| 半成品 | product / product_model |
在「半成品」(product 277) 下新增 product_model 468/469/470:粉磨细料、复配混合料、待检成品;**新增 product_model 472 掺配混合料**,单位吨 |
| 半成品库存 | stock_inventory |
468/469/470 各一条(id 117/118/119),另加 472 一条(batch 20260905-掺配混合料-001,96.40 吨),避免新销售单库存校验报"库存不足" |
| 工艺参数 | technology_param / technology_operation_param |
新增 PARAM_0018 掺配比例偏差(%,≤1%)、PARAM_0019 均化时间(min,≥15),挂到工序 49 |
| BOM 结构 | technology_bom_structure |
bom 1–18 按档位重建为 144 行,三/四/五层嵌套 |
| 路线工序 | technology_routing_operation |
共 78 行(2×3 + 8×4 + 8×5);原 id 1–18 保留并改写为各路线末道工序(出厂检验),其余新增 |
[ ] 内为该行的消耗工序)CLK(以 bom 10 / 硫铝酸钙熟料为例):
硫铝酸钙熟料 (根,无工序)
└── 待检成品 [出厂检验 47]
└── 粉磨细料 [成品包装 46]
└── 熟料 1.00 [原料破碎研磨 42]
STD(以 bom 1 / 膨胀剂 CSA 为例):
膨胀剂 CSA (根,无工序)
└── 待检成品 [出厂检验 47]
└── 复配混合料 [成品包装 46]
├── 粉磨细料 [粉剂原料复配 43]
│ ├── 熟料 0.55 [原料破碎研磨 42]
│ ├── 石膏 0.20 [原料破碎研磨 42]
│ └── 石灰石 0.15 [原料破碎研磨 42]
└── 粉煤灰 0.10 [粉剂原料复配 43]
FN(以 bom 6 为例):
复合功能型外加剂 (根,无工序)
└── 待检成品 [出厂检验 47]
└── 掺配混合料 [成品包装 46]
├── 复配混合料 [成品复合掺配 49]
│ └── 粉磨细料 [粉剂原料复配 43]
│ ├── 熟料 0.50 [原料破碎研磨 42]
│ ├── 石膏 0.15 [原料破碎研磨 42]
│ └── 石灰石 0.15 [原料破碎研磨 42]
└── 粉煤灰 0.20 [成品复合掺配 49]
粉煤灰挂在复配层(或掺配层)而不是粉磨层,是为了让该工序有两个投入,业务上更合理;为保证 id 顺序,插入时它排在熟料/石膏/石灰石**之前**。
每条路线一套 [熟料, 石膏, 石灰石, 粉煤灰] 配比(product_model 464/461/462/465),避免 18 条 BOM 完全相同:
| bom | 配比(熟料/石膏/石灰石/粉煤灰) | bom | 配比 |
|---|---|---|---|
| 1 | 0.55 / 0.20 / 0.15 / 0.10 | 10 | 1.00 / — / — / — |
| 2 | 0.45 / 0.25 / 0.20 / 0.10 | 11 | 0.55 / 0.20 / 0.15 / 0.10 |
| 3 | 0.60 / 0.15 / 0.15 / 0.10 | 12 | 0.60 / 0.15 / 0.15 / 0.10 |
| 4 | 0.50 / 0.20 / 0.20 / 0.10 | 13 | 0.50 / 0.20 / 0.20 / 0.10 |
| 5 | 0.40 / 0.25 / 0.25 / 0.10 | 14 | 1.00 / — / — / — |
| 6 | 0.50 / 0.15 / 0.15 / 0.20 | 15 | 0.45 / 0.15 / 0.20 / 0.20 |
| 7 | 0.45 / 0.15 / 0.15 / 0.25 | 16 | 0.40 / 0.15 / 0.15 / 0.30 |
| 8 | 0.40 / 0.15 / 0.15 / 0.30 | 17 | 0.50 / 0.15 / 0.15 / 0.20 |
| 9 | 0.45 / 0.20 / 0.15 / 0.20 | 18 | 0.45 / 0.20 / 0.15 / 0.20 |
bom 10、14 为熟料单料,无掺合料。
| 表 | 行数 | 说明 |
|---|---|---|
production_order |
82 | 全部 status=3(已完成) |
production_order_routing |
82 | 每单 1 条路线快照 |
production_order_routing_operation |
82 | 每单**只有 1 行**,drag_sort=1,technology_operation_id=43 |
production_operation_task |
82 | 每单 1 张工单,全部 status=4(完工) |
production_bom_structure |
389 | 平铺两层(物料直接挂根节点) |
production_product_main |
82 | 报工主记录 |
production_product_input / _output |
307 / 82 | 投入 / 产出明细 |
quality_inspect |
87 | 82 条 inspect_type=2(出厂检验)+ 5 条 inspect_type=0(原材料) |
stock_out_record |
399 | 310 条 record_type=14(生产领料)+ 89 条 record_type=13(销售出库) |
关键前提:现有 82 条报工/质检/入库记录**语义上已经属于最后一道工序**。原订单只有一道工序且 is_quality=1,报工时 isLastOperation=true,生成 inspect_type=2 出厂检验单,入库 record_type=6(质检-合格入库)。因此:
technology_routing_operation id 1–18、订单级 ror 原行、production_operation_task 原工单 → 直接作为末道工序(出厂检验)保留,不重建。production_product_main / quality_inspect / stock_in_record → 末道工序的完整链路,**不动**。production_order_routing_operation.technology_routing_operation_id ──► technology_routing_operation.id (1–18,不可删)
production_operation_task.technology_routing_operation_id ──► technology_routing_operation.id
production_operation_task.production_order_routing_operation_id ──► production_order_routing_operation.id
production_product_main.production_operation_task_id ──► production_operation_task.id
production_product_input.production_product_main_id ──► production_product_main.id
stock_out_record.record_id (record_type=14) ──► production_product_input.id
所以订单级路线工序是**改写**(UPDATE 保留 id)而不是删除重建。
production_bom_structure:按该订单 bom_id 对应主数据的档位形状镜像重嵌套(删除原平铺行后按深度递增顺序重插),根节点 demanded_quantity 取原根值,逐层 demanded_quantity = unit_quantity × 父行demanded。production_order_routing_operation:原行 UPDATE 为末道工序(47),新增 2/3/4 行(视档位),drag_sort 依次递增;technology_routing_operation_id 指向主数据侧对应档位的路线工序 id。production_operation_task:新增对应工单,status=4,plan_quantity = complete_quantity = 订单数量,工单号 GD + yyyyMMdd + 3位流水(延续号,避免重号),计划/实际时间落在订单计划开始 ~ 2026-09-20 之间且逐道递增;create_user / dept_id 沿用该订单原值。production_order_pick:310 条领料按投入物料改挂到真正消耗它的工序(464/461/462 → 42;465 → 43(STD) / 49(FN)),并按组内配比重算 quantity / demanded_quantity / actual_qty(2 位小数,末行补差)。production_order_routing_operation_param:订单创建路径会从 technology_routing_operation_param 快照参数并写入 technologyRoutingOperationParamId;本次对存量行用 UPDATE ... JOIN 回填该外键,命中 1549 行,剩余 null = 0。production_order(保持 status=3)、production_product_main、production_product_input、production_product_output、quality_inspect、stock_in_record、stock_out_record、stock_inventory 全部未动。
已知缺口:前 2~4 道工序"已完工但查不到报工记录"(现有报工只有末道工序那一条)。如需补齐,见第七节。
| 主数据 | 行数 | 订单级 | 行数 |
|---|---|---|---|
technology_bom_structure |
144 | production_bom_structure |
641 |
technology_routing_operation |
78 | production_order_routing_operation |
334 |
technology_routing_operation_param |
348 | production_operation_task |
334 |
production_order_routing_operation_param |
1549(快照外键 null=0) | ||
production_order_pick |
310 |
订单侧工序数分布与档位一致:**4 道 × 62 单 / 3 道 × 7 单 / 5 道 × 13 单**,合计 82 单。
BOM 行数校验:62×8(STD) + 7×4(CLK) + 13×9(FN) = 641。
改完后按下面的顺序断言,任一不过就整体回滚:
production_order_routing_operation = 334;production_operation_task = 334;production_bom_structure = 641。TechnologyRoutingServiceImpl(主路线,id 倒序)和 ProductionBomStructureServiceImpl.buildDesiredRoutingOperationList(订单级,深度倒序+后序)两套算法在脚本里复刻一遍,对每个订单的**订单级 BOM** 分别推导,断言两边都得到与档位一致的工序序列,且与 production_order_routing_operation 实际落库结果逐条一致。实测 82/82 单通过。production_operation_task.production_order_routing_operation_id 都能查到行;每个 technology_routing_operation_id 都能查到主数据行;原有 82 条 production_product_main 的 production_operation_task_id 仍指向存在的工单。drag_sort 排序(ProductionOperationTaskMapper.xml、ProductionOrderRoutingOperationMapper.xml、TechnologyRoutingOperationMapper.xml),故保留的末道工序行(订单级 ror id 1–82)不会错位显示。复刻两套推导算法的 JS 在 C:\Users\云\AppData\Local\Temp\fix_route.js,可复用其 techPath() / orderPath() 两个函数。
technology_routing_operation id 1–18,也不要删除订单级原有的路线工序行——82 张工单和 82 条订单级工序都引用着它们。只能 UPDATE。input_value 的参数快照。是否补齐需业务确认(涉及写入测量值)。create_user。114.132.189.42:9099 写入,连接参数 dateStrings: true。