package jnpf.bizcommon.audit.service.sign;
import jnpf.bizcommon.audit.service.title.AuditTitleSpec;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 签名字段候选挑选的纯 JVM 断言。
*
*
判定规则为什么长这样:{@code biz_log_enabled} 在「操作记录标题」语境里是
* "这个字段进标题",在「多签名」语境里是"这是签名字段"——同一个开关两种语义,
* 光看开关分不出来。于是判定拆成三层:
*
* - {@code biz_sign} 及命名规则命中 {@code isCredentialReference} 的字段:恒为签名字段,
* 不需要勾(这批字段本就已被强制脱敏);
* - 其余勾选字段:值拿去查签名表,命中的当签名字段;
* - 没命中的当标题字段。
*
* 本探针只覆盖第 1、2 层的候选挑选(纯逻辑);"查表命中"那一跳需要 DB,放端到端。
*
* runner:{@code tasks/audit-sign-field-checks.sh}
*/
public final class AuditSignFieldProbe {
private static int pass;
private static int fail;
public static void main(String[] args) {
credentialNamesAreAlwaysCandidates();
enabledFieldsAreCandidates();
emptyValuesAreNotCandidates();
unmarkedNonCredentialFieldsAreNotCandidates();
candidatesAreDeduplicatedAndStable();
configuredFieldsTakePriority();
System.out.println("=== 签名字段候选断言:" + pass + " 通过 / " + fail + " 失败 ===");
if (fail > 0) {
System.exit(1);
}
}
/** S1 biz_sign 恒为候选,哪怕表单一个字段都没勾。 */
private static void credentialNamesAreAlwaysCandidates() {
Map row = row("biz_sign", "852000000000000001");
List c = AuditSignEvidenceReader.candidateFields(
row, AuditTitleSpec.EMPTY, Collections.emptyList());
check("S1 biz_sign 恒为候选", c.contains("biz_sign"), "实际 " + c);
Map row2 = row("fuhe_sign", "852000000000000002");
List c2 = AuditSignEvidenceReader.candidateFields(
row2, AuditTitleSpec.EMPTY, Collections.emptyList());
check("S1 *_sign 命名规则命中", c2.contains("fuhe_sign"), "实际 " + c2);
}
/** S2 勾选字段进候选(是不是签名由查表定,不由本方法定)。 */
private static void enabledFieldsAreCandidates() {
Map row = row("pizhun_ren", "852000000000000003");
AuditTitleSpec spec = new AuditTitleSpec(
Arrays.asList("pizhun_ren"), Collections.emptyList());
List c = AuditSignEvidenceReader.candidateFields(row, spec, Collections.emptyList());
check("S2 勾选字段进候选", c.contains("pizhun_ren"), "实际 " + c);
}
/** S3 值为空的不进候选——空值查签名表纯属浪费,还会把空串当 IN 参数。 */
private static void emptyValuesAreNotCandidates() {
Map row = new LinkedHashMap();
row.put("biz_sign", "");
row.put("fuhe_sign", null);
row.put("pizhun_ren", " ");
AuditTitleSpec spec = new AuditTitleSpec(
Arrays.asList("pizhun_ren"), Collections.emptyList());
List c = AuditSignEvidenceReader.candidateFields(row, spec, Collections.emptyList());
check("S3 空值不进候选", c.isEmpty(), "实际 " + c);
}
/** S4 既没勾、名字也不像签名的字段不进候选——否则每次保存都要拿一堆业务值去撞签名表。 */
private static void unmarkedNonCredentialFieldsAreNotCandidates() {
Map row = row("chanpin_mingcheng", "青岩丹");
List c = AuditSignEvidenceReader.candidateFields(
row, AuditTitleSpec.EMPTY, Collections.emptyList());
check("S4 未勾选的普通字段不进候选", c.isEmpty(), "实际 " + c);
}
/** S5 同一字段两条路径都命中时只出现一次,且顺序稳定(幂等键要靠它稳定)。 */
private static void candidatesAreDeduplicatedAndStable() {
Map row = new LinkedHashMap();
row.put("biz_sign", "852000000000000001");
row.put("pizhun_ren", "852000000000000003");
AuditTitleSpec spec = new AuditTitleSpec(
Arrays.asList("biz_sign", "pizhun_ren"),
Collections.emptyList());
List a = AuditSignEvidenceReader.candidateFields(row, spec, Collections.emptyList());
List b = AuditSignEvidenceReader.candidateFields(row, spec, Collections.emptyList());
check("S5 去重", a.size() == 2, "实际 " + a);
check("S5 顺序稳定", a.equals(b), a + " vs " + b);
}
/**
* S6(评审修复轮次 1,D4):配置来源必须排在候选集合最前面——这是本任务(D4)新加的行为,
* 前 5 条断言都是改动前就有的旧覆盖,全传空 {@code configuredFields},测不到这条。
*
* row 里故意先放一个命名规则命中的字段({@code fuhe_sign}),若没有配置来源它本应排最前;
* 再放一个只靠配置来源才进候选的字段({@code pizhun_ren},不命中命名规则、未勾选)。
* 断言 {@code get(0)} 是配置字段,直接执行验证优先级,而不是像
* {@code tasks/audit-delete-coverage-checks.sh} 那样只看源码文本形状——
* 容器换成无序 Set 这类"文本还在、行为已变"的回归,源码级断言未必能稳定抓住,靠这条兜底。
*/
private static void configuredFieldsTakePriority() {
Map row = new LinkedHashMap();
row.put("fuhe_sign", "852000000000000010");
row.put("pizhun_ren", "852000000000000011");
List configured = Arrays.asList("pizhun_ren");
List c = AuditSignEvidenceReader.candidateFields(row, AuditTitleSpec.EMPTY, configured);
check("S6 配置来源排在候选最前", !c.isEmpty() && "pizhun_ren".equals(c.get(0)), "实际 " + c);
check("S6 命名规则命中的字段仍在候选里(兜底未失效)", c.contains("fuhe_sign"), "实际 " + c);
}
private static Map row(String field, Object value) {
Map row = new LinkedHashMap();
row.put(field, value);
return row;
}
private static void check(String name, boolean ok, String detail) {
if (ok) {
pass++;
System.out.println(" ✅ " + name);
} else {
fail++;
System.out.println(" ❌ " + name + " —— " + detail);
}
}
}