feat(material): M1-04 材料项定义与内置模板
发布前置校验里最后缺的一项(40018「未配置必传材料项」)终于能配了。
按计划表这一项是后端(4 人日),材料规则的界面属 M1-09。
交付:
· 材料项 CRUD(《03-接口设计》§7.10):GET/POST /tasks/{id}/materials、
PUT/DELETE /materials/{id}
· 三个内置模板(TPL_SCHOOL_ROLL / TPL_SOCIAL / TPL_ZHUANXIANG),幂等应用
· GET /material-catalog:材料码/档位/格式白名单/模板的静态清单,
供界面下拉使用(接口是契约外的,理由见 Controller 注释:把这份常量在前端
再抄一份会造成两份真相,而别名正是识别历史文件名用的)
落在代码里的三件事:
1) 默认值按两个维度分别取(MaterialCatalog):
· 按材料码定 名字/别名/默认压缩档/份数/是否合并
· 按压缩档定 固定尺寸(P1)/长边下限(P2)/目标DPI(P3)/输出格式
把份数塞进档位默认里是很容易犯的错 —— 那样所有 P2 材料都会变成"要传 2 份",
而"身份证要正反面"是这件材料的事实,不是档位的规则。
2) WORK_PROOF 的名字就是「工作证明」。机构模板里「职业技能等级个人申报表」
的规范文件名就是「工作证明」,这是机构既有习惯,不要"修正" —— 改了会让
导出包与该机构一直以来的归档口径对不上,带教老师只认这个名字。
3) 关联键是 (task_id, material_code),不是 material_def_id(表里没有那个外键)。
所以 40027「已有文件不可删」必须按码查 —— 用 id 查会永远返回 0,
这道校验形同虚设,后果是"删了材料定义,文件成了孤儿"。
本轮修掉的 2 个真问题:
1) NOT NULL DEFAULT 与"档位不适用就置空"打架(同一类 bug 的第二次):
min_long_side(NOT NULL DEFAULT 1400)与 output_format(NOT NULL DEFAULT 'jpg')
在切档位时被写成 null,PostgreSQL 直接 500。而更根本的是:契约 §3.4 明确写着
outputFormat 的 null = 保持原格式 —— 这一列本就不该是 NOT NULL。
已由 V5__material_profile_params_nullable.sql 把四个档位参数统一成
"不适用时为空"(另两个本来就可空),并同步更正两份 DDL 文档。
⚠️ 这与上一轮的 expected_count 是同一类问题:NOT NULL DEFAULT x 看起来无害,
但它让"未设置/不适用"无法表达,而契约往往又要求能表达。
动"可选数字/字符串"字段前,先查 information_schema.columns 的 is_nullable。
2) 我的两条 E2E 断言本身是坏的:
· 把 lower_case 断言成"应被拒" —— 实际服务端把它规范化成 LOWER_CASE 后
接受了,这是对输入友好的正确行为,错的是断言;
· "改档位后参数跟着变"用了 P3,而 OTHER_PROOF 的默认档本来就是 P3,
于是无论改没改都会通过(假通过)。
教训:断言要能真的失败。
顺带整理:
· 抽出 support/NamingTemplate:任务级 naming_template 与材料级 name_pattern
必须共用同一套校验规则。两处各写一套会出现"任务级认的变量材料级不认",
而这类不一致的表现是"发布时通过、导出时文件名叫错",最难在测试里撞上。
TaskService 已改为委托。
· materialTemplateId(契约里是 int64)不再空转:§7.10 的三个模板在文档与代码里
都是具名常量,库里也没有"材料模板表",拿 id 指代常量就得先造一张表放它们。
新增 materialTemplateCode(字符串)承接内置模板,materialTemplateId 留给
M1-05 的 collect_rule_template(那是真正的一行一条,id 才有意义)。
已知缺口(写进了代码注释与交接摘要):
· minSizeKb 恒为 null:契约 §3.4 给了字段但"对应列"是 —,而《04》又用到
materialDef.minSizeKb,两侧口径没对齐;且具体阈值属待办 D5「一寸照片阈值标定」,
没有实测数据。编一个数会让它看起来"已实现",卡住真材料时没人说得清为什么。
· 条件显示只做存取与形状校验,求值在填报引擎 M3-10。
· 40030(审核中不可删材料)属 M5,当前删除只查 40027。
验收:e2e-m1-materials.py 68/68;五条 E2E 合计 278 项断言全通过;
Flyway 迁移 1~5 全部 success。
This commit is contained in:
+56
@@ -0,0 +1,56 @@
|
||||
-- ============================================================================
|
||||
-- collect_material_def:让四个「压缩档位参数」口径一致(不适用时为空)
|
||||
-- ============================================================================
|
||||
--
|
||||
-- 背景:这张表有四个"只在某个档位下生效"的参数:
|
||||
-- fixed_size 仅 P1 证件照(295x413)
|
||||
-- min_long_side 仅 P2 证件翻拍(长边下限)
|
||||
-- target_dpi 仅 P3 扫描件
|
||||
-- output_format 输出的图片格式
|
||||
--
|
||||
-- V1 建表时给了它们不一致的约束:
|
||||
-- fixed_size varchar(32) -- 可空
|
||||
-- target_dpi integer -- 可空
|
||||
-- min_long_side integer NOT NULL DEFAULT 1400 -- 不可空
|
||||
-- output_format varchar(16) NOT NULL DEFAULT 'jpg' -- 不可空
|
||||
--
|
||||
-- 于是"切到某个档位时把不适用的参数置空"这件事,对两个字段成立、对另两个字段报错
|
||||
-- (实测:改档位到 P3 时直接 500,PostgreSQL 报
|
||||
-- null value in column "min_long_side" violates not-null constraint)。
|
||||
--
|
||||
-- 为什么改成"都可空"而不是"都存默认值":
|
||||
--
|
||||
-- 1) **output_format 的 null 是有语义的**。《03-接口设计》§3.4 明确写着
|
||||
-- 「输出格式;null = 保持原格式」。列上的 NOT NULL 让这个契约根本无法表达 ——
|
||||
-- 契约优先,这一条没有商量余地。
|
||||
--
|
||||
-- 2) **min_long_side 存 1400 会误导**。它的注释写着「仅 P2 生效」,
|
||||
-- 但在 P1 的材料行里读出 1400,无论是人看还是代码看都会以为"这张证件照有长边下限"。
|
||||
-- 而真相是"这个参数对本档位不适用"。**"不适用"和"有一个下限"是两件事**,
|
||||
-- 用同一个值表示它们,迟早有人据此写出错误的判断。
|
||||
--
|
||||
-- 3) **一致性本身就是价值**。四个同类参数两套规则,下次再加一个参数时
|
||||
-- 没人说得清该跟随哪一套。定成"档位参数不适用时为空",只有一条规则。
|
||||
--
|
||||
-- ⚠️ 历史行里已有的 1400 / 'jpg' 保持原样,不动 ——
|
||||
-- 无法判断它是"当时不适用被写了默认值"还是"确实设置过"。猜错会让
|
||||
-- 某张证件照突然多出一个长边下限。开发者本地的测试数据由
|
||||
-- scripts/e2e-m1-materials.py 的清理段收拾。
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE collect_material_def
|
||||
ALTER COLUMN min_long_side DROP NOT NULL;
|
||||
|
||||
ALTER TABLE collect_material_def
|
||||
ALTER COLUMN min_long_side DROP DEFAULT;
|
||||
|
||||
ALTER TABLE collect_material_def
|
||||
ALTER COLUMN output_format DROP NOT NULL;
|
||||
|
||||
ALTER TABLE collect_material_def
|
||||
ALTER COLUMN output_format DROP DEFAULT;
|
||||
|
||||
COMMENT ON COLUMN collect_material_def.min_long_side IS
|
||||
'分辨率下限(长边px),仅 P2 证件翻拍档生效;其余档位为 NULL 表示不适用';
|
||||
COMMENT ON COLUMN collect_material_def.output_format IS
|
||||
'输出格式;NULL 表示保持原格式(见《03-接口设计》§3.4)。P4 截图档固定 png';
|
||||
Reference in New Issue
Block a user