Files
MES/yawei-mes/.sql/2026-03-27_v2.0.014_周启威_BOM多级优化.sql

262 lines
11 KiB
MySQL
Raw Permalink Normal View History

2026-04-02 10:38:23 +08:00
-- ============================================================
-- 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);