yxh
3 天以前 1f23e7dfc186c65434eda46fd6756d41fd01e867
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
package cn.lihu.jh.module.ecg.service.queue;
 
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
 
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
 
/**
 * 「自动补人」周期兜底任务。
 *
 * <h3>为什么需要它</h3>
 * 分流({@code 排队中(10) → 候诊中(20)})原本是**纯事件驱动**的,只在下面这些时机跑一次:
 * <ul>
 *   <li>签到时 —— {@code AppointmentServiceImpl#appoitmentConfirm} → {@code queue()} → {@code startHurryUpOneCheckType}</li>
 *   <li>医生入座 / 恢复接诊 / 手动开诊 / 叫号 / 检查完成 —— 各自 {@code startHurryUpOneBed}</li>
 *   <li>开诊 · 服务重启 · 诊室重置 —— {@code startBedReload} 里的 {@code hurryupAllBed}</li>
 * </ul>
 * 一旦某次触发没有跑到(事务未提交就查库、执行器异常、服务刚好在重启、
 * 工位当时还没进分流队列……),患者就会**永远**停在「排队中(未分配工位)」,
 * 只能由医生在工作台手工点「拉入」。
 *
 * <p>现场反馈:运动试验签到后出现该现象(低频检查项目,执行器空闲 ⇒
 * 分流任务必然跑在事务提交之前,见 {@code QueueServiceImpl#runAfterCommit})。
 *
 * <h3>做了什么</h3>
 * 每 {@value #INTERVAL_SECONDS} 秒对**所有开诊工位**触发一次自动补人。
 * 只是"多跑一次幂等的补人",门闸(未开诊 / 停止候诊 / 候诊区已满)全部保持不变,
 * 因此不会改变既有业务规则,只把"漏网的排队患者"在 1 分钟内补上。
 *
 * <p>用独立的单线程守护调度器,**不占用** {@code DynamicSchedulingConfig} 里
 * 那个只有 1 个线程的 cron 池(避免影响开诊/闭诊/每日重置任务)。
 */
@Component
@Slf4j
public class QueueAutoRefillJob {
 
    /** 补人间隔(秒) */
    private static final long INTERVAL_SECONDS = 60L;
 
    /** 启动后首次延迟(秒):避开启动阶段(此时还没开诊、工位也没装载完) */
    private static final long INITIAL_DELAY_SECONDS = 60L;
 
    @Resource
    private QueueService queueService;
 
    private ScheduledExecutorService scheduler;
 
    @PostConstruct
    public void start() {
        scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
            Thread t = new Thread(r, "ecg-queue-auto-refill");
            t.setDaemon(true);
            return t;
        });
        scheduler.scheduleWithFixedDelay(() -> {
            try {
                // 异步投递到 QueueServiceImpl 的串行执行器;这里只负责"按时踹一脚"
                queueService.startHurryUpAll();
            } catch (Exception e) {
                log.error("[QueueAutoRefillJob] 周期补人触发异常(下一轮继续)", e);
            }
        }, INITIAL_DELAY_SECONDS, INTERVAL_SECONDS, TimeUnit.SECONDS);
 
        log.info("[QueueAutoRefillJob] 已启动:每 {} 秒兜底触发一次「全体工位自动补人」"
                + "(门闸不变:未开诊/停止候诊/候诊区已满 时不会补人)", INTERVAL_SECONDS);
    }
 
    @PreDestroy
    public void stop() {
        if (null != scheduler) {
            scheduler.shutdownNow();
            scheduler = null;
        }
    }
}