刘光辉
昨天 bb638871a7fb692d80f1b7a758f991dc0879002c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package jnpf.bizcommon.audit.service.value;
 
import jnpf.bizcommon.audit.entity.model.AuditFieldDiffViewVO;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
 
/** userSelect 用户 id → 「真实姓名/账号」的批量解析、控件类型判据与租户边界探针。 */
public final class JnpfUserFieldValueResolverProbe {
 
    private static int passed;
    private static int failed;
 
    public static void main(String[] args) {
        CountingLookup lookup = new CountingLookup();
        JnpfUserFieldValueResolver resolver = new JnpfUserFieldValueResolver(lookup);
 
        List<AuditFieldDiffViewVO> fields = Arrays.asList(
                field("tijiao_ren", "userSelect", "", "u-admin"),
                field("fuhe_ren", "UserSelect", "u-admin", "u-ys"),
                field("chouyang_ren", "userSelect", "", "[\"u-ys\",\"u-gone\"]"),
                // 两个非 userSelect 干扰项刻意用**独有**的值:若用 u-admin 这种已被真实用户
                // 字段收集过的 id,即便解析器错误地把它们也收进去,Set 去重后条数纹丝不动,
                // 断言就成了永远转不红的摆设(L077 变体 5/8 同款陷阱)。
                field("beizhu", "input", "", "u-only-in-beizhu"),
                field("zhuangtai", "select", "", "u-only-in-zhuangtai"));
 
        resolver.resolve(new AuditFieldValueContext("lims_ceshi_huichuan", "0", null), fields);
 
        check("整批只查一次库", lookup.calls == 1, "calls=" + lookup.calls);
        check("租户边界透传进查询", "0".equals(lookup.lastTenantId), String.valueOf(lookup.lastTenantId));
 
        // 判据是控件类型不是字段名:非 userSelect 的列即便存着用户 id 也不碰,
        // 否则会把恰好等于某个用户 id 的普通文本篡改成人名。
        check("非 userSelect 控件不被解析",
                fields.get(3).getNewDisplay() == null && fields.get(4).getNewDisplay() == null,
                fields.get(3).getNewDisplay() + " / " + fields.get(4).getNewDisplay());
        check("非 userSelect 控件的值不进 ID 收集",
                lookup.requestedIds.equals(Arrays.asList("u-admin", "u-ys", "u-gone")),
                lookup.requestedIds.toString());
 
        check("展示格式为 realName/account", "管理员/admin".equals(fields.get(0).getNewDisplay()),
                fields.get(0).getNewDisplay());
        check("控件类型大小写不敏感", "yangsui/ys0001".equals(fields.get(1).getNewDisplay()),
                fields.get(1).getNewDisplay());
        check("oldData 与 newData 两侧都解析", "管理员/admin".equals(fields.get(1).getOldDisplay()),
                fields.get(1).getOldDisplay());
        check("空值解析成空串而不是「名称暂不可用」", "".equals(fields.get(0).getOldDisplay()),
                fields.get(0).getOldDisplay());
        check("多选按请求顺序解析,单项查不到才回落",
                "yangsui/ys0001、名称暂不可用".equals(fields.get(2).getNewDisplay()),
                fields.get(2).getNewDisplay());
 
        // 前序解析器(表专用/字典)已经填过 display 的字段不重复查库——
        // @Order 建立的优先级靠这个 if 才落得下去。
        CountingLookup skipLookup = new CountingLookup();
        AuditFieldDiffViewVO resolved = field("qingyan_ren", "userSelect", "", "u-admin");
        resolved.setNewDisplay("管理员");
        new JnpfUserFieldValueResolver(skipLookup).resolve(
                new AuditFieldValueContext("lims_qingyandan", "0", null),
                Collections.singletonList(resolved));
        check("前序解析器已填的 display 不被覆盖,也不触发查库",
                skipLookup.calls == 0 && "管理员".equals(resolved.getNewDisplay()),
                skipLookup.calls + " / " + resolved.getNewDisplay());
 
        // 租户拿不到时宁可不解析,也不做一次无边界的全库查询。
        CountingLookup blankTenant = new CountingLookup();
        new JnpfUserFieldValueResolver(blankTenant).resolve(
                new AuditFieldValueContext("lims_ceshi_huichuan", "", null),
                Collections.singletonList(field("tijiao_ren", "userSelect", "", "u-admin")));
        check("空租户 fail-closed(不查库)", blankTenant.calls == 0,
                String.valueOf(blankTenant.calls));
 
        check("supports 对任意表恒为 true(userSelect 是平台控件)",
                resolver.supports("lims_ceshi_huichuan") && resolver.supports("any_table")
                        && resolver.supports(null),
                "supports 收窄了适用表");
 
        // 展示格式装配本身(验收标准「管理员/admin」的直接判据)
        check("userDisplay 拼 realName/account",
                "管理员/admin".equals(JnpfUserFieldValueResolver.userDisplay("管理员", "admin")),
                JnpfUserFieldValueResolver.userDisplay("管理员", "admin"));
        check("userDisplay 缺账号时不留孤零零斜杠",
                "管理员".equals(JnpfUserFieldValueResolver.userDisplay("管理员", null))
                        && "管理员".equals(JnpfUserFieldValueResolver.userDisplay("管理员", "  ")),
                JnpfUserFieldValueResolver.userDisplay("管理员", null));
        check("userDisplay 缺姓名时退回账号",
                "admin".equals(JnpfUserFieldValueResolver.userDisplay(null, "admin")),
                JnpfUserFieldValueResolver.userDisplay(null, "admin"));
        check("userDisplay 两边都空返回 null(调用方据此不入 map)",
                JnpfUserFieldValueResolver.userDisplay(null, null) == null,
                String.valueOf(JnpfUserFieldValueResolver.userDisplay(null, null)));
 
        // @Order 常量的相对次序即解析优先级;数值写反会让通用解析器抢在表专用之前。
        check("解析器优先级 表专用 < 字典 < 用户兜底",
                LimsQingyandanFieldValueResolver.ORDER < JnpfDictionaryFieldValueResolver.ORDER
                        && JnpfDictionaryFieldValueResolver.ORDER < JnpfUserFieldValueResolver.ORDER,
                LimsQingyandanFieldValueResolver.ORDER + " / "
                        + JnpfDictionaryFieldValueResolver.ORDER + " / "
                        + JnpfUserFieldValueResolver.ORDER);
 
        System.out.println("=== 用户字段解析断言:" + passed + " 通过 / " + failed + " 失败 ===");
        System.exit(failed == 0 ? 0 : 1);
    }
 
    private static AuditFieldDiffViewVO field(String name, String componentType,
                                              String oldData, String newData) {
        AuditFieldDiffViewVO field = new AuditFieldDiffViewVO();
        field.setField(name);
        field.setComponentType(componentType);
        field.setOldData(oldData);
        field.setNewData(newData);
        return field;
    }
 
    private static final class CountingLookup implements JnpfUserFieldValueResolver.UserLookup {
        private int calls;
        private String lastTenantId;
        private final List<String> requestedIds = new ArrayList<>();
 
        @Override
        public Map<String, String> names(String tenantId, Set<String> ids) {
            calls++;
            lastTenantId = tenantId;
            requestedIds.addAll(ids);
            Map<String, String> result = new LinkedHashMap<>();
            result.put("u-admin", "管理员/admin");
            result.put("u-ys", "yangsui/ys0001");
            return result;   // u-gone 刻意缺席:模拟用户被删/查不到
        }
    }
 
    private static void check(String name, boolean ok, String detail) {
        if (ok) {
            passed++;
            System.out.println("  [PASS] " + name);
        } else {
            failed++;
            System.out.println("  [FAIL] " + name + " -- " + detail);
        }
    }
 
    private JnpfUserFieldValueResolverProbe() {
    }
}