yxh
6 天以前 68ff33beff11310e830b0615b5a81f83fad0f683
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 cn.lihu.jh.module.ecg.enums;
 
import cn.lihu.jh.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
 
import java.util.Arrays;
 
/**
 * 排队序号规则枚举 —— 在「检查类型管理」(system/check-type) 的 {@code check_type.seq_rule} 上配置。
 *
 * <h3>背景</h3>
 * 签到时需要给患者一个「排队序号」,不同检查项目用不同规则。原实现**只支持一种**
 * (按时段取号,见 {@link #TIMESLOT}),本枚举把三种规则显式化,便于按项目配置。
 *
 * <h3>三种规则</h3>
 * <ul>
 *   <li>{@link #APPOINT} —— **直接用预约系统下发的预约号**,签到时不再分配新序号;</li>
 *   <li>{@link #CHECKIN} —— **签到时给签到序号**:同一天、同一检查类型内,第 1 个签到是 1 号,
 *       第 2 个是 2 号……;并按「每 9 个号预留 1 个」跳过预留号,留给加急患者;</li>
 *   <li>{@link #TIMESLOT} —— **按预约时间段给号**(现状规则):号池按
 *       {@code (check_type, time_slot)} 维护,容量受时段配置限制。</li>
 * </ul>
 *
 * <h3>容量语义差异(重要)</h3>
 * {@link #APPOINT} 与 {@link #CHECKIN} **不设当日上限**(号连续递增,发完即止不存在),
 * 只有 {@link #TIMESLOT} 保留原有的时段容量控制。见 {@link #isCapacityLimited()}。
 *
 * <h3>与现有 {@code timeslot_book_num} / {@code timeslot_reserved_num} 的关系</h3>
 * 那两个字段是**时段号池**的参数,仅在 {@link #TIMESLOT} 下生效;
 * {@link #CHECKIN} 的预留规则固定为「每 9 个跳 1 个」(9 个普通号 + 1 个预留号 = 一轮 10 个)。
 *
 * @author 心电排队装机系统
 */
@Getter
@AllArgsConstructor
public enum SeqRuleEnum implements IntArrayValuable {
 
    /** ① 用预约系统的预约号(不再分配新号) */
    APPOINT(0, "预约号"),
    /** ② 签到时给签到序号(当天同检查类型内连续递增,每 9 个预留 1 个) */
    CHECKIN(1, "签到序号"),
    /** ③ 按预约时间段给号(现状规则,号池按时段维护) */
    TIMESLOT(2, "时段号");
 
    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SeqRuleEnum::getRule).toArray();
 
    /** 默认规则:保持与改造前一致的行为,避免存量数据未配置时行为变化 */
    public static final SeqRuleEnum DEFAULT = TIMESLOT;
 
    /**
     * 一轮号的数量:9 个普通号 + 1 个预留号。
     * 预留号用于加急患者({@code jjbz=J})。
     */
    public static final int ROUND_SIZE = 10;
 
    /** 一轮中被预留的号个数(每 {@link #ROUND_SIZE} 个留 1 个) */
    public static final int RESERVED_PER_ROUND = 1;
 
    /** 规则值 */
    private final Integer rule;
    /** 规则名 */
    private final String name;
 
    @Override
    public int[] array() {
        return ARRAYS;
    }
 
    /**
     * 该规则下是否需要检查「时段号池容量」。
     * <p>
     * 仅 {@link #TIMESLOT} 需要 —— 它受 {@code queue_sequence.queue_full} 限制;
     * 另两种规则号值是连续递增的,不设上限。
     */
    public boolean isCapacityLimited() {
        return this == TIMESLOT;
    }
 
    /**
     * 按规则值解析枚举;无法识别或为 null 时返回 {@link #DEFAULT}。
     * <p>
     * 之所以不抛异常:该值来自数据库配置,存量行可能为 null;
     * 取默认值可保证升级后**行为与改造前一致**,不因未配置而阻断签到。
     */
    public static SeqRuleEnum parse(Integer rule) {
        if (rule == null) {
            return DEFAULT;
        }
        for (SeqRuleEnum item : values()) {
            if (item.rule.equals(rule)) {
                return item;
            }
        }
        return DEFAULT;
    }
 
    /**
     * 判断给定号是否为「预留号」(留给加急患者)。
     * <p>
     * 规则:每 {@link #ROUND_SIZE} 个号留 {@link #RESERVED_PER_ROUND} 个。
     * 以 1 为起点时,每轮的第 10 个(10、20、30…)为预留号。
     *
     * @param seq 候选号(从 1 开始)
     * @return true 表示该号需跳过(除非是加急患者)
     */
    public static boolean isReservedSeq(int seq) {
        return seq > 0 && seq % ROUND_SIZE == 0;
    }
 
    /**
     * 取 {@code from} 及之后第一个「非预留号」。
     *
     * <p>用于普通患者:若计数器算出的号恰好是预留号(10 的倍数),
     * 则改用下一个号,把预留号留给加急患者。
     *
     * <p>注意与调用方 {@code advanceNormalSeq} 的配合:计数器在同一事务内
     * <b>也要</b>跳过该预留号(多 +1),否则下一次会把同一个号再发一次。
     * 详见 {@code QueueSequenceMapper#advanceNormalSeq} 的说明。
     *
     * @param from 起始号(含)
     * @return 第一个非预留号
     */
    public static int nextNormalSeq(int from) {
        int seq = Math.max(1, from);
        while (isReservedSeq(seq)) {
            seq++;
        }
        return seq;
    }
 
    /**
     * 取下一个预留号(给加急患者)。
     * <p>
     * 若 {@code from} 本身已是预留号则直接返回;否则跳到本轮的预留号。
     * 例:from=1 → 10;from=10 → 10;from=11 → 20。
     *
     * @param from 起始号(含)
     * @return 不小于 {@code from} 的第一个预留号
     */
    public static int nextReservedSeq(int from) {
        int seq = Math.max(1, from);
        int rem = seq % ROUND_SIZE;
        if (rem == 0) {
            return seq;
        }
        return seq + (ROUND_SIZE - rem);
    }
 
}