-- 本文件是人工执行的 MySQL 前向补丁,未自动执行。 -- 前置条件:必须先成功执行 20260727_promote_business_menus_mysql.sql,将三个业务目录提升为一级菜单。 -- 应用本补丁后禁止逆序重跑旧补丁;所有补丁只允许按日期顺序前向执行。 -- 执行前备份数据库,并在维护窗口使用独立连接运行标准 mysql batch。 -- 旧图标和目标图标状态均可通过写前校验,因此本补丁可幂等重跑。 -- 标准 mysql batch 遇错后必须退出并关闭连接,使未提交事务回滚;禁止使用 mysql --force。 drop temporary table if exists business_menu_icon_expected; create temporary table business_menu_icon_expected ( menu_id bigint not null, menu_name varchar(50) not null, path varchar(200), component varchar(255), query varchar(255), route_name varchar(50), is_frame int, is_cache int, menu_type char(1), visible char(1), status char(1), perms varchar(100), parent_id bigint not null, order_num int not null, old_icon varchar(100) not null, new_icon varchar(100) not null, primary key (menu_id) ) engine=innodb; insert into business_menu_icon_expected ( menu_id, menu_name, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, parent_id, order_num, old_icon, new_icon ) values (121, '基础资料维护', 'businessMaster', null, '', '', 1, 0, 'M', '0', '0', '', 0, 1, '#', 'dict'), (122, '业务管理', 'businessOperation', null, '', '', 1, 0, 'M', '0', '0', '', 0, 2, '#', 'form'), (123, '报表查询', 'businessReport', null, '', '', 1, 0, 'M', '0', '0', '', 0, 3, '#', 'chart'); drop temporary table if exists business_menu_icon_guard; create temporary table business_menu_icon_guard ( guard_token varchar(64) not null, primary key (guard_token) ) engine=innodb; start transaction; select menu_id from sys_menu where menu_id in (121, 122, 123) order by menu_id for update; -- 写前只接受三行全部使用旧占位图标或全部使用目标图标。 insert into business_menu_icon_guard (guard_token) select guard_token from ( select 'business-menu-icon-precondition-conflict' as guard_token union all select if( count(actual.menu_id) = 3 and sum( (binary actual.menu_name <=> binary expected.menu_name) and (binary actual.path <=> binary expected.path) and (binary actual.component <=> binary expected.component) and (binary actual.query <=> binary expected.query) and (binary actual.route_name <=> binary expected.route_name) and (actual.is_frame <=> expected.is_frame) and (actual.is_cache <=> expected.is_cache) and (binary actual.menu_type <=> binary expected.menu_type) and (binary actual.visible <=> binary expected.visible) and (binary actual.status <=> binary expected.status) and (binary actual.perms <=> binary expected.perms) and (actual.parent_id <=> expected.parent_id) and (actual.order_num <=> expected.order_num) ) = 3 and ( sum((binary actual.icon <=> binary expected.old_icon)) = 3 or sum((binary actual.icon <=> binary expected.new_icon)) = 3 ), 'business-menu-icon-precondition-verified', 'business-menu-icon-precondition-conflict' ) as guard_token from business_menu_icon_expected as expected left join sys_menu as actual on actual.menu_id = expected.menu_id ) as precondition_guard_rows; -- 唯一永久 DML:只更新三个业务一级目录的图标。 update sys_menu set icon = case menu_id when 121 then 'dict' when 122 then 'form' when 123 then 'chart' else icon end where menu_id in (121, 122, 123); -- 提交前再次核对三行稳定身份和目标图标。 insert into business_menu_icon_guard (guard_token) select guard_token from ( select 'business-menu-icon-postcondition-conflict' as guard_token union all select if( count(actual.menu_id) = 3 and sum( (binary actual.menu_name <=> binary expected.menu_name) and (binary actual.path <=> binary expected.path) and (binary actual.component <=> binary expected.component) and (binary actual.query <=> binary expected.query) and (binary actual.route_name <=> binary expected.route_name) and (actual.is_frame <=> expected.is_frame) and (actual.is_cache <=> expected.is_cache) and (binary actual.menu_type <=> binary expected.menu_type) and (binary actual.visible <=> binary expected.visible) and (binary actual.status <=> binary expected.status) and (binary actual.perms <=> binary expected.perms) and (actual.parent_id <=> expected.parent_id) and (actual.order_num <=> expected.order_num) ) = 3 and sum((binary actual.icon <=> binary expected.new_icon)) = 3, 'business-menu-icon-postcondition-verified', 'business-menu-icon-postcondition-conflict' ) as guard_token from business_menu_icon_expected as expected left join sys_menu as actual on actual.menu_id = expected.menu_id ) as postcondition_guard_rows; commit; drop temporary table business_menu_icon_guard; drop temporary table business_menu_icon_expected; select menu_id, menu_name, parent_id, order_num, path, icon from sys_menu where menu_id in (121, 122, 123) order by order_num, menu_id;