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

763 lines
51 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- ==============================================================================
-- 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;