Files
form-center-service/sql/h2_userbase_app_and_modules.sql
T
chenxing dc76574118
build / build-push (push) Successful in 2m12s
feat(t10-admin): 管理员体系——校级管理员+单表单管理员+锚点退役
- 新增 school_admin/form_admin 两表(sql/t10_admin_domain.sql,含 answer.submitter_sch_id 加列)与 AdminDomainController/AdminDomainService/SchoolUserTreeService
- DataScopeService 范围体系按 P80 验收口径落地:入口只认模块行、查询域=会话校、两表只升范围;新增 SchoolScopeImplementor
- fc:scope:all / SCOPE_ALL_CUSTOM_ID / anchorCustomIds / MODULE_DICT 锚点全链路退役,全仓引用归零;h2 登记脚本 6→5 行(快应用段逐字未动)
- AnswerScopeQueryBuilder 校域不变式:answer.sch_id 恒=会话校,基础过滤 sch_id+app_code+code+is_deleted 不变
- FormAdminGuard:targetSchId=0(基础表单)仅超管可维护;FormAdminController/AnswerReadController 等按管理员范围收口
- 测试基线 190→219/0/0/0:DataScopeServiceTest 22、FormAdminGuardTest 14、AdminScopeIT 8、AdminDomainIT 10、AnswerReadIT 改写 1、FormBaseControllerTest 7 等
2026-10-15 20:25:03 +08:00

204 lines
16 KiB
SQL
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.
-- ============================================================================
-- 表单中心 H2 + 第一个快应用:UserBase 侧应用行/模块行/授权登记脚本
-- 编制:Qoder(T9 联调轮)|日期:2026-10-15|改版:T10 中心段 6→5 行(手册 §2.2 v1.5,锚点行退役)
-- 依据:《表单中心执行手册》§2.2(中心应用 v1.5 五条模块行表 + 验证口径)、
-- 《表单中心建设方案 v1.4》§6.2(快应用登记 checklist R8)、§6.5(中心=BUSINESS 应用,
-- 模块行与快应用模块行两棵树不混)
-- 《表单中心管理员体系方案-20261015》§3/§7(T10 改版:中心 6→5 行,customId 数据范围锚点退役)
-- 先例:D:\SoftProject\yunhai\UserBase\flow-center接入UserBase菜单.sql(同一套形态与幂等写法)
--
-- ⚠ 纪律:本脚本**不由 AI 执行**(沿 flow-center 先例"AI 不代跑,交由你在 dev / prod 各自执行")。
-- H2 的既定路径是 UserBase 管理端(超管)逐项录入;本脚本是同一份数据的批量/复核形态,
-- 两者取其一即可,重复执行安全(全部按唯一键做"不存在才插",不覆盖人工改过的值)。
-- 目标库=**UserBase 的库**(本地 bsch_userbase / 160 容器网 userbase 库),不是 bsch_formcenter。
-- MySQL 5.7 兼容(无 CTE / 无窗口函数)。
-- ============================================================================
-- ----------------------------------------------------------------------------
-- 步骤 0:定位 form-center 的应用主键(关键,排第一大坑)
-- 开放接口按「调用方 app_key → application_info 主键 id」反查,而 app_module.app_id 存的是【主键 id】,
-- 不是 app_key(100xxx)。中心后端签名用的 app_key 由 projman 注入(P7:jar 内占位 100021 上线前替换)。
-- 期望下面这条命中一行;命中 0 行=应用行还没建(走 0b 建,或去管理端建)。
-- ----------------------------------------------------------------------------
SELECT id AS app_id_pk, app_key, name, app_code, app_nature, pc_url
FROM application_info
WHERE app_code = 'form-center' OR app_key = 100021;
SET @center_app_id = 0; -- ← 按上一步结果填:form-center 在 application_info 的主键 id
SET @center_app_key = 100021; -- ← 按 UserBase 注册所得 app_key 填(占位值仅本地)
SET @operator = 0; -- create_user 占位(0=系统)
-- 0b(可选):应用行缺失时才需要。app_secret 请改成 UserBase 注册实际下发的值,
-- 不要把这里的占位串带到任何环境(D7:一期无写 oapi,应用行由超管建=本行就是那个"手工建")。
-- INSERT INTO application_info (app_key, app_secret, name, app_code, app_nature, pc_url, pc_icon,
-- clients, create_time, create_user)
-- SELECT @center_app_key, 'REPLACE-WITH-REAL-SECRET', '表单中心', 'form-center', 'BUSINESS',
-- 'http://base.yunhaixingtong.com/form-center/front/', 'category', JSON_ARRAY('pc'),
-- NOW(), @operator
-- WHERE @center_app_id = 0;
-- ----------------------------------------------------------------------------
-- 步骤 1:中心应用五条模块行(执行手册 §2.2 v1.5 表;path 必须逐字等于 front/src/router/routes.ts)
-- ①表单设计 /admin/forms ②数据管理 /admin/data ③统计 /admin/stats
-- ④应用设置 /admin/app(页内 tab:角色设置/字典管理,字典 API 摊这一行把关) ⑤我的填写 /my(全员)
-- v1.5 改版(T10,2026-10-15 用户裁决):
-- · 原「字典管理 /admin/dicts」独立行撤销,迁入应用设置页内 tab(不占 modId、不进导航);
-- · 原「数据全量」path 空锚点行撤销——UserBase 侧 customId 数据范围协议退役,
-- 升级载体改为中心自建的管理员两表(school_admin / form_admin,见《管理员体系方案》§4/§5.1)。
-- **中心应用自此不再有 path 空行**;前端 menuNav 的"path 空跳过"规则保留给快应用面的按钮锚点。
-- 子页面路由(/admin/forms/designer、/admin/data/detail、/app/:appCode/data/detail、/admin/app 的页内 tab)
-- **不建模块行**(手册 §2.2 注记 + P63/P77):不占 modId、不进导航,前端按 meta.parentPath 归位。
-- custom_id 用 200~204 段(避开 flow-center 的 100~105;后端 resolveModId 只认数字 customId)
-- ----------------------------------------------------------------------------
INSERT INTO app_module
(app_id, parent_id, name, custom_id, path, icon, level, sort_no, create_user, create_time)
SELECT * FROM (
SELECT @center_app_id AS app_id, NULL AS parent_id, '表单设计' AS name, '200' AS custom_id,
'/admin/forms' AS path, 'word' AS icon, 1 AS level, 200 AS sort_no, @operator AS create_user, NOW() AS create_time
UNION ALL SELECT @center_app_id, NULL, '数据管理', '201', '/admin/data', 'category', 1, 201, @operator, NOW()
UNION ALL SELECT @center_app_id, NULL, '统计', '202', '/admin/stats','tongji', 1, 202, @operator, NOW()
UNION ALL SELECT @center_app_id, NULL, '应用设置', '203', '/admin/app', 'zidingyi', 1, 203, @operator, NOW()
UNION ALL SELECT @center_app_id, NULL, '我的填写', '204', '/my', '', 1, 204, @operator, NOW()
) t
WHERE @center_app_id > 0
AND NOT EXISTS (SELECT 1 FROM app_module m WHERE m.app_id = t.app_id AND m.custom_id = t.custom_id);
-- ----------------------------------------------------------------------------
-- 步骤 2:第一个快应用(演练对象,方案 §6.2 checklist R8 四步的前两步)
-- 快应用=真实应用行 + 无自有后端 + pc_url 指中心分发路由。app_code 一经确定不可改
-- (表单归属列 app_code 与模块树查询都按它走,P25/D8)。
-- ----------------------------------------------------------------------------
SET @quick_code = 'fc-drill'; -- ← 演练用快应用 app_code(前端 mock 桩也叫这个名字)
SET @quick_app_key = 0; -- ← UserBase 注册所得 app_key(无后端应用也要有身份行)
SELECT id AS quick_app_id_pk, app_key, name, app_code, app_nature
FROM application_info WHERE app_code = @quick_code;
SET @quick_app_id = 0; -- ← 按上一步结果填;0=还没建,走 2b
-- 2b(可选):快应用应用行(BUSINESS 之外按平台既有 nature 填;无自有后端=不建 projman 项目)
-- INSERT INTO application_info (app_key, app_secret, name, app_code, app_nature, pc_url, pc_icon,
-- clients, create_time, create_user)
-- SELECT @quick_app_key, 'REPLACE-WITH-REAL-SECRET', '表单演练应用', @quick_code, 'BUSINESS',
-- CONCAT('http://base.yunhaixingtong.com/form-center/app/', @quick_code, '/'), 'edit',
-- JSON_ARRAY('pc'), NOW(), @operator
-- WHERE @quick_app_id = 0;
-- SET @quick_app_id = LAST_INSERT_ID(); -- ← 紧接着上面那条 INSERT 生效;否则手工填
-- 模块行五条(方案 §6.2 表的典型配法)。path 形态:
-- 填报 = /f/{code}(该快应用名下的表单 code,经 resolve 换 uuid——快应用不授权定义管理行,§3.4)
-- 数据查看/统计 = /app/{app_code}/data|stats(AppContext.vue 的分派段)
-- 导入/导出 = path 留空的按钮级锚点(导出按钮显隐按它判,P76 修的就是"判哪棵树")
INSERT INTO app_module
(app_id, parent_id, name, custom_id, path, icon, level, sort_no, create_user, create_time)
SELECT * FROM (
SELECT @quick_app_id AS app_id, NULL AS parent_id, '填报' AS name, '300' AS custom_id,
CONCAT('/f/', @quick_code, '_form') AS path, 'edit' AS icon, 1 AS level, 300 AS sort_no,
@operator AS create_user, NOW() AS create_time
UNION ALL SELECT @quick_app_id, NULL, '数据查看', '301', CONCAT('/app/', @quick_code, '/data'), 'category', 1, 301, @operator, NOW()
UNION ALL SELECT @quick_app_id, NULL, '统计', '302', CONCAT('/app/', @quick_code, '/stats'), 'tongji', 1, 302, @operator, NOW()
UNION ALL SELECT @quick_app_id, NULL, '导入', '303', '', '', 1, 303, @operator, NOW()
UNION ALL SELECT @quick_app_id, NULL, '导出', '304', '', '', 1, 304, @operator, NOW()
) t
WHERE @quick_app_id > 0
AND NOT EXISTS (SELECT 1 FROM app_module m WHERE m.app_id = t.app_id AND m.custom_id = t.custom_id);
-- ----------------------------------------------------------------------------
-- 步骤 3:授权 + 开通(方案 §6.1 三层:①应用开通(校级)②模块授权(组/角色)③数据范围)
-- 判定口径(UserBase 侧现成实现):最终模块权限 =(组授权模块 ∩ 已开通应用)∪(启用角色授权模块)。
-- 组授权路径**必须先开通应用**(开通不继承,子组要自己开通或挂在已开通的祖先链上)。
-- ----------------------------------------------------------------------------
-- 3.1 中心管理员组(示例:把 @grp_admin 换成目标学校的管理员组 id;sys_group 里查)
SET @grp_admin = 0; -- ← 中心管理员组
SET @grp_all = 0; -- ← 全员组(学校根组/基础组,"我的填写"给它)
SET @grp_teacher = 0; -- ← 快应用教师组(数据查看 + 导出锚点)
SET @grp_student = 0; -- ← 快应用学生组(只给填报行)
-- 3.2 应用开通(中心 + 快应用各一条;@school_id 是要开通的学校 id)
SET @school_id = 0; -- ← 目标学校 id
INSERT INTO sys_group_app_rel (group_id, app_id, create_user, create_time)
SELECT g.group_id, a.app_id, @operator, NOW()
FROM (SELECT @grp_admin AS group_id, @center_app_id AS app_id UNION ALL
SELECT @grp_all, @center_app_id UNION ALL
SELECT @grp_teacher, @quick_app_id UNION ALL
SELECT @grp_student, @quick_app_id) a
JOIN (SELECT 1 AS dummy) g ON a.group_id > 0
WHERE NOT EXISTS (SELECT 1 FROM sys_group_app_rel r
WHERE r.group_id = a.group_id AND r.app_id = a.app_id);
-- 3.3 中心模块授权:管理员组=四个管理行(表单设计/数据管理/统计/应用设置),全员组=只"我的填写"。
-- v1.5 起**没有"要看全校数据就多勾一行"这回事**:数据范围升级改由中心的管理员两表给
-- (/admin/app 的角色设置 tab 加校级表单管理员,或表单列表/设计器给单表单管理员)。
INSERT INTO sys_group_module_perm (group_id, module_id, inherit_flag, create_user, create_time)
SELECT @grp_admin, m.id, 0, @operator, NOW()
FROM app_module m
WHERE m.app_id = @center_app_id AND @grp_admin > 0 AND m.path <> '/my'
AND NOT EXISTS (SELECT 1 FROM sys_group_module_perm p WHERE p.group_id = @grp_admin AND p.module_id = m.id);
INSERT INTO sys_group_module_perm (group_id, module_id, inherit_flag, create_user, create_time)
SELECT @grp_all, m.id, 0, @operator, NOW()
FROM app_module m
WHERE m.app_id = @center_app_id AND @grp_all > 0 AND m.path = '/my'
AND NOT EXISTS (SELECT 1 FROM sys_group_module_perm p WHERE p.group_id = @grp_all AND p.module_id = m.id);
-- 3.4 快应用模块授权:教师组=数据查看+统计+导出锚点(不给填报也算得出数据);学生组=只给填报行
INSERT INTO sys_group_module_perm (group_id, module_id, inherit_flag, create_user, create_time)
SELECT @grp_teacher, m.id, 0, @operator, NOW()
FROM app_module m
WHERE m.app_id = @quick_app_id AND @grp_teacher > 0 AND COALESCE(m.custom_id,'') <> '303' -- 303=导入(1.5 预留位不授)
AND NOT EXISTS (SELECT 1 FROM sys_group_module_perm p WHERE p.group_id = @grp_teacher AND p.module_id = m.id);
INSERT INTO sys_group_module_perm (group_id, module_id, inherit_flag, create_user, create_time)
SELECT @grp_student, m.id, 0, @operator, NOW()
FROM app_module m
WHERE m.app_id = @quick_app_id AND @grp_student > 0 AND m.level = 1 AND m.path LIKE '/f/%'
AND NOT EXISTS (SELECT 1 FROM sys_group_module_perm p WHERE p.group_id = @grp_student AND p.module_id = m.id);
-- 3.5 ⚠ 过渡口径(T9 报告 §5.4 末段 / 问题单 P78 附注,需裁决后才可能取消这一步):
-- 快应用的数据视图复用 /api/form/admin/data/**,而后端守卫扫的是【中心会话树】(签名调用方=表单中心自己),
-- 不消费快应用自己的"数据查看"行。只勾快应用行的教师组进应用数据视图会被后端拒。
-- 演练要端到端跑通,就必须给同一批组**再勾中心的 /admin/data 行**(下面这条就是干这个的)。
-- 这不是"权限放宽"的偷懒——它把两棵树的关系显式写在登记脚本里,避免演练时把它当 bug 报。
INSERT INTO sys_group_module_perm (group_id, module_id, inherit_flag, create_user, create_time)
SELECT @grp_teacher, m.id, 0, @operator, NOW()
FROM app_module m
WHERE m.app_id = @center_app_id AND @grp_teacher > 0 AND m.path = '/admin/data'
AND NOT EXISTS (SELECT 1 FROM sys_group_module_perm p WHERE p.group_id = @grp_teacher AND p.module_id = m.id);
-- ----------------------------------------------------------------------------
-- 步骤 4:验收(对应任务书 T9 演练清单与手册 §2.2"验证"行)
-- ①超管登录中心:/form-center/api/base/GetUserMod 返回步骤 1 的树(前端导航恰 5 项)
-- ②无授权用户:只见"我的填写"
-- ③快应用上下文:/form-center/api/base/GetUserMod?appCode=fc-drill 返回步骤 2 那棵树
-- ⚠ 这条要 **H4 上线**(UserBase /oapi/user/modules 的可选 appCode 参数)+ 中心消费侧接线才通;
-- 中心侧当前按 P3 返回空树+warn,见 T9 报告问题单 P78。
-- ④教师组账号进 /form-center/app/fc-drill/data:导出按钮出现(快应用按钮锚点判定生效,P76)
-- ⑤学生组账号:V16 门户可见性由开通闸门决定;进应用只见填报
-- ⑥中心应用管理端进 /form-center/#/admin/app:应用设置页出两个 tab(角色设置/字典管理),
-- 角色设置里加的人落 school_admin(数据范围升级,不是菜单行)——T10 V43/V46
-- ----------------------------------------------------------------------------
SELECT '中心模块行数(期望 5)' AS chk, COUNT(*) AS n FROM app_module WHERE app_id = @center_app_id
UNION ALL
SELECT '快应用模块行数(期望 5)', COUNT(*) FROM app_module WHERE app_id = @quick_app_id
UNION ALL
SELECT '中心 path 空行数(期望 0,锚点已退役)', COUNT(*) FROM app_module
WHERE app_id = @center_app_id AND (path IS NULL OR path = '')
UNION ALL
SELECT '快应用 path 空锚点数(期望 2=导入/导出)', COUNT(*) FROM app_module
WHERE app_id = @quick_app_id AND (path IS NULL OR path = '')
UNION ALL
SELECT '已开通中心应用的组数', COUNT(*) FROM sys_group_app_rel WHERE app_id = @center_app_id
UNION ALL
SELECT '已授权中心模块的组-行对数', COUNT(*) FROM sys_group_module_perm p
JOIN app_module m ON m.id = p.module_id WHERE m.app_id = @center_app_id;
-- ----------------------------------------------------------------------------
-- 回滚(演练完要撤就跑这段;顺序与建立相反)
-- DELETE p FROM sys_group_module_perm p JOIN app_module m ON m.id=p.module_id
-- WHERE m.app_id IN (@center_app_id, @quick_app_id);
-- DELETE FROM sys_group_app_rel WHERE app_id IN (@center_app_id, @quick_app_id);
-- DELETE FROM app_module WHERE app_id IN (@center_app_id, @quick_app_id);
-- 应用行不在此删(D7:应用身份由超管在管理端维护,脚本不碰主数据删除)
-- ----------------------------------------------------------------------------