编辑 | blame | 历史 | 原始文档

历史生产订单/工单工艺路线整改方案

状态:**已执行(2026-09-21)**。主数据侧(18 条工艺路线)与订单侧 82 张生产订单及全部工单均已改造完成并通过推导校验。

本文档记录改造的原因、推导机制、执行内容、最终结果与校验方法,供后续排查与前端联调参考。


一、改造背景与目标

改造前 18 条工艺路线每条**只有 1 道工序**,且 18 条路线同名、工序序列完全相同、BOM 配方也完全相同。原因是两处推导机制把同层多工序去重合并,同时造数据时按同一模板复制。

两个目标:

  1. 让工序数合理——按产品性质拆成 3 档工艺,不再全部单工序。
  2. 让路线差异化——不同产品用不同工序序列、不同配方,避免"一模一样"。

二、必须先理解的推导机制

这两段逻辑决定了路线与 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 排序。

由此得出的硬约束

  1. 要得到有序的 N 道工序,必须是 N 层嵌套,**且深层节点的 id 必须大于浅层**(因为主路线走 id 倒序)。平铺的同层多工序会让两条路径推出**相反**的顺序。
  2. 同一父节点下的所有行**必须填同一个工序 id**(前端 validateProcessConsistency 与 handleBomProcessChange 都会提示"同一层级的工序必须一致")。
  3. 只有**最后一道工序**可以设 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 × 父节点需求量,根节点需求量 = 订单数量。


三、主数据侧改造内容(已执行)

1. 三档工艺

按产品性质把 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 也跟着分档,不再全部同名:

  • CLK:熟料破碎粉磨与包装工艺路线
  • STD:粉剂类膨胀剂常规工艺路线
  • FN:复合功能型外加剂工艺路线

2. 工序与半成品主数据

对象 表 变更
工序 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 保留并改写为各路线末道工序(出厂检验),其余新增

3. 三档 BOM 形状([ ] 内为该行的消耗工序)

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 顺序,插入时它排在熟料/石膏/石灰石**之前**。

4. 18 套独立配方

每条路线一套 [熟料, 石膏, 石灰石, 粉煤灰] 配比(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 为熟料单料,无掺合料。


四、订单侧执行内容

1. 现状盘点(改造前)

表 行数 说明
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 → 末道工序的完整链路,**不动**。

2. 引用关系(动数据前必须知道)

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)而不是删除重建。

3. 实际执行步骤(单事务)

  1. production_bom_structure:按该订单 bom_id 对应主数据的档位形状镜像重嵌套(删除原平铺行后按深度递增顺序重插),根节点 demanded_quantity 取原根值,逐层 demanded_quantity = unit_quantity × 父行demanded。
  2. production_order_routing_operation:原行 UPDATE 为末道工序(47),新增 2/3/4 行(视档位),drag_sort 依次递增;technology_routing_operation_id 指向主数据侧对应档位的路线工序 id。
  3. production_operation_task:新增对应工单,status=4,plan_quantity = complete_quantity = 订单数量,工单号 GD + yyyyMMdd + 3位流水(延续号,避免重号),计划/实际时间落在订单计划开始 ~ 2026-09-20 之间且逐道递增;create_user / dept_id 沿用该订单原值。
  4. production_order_pick:310 条领料按投入物料改挂到真正消耗它的工序(464/461/462 → 42;465 → 43(STD) / 49(FN)),并按组内配比重算 quantity / demanded_quantity / actual_qty(2 位小数,末行补差)。
  5. production_order_routing_operation_param:订单创建路径会从 technology_routing_operation_param 快照参数并写入 technologyRoutingOperationParamId;本次对存量行用 UPDATE ... JOIN 回填该外键,命中 1549 行,剩余 null = 0。

4. 不改动的表

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。


六、校验方法

改完后按下面的顺序断言,任一不过就整体回滚:

  1. 条数:production_order_routing_operation = 334;production_operation_task = 334;production_bom_structure = 641。
  2. 两套推导算法一致性:把 TechnologyRoutingServiceImpl(主路线,id 倒序)和 ProductionBomStructureServiceImpl.buildDesiredRoutingOperationList(订单级,深度倒序+后序)两套算法在脚本里复刻一遍,对每个订单的**订单级 BOM** 分别推导,断言两边都得到与档位一致的工序序列,且与 production_order_routing_operation 实际落库结果逐条一致。实测 82/82 单通过。
    这一步很关键:它保证在前端把某条路线重新保存一遍,顺序不会乱。
  3. 引用完整性:每个 production_operation_task.production_order_routing_operation_id 都能查到行;每个 technology_routing_operation_id 都能查到主数据行;原有 82 条 production_product_main 的 production_operation_task_id 仍指向存在的工单。
  4. 排序:三处列表查询均按 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。
  • 不要在订单级只扩工序行而不扩 BOM。订单级的 BOM 页和路线页是两个 tab,BOM 不一起重嵌套的话,前端重新保存路线时会按旧 BOM 推导,把多道工序又打回 1 道。
  • 报工参数快照缺口(待定):82 条既有报工只挂了工序,尚无带实测 input_value 的参数快照。是否补齐需业务确认(涉及写入测量值)。
  • 中间品库存:CLK/STD/FN 的中间品(468/469/470/472)库存为手动播的种子数据,无对应生产来源。若后续补前几道工序的报工/入库,可覆盖这些手动数据。
  • 日期:所有新数据时间落在订单计划开始 ~ 2026-09-20 之间,且工序顺序递增。
  • 人员:未使用 admin(user_id 1),沿用各订单现有的 create_user。
  • 写库方式:本地后端/Redis 起不来,数据通过 Node + mysql2 脚本直连 114.132.189.42:9099 写入,连接参数 dateStrings: true。