刘光辉
昨天 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
package jnpf.bizcommon.audit.service.title;
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
 
/** 一次操作的记录标题:主表一行 + 子表每条记录一行。 */
public final class AuditRecordTitle {
 
    /** {@link #subScope()} 的取值:输入侧没看全——还有子表行(甚至整张子表)根本没读。 */
    public static final String SCOPE_SCAN_TRUNCATED = "scanTruncated";
 
    /** {@link #subScope()} 的取值:扫描完整,但产出的标题条数撞了上限被截。 */
    public static final String SCOPE_ROWS_TRUNCATED = "rowsTruncated";
 
    public static final AuditRecordTitle EMPTY =
            new AuditRecordTitle(null, Collections.<String>emptyList(), null, false, null);
 
    private final String mainTitle;
    private final List<String> subTitles;
    private final String subTable;
    private final boolean subDataUnavailable;
    private final String subScope;
 
    public AuditRecordTitle(String mainTitle, List<String> subTitles,
                            String subTable, boolean subDataUnavailable, String subScope) {
        this.mainTitle = mainTitle == null || mainTitle.trim().isEmpty() ? null : mainTitle;
        this.subTitles = subTitles == null || subTitles.isEmpty()
                ? Collections.<String>emptyList()
                : Collections.unmodifiableList(new ArrayList<String>(subTitles));
        this.subTable = subTable;
        this.subDataUnavailable = subDataUnavailable;
        this.subScope = subScope;
    }
 
    /** 主表标题行;一个字段都没勾、或勾了但全为空时返回 null(调用方回落 bizCode)。 */
    public String mainTitle() {
        return mainTitle;
    }
 
    /** 子表标题行,一条子记录一行。**未必是全量**,看 {@link #subScope()}。 */
    public List<String> subTitles() {
        return subTitles;
    }
 
    /**
     * 子表标题的完整性。{@code null} = 完整;否则是 {@link #SCOPE_SCAN_TRUNCATED}
     * 或 {@link #SCOPE_ROWS_TRUNCATED}。
     *
     * <p><b>为什么必须有这个</b>(2026-08-05 Codex 复审 Low):两道上限一直是**悄悄**截的,
     * 读的人看到 200 条会以为那就是全部。资源上界下推之后又多了一种截法——额度耗尽时
     * 后面整张子表都不碰,连「事件里没带这批数据」({@link #subDataUnavailable()})都测不出来,
     * 于是「判不了」被伪装成了「没发生」(L075,本批反复在防的同一个形状)。
     *
     * <p><b>为什么不是「把没看的补看完」</b>:那要为每张剩余子表整份反序列化一遍,
     * 正是资源上界刚拆掉的那笔开销。诚实地说「我没看全」比花大价钱看全更划算——
     * 真要全量,读的人可以去查那条记录本身。
     *
     * <p>{@code scanTruncated} 优先于 {@code rowsTruncated}:输入侧就没看全时,
     * 产出侧截没截已经不是最重要的信息了。
     */
    public String subScope() {
        return subScope;
    }
 
    public String subTable() {
        return subTable;
    }
 
    /** 表单配了子表标题字段,但事件里取不到子表数据——如实留痕,不能当成"没有子表"。 */
    public boolean subDataUnavailable() {
        return subDataUnavailable;
    }
 
    public boolean isEmpty() {
        return mainTitle == null && subTitles.isEmpty() && !subDataUnavailable;
    }
}