Files
MES/yawei-mes/.sql/2026-03-27_v2.0.014_周启威_BOM多级优化.sql
2026-04-02 10:39:03 +08:00

262 lines
11 KiB
SQL
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- ============================================================
-- BOM单多级优化 - 数据库变更脚本
-- 版本: v2.0.016
-- 作者: 周启威
-- 日期: 2026-03-27
-- 说明: 在 md_new_bom_item 新增子BOM引用字段支持多级BOM结构
-- ============================================================
-- --------------------------------------------------
-- 1. md_new_bom_item 表新增字段
-- - sub_bom_id : 引用的子BOM主键ID当 is_sub_bom=1 时有效)
-- - is_sub_bom : 是否子BOM项 0-否 1-是
-- - level : BOM明细层级深度0=顶层物料1=子BOM展开第一层以此类推
-- - is_key_material: 是否关键物料(从 is_key_component 独立出来更通用)
-- --------------------------------------------------
ALTER TABLE `md_new_bom_item`
ADD COLUMN `sub_bom_id` bigint(20) DEFAULT NULL COMMENT '引用的子BOM主键ID多级BOM用' AFTER `process_route`,
ADD COLUMN `is_sub_bom` tinyint(1) DEFAULT '0' COMMENT '是否子BOM项 0-否 1-是' AFTER `sub_bom_id`,
ADD COLUMN `level` int(11) DEFAULT '0' COMMENT 'BOM明细层级深度0=顶层)' AFTER `is_sub_bom`,
ADD KEY `idx_sub_bom_id` (`sub_bom_id`);
-- --------------------------------------------------
-- 2. md_new_bom 表新增字段(辅助多级展示)
-- - max_level : BOM最大层级深度由系统计算写入供前端展示参考
-- - is_multilevel : 是否多级BOM 0-否 1-是
-- - component_count : 直接子件数量含子BOM项
-- --------------------------------------------------
ALTER TABLE `md_new_bom`
ADD COLUMN `max_level` int(11) DEFAULT '0' COMMENT 'BOM最大层级深度' AFTER `sort_order`,
ADD COLUMN `is_multilevel` tinyint(1) DEFAULT '0' COMMENT '是否多级BOM 0-否 1-是' AFTER `max_level`,
ADD COLUMN `component_count` int(11) DEFAULT '0' COMMENT '直接子件数量含子BOM项' AFTER `is_multilevel`;
-- --------------------------------------------------
-- 3. 新增 BOM 层级关系表 md_new_bom_level_relation
-- 用于记录任意BOM到其直接父BOM的引用关系支持快速查询"哪些BOM用到了我"
-- 子BOM被引用时自动写入废弃/删除时清理
-- --------------------------------------------------
CREATE TABLE IF NOT EXISTS `md_new_bom_level_relation` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`parent_bom_id` bigint(20) NOT NULL COMMENT '父级BOM主键ID',
`parent_material_id` bigint(20) NOT NULL COMMENT '父级产品物料ID',
`child_bom_id` bigint(20) NOT NULL COMMENT '子级BOM主键ID',
`child_material_id` bigint(20) NOT NULL COMMENT '子级产品物料ID',
`level` int(11) NOT NULL DEFAULT '1' COMMENT '层级1=直接子级2=孙子级,以此类推)',
`bom_item_id` bigint(20) DEFAULT NULL COMMENT '对应明细行ID来源 md_new_bom_item.id',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_parent_bom_id` (`parent_bom_id`),
KEY `idx_child_bom_id` (`child_bom_id`),
KEY `idx_parent_material_id` (`parent_material_id`),
KEY `idx_child_material_id` (`child_material_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='BOM多级层级关系表';
-- --------------------------------------------------
-- 4. 新增 BOM 多级展开视图 v_bom_multilevel_expand
-- 将多级BOM按层级展开为扁平视图方便前端树形展示和报表
-- 注意MySQL 8.0+ 递归CTE实现
-- --------------------------------------------------
DROP VIEW IF EXISTS `v_bom_multilevel_expand`;
CREATE ALGORITHM=MERGE SQL SECURITY INVOKER VIEW `v_bom_multilevel_expand` AS
SELECT
-- 层级标识(用于前端树形缩进)
CAST(REPEAT(' ', `r`.`level` - 1) AS CHAR(100)) AS `indent_space`,
`r`.`level` AS `bom_level`,
`r`.`parent_bom_id` AS `parent_bom_id`,
`r`.`child_bom_id` AS `bom_id`,
`b`.`bom_number` AS `bom_number`,
`b`.`product_code` AS `product_code`,
`b`.`product_name` AS `product_name`,
`b`.`specification` AS `specification`,
`b`.`version` AS `bom_version`,
`b`.`bom_type` AS `bom_type`,
`b`.`status` AS `bom_status`,
`b`.`base_quantity` AS `base_quantity`,
`b`.`unit_name` AS `unit_name`,
`b`.`max_level` AS `max_level`,
`b`.`is_multilevel` AS `is_multilevel`,
`r`.`bom_item_id` AS `bom_item_id`,
`r`.`parent_material_id` AS `parent_material_id`,
`r`.`child_material_id` AS `child_material_id`,
`b`.`material_cost` AS `direct_material_cost`,
`b`.`labor_cost` AS `direct_labor_cost`,
`b`.`manufacturing_cost` AS `direct_manufacturing_cost`,
`b`.`total_cost` AS `direct_total_cost`,
`b`.`create_time` AS `create_time`,
`b`.`update_time` AS `update_time`
FROM `md_new_bom_level_relation` `r`
INNER JOIN `md_new_bom` `b` ON `r`.`child_bom_id` = `b`.`id`
ORDER BY `r`.`parent_bom_id`, `r`.`level`, `b`.`product_name`;
-- --------------------------------------------------
-- 5. 初始化存储过程 sp_refresh_bom_level_relation
-- 在BOM发布时调用更新 md_new_bom_level_relation 表
-- 逻辑扫描指定BOM的所有明细行按 is_sub_bom=1 的 sub_bom_id 递归收集子BOM
-- --------------------------------------------------
DROP PROCEDURE IF EXISTS `sp_refresh_bom_level_relation`;
DELIMITER $$
CREATE PROCEDURE `sp_refresh_bom_level_relation`(
IN p_bom_id BIGINT -- 起始BOM ID通常为发布的BOM
)
BEGIN
DECLARE v_done INT DEFAULT 0;
DECLARE v_max_level INT DEFAULT 10; -- 最大递归层级保护
-- 临时表:递归收集 BOM->子BOM 关系
DROP TEMPORARY TABLE IF EXISTS tmp_bom_relation;
CREATE TEMPORARY TABLE tmp_bom_relation (
parent_bom_id BIGINT,
parent_material_id BIGINT,
child_bom_id BIGINT,
child_material_id BIGINT,
level INT,
bom_item_id BIGINT
);
-- 递归游标从指定BOM出发收集所有 is_sub_bom=1 的明细行对应的子BOM
INSERT INTO tmp_bom_relation
SELECT DISTINCT
p_bom_id AS parent_bom_id,
b.material_id AS parent_material_id,
item.sub_bom_id AS child_bom_id,
item.material_id AS child_material_id,
1 AS level,
item.id AS bom_item_id
FROM md_new_bom b
INNER JOIN md_new_bom_item item ON item.bom_id = b.id AND item.is_sub_bom = 1 AND item.sub_bom_id IS NOT NULL
WHERE b.id = p_bom_id;
-- 逐层展开最多10层防止死循环
level_loop: WHILE v_done = 0 DO
INSERT IGNORE INTO tmp_bom_relation
SELECT DISTINCT
rel.child_bom_id AS parent_bom_id,
sub.material_id AS parent_material_id,
item.sub_bom_id AS child_bom_id,
item.material_id AS child_material_id,
rel.level + 1 AS level,
item.id AS bom_item_id
FROM tmp_bom_relation rel
INNER JOIN md_new_bom sub ON sub.id = rel.child_bom_id
INNER JOIN md_new_bom_item item ON item.bom_id = sub.id AND item.is_sub_bom = 1 AND item.sub_bom_id IS NOT NULL
WHERE rel.level < v_max_level;
IF ROW_COUNT() = 0 THEN
SET v_done = 1;
END IF;
END WHILE level_loop;
-- 删除旧关系以该BOM为父级的所有记录
DELETE FROM md_new_bom_level_relation WHERE parent_bom_id = p_bom_id;
-- 写入新关系
INSERT INTO md_new_bom_level_relation
(parent_bom_id, parent_material_id, child_bom_id, child_material_id, level, bom_item_id)
SELECT parent_bom_id, parent_material_id, child_bom_id, child_material_id, level, bom_item_id
FROM tmp_bom_relation;
-- 清理临时表
DROP TEMPORARY TABLE tmp_bom_relation;
END$$
DELIMITER ;
-- --------------------------------------------------
-- 6. 触发器md_new_bom_item 增删改后自动刷新层级关系
-- --------------------------------------------------
DROP TRIGGER IF EXISTS `trg_md_new_bom_item_after_insert`;
DELIMITER $$
CREATE TRIGGER `trg_md_new_bom_item_after_insert`
AFTER INSERT ON `md_new_bom_item`
FOR EACH ROW
BEGIN
-- 如果插入的是子BOM行且该BOM已有父BOM引用关系则需要刷新关系表
-- 此处仅记录,待 BOM 发布时统一刷新 sp_refresh_bom_level_relation
-- 实际刷新由业务层在 publishBomVersion 时调用存储过程
-- 此触发器仅用于:删除 bom_item 时连带清理关系记录
END$$
DELIMITER ;
DROP TRIGGER IF EXISTS `trg_md_new_bom_item_after_delete`;
DELIMITER $$
CREATE TRIGGER `trg_md_new_bom_item_after_delete`
AFTER DELETE ON `md_new_bom_item`
FOR EACH ROW
BEGIN
-- 删除时清理孤立的关系记录
DELETE FROM md_new_bom_level_relation WHERE bom_item_id = OLD.id;
END$$
DELIMITER ;
-- --------------------------------------------------
-- 7. 批量初始化脚本对现有已发布的多级BOM执行刷新
-- 示例:对 id = 1 的 BOM 执行一次层级关系构建
-- 正式上线时,取消下面注释并填写实际 BOM ID 列表
-- --------------------------------------------------
-- CALL sp_refresh_bom_level_relation(1);
-- CALL sp_refresh_bom_level_relation(2);
-- CALL sp_refresh_bom_level_relation(3);
-- --------------------------------------------------
-- 8. 更新 BOM 主表的 max_level / is_multilevel 辅助字段
-- 在 sp_refresh_bom_level_relation 执行后调用
-- --------------------------------------------------
DROP PROCEDURE IF EXISTS `sp_update_bom_multilevel_flags`;
DELIMITER $$
CREATE PROCEDURE `sp_update_bom_multilevel_flags`(
IN p_bom_id BIGINT
)
BEGIN
DECLARE v_max_level INT;
DECLARE v_is_multilevel INT;
DECLARE v_component_count INT;
-- 计算最大层级
SELECT COALESCE(MAX(level), 0) INTO v_max_level
FROM md_new_bom_level_relation
WHERE parent_bom_id = p_bom_id;
-- 判断是否多级直接子级中存在子BOM项即为多级
SELECT COUNT(*) INTO v_is_multilevel
FROM md_new_bom_item
WHERE bom_id = p_bom_id AND is_sub_bom = 1 AND sub_bom_id IS NOT NULL;
-- 统计直接子件数量
SELECT COUNT(*) INTO v_component_count
FROM md_new_bom_item
WHERE bom_id = p_bom_id;
UPDATE md_new_bom
SET max_level = v_max_level,
is_multilevel = IF(v_max_level > 0 OR v_is_multilevel > 0, 1, 0),
component_count = v_component_count,
update_time = NOW()
WHERE id = p_bom_id;
END$$
DELIMITER ;
-- 示例调用:
-- CALL sp_update_bom_multilevel_flags(1);