P95 落地后的独立修复批,四条存量逻辑硬伤(填报入口层不动): - 跨版本编辑必挂:客户端按当前发布版渲染、服务端按钉住旧版校验,升版后编辑永远 400(含未知字段/缺必填)。改为编辑态吃 /my/detail 的钉住版 groups。 - 编辑不吃截止/停用闸:先占位、截止后随便改可绕过 deadline。editBySubmitter 补 截止/停用/已删三闸,与 submit 同口径。 - 自填 subject 落 NULL:subject_id 为空的自填行进不了"关于我的"(/my/about 与 ABOUT_ME 谓词都按 subject 查),V18"含代填"实际退化成"仅代填"。落库归一为 coalesce(subjectId, submitterId);限次键本就是 coalesce,不受影响。 - answer_no 并发重号:count+1 的 count 与 insert 之间有并发窗口,报名号是对外凭证。 加 uk_answer_no 兜底 + 撞号重算序号重试(3 次封顶),按异常消息列名区分撞的是 报名号还是幂等键(MySQL/H2 通用)。 语义变更备案:自填行 subject 由 NULL 变本人,读矩阵"学生x面向学生=关于我"这一格 此后含自填行,可见集变大(DataScopeService/矩阵规则一字未动,变的是数据形状)。 三处断言随行为更新:AdminScopeIT、AnswerReadIT、MyAnswerIT(+ReadFixture 注释)。 验证:mvn 261 绿(+2 新用例:并发报名号不重复、编辑闸三态)。
四条读出路径(列表/详情/统计/导出)与管理动作行级把关共用 AnswerScopeQueryBuilder 一个谓词构造器(契约 §8 红线);数据范围按 SysRoleEnum 矩阵×audience 解析 + customId=fc:scope:all 扫描授权模块树,多来源取并集、全不命中回落仅本人提交。 四铁律落地:两段式列表禁 SELECT values_json、keyset 仅提交时间序(游标对外是 uuid)、 覆盖索引化(新增 idx_answer_form_time)、统计缓存键含 form_uuid+version 且仅升版失效+短 TTL。 T3 验收裁决前置三项:P25 answer.form_uuid 精确钉版溯源(删平台级回退启发式)、 P33 代填 subject 存在性+同校(SubjectDirectory 策略接口,生产走 /oapi/user/byId)、 P34 附件 key 与 store_key 一致性比对;P21 FormAdminGuard 整体替换为"授权模块集"口径 (ModuleTreeSource 策略接口,与 scope 解析共用同一棵树)。 新增对外读接口 GET /oapi/form/schema(内联 options+valueSchema)与 /oapi/form/answer (钉住版本 schema+值文档);导出 easyexcel 流式同步响应,选项列显 label、附件列显 fileName。 自验:mvn -B -o clean package 177 条全绿(本轮新增 56,23 条打真实 MySQL), 覆盖 V9/V11/V23/V27/V28/V37/V42 + V30 读侧半边 + V39 范围半边; V9 以"发布时补建索引行"实现(方案未指定机制,记 P36)。详见 docs/T4-report.md(问题单 P35~P46)。
答卷域是"校验只写一遍、索引只有一份"这条选型(D2 模型 2b)的兑现处,本轮把它一次落齐。 - 提交链路按语义排死顺序:钉版解析→截止→幂等命中→subject 授权→值校验→附件同校归属 →fill_quota 占位→落 answer→写索引。幂等必须在限次之前(重放那条本就占着名额), 限次不做先 count 再插而是事务内撞 uk 即拒(并发下 count 靠不住,方案 §5.4) - 钉版与定义域共用同一条 resolve 遮蔽实现;升版窗口旧版仍可填并钉旧版(P20), 请求体不含 version/schId/submitterId——身份结构性不可注入(V17) - FormValueValidator 按契约 §3 逐类校验值形状:date 严格 YYYY-MM-DD 且必须是真实日期、 number 空值只认 null、选项值取内联 ∪ allowedValues(含软删条目)、 附件只认 {id,fileName,key} 且拒绝对 URL/跨目录 key - answer_index 只写显式 searchable 字段、多值一行一个、v_str 截 191;编辑按钉住版本重建, 软删同步删行且释放 fill_quota 名额;留痕只增不改,改状态无任何转移规则校验(V15) - 上传模块自写平台标准版:FILE_PATH 三层兜底链、随机存储名、平台 15 项黑名单+svg、 ≤50MB(并按 P12 显式配 spring.servlet.multipart,默认 1MB 会挡掉合法附件) - 数据面刻意不建列表/详情/统计/导出读接口:四条读出路径必须共用 T4 的 scope 构造器, 现在写一个无范围过滤的详情接口就是给 T4 留后门 - T2 已验收文件仅按裁决改动:FormAdminGuard 收紧为 {2,7}(P21)并加 canManage; FormDefService 抽出 resolvePublished/新增 pinnedForData(数据面溯源含软删表单); FormRepository 加数据面取域查询;application.yml 只加 multipart 两行 自验:mvn -B -o clean package 绿(121 测试,本轮新增 54 条含 39 条打真实 MySQL, V32 并发面用双线程非回滚 IT 取证),逐 V 证据与问题单 P23~P32 见 docs/T3-report.md