-- ============================================================================== -- RailwaySystem QA Bootstrap: 03_system_permissions_and_menus.sql (规范事务版) -- 目标数据库: railwaysystem_fusion_qa_20261013 (schema: public) -- 来源追溯: sql/system/kingbase/ (004, 005, 009, 010, 012, 014, 015) -- 包含内容: 培训业务三大角色 (train_admin, train_teacher, train_student)、 -- 业务端系统菜单、按钮权限、身份绑定索引与角色菜单关联 -- 事务边界: 严格单一事务 (首尾单次 BEGIN ... COMMIT,无内部嵌套事务) -- ============================================================================== BEGIN; SET search_path TO public, pg_catalog, sys_catalog; -- >>> 来源模块: sql/system/kingbase/004_rbac_identity_binding.sql >>> -- ============================================================================= -- RailwaySystem 数据库 RBAC 迁移脚本: 004_rbac_identity_binding.sql -- 适用数据库: KingbaseES V009R003C018 (schema: public) -- 执行目标: 建立 sys_user 与业务主体(教师/学员)物理约束与部分唯一索引 -- 事务边界: 全程在显式事务 BEGIN ... COMMIT 中执行 -- 幂等保证: 仅使用 ADD COLUMN IF NOT EXISTS 与 CREATE INDEX IF NOT EXISTS,严禁 DROP / TRUNCATE -- 设计依据: docs/development/rbac-r2/01_identity_binding_design.md 及 06_migration_plan.md -- ============================================================================= -- 步骤 1: 确保 user_id 列存在 (幂等容错,已存在则静默跳过) ALTER TABLE public.train_teacher ADD COLUMN IF NOT EXISTS user_id BIGINT; ALTER TABLE public.train_student ADD COLUMN IF NOT EXISTS user_id BIGINT; -- 步骤 2: 建立教师账号部分唯一索引 (无 DROP,幂等容错,忽略已删除 del_flag='2' 与空值) CREATE UNIQUE INDEX IF NOT EXISTS uq_train_teacher_user_id ON public.train_teacher(user_id) WHERE user_id IS NOT NULL AND del_flag = '0'; -- 步骤 3: 建立学员账号部分唯一索引 (无 DROP,幂等容错,忽略已删除 del_flag='2' 与空值) CREATE UNIQUE INDEX IF NOT EXISTS uq_train_student_user_id ON public.train_student(user_id) WHERE user_id IS NOT NULL AND del_flag = '0'; -- 步骤 4: 更新字段元数据注释 COMMENT ON COLUMN train_teacher.user_id IS '关联sys_user.user_id (唯一绑定教师登录账号)'; COMMENT ON COLUMN train_student.user_id IS '关联sys_user.user_id (唯一绑定学员登录账号)'; -- 步骤 5: 迁移完整性校验 DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_indexes WHERE tablename = 'train_teacher' AND indexname = 'uq_train_teacher_user_id' ) THEN RAISE EXCEPTION 'uq_train_teacher_user_id partial unique index creation failed'; END IF; IF NOT EXISTS ( SELECT 1 FROM pg_indexes WHERE tablename = 'train_student' AND indexname = 'uq_train_student_user_id' ) THEN RAISE EXCEPTION 'uq_train_student_user_id partial unique index creation failed'; END IF; END $$; -- >>> 来源模块: sql/system/kingbase/005_training_roles_permissions.sql >>> -- ============================================================================= -- RailwaySystem 数据库 RBAC 迁移脚本: 005_training_roles_permissions.sql -- 适用数据库: KingbaseES V009R003C018 (schema: public) -- 执行目标: 预置培训业务三角色、统一班牌权限命名空间、补齐按钮权限与角色关联 -- 事务边界: 全程在显式事务 BEGIN ... COMMIT 中执行 -- 防御保证: 内置 Collision Guard 静态检查,绝不覆盖已有外来资源,冲突即显式 ABORT -- 设计依据: docs/development/rbac-r2/02_role_permission_matrix.md 及 06_migration_plan.md -- ============================================================================= -- ============================================================================= -- 步骤 0: ID 碰撞安全守卫 (Migration Collision Guard) -- ============================================================================= DO $$ DECLARE rec RECORD; BEGIN -- 0.1 检查 role_id 3, 4, 5 的碰撞与业务键一致性 FOR rec IN SELECT role_id, role_key FROM public.sys_role WHERE role_id IN (3, 4, 5) LOOP IF rec.role_id = 3 AND rec.role_key <> 'train_admin' THEN RAISE EXCEPTION 'Collision detected: sys_role.role_id=3 is already occupied by role_key=%', rec.role_key; ELSIF rec.role_id = 4 AND rec.role_key <> 'train_teacher' THEN RAISE EXCEPTION 'Collision detected: sys_role.role_id=4 is already occupied by role_key=%', rec.role_key; ELSIF rec.role_id = 5 AND rec.role_key <> 'train_student' THEN RAISE EXCEPTION 'Collision detected: sys_role.role_id=5 is already occupied by role_key=%', rec.role_key; END IF; END LOOP; -- 0.2 检查 role_key 存在时是否与预期 role_id 发生错位 FOR rec IN SELECT role_id, role_key FROM public.sys_role WHERE role_key IN ('train_admin', 'train_teacher', 'train_student') LOOP IF rec.role_key = 'train_admin' AND rec.role_id <> 3 THEN RAISE EXCEPTION 'Collision detected: role_key=train_admin exists with mismatched role_id=%', rec.role_id; ELSIF rec.role_key = 'train_teacher' AND rec.role_id <> 4 THEN RAISE EXCEPTION 'Collision detected: role_key=train_teacher exists with mismatched role_id=%', rec.role_id; ELSIF rec.role_key = 'train_student' AND rec.role_id <> 5 THEN RAISE EXCEPTION 'Collision detected: role_key=train_student exists with mismatched role_id=%', rec.role_id; END IF; END LOOP; -- 0.3 检查 menu_id 2201 ~ 2244 的碰撞:若ID已存在,必须属于预期本系统权限,严禁覆盖外来资源 IF EXISTS ( SELECT 1 FROM public.sys_menu WHERE menu_id BETWEEN 2201 AND 2244 AND (perms IS NULL OR perms NOT LIKE 'train:%') ) THEN RAISE EXCEPTION 'Collision detected: sys_menu.menu_id in 2201..2244 is occupied by non-train permissions'; END IF; END $$; -- ============================================================================= -- 步骤 1: 预置 3 个核心业务角色 (train_admin, train_teacher, train_student) -- ============================================================================= INSERT INTO public.sys_role (role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) SELECT 3, '培训管理员', 'train_admin', 3, '1', 1, 1, '0', '0', 'admin', CURRENT_TIMESTAMP, '', NULL, '一期业务全局管理角色' WHERE NOT EXISTS (SELECT 1 FROM public.sys_role WHERE role_id = 3 OR role_key = 'train_admin'); INSERT INTO public.sys_role (role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) SELECT 4, '培训教师', 'train_teacher', 4, '2', 1, 1, '0', '0', 'admin', CURRENT_TIMESTAMP, '', NULL, '教师专属教学工作台' WHERE NOT EXISTS (SELECT 1 FROM public.sys_role WHERE role_id = 4 OR role_key = 'train_teacher'); INSERT INTO public.sys_role (role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) SELECT 5, '培训学员', 'train_student', 5, '2', 1, 1, '0', '0', 'admin', CURRENT_TIMESTAMP, '', NULL, '学员专属个人培训中心' WHERE NOT EXISTS (SELECT 1 FROM public.sys_role WHERE role_id = 5 OR role_key = 'train_student'); -- ============================================================================= -- 步骤 2: 规范统一电子班牌权限命名空间 (system:board:* -> train:board:*) -- ============================================================================= UPDATE public.sys_menu SET perms = REPLACE(perms, 'system:board:', 'train:board:'), update_by = 'admin', update_time = CURRENT_TIMESTAMP WHERE perms LIKE 'system:board:%'; -- ============================================================================= -- 步骤 3: 补齐基础培训管理模块按钮级权限 (sys_menu, menu_type='F') -- ============================================================================= -- 3.1 教师管理 (parent: 2155) 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, remark) SELECT 2201, '教师查询', 2155, 1, '#', '', '', 1, 0, 'F', '0', '0', 'train:teacher:list', '#', 'admin', CURRENT_TIMESTAMP, '教师列表查询' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2201 OR perms = 'train:teacher:list'); 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, remark) SELECT 2202, '教师详情', 2155, 2, '#', '', '', 1, 0, 'F', '0', '0', 'train:teacher:query', '#', 'admin', CURRENT_TIMESTAMP, '教师详情查询' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2202 OR perms = 'train:teacher:query'); 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, remark) SELECT 2203, '教师新增', 2155, 3, '#', '', '', 1, 0, 'F', '0', '0', 'train:teacher:add', '#', 'admin', CURRENT_TIMESTAMP, '教师新增' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2203 OR perms = 'train:teacher:add'); 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, remark) SELECT 2204, '教师修改', 2155, 4, '#', '', '', 1, 0, 'F', '0', '0', 'train:teacher:edit', '#', 'admin', CURRENT_TIMESTAMP, '教师修改' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2204 OR perms = 'train:teacher:edit'); 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, remark) SELECT 2241, '教师账号关联', 2155, 5, '#', '', '', 1, 0, 'F', '0', '0', 'train:teacher:bindUser', '#', 'admin', CURRENT_TIMESTAMP, '教师系统账号关联' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2241 OR perms = 'train:teacher:bindUser'); 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, remark) SELECT 2242, '教师账号解绑', 2155, 6, '#', '', '', 1, 0, 'F', '0', '0', 'train:teacher:unbindUser', '#', 'admin', CURRENT_TIMESTAMP, '教师系统账号解绑' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2242 OR perms = 'train:teacher:unbindUser'); 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, remark) SELECT 2245, '教师删除', 2155, 7, '#', '', '', 1, 0, 'F', '0', '0', 'train:teacher:remove', '#', 'admin', CURRENT_TIMESTAMP, '教师档案安全删除' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2245 OR perms = 'train:teacher:remove'); -- 3.2 学员管理 (parent: 2156) 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, remark) SELECT 2205, '学员查询', 2156, 1, '#', '', '', 1, 0, 'F', '0', '0', 'train:student:list', '#', 'admin', CURRENT_TIMESTAMP, '学员列表查询' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2205 OR perms = 'train:student:list'); 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, remark) SELECT 2206, '学员详情', 2156, 2, '#', '', '', 1, 0, 'F', '0', '0', 'train:student:query', '#', 'admin', CURRENT_TIMESTAMP, '学员详情查询' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2206 OR perms = 'train:student:query'); 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, remark) SELECT 2207, '学员新增', 2156, 3, '#', '', '', 1, 0, 'F', '0', '0', 'train:student:add', '#', 'admin', CURRENT_TIMESTAMP, '学员新增' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2207 OR perms = 'train:student:add'); 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, remark) SELECT 2243, '学员账号关联', 2156, 4, '#', '', '', 1, 0, 'F', '0', '0', 'train:student:bindUser', '#', 'admin', CURRENT_TIMESTAMP, '学员系统账号关联' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2243 OR perms = 'train:student:bindUser'); 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, remark) SELECT 2244, '学员账号解绑', 2156, 5, '#', '', '', 1, 0, 'F', '0', '0', 'train:student:unbindUser', '#', 'admin', CURRENT_TIMESTAMP, '学员系统账号解绑' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2244 OR perms = 'train:student:unbindUser'); 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, remark) SELECT 2246, '学员删除', 2156, 6, '#', '', '', 1, 0, 'F', '0', '0', 'train:student:remove', '#', 'admin', CURRENT_TIMESTAMP, '学员档案安全删除' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2246 OR perms = 'train:student:remove'); -- 3.3 教室管理 (parent: 2157) 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, remark) SELECT 2208, '教室查询', 2157, 1, '#', '', '', 1, 0, 'F', '0', '0', 'train:classroom:list', '#', 'admin', CURRENT_TIMESTAMP, '教室列表查询' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2208 OR perms = 'train:classroom:list'); 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, remark) SELECT 2209, '教室修改', 2157, 2, '#', '', '', 1, 0, 'F', '0', '0', 'train:classroom:edit', '#', 'admin', CURRENT_TIMESTAMP, '教室修改' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2209 OR perms = 'train:classroom:edit'); -- 3.4 实训场地 (parent: 2158) 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, remark) SELECT 2210, '场地查询', 2158, 1, '#', '', '', 1, 0, 'F', '0', '0', 'train:site:list', '#', 'admin', CURRENT_TIMESTAMP, '实训场地列表查询' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2210 OR perms = 'train:site:list'); 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, remark) SELECT 2211, '场地修改', 2158, 2, '#', '', '', 1, 0, 'F', '0', '0', 'train:site:edit', '#', 'admin', CURRENT_TIMESTAMP, '实训场地修改' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2211 OR perms = 'train:site:edit'); -- 3.5 宿舍管理 (parent: 2161) 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, remark) SELECT 2212, '宿舍查询', 2161, 1, '#', '', '', 1, 0, 'F', '0', '0', 'train:dormitory:list', '#', 'admin', CURRENT_TIMESTAMP, '宿舍列表查询' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2212 OR perms = 'train:dormitory:list'); 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, remark) SELECT 2213, '宿舍修改', 2161, 2, '#', '', '', 1, 0, 'F', '0', '0', 'train:dormitory:edit', '#', 'admin', CURRENT_TIMESTAMP, '宿舍修改' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2213 OR perms = 'train:dormitory:edit'); -- 3.6 课程管理 (parent: 2163) 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, remark) SELECT 2214, '课程查询', 2163, 1, '#', '', '', 1, 0, 'F', '0', '0', 'train:course:list', '#', 'admin', CURRENT_TIMESTAMP, '课程列表查询' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2214 OR perms = 'train:course:list'); 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, remark) SELECT 2215, '课程详情', 2163, 2, '#', '', '', 1, 0, 'F', '0', '0', 'train:course:query', '#', 'admin', CURRENT_TIMESTAMP, '课程详情查询' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2215 OR perms = 'train:course:query'); 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, remark) SELECT 2216, '课程新增', 2163, 3, '#', '', '', 1, 0, 'F', '0', '0', 'train:course:add', '#', 'admin', CURRENT_TIMESTAMP, '课程新增' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2216 OR perms = 'train:course:add'); 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, remark) SELECT 2217, '课程修改', 2163, 4, '#', '', '', 1, 0, 'F', '0', '0', 'train:course:edit', '#', 'admin', CURRENT_TIMESTAMP, '课程修改' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2217 OR perms = 'train:course:edit'); -- 3.7 培训计划 (parent: 2164) 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, remark) SELECT 2218, '计划查询', 2164, 1, '#', '', '', 1, 0, 'F', '0', '0', 'train:plan:list', '#', 'admin', CURRENT_TIMESTAMP, '培训计划列表' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2218 OR perms = 'train:plan:list'); 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, remark) SELECT 2219, '计划详情', 2164, 2, '#', '', '', 1, 0, 'F', '0', '0', 'train:plan:query', '#', 'admin', CURRENT_TIMESTAMP, '培训计划详情' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2219 OR perms = 'train:plan:query'); 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, remark) SELECT 2220, '计划新增', 2164, 3, '#', '', '', 1, 0, 'F', '0', '0', 'train:plan:add', '#', 'admin', CURRENT_TIMESTAMP, '培训计划新增' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2220 OR perms = 'train:plan:add'); 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, remark) SELECT 2221, '计划修改', 2164, 4, '#', '', '', 1, 0, 'F', '0', '0', 'train:plan:edit', '#', 'admin', CURRENT_TIMESTAMP, '培训计划修改' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2221 OR perms = 'train:plan:edit'); 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, remark) SELECT 2222, '计划结训', 2164, 5, '#', '', '', 1, 0, 'F', '0', '0', 'train:plan:complete', '#', 'admin', CURRENT_TIMESTAMP, '培训计划结束归档' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2222 OR perms = 'train:plan:complete'); -- 3.8 课次与排课 (parent: 2164) 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, remark) SELECT 2223, '排课查询', 2164, 6, '#', '', '', 1, 0, 'F', '0', '0', 'train:lesson:query', '#', 'admin', CURRENT_TIMESTAMP, '排课课表查询' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2223 OR perms = 'train:lesson:query'); 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, remark) SELECT 2224, '排课新增', 2164, 7, '#', '', '', 1, 0, 'F', '0', '0', 'train:lesson:add', '#', 'admin', CURRENT_TIMESTAMP, '排课课次新增' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2224 OR perms = 'train:lesson:add'); 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, remark) SELECT 2225, '排课修改', 2164, 8, '#', '', '', 1, 0, 'F', '0', '0', 'train:lesson:edit', '#', 'admin', CURRENT_TIMESTAMP, '排课课次修改' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2225 OR perms = 'train:lesson:edit'); 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, remark) SELECT 2226, '排课删除', 2164, 9, '#', '', '', 1, 0, 'F', '0', '0', 'train:lesson:remove', '#', 'admin', CURRENT_TIMESTAMP, '排课课次删除' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2226 OR perms = 'train:lesson:remove'); -- 3.9 参训学员分班 (parent: 2164) 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, remark) SELECT 2227, '参训名单查询', 2164, 10, '#', '', '', 1, 0, 'F', '0', '0', 'train:enrollment:list', '#', 'admin', CURRENT_TIMESTAMP, '参训花名册列表' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2227 OR perms = 'train:enrollment:list'); 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, remark) SELECT 2228, '参训记录详情', 2164, 11, '#', '', '', 1, 0, 'F', '0', '0', 'train:enrollment:query', '#', 'admin', CURRENT_TIMESTAMP, '参训记录详情' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2228 OR perms = 'train:enrollment:query'); 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, remark) SELECT 2229, '参训人员添加', 2164, 12, '#', '', '', 1, 0, 'F', '0', '0', 'train:enrollment:add', '#', 'admin', CURRENT_TIMESTAMP, '参训学员录入' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2229 OR perms = 'train:enrollment:add'); 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, remark) SELECT 2230, '参训人员修改', 2164, 13, '#', '', '', 1, 0, 'F', '0', '0', 'train:enrollment:edit', '#', 'admin', CURRENT_TIMESTAMP, '参训学员状态修改' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2230 OR perms = 'train:enrollment:edit'); 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, remark) SELECT 2231, '参训人员移除', 2164, 14, '#', '', '', 1, 0, 'F', '0', '0', 'train:enrollment:remove', '#', 'admin', CURRENT_TIMESTAMP, '参训学员移除' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2231 OR perms = 'train:enrollment:remove'); 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, remark) SELECT 2232, '参训人员导入', 2164, 15, '#', '', '', 1, 0, 'F', '0', '0', 'train:enrollment:import', '#', 'admin', CURRENT_TIMESTAMP, '参训学员批量导入' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2232 OR perms = 'train:enrollment:import'); -- 3.10 电子班牌标准权限 (parent: 2174) 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, remark) SELECT 2233, '班牌列表', 2174, 1, '#', '', '', 1, 0, 'F', '0', '0', 'train:board:list', '#', 'admin', CURRENT_TIMESTAMP, '电子班牌列表' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2233 OR perms = 'train:board:list'); 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, remark) SELECT 2234, '班牌详情', 2174, 2, '#', '', '', 1, 0, 'F', '0', '0', 'train:board:query', '#', 'admin', CURRENT_TIMESTAMP, '电子班牌详情' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2234 OR perms = 'train:board:query'); 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, remark) SELECT 2235, '班牌新增', 2174, 3, '#', '', '', 1, 0, 'F', '0', '0', 'train:board:add', '#', 'admin', CURRENT_TIMESTAMP, '电子班牌新增' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2235 OR perms = 'train:board:add'); 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, remark) SELECT 2236, '班牌修改', 2174, 4, '#', '', '', 1, 0, 'F', '0', '0', 'train:board:edit', '#', 'admin', CURRENT_TIMESTAMP, '电子班牌修改' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2236 OR perms = 'train:board:edit'); 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, remark) SELECT 2237, '班牌删除', 2174, 5, '#', '', '', 1, 0, 'F', '0', '0', 'train:board:remove', '#', 'admin', CURRENT_TIMESTAMP, '电子班牌删除' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2237 OR perms = 'train:board:remove'); 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, remark) SELECT 2238, '班牌导出', 2174, 6, '#', '', '', 1, 0, 'F', '0', '0', 'train:board:export', '#', 'admin', CURRENT_TIMESTAMP, '电子班牌导出' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2238 OR perms = 'train:board:export'); -- 3.11 自服务专属菜单与页面路由 (挂载于根节点 0,供教师与学员在侧边栏直达工作台) 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, remark) SELECT 2239, '教师工作台', 0, 99, 'teacher', 'train/me/teacher/index', '', 1, 0, 'C', '0', '0', 'train:me:teacher', 'education', 'admin', CURRENT_TIMESTAMP, '教师专属工作台页面及自服务权限' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2239 OR perms = 'train:me:teacher'); 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, remark) SELECT 2240, '学员中心', 0, 100, 'student', 'train/me/student/index', '', 1, 0, 'C', '0', '0', 'train:me:student', 'user', 'admin', CURRENT_TIMESTAMP, '学员专属个人培训中心页面及自服务权限' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2240 OR perms = 'train:me:student'); -- ============================================================================= -- 步骤 4: 为业务角色分配菜单权限映射 (sys_role_menu) -- ============================================================================= -- 4.1 train_admin (role_id = 3): 授予全部培训模块菜单与按钮 INSERT INTO public.sys_role_menu (role_id, menu_id) SELECT 3, m.menu_id FROM public.sys_menu m WHERE (m.menu_id BETWEEN 2154 AND 2183 OR m.menu_id BETWEEN 2201 AND 2238 OR m.menu_id BETWEEN 2241 AND 2244) AND NOT EXISTS ( SELECT 1 FROM public.sys_role_menu rm WHERE rm.role_id = 3 AND rm.menu_id = m.menu_id ); -- 4.2 train_teacher (role_id = 4): 授予教师专属自服务权限 INSERT INTO public.sys_role_menu (role_id, menu_id) SELECT 4, 2239 WHERE NOT EXISTS ( SELECT 1 FROM public.sys_role_menu rm WHERE rm.role_id = 4 AND rm.menu_id = 2239 ); -- 4.3 train_student (role_id = 5): 授予学员专属自服务权限 INSERT INTO public.sys_role_menu (role_id, menu_id) SELECT 5, 2240 WHERE NOT EXISTS ( SELECT 1 FROM public.sys_role_menu rm WHERE rm.role_id = 5 AND rm.menu_id = 2240 ); -- ============================================================================= -- 步骤 5: 迁移验证自检 -- ============================================================================= DO $$ BEGIN IF (SELECT count(*) FROM public.sys_role WHERE role_key IN ('train_admin', 'train_teacher', 'train_student')) <> 3 THEN RAISE EXCEPTION 'Training roles count verification failed'; END IF; IF NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE perms = 'train:me:teacher') THEN RAISE EXCEPTION 'Teacher self-service permission missing'; END IF; IF NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE perms = 'train:me:student') THEN RAISE EXCEPTION 'Student self-service permission missing'; END IF; IF NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE perms = 'train:teacher:bindUser') THEN RAISE EXCEPTION 'Teacher binding permission missing'; END IF; IF NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE perms = 'train:student:bindUser') THEN RAISE EXCEPTION 'Student binding permission missing'; END IF; IF NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE perms = 'train:teacher:remove') THEN RAISE EXCEPTION 'Teacher safe delete permission missing'; END IF; IF NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE perms = 'train:student:remove') THEN RAISE EXCEPTION 'Student safe delete permission missing'; END IF; IF EXISTS (SELECT 1 FROM public.sys_menu WHERE perms LIKE 'system:board:%') THEN RAISE EXCEPTION 'Legacy system:board:* permission namespace still exists'; END IF; END $$; -- >>> 来源模块: sql/system/kingbase/009_dorm_authorization_orchestration.sql >>> -- ============================================================================== -- 脚本名称: 009_dorm_authorization_orchestration.sql -- 业务模块: DMS/外部物联网集成 - 宿舍门禁业务授权编排扩展模型 (R.3B Wave 3A) -- 适用数据库: KingbaseES V8 / V9 (PostgreSQL 方言兼容) -- 安全编号: 009 (001-003 基线, 004-005 R.2分支占用, 006 Wave0, 007 保留禁止执行, 008 Wave1) -- 幂等性设计: 支持重复安全执行,纯增量列结构与索引,不含破坏性语句 -- 安全红线: 严禁引入明文密码列,密码仅在内存短暂留存,严禁落库 -- ============================================================================== -- 1. 扩展 train_dorm_auth 业务授权字段(增量列添加,防冲突幂等设计) -- 1.1 凭据类型列 (默认 ONLINE_PASSWORD,未来可扩展 QR, CARD, MOBILE_KEY) ALTER TABLE public.train_dorm_auth ADD COLUMN IF NOT EXISTS credential_type VARCHAR(32) DEFAULT 'ONLINE_PASSWORD' NOT NULL; COMMENT ON COLUMN public.train_dorm_auth.credential_type IS '凭据类型 (ONLINE_PASSWORD 在线密码, QR 二维码, CARD 卡片, MOBILE_KEY 手机钥匙)'; -- 1.2 厂商中立业务授权状态列 (从历史 face_sync_status 进化为语义严谨的独立业务状态) -- 状态枚举:PENDING_GRANT(待下发), GRANTING(下发中), ACTIVE(已生效), PENDING_REVOKE(待注销), REVOKING(注销中), REVOKED(已注销), ERROR(下发或注销失败) ALTER TABLE public.train_dorm_auth ADD COLUMN IF NOT EXISTS auth_status VARCHAR(32) DEFAULT 'PENDING_GRANT' NOT NULL; COMMENT ON COLUMN public.train_dorm_auth.auth_status IS '业务授权状态 (PENDING_GRANT, GRANTING, ACTIVE, PENDING_REVOKE, REVOKING, REVOKED, ERROR)'; -- 1.3 关联外部操作流水ID (用于关联并追踪当前最新驱动的 ExternalOperation) ALTER TABLE public.train_dorm_auth ADD COLUMN IF NOT EXISTS latest_operation_id BIGINT; COMMENT ON COLUMN public.train_dorm_auth.latest_operation_id IS '最近一次绑定的外部操作流水ID (关联 train_external_operation.operation_id)'; -- 1.4 授权操作世代/版本号 (用于防旧操作晚到结果覆盖更新状态,保证操作时序单调性) ALTER TABLE public.train_dorm_auth ADD COLUMN IF NOT EXISTS operation_version INTEGER DEFAULT 1 NOT NULL; COMMENT ON COLUMN public.train_dorm_auth.operation_version IS '授权操作版本号 (乐观并发与迟到操作防覆写防护)'; -- 2. 索引优化 (幂等添加) CREATE INDEX IF NOT EXISTS idx_dorm_auth_status ON public.train_dorm_auth (auth_status); CREATE INDEX IF NOT EXISTS idx_dorm_auth_latest_op ON public.train_dorm_auth (latest_operation_id); -- >>> 来源模块: sql/system/kingbase/010_dms_access_management_permissions.sql >>> -- ============================================================================= -- 脚本名称: 010_dms_access_management_permissions.sql -- 业务模块: DMS门禁与外部资源映射管理端权限扩展 (R.3B Wave 3B) -- 适用数据库: KingbaseES, schema public -- 范围: 仅限 sys_menu 按钮级权限增量插入,不改动已有角色分配 (sys_role_menu Δ = 0) -- 幂等性设计: 支持重复执行,WHERE NOT EXISTS 防重,无破坏性语句 -- 安全红线: 严禁引入远程应急开门权限,严禁普通学员/教师授权 -- ============================================================================= -- 1. 门禁与映射查看权限 (train:access:query) 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 2191, '门禁与资源映射查看', 2060, 1, '#', '', '', 1, 0, 'F', '0', '0', 'train:access:query', '#', 'admin', CURRENT_TIMESTAMP, '', NULL, '门禁授权与外部资源映射查看权限' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE perms = 'train:access:query'); -- 2. 门禁资源映射管理权限 (train:access:manage) 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 2192, '门禁资源映射维护', 2060, 2, '#', '', '', 1, 0, 'F', '0', '0', 'train:access:manage', '#', 'admin', CURRENT_TIMESTAMP, '', NULL, '门禁房间与门锁资源绑定解绑维护权限' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE perms = 'train:access:manage'); -- 3. 门禁手动对账权限 (train:access:reconcile) 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 2193, '门禁手动对账触发', 2060, 3, '#', '', '', 1, 0, 'F', '0', '0', 'train:access:reconcile', '#', 'admin', CURRENT_TIMESTAMP, '', NULL, '门禁权限手动对账触发执行权限' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE perms = 'train:access:reconcile'); -- >>> 来源模块: sql/system/kingbase/012_analytics_v1_permissions.sql >>> -- ============================================================================= -- Migration: Analytics V1 Permissions & Legacy Menu Deactivation -- Target: KingbaseES, schema public -- Scope: Deactivate legacy dropped score menus, add attendance analytics menu & dashboard permissions -- ============================================================================= -- 1. Deactivate legacy score menus (2166, 2175, 2176) UPDATE public.sys_menu SET status = '1', visible = '1', update_by = 'admin', update_time = CURRENT_TIMESTAMP, remark = '已废弃旧表停用(V2架构)' WHERE menu_id IN (2166, 2175, 2176); -- 2. Insert attendance analytics menu under '统计分析' (parent_id = 2169) 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 2251, '考勤分析', 2169, 1, 'attendance', 'train/analysis/attendance/index', '', 1, 0, 'C', '0', '0', 'train:analysis:attendance', 'chart', 'admin', CURRENT_TIMESTAMP, '', NULL, '考勤专题分析页面' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2251); -- 3. Insert attendance query button permission 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 2252, '考勤分析查询', 2251, 1, '#', '', '', 1, 0, 'F', '0', '0', 'train:analysis:attendance:query', '#', 'admin', CURRENT_TIMESTAMP, '', NULL, '考勤分析数据查询' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2252); -- 4. Insert dashboard button permission 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 2253, '管理驾驶舱', 2169, 0, '#', '', '', 1, 0, 'F', '0', '0', 'train:analysis:dashboard', '#', 'admin', CURRENT_TIMESTAMP, '', NULL, '管理端驾驶舱权限' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2253); -- 5. Grant permissions to train_admin role (role_id = 3) INSERT INTO public.sys_role_menu (role_id, menu_id) SELECT 3, 2251 WHERE NOT EXISTS (SELECT 1 FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2251); INSERT INTO public.sys_role_menu (role_id, menu_id) SELECT 3, 2252 WHERE NOT EXISTS (SELECT 1 FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2252); INSERT INTO public.sys_role_menu (role_id, menu_id) SELECT 3, 2253 WHERE NOT EXISTS (SELECT 1 FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2253); -- 6. Verification DO $$ BEGIN IF (SELECT count(*) FROM public.sys_menu WHERE menu_id = 2251 AND perms = 'train:analysis:attendance') <> 1 THEN RAISE EXCEPTION 'Menu 2251 verification failed'; END IF; IF (SELECT count(*) FROM public.sys_menu WHERE menu_id = 2252 AND perms = 'train:analysis:attendance:query') <> 1 THEN RAISE EXCEPTION 'Menu 2252 verification failed'; END IF; IF (SELECT count(*) FROM public.sys_menu WHERE menu_id = 2253 AND perms = 'train:analysis:dashboard') <> 1 THEN RAISE EXCEPTION 'Menu 2253 verification failed'; END IF; IF (SELECT count(*) FROM public.sys_role_menu WHERE role_id = 3 AND menu_id IN (2251, 2252, 2253)) <> 3 THEN RAISE EXCEPTION 'Role assignment verification failed'; END IF; END $$; -- >>> 来源模块: sql/system/kingbase/014_dms_access_safety_and_accommodation_permissions.sql >>> -- ============================================================================= -- Migration: 014 DMS Access Safety & Accommodation Permissions -- Target: KingbaseES, schema public -- Scope: -- 1. Register '门禁管理' menu (2060) under '教学设施' (2154) -- 2. Register '分配住宿' button permission (2264) under '住宿管理' (2261) -- 3. Ensure minimal operational access permissions (2191, 2192, 2193) exist -- 4. Grant minimal necessary permissions to train_admin role (role_id = 3): -- - 2060 (门禁管理页面) -- - 2191 (train:access:query) -- - 2192 (train:access:manage) -- - 2193 (train:access:reconcile) -- - 2264 (train:accommodation:add) -- 5. Safety Guard: Explicitly verify train_admin DOES NOT have accommodation:remove or remote unlock -- 6. Post-migration verification assertion block -- ============================================================================= -- 1. Register '门禁管理' menu under '教学设施' (parent_id = 2154) 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 2060, '门禁管理', 2154, 9, 'auth', 'train/auth/index', '', 1, 0, 'C', '0', '0', 'train:access:query', 'lock', 'admin', CURRENT_TIMESTAMP, '', NULL, '门禁授权与设备状态管理' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2060); -- Update menu 2060 in case it existed with different params UPDATE public.sys_menu SET parent_id = 2154, path = 'auth', component = 'train/auth/index', perms = 'train:access:query', status = '0', visible = '0', update_by = 'admin', update_time = CURRENT_TIMESTAMP WHERE menu_id = 2060; -- 2. Ensure access permissions (2191, 2192, 2193) exist under parent 2060 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 2191, '门禁与资源映射查看', 2060, 1, '#', '', '', 1, 0, 'F', '0', '0', 'train:access:query', '#', 'admin', CURRENT_TIMESTAMP, '', NULL, '门禁授权与外部资源映射查看权限' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2191); 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 2192, '门禁资源映射维护', 2060, 2, '#', '', '', 1, 0, 'F', '0', '0', 'train:access:manage', '#', 'admin', CURRENT_TIMESTAMP, '', NULL, '门禁凭据下发、注销与映射维护权限' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2192); 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 2193, '门禁手动对账触发', 2060, 3, '#', '', '', 1, 0, 'F', '0', '0', 'train:access:reconcile', '#', 'admin', CURRENT_TIMESTAMP, '', NULL, '门禁权限手动对账触发执行权限' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2193); -- 3. Register '分配住宿' permission under '住宿管理' (parent_id = 2261) 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 2264, '分配住宿', 2261, 3, '#', '', '', 1, 0, 'F', '0', '0', 'train:accommodation:add', '#', 'admin', CURRENT_TIMESTAMP, '', NULL, '分配学员住宿安排权限' WHERE NOT EXISTS (SELECT 1 FROM public.sys_menu WHERE menu_id = 2264); -- 4. Grant minimal operational permissions to train_admin role (role_id = 3) -- 门禁管理页面 INSERT INTO public.sys_role_menu (role_id, menu_id) SELECT 3, 2060 WHERE NOT EXISTS (SELECT 1 FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2060); -- 门禁与资源映射查看 INSERT INTO public.sys_role_menu (role_id, menu_id) SELECT 3, 2191 WHERE NOT EXISTS (SELECT 1 FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2191); -- 门禁资源维护与下发/注销 INSERT INTO public.sys_role_menu (role_id, menu_id) SELECT 3, 2192 WHERE NOT EXISTS (SELECT 1 FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2192); -- 门禁手动对账 INSERT INTO public.sys_role_menu (role_id, menu_id) SELECT 3, 2193 WHERE NOT EXISTS (SELECT 1 FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2193); -- 分配住宿 INSERT INTO public.sys_role_menu (role_id, menu_id) SELECT 3, 2264 WHERE NOT EXISTS (SELECT 1 FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2264); -- 5. Post-migration Verification Assertions DO $$ BEGIN -- Verify 2060 exists and active IF (SELECT count(*) FROM public.sys_menu WHERE menu_id = 2060 AND perms = 'train:access:query' AND status = '0') <> 1 THEN RAISE EXCEPTION 'Menu 2060 verification failed'; END IF; -- Verify 2191, 2192, 2193 exist IF (SELECT count(*) FROM public.sys_menu WHERE menu_id IN (2191, 2192, 2193)) <> 3 THEN RAISE EXCEPTION 'Access button permissions (2191, 2192, 2193) verification failed'; END IF; -- Verify 2264 exists and active IF (SELECT count(*) FROM public.sys_menu WHERE menu_id = 2264 AND perms = 'train:accommodation:add' AND status = '0') <> 1 THEN RAISE EXCEPTION 'Menu 2264 verification failed'; END IF; -- Verify role_id = 3 has 2060, 2191, 2192, 2193, 2264 IF (SELECT count(*) FROM public.sys_role_menu WHERE role_id = 3 AND menu_id IN (2060, 2191, 2192, 2193, 2264)) <> 5 THEN RAISE EXCEPTION 'Role assignment for train_admin verification failed'; END IF; -- Security Guard: Verify train_admin (role_id=3) DOES NOT have any forbidden remove or mutation permissions IF (SELECT count(*) FROM public.sys_role_menu rm JOIN public.sys_menu m ON m.menu_id = rm.menu_id WHERE rm.role_id = 3 AND m.perms IN ('train:course:remove', 'train:teacher:remove', 'train:student:remove', 'train:accommodation:remove')) > 0 THEN RAISE EXCEPTION 'Security violation: train_admin role must not have remove permissions'; END IF; END $$; -- >>> 来源模块: sql/system/kingbase/015_dms_package_a_hardening.sql >>> -- ============================================================================ -- Migration: 015_dms_package_a_hardening.sql -- Description: Package A.1 DMS 一致性加固与最小权限拆分 -- 1. 权限最小化拆分:新增 2194 (train:access:credential),从 train_admin 移除 2192 (train:access:manage) 并保证保留 2263 (train:accommodation:edit) -- 2. 外部操作状态加固:确保 train_external_operation 支持 DISPATCHING 与 AMBIGUOUS -- 3. 幂等与断言校验 -- ============================================================================ \set ON_ERROR_STOP on -- 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; -- 确保培训管理员 (role_id = 3) 拥有 2263 (train:accommodation:edit,办理入住/退宿/调房) INSERT INTO public.sys_role_menu (role_id, menu_id) SELECT 3, 2263 WHERE NOT EXISTS (SELECT 1 FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2263); -- 5. 状态机约束兼容性加固 (train_external_operation) -- 确保 operation_status 允许 DISPATCHING 与 AMBIGUOUS ALTER TABLE public.train_external_operation DROP CONSTRAINT IF EXISTS chk_external_operation_status; ALTER TABLE public.train_external_operation ADD CONSTRAINT chk_external_operation_status CHECK (operation_status IN ('CREATED','DISPATCHING','REQUESTED','ACCEPTED','PENDING','RETRY_WAIT','AMBIGUOUS','SUCCESS','FAILED','TIMEOUT')); -- 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:edit,办理入住/退宿/调房) IF (SELECT count(*) FROM public.sys_role_menu WHERE role_id = 3 AND menu_id = 2263) <> 1 THEN RAISE EXCEPTION 'Assertion failed: role 3 missing menu 2263 (train:accommodation:edit)'; END IF; -- 断言 4b: train_admin (role_id = 3) 严禁拥有破坏性删除权限 train:accommodation:remove IF (SELECT count(*) FROM public.sys_role_menu rm JOIN public.sys_menu m ON m.menu_id = rm.menu_id WHERE rm.role_id = 3 AND m.perms = 'train:accommodation:remove') <> 0 THEN RAISE EXCEPTION 'Security violation: role 3 has forbidden permission 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; -- 断言 6: operation_status CHECK 约束已生效且包含 DISPATCHING 与 AMBIGUOUS IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conrelid = 'train_external_operation'::regclass AND contype = 'c' AND pg_get_constraintdef(oid) LIKE '%DISPATCHING%' AND pg_get_constraintdef(oid) LIKE '%AMBIGUOUS%' ) THEN RAISE EXCEPTION 'Assertion failed: operation_status constraint invalid or missing DISPATCHING/AMBIGUOUS'; END IF; END $$; COMMIT;