Files
railwaysystem/qa-bootstrap/sql/03_system_permissions_and_menus.sql
T

762 lines
51 KiB
PL/PgSQL
Raw Normal View History

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