package jnpf.bizcommon.audit.service.title; import java.util.List; /** * 表单勾选({@code __config__.biz_log_enabled})解析的纯 JVM 回归网。 * *

为什么不复用 {@code AuditFormRegistry.collectFields}:它递归时把子表 children * 拍平进同一个 map,天生分不出主表/子表;而它被 {@code resolveFieldMetadata} 与 * {@code resolveBaselineFields} 两条已验证路径依赖、压着 {@code AuditL0PartialSnapshotProbe} * 的 46 条断言。标题是新语义,另起纯函数,风险隔离。 * *

本组管的错法:顺序被打乱(业务勾 a/c 显示成 c/a)、子表字段混进主表标题、 * 布局容器(栅格/标签页)里的字段被漏掉。这三种端到端都只表现为"标题看着不太对", * 没有报错、没有日志,只能靠断言钉死。 * *

runner:{@code tasks/audit-title-spec-checks.sh} */ public final class AuditTitleSpecProbe { private static int pass; private static int fail; public static void main(String[] args) { noneEnabled(); mainFieldsKeepFormOrder(); layoutContainersAreTraversed(); subTableIsSeparated(); subTableFieldsNeverLeakIntoMain(); malformedInputDegradesToEmpty(); System.out.println("=== 表单勾选解析断言:" + pass + " 通过 / " + fail + " 失败 ==="); if (fail > 0) { System.exit(1); } } /** T1 一个都没勾 → EMPTY。这是当前库里 148 份表单的真实状态,必须是安全默认。 */ private static void noneEnabled() { String json = "{\"fields\":[" + field("a", "input", false) + "," + field("b", "input", false) + "]}"; AuditTitleSpec spec = AuditTitleSpecParser.parse(json); check("T1 一个都没勾 → isEmpty", spec.isEmpty(), "勾选为空时不应产出任何标题字段"); } /** T2 顺序必须是表单出现顺序,不是字母序——业务勾 a/c 就得是 a/c。 */ private static void mainFieldsKeepFormOrder() { String json = "{\"fields\":[" + field("zebra", "input", true) + "," + field("apple", "input", true) + "," + field("mango", "input", false) + "]}"; List main = AuditTitleSpecParser.parse(json).mainFields(); check("T2 只收勾选字段", main.size() == 2, "实际 " + main); check("T2 保持表单顺序(非字母序)", "zebra".equals(main.get(0)) && "apple".equals(main.get(1)), "实际 " + main); } /** T3 栅格/标签页这类布局容器的 children 是主表字段,必须继续递归。 */ private static void layoutContainersAreTraversed() { String json = "{\"fields\":[{\"__config__\":{\"jnpfKey\":\"row\",\"children\":[" + field("inside", "input", true) + "]}}]}"; List main = AuditTitleSpecParser.parse(json).mainFields(); check("T3 布局容器内的勾选字段被收进主表", main.size() == 1 && "inside".equals(main.get(0)), "实际 " + main); } /** T4 子表控件独立成组,带上表名。 */ private static void subTableIsSeparated() { String json = "{\"fields\":[" + field("main1", "input", true) + ",{\"__vModel__\":\"tableField6da682\",\"__config__\":{\"jnpfKey\":\"table\"," + "\"tableName\":\"lims_jianyan_xiang\",\"children\":[" + field("sub1", "input", true) + "," + field("sub2", "input", false) + "," + field("sub3", "input", true) + "]}}]}"; AuditTitleSpec spec = AuditTitleSpecParser.parse(json); check("T4 主表只有 main1", spec.mainFields().size() == 1 && "main1".equals(spec.mainFields().get(0)), "实际 " + spec.mainFields()); check("T4 识别出 1 个子表", spec.subTables().size() == 1, "实际 " + spec.subTables().size()); AuditTitleSpec.SubTableSpec sub = spec.subTables().get(0); check("T4 子表 vModel", "tableField6da682".equals(sub.vModel()), "实际 " + sub.vModel()); check("T4 子表表名", "lims_jianyan_xiang".equals(sub.tableName()), "实际 " + sub.tableName()); check("T4 子表勾选字段按顺序", sub.fields().size() == 2 && "sub1".equals(sub.fields().get(0)) && "sub3".equals(sub.fields().get(1)), "实际 " + sub.fields()); } /** T5 子表字段绝不能出现在主表标题里——否则主表那一行会混进子表的值。 */ private static void subTableFieldsNeverLeakIntoMain() { String json = "{\"fields\":[{\"__vModel__\":\"tf1\",\"__config__\":{\"jnpfKey\":\"table\"," + "\"tableName\":\"t_sub\",\"children\":[" + field("leaky", "input", true) + "]}}]}"; AuditTitleSpec spec = AuditTitleSpecParser.parse(json); check("T5 子表字段不进主表", spec.mainFields().isEmpty(), "主表混入了子表字段:" + spec.mainFields()); } /** T6 非法/空输入一律退化成 EMPTY,绝不抛异常——层 0 的 handler 外层虽有兜底,但那会丢整条事件。 */ private static void malformedInputDegradesToEmpty() { check("T6 null 输入", AuditTitleSpecParser.parse(null).isEmpty(), "应为 EMPTY"); check("T6 空串输入", AuditTitleSpecParser.parse(" ").isEmpty(), "应为 EMPTY"); check("T6 非法 JSON", AuditTitleSpecParser.parse("{not json").isEmpty(), "应为 EMPTY"); check("T6 没有 fields 键", AuditTitleSpecParser.parse("{\"x\":1}").isEmpty(), "应为 EMPTY"); } private static String field(String vModel, String jnpfKey, boolean enabled) { return "{\"__vModel__\":\"" + vModel + "\",\"__config__\":{\"jnpfKey\":\"" + jnpfKey + "\",\"biz_log_enabled\":" + enabled + "}}"; } private static void check(String name, boolean ok, String detail) { if (ok) { pass++; System.out.println(" ✅ " + name); } else { fail++; System.out.println(" ❌ " + name + " —— " + detail); } } }