-- ============================================================
-- 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);
