diff --git a/sql/h2_userbase_app_and_modules.sql b/sql/h2_userbase_app_and_modules.sql index e593b1a..ffe0582 100644 --- a/sql/h2_userbase_app_and_modules.sql +++ b/sql/h2_userbase_app_and_modules.sql @@ -1,8 +1,16 @@ -- ============================================================================ -- 表单中心 H2 + 第一个快应用:UserBase 侧应用行/模块行/授权登记脚本 -- 编制:Qoder(T9 联调轮)|日期:2026-10-15|改版:T10 中心段 6→5 行(手册 §2.2 v1.5,锚点行退役) +-- 改版:**P95(2026-10-16)快应用段 5→4 行——`/f/{code}` 填报行及其组授权整条撤销** +-- 终态口径(任务书 P95 §0.1 三层角色模型,用户 2026-10-16 裁决):填报人群由表单中心按 +-- `audience × 会话身份 × 管理员两表` 自解析(后端 FillAccessService + GET /api/form/my/fillable), +-- UserBase 侧**不再逐表单配填报模块行**——发一张表单求一次超管的成本就此归零。 +-- 快应用树只剩四种行:数据查看 / 统计 两个页面行 + 导入 / 导出 两个 path 空按钮锚点。 +-- **中心应用五行(200~204)与 /admin/* 四行原样不动**(P95 红线 2:管理端入口仍只认模块行)。 +-- ⚠ 存量环境里已录入的 `/f/` 行不在本脚本删除范围内(脚本只做"不存在才插",从不删行)—— +-- 清理清单见 docs/P95-report.md 的「存量清理」段,由超管在管理端手点删。 -- 依据:《表单中心执行手册》§2.2(中心应用 v1.5 五条模块行表 + 验证口径)、 --- 《表单中心建设方案 v1.4》§6.2(快应用登记 checklist R8)、§6.5(中心=BUSINESS 应用, +-- 《表单中心建设方案 v1.4》§6.2(快应用登记 checklist R8;P95 注记后收敛为两步)、§6.5(中心=BUSINESS 应用, -- 模块行与快应用模块行两棵树不混) -- 《表单中心管理员体系方案-20261015》§3/§7(T10 改版:中心 6→5 行,customId 数据范围锚点退役) -- 先例:D:\SoftProject\yunhai\UserBase\flow-center接入UserBase菜单.sql(同一套形态与幂等写法) @@ -64,7 +72,7 @@ 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 四步的前两步) +-- 步骤 2:第一个快应用(演练对象,方案 §6.2 checklist R8;**P95 注记后 R8 只剩①建应用行 + ④学校开通**) -- 快应用=真实应用行 + 无自有后端 + pc_url 指中心分发路由。app_code 一经确定不可改 -- (表单归属列 app_code 与模块树查询都按它走,P25/D8)。 -- ---------------------------------------------------------------------------- @@ -85,20 +93,20 @@ SET @quick_app_id = 0; -- ← 按上一步结果填;0=还没建 -- WHERE @quick_app_id = 0; -- SET @quick_app_id = LAST_INSERT_ID(); -- ← 紧接着上面那条 INSERT 生效;否则手工填 --- 模块行五条(方案 §6.2 表的典型配法)。path 形态: --- 填报 = /f/{code}(该快应用名下的表单 code,经 resolve 换 uuid——快应用不授权定义管理行,§3.4) +-- 模块行四条(P95 终态;方案 §6.2 表在 P95 注记后只剩这四行是 UserBase 侧的事)。path 形态: -- 数据查看/统计 = /app/{app_code}/data|stats(AppContext.vue 的分派段) -- 导入/导出 = path 留空的按钮级锚点(导出按钮显隐按它判,P76 修的就是"判哪棵树") +-- ⚠ 原「填报 /f/{code}」行**自 P95 起不再登记**:填报入口由中心按三层模型解析 +-- (GET /api/form/my/fillable),模块行不参与;custom_id 300 段就此空置,不要再复用成别的行。 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, + SELECT @quick_app_id AS app_id, NULL AS parent_id, '数据查看' AS name, '301' AS custom_id, + CONCAT('/app/', @quick_code, '/data') AS path, 'category' AS icon, 1 AS level, 301 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() + 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); @@ -111,8 +119,9 @@ WHERE @quick_app_id > 0 -- 3.1 中心管理员组(示例:把 @grp_admin 换成目标学校的管理员组 id;sys_group 里查) SET @grp_admin = 0; -- ← 中心管理员组 SET @grp_all = 0; -- ← 全员组(学校根组/基础组,"我的填写"给它) -SET @grp_teacher = 0; -- ← 快应用教师组(数据查看 + 导出锚点) -SET @grp_student = 0; -- ← 快应用学生组(只给填报行) +SET @grp_teacher = 0; -- ← 快应用教师组(数据查看 + 统计 + 导出锚点) +SET @grp_student = 0; -- ← 快应用学生组:**P95 起只需 3.2 的应用开通(门户可见性),不再勾任何模块行** + -- 填报入口由中心按三层模型给(面向学生的表单自动出现在学生账号的「可填写」里) -- 3.2 应用开通(中心 + 快应用各一条;@school_id 是要开通的学校 id) SET @school_id = 0; -- ← 目标学校 id @@ -141,20 +150,16 @@ SELECT @grp_all, m.id, 0, @operator, NOW() 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 快应用模块授权:教师组=数据查看+统计+导出锚点(不给填报也算得出数据);学生组=只给填报行 +-- 3.4 快应用模块授权:教师组=数据查看+统计+导出锚点(导入 303 是 1.5 预留位不授)。 +-- P95 起**学生组这一段整条撤销**:填报入口不再由模块行给(原"学生组=只给 /f/{code} 行"), +-- 学生所在组只需要 3.2 的**应用开通**(门户可见性与闸门照旧,V16 语义不变)—— +-- 能不能填哪张表由中心按三层模型判(后端 FillAccessService)。 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/**,而后端守卫扫的是【中心会话树】(签名调用方=表单中心自己), -- 不消费快应用自己的"数据查看"行。只勾快应用行的教师组进应用数据视图会被后端拒。 @@ -174,13 +179,19 @@ SELECT @grp_teacher, m.id, 0, @operator, NOW() -- ⚠ 这条要 **H4 上线**(UserBase /oapi/user/modules 的可选 appCode 参数)+ 中心消费侧接线才通; -- 中心侧当前按 P3 返回空树+warn,见 T9 报告问题单 P78。 -- ④教师组账号进 /form-center/app/fc-drill/data:导出按钮出现(快应用按钮锚点判定生效,P76) --- ⑤学生组账号:V16 门户可见性由开通闸门决定;进应用只见填报 +-- ⑤学生组账号:V16 门户可见性由开通闸门决定;进应用的「填报」档看到该应用下**面向学生**的表单 +-- (P95:这张清单来自 GET /api/form/my/fillable,与快应用树里有没有 `/f/` 行无关—— +-- 删掉存量 `/f/` 行后门户导航、「可填写」列表、分享直链三路都不受影响,见 P95 报告收尾清单) -- ⑥中心应用管理端进 /form-center/#/admin/app:应用设置页出两个 tab(角色设置/字典管理), -- 角色设置里加的人落 school_admin(数据范围升级,不是菜单行)——T10 V43/V46 +-- ⑦P95 新判据(V52):快应用树里 `/f/` 行数=0(下面倒数第二条 chk),且中心段仍是 200~204 五行 -- ---------------------------------------------------------------------------- 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 +SELECT '快应用模块行数(P95 期望 4)', COUNT(*) FROM app_module WHERE app_id = @quick_app_id +UNION ALL +SELECT '快应用 /f/ 填报行数(P95 期望 0)', COUNT(*) FROM app_module + WHERE app_id = @quick_app_id AND path LIKE '/f/%' UNION ALL SELECT '中心 path 空行数(期望 0,锚点已退役)', COUNT(*) FROM app_module WHERE app_id = @center_app_id AND (path IS NULL OR path = '') diff --git a/src/main/java/com/yunhai/formcenter/controller/api/FormMyAnswerController.java b/src/main/java/com/yunhai/formcenter/controller/api/FormMyAnswerController.java index dc3bc0a..c52f65c 100644 --- a/src/main/java/com/yunhai/formcenter/controller/api/FormMyAnswerController.java +++ b/src/main/java/com/yunhai/formcenter/controller/api/FormMyAnswerController.java @@ -3,6 +3,7 @@ package com.yunhai.formcenter.controller.api; import com.yunhai.formcenter.model.MyModels; import com.yunhai.formcenter.model.ReadModels; import com.yunhai.formcenter.service.AnswerReadService; +import com.yunhai.formcenter.service.FillableFormService; import com.yunhai.platform.apistd.BizException; import com.yunhai.platform.utils.exception.ErrorCode; import com.yunhai.platform.utils.model.BoaUser; @@ -38,6 +39,7 @@ import java.util.List; public class FormMyAnswerController { private final AnswerReadService readService; + private final FillableFormService fillableService; /** 我的答卷列表(按 submitter 过滤,"我的提交"段) */ @GetMapping("/list") @@ -96,6 +98,24 @@ public class FormMyAnswerController { return readService.editBySubmitter(user, body.getAnswerId(), body.getValues(), body.getSummary()); } + /** + * 「可填写」列表(P95 W3,V50):填报入口清单,不是答卷清单—— + * 域=会话校 ∪ 平台级且本校遮蔽平台、仅已发布未软删,逐行按 P95 三层模型过入口层,NONE 不入集。 + * + *
这一把查询把 P91 那个"从会话菜单树挑 {@code /f/} 行"的前端推导整条换掉: + * 谁能填什么从此由中心按 {@code audience × 身份} 自解析,UserBase 侧不再需要逐表单配模块行 + * (任务书 P95 §0)。它不看模块行、也不看数据范围矩阵:入口层与那两条线正交 + * ({@code FillAccessService} 类注释记了三条线的分工)。
+ * + * @param appCode 可选,缺省=全部应用;快应用上下文({@code /app/{appCode}/})传它自身以收窄清单 + */ + @GetMapping("/fillable") + public List它不是答卷行:这一把查询还没写任何数据,只回答"这个人能进哪张表的填报页、 + * 进完之后按 P95 三层模型他算哪一层"。所以字段是表单身份 + 两个入口层结论, + * 与 {@link ItemView} 没有共同祖先。
+ * + *{@code canFill} 与 {@code myTier} 是两件事,前端两处消费: + * {@code canFill=false} 且 {@code myTier=MANAGE} 的行(教师×面向学生)在「可填写」列表里不出现, + * 但要留在"数据管理"tab 的候选里——这正是 P95 把入口层结论下发而不是只下发一个布尔的原因。 + * 数字口径与定义域一致:{@code audience}/{@code fillLimit} 下发列值(1/2、0/1), + * {@code myTier} 下发枚举名(ADMIN/MANAGE/FILL,NONE 的行根本不入集)。
+ */ + @Getter + @Setter + public static class FillableRow { + private String code; + private String name; + private String appCode; + /** 面向人群:1 学生 / 2 教师(契约 §10 列值) */ + private Integer audience; + /** yyyy-MM-dd HH:mm:ss;null=不限截止 */ + private String deadline; + /** 限填次数:0 不限 / 1 限一次 */ + private Integer fillLimit; + private String groupTag; + /** 能否以本人身份填报(层①∪层③) */ + private Boolean canFill; + /** 并集里的最高层:ADMIN > MANAGE > FILL(NONE 不入集,故不会为 null) */ + private String myTier; + } } diff --git a/src/main/java/com/yunhai/formcenter/repository/FormRepository.java b/src/main/java/com/yunhai/formcenter/repository/FormRepository.java index b2e4a63..7175b98 100644 --- a/src/main/java/com/yunhai/formcenter/repository/FormRepository.java +++ b/src/main/java/com/yunhai/formcenter/repository/FormRepository.java @@ -43,4 +43,14 @@ public interface FormRepository extends JpaRepository