-- ============================================================================ -- Migration: 015_dms_package_a_hardening.sql -- Description: Package A.1 DMS 一致性加固与最小权限拆分 -- 1. 权限最小化拆分:新增 2194 (train:access:credential),从 train_admin 移除 2192 (train:access:manage) -- 2. 床位唯一性约束:检测无重复记录时创建有效床位部分唯一索引 -- 3. 幂等与断言校验 -- ============================================================================ \set ON_ERROR_STOP on BEGIN; -- 1. 注册 2194 (train:access:credential) 门禁凭据下发与注销专用权限 INSERT INTO public.sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) SELECT 2194, '门禁凭据操作', 2060, 4, '#', '', '', 1, 0, 'F', '0', '0', 'train:access:credential', '#', 'admin', CURRENT_TIMESTAMP, '', NULL, '门禁凭据下发与注销权限' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2194); -- 2. 超级管理员角色 (role_id = 1) 授予 2194 INSERT INTO public.sys_role_menu (role_id, menu_id) SELECT 1, 2194 WHERE NOT EXISTS (SELECT 1 FROM public.sys_role_menu WHERE role_id = 1 AND menu_id = 2194); -- 3. 培训管理员角色 (role_id = 3) 调整权限 (MIN-09): -- 授予 2194 (train:access:credential) INSERT INTO public.sys_role_menu (role_id, menu_id) SELECT 3, 2194 WHERE NOT EXISTS (SELECT 1 FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2194); -- 从 train_admin (role_id = 3) 回收 2192 (train:access:manage,硬件映射与维护权限) DELETE FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2192; -- 从 train_admin (role_id = 3) 回收 2263 (train:accommodation:remove,物理删除住宿历史权限) DELETE FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2263; -- 4. 有效床位唯一性约束 (Section 17) -- 先只读检查是否存在已有的活跃床位重复数据 DO $$ DECLARE v_dup_groups INTEGER; BEGIN SELECT COUNT(*) INTO v_dup_groups FROM ( SELECT dorm_id, bed_no FROM train_accommodation WHERE accommodation_status IN (0, 1) AND bed_no IS NOT NULL GROUP BY dorm_id, bed_no HAVING COUNT(*) > 1 ) t; IF v_dup_groups = 0 THEN IF NOT EXISTS ( SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = 'idx_train_acc_dorm_bed_active_uniq' ) THEN CREATE UNIQUE INDEX idx_train_acc_dorm_bed_active_uniq ON train_accommodation (dorm_id, bed_no) WHERE accommodation_status IN (0, 1) AND bed_no IS NOT NULL; RAISE NOTICE 'Created partial unique index idx_train_acc_dorm_bed_active_uniq successfully.'; END IF; ELSE RAISE NOTICE 'ACTIVE_BED_DUPLICATE_GROUP_COUNT = %, skipping unique index creation to prevent business data destruction.', v_dup_groups; END IF; END $$; -- 5. 状态机约束兼容性检查 (train_external_operation) -- 确保 operation_status 允许 DISPATCHING 与 AMBIGUOUS DO $$ DECLARE v_conname TEXT; BEGIN SELECT conname INTO v_conname FROM pg_constraint WHERE conrelid = 'train_external_operation'::regclass AND contype = 'c' AND pg_get_constraintdef(oid) LIKE '%operation_status%'; IF v_conname IS NOT NULL THEN EXECUTE 'ALTER TABLE train_external_operation DROP CONSTRAINT ' || quote_ident(v_conname); EXECUTE 'ALTER TABLE train_external_operation ADD CONSTRAINT chk_external_operation_status ' || 'CHECK (operation_status IN (''CREATED'',''DISPATCHING'',''REQUESTED'',''ACCEPTED'',''PENDING'',''RETRY_WAIT'',''AMBIGUOUS'',''SUCCESS'',''FAILED'',''TIMEOUT''))'; END IF; END $$; -- 6. 后置断言验证 DO $$ BEGIN -- 断言 1: 2194 已正确注册 IF (SELECT count(*) FROM public.sys_menu WHERE menu_id = 2194 AND perms = 'train:access:credential' AND status = '0') <> 1 THEN RAISE EXCEPTION 'Assertion failed: menu 2194 train:access:credential not found or invalid'; END IF; -- 断言 2: train_admin (role_id = 3) 拥有 2194 (train:access:credential) IF (SELECT count(*) FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2194) <> 1 THEN RAISE EXCEPTION 'Assertion failed: role 3 not granted menu 2194'; END IF; -- 断言 3: train_admin (role_id = 3) 不得拥有 2192 (train:access:manage) IF (SELECT count(*) FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2192) <> 0 THEN RAISE EXCEPTION 'Assertion failed: role 3 still has menu 2192 (train:access:manage)'; END IF; -- 断言 4: train_admin (role_id = 3) 不得拥有 2263 (train:accommodation:remove) IF (SELECT count(*) FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2263) <> 0 THEN RAISE EXCEPTION 'Assertion failed: role 3 has forbidden menu 2263 (train:accommodation:remove)'; END IF; -- 断言 5: train_admin 拥有 2264 (train:accommodation:add) IF (SELECT count(*) FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2264) <> 1 THEN RAISE EXCEPTION 'Assertion failed: role 3 missing menu 2264 (train:accommodation:add)'; END IF; END $$; COMMIT;