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);
|
}
|
|
}
|