eight
2024-08-23 f696df5ce36beb3af261bae782fa0a418b83b9e6
jh-module-ecg/jh-module-ecg-biz/src/main/java/cn/lihu/jh/module/ecg/service/queue/queueServiceImpl.java
@@ -35,6 +35,14 @@
    final static Integer MAX_QUEUE_NUM = 3;
    @Resource
    private queueMapper queueMapper;
    @Resource
    private RoomMapper roomMapper;
    AtomicInteger openingFlag = new AtomicInteger(0);
    AtomicInteger curSeqNum = new AtomicInteger(0);
    PriorityBlockingQueue<BedQueueBO> priorityQueue = new PriorityBlockingQueue<>();
@@ -42,11 +50,6 @@
    ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    @Resource
    private queueMapper queueMapper;
    @Resource
    private RoomMapper roomMapper;
    @Override
    public Integer createqueue(QueueSaveReqVO createReqVO) {
@@ -90,7 +93,12 @@
        return queueMapper.selectPage(pageReqVO);
    }
    public void initQueue() {
    /**
     * !!开诊期间,不能执行这个方法,否则会有 P0 问题
     * 1. 每天开诊前 从DB同步工位的患者队列数据到 工位优先队列
     * 2. 服务运维重启时
     */
    public void initBedQueueAndSeqNumFromDB() {
        priorityQueue.clear();
        mapBedVsQueue.clear();
@@ -106,7 +114,11 @@
        bedQueueBOList.forEach(item -> {
            item.maxQueueNum = MAX_QUEUE_NUM;
            Optional<QueueStatisticDO> queueStatisticDOOptional = queueStatisticDOList.stream().filter(it->it.getRoomId()==item.roomId && it.getBedNo().equals(item.getBedNo())).findFirst();
            item.queueNum.set( queueStatisticDOOptional.isPresent() ? queueStatisticDOOptional.get().getTotalInStatus() : 0 );
            int queueNum = queueStatisticDOOptional.isPresent() ? queueStatisticDOOptional.get().getTotalInStatus() : 0;
            if ( MAX_QUEUE_NUM < queueNum )
                throw new RuntimeException("init: exceed max queue number!");
            item.queueNum.set( queueNum );
            priorityQueue.offer(item);
            mapBedVsQueue.put(String.format("%09d%s", item.roomId, item.bedNo), item);
        });
@@ -115,26 +127,40 @@
        curSeqNum = new AtomicInteger(null == num ? 0 : num);
    }
    public void reorderQueue() {
        // 根据预约前后,从DB 获取 队列中 就诊准备中人员 列表
        List<Byte> queueStatusList = new ArrayList<>();
        queueStatusList.add(QueueStatusEnum.READY.getStatus());
        List<QueueDO> queueDOList = queueMapper.getOrderedQueue(queueStatusList);
        if (queueDOList.isEmpty())
            return;
        AtomicInteger seqNum = new AtomicInteger(1);
        queueDOList.forEach(item -> {item.setSeqNum(seqNum.getAndIncrement());});
        queueMapper.updateBatch(queueDOList);
        curSeqNum.set( seqNum.get() );
    }
    /**
     * TODO 新开队列时,需要把排队中的人 转到 就诊准备 状态
     * 等到取下一个 排队中人员 的逻辑完成后,再回来不错
     */
    public void hurryup() {
        if (0 == openingFlag.get())
            return;
        // 处理 过号-回来 的人
        for (BedQueueBO bedQueueBO : mapBedVsQueue.values()) {
            while (bedQueueBO.queueNum.get() < bedQueueBO.maxQueueNum) {
                // 查看 当前工位 是否有过号-回来的患者
                Integer updateNum = queueMapper.procPassedReturnPatient(
                        bedQueueBO.getRoomId(),
                        bedQueueBO.getRoomName(),
                        bedQueueBO.getBedNo(),
                        curSeqNum.get() + 1,
                        QueueStatusEnum.PASSED_RETURN.getStatus(),
                        QueueStatusEnum.READY.getStatus());
                if (null == updateNum || 0 == updateNum)
                    break;
                curSeqNum.getAndIncrement();
                // 可能已经【并发的】在 nextPatient 中改变了值
                bedQueueBO.queueNum.incrementAndGet();
                // 可能已经【并发的】在 nextPatient 中改变了优先队列顺序
                priorityQueue.remove(bedQueueBO);
                priorityQueue.offer(bedQueueBO);
            }
        }
        // 处理 排队中 患者
        while (true) {
            BedQueueBO bedQueueBO = priorityQueue.peek();
            if (null == bedQueueBO)
@@ -142,18 +168,19 @@
            int curQueueNum = bedQueueBO.queueNum.get();
            if (curQueueNum > bedQueueBO.maxQueueNum)
                throw new RuntimeException("exceed max queue number!");
                throw new RuntimeException("hurryup: exceed max queue number!");
            if (curQueueNum == bedQueueBO.maxQueueNum)
                return;
            // 查看 是否有排队中的患者
            Integer updateNum = queueMapper.preemptPatient(
                    bedQueueBO.getRoomId(),
                    bedQueueBO.getRoomName(),
                    bedQueueBO.getBedNo(),
                    curSeqNum.get() + 1,
                    QueueStatusEnum.WAITING.getStatus(),
                    QueueStatusEnum.READY.getStatus());
                                bedQueueBO.getRoomId(),
                                bedQueueBO.getRoomName(),
                                bedQueueBO.getBedNo(),
                                curSeqNum.get() + 1,
                                QueueStatusEnum.WAITING.getStatus(),
                                QueueStatusEnum.READY.getStatus());
            // 没有抢到排队患者
            if (null == updateNum || 0 == updateNum) {
@@ -181,15 +208,21 @@
        QueueDO queue = BeanUtils.toBean(queueSaveReqVO, QueueDO.class);
        queueMapper.insert(queue);
        singleThreadExecutor.submit( () -> {
            hurryup();
        });
        if (0 == openingFlag.get())
            return;
        startHurryUp();
    }
    private void nextPatient(Long roomId, String bedNo) {
        // 从 DB 把 序号最小的 就诊准备中的人 设置为就诊中
        queueMapper.updateQueueStatus(roomId, bedNo,
        Integer updateNum = queueMapper.updateQueueStatus(roomId, bedNo,
                QueueStatusEnum.READY.getStatus(), QueueStatusEnum.ONSTAGE.getStatus());
        // 该工位 没有 就诊准备中 人员
        if (null == updateNum || 0 == updateNum) {
            return;
        }
        // 优先队列中 该工位 就诊准备中人的数量 减一
        BedQueueBO bo = mapBedVsQueue.get(String.format("%09d%s", roomId, bedNo));
@@ -197,9 +230,7 @@
        priorityQueue.remove(bo);
        priorityQueue.offer(bo);
        singleThreadExecutor.submit( () -> {
            hurryup();
        });
        startHurryUp();
    }
    public void finishNextPatient(Long roomId, String bedNo) {
@@ -244,4 +275,36 @@
        return patientStatisticVO;
    }
    public void startBiz() {
        // 清除非当天的排队人员
        queueMapper.clearQueue();
        initBedQueueAndSeqNumFromDB();
        openingFlag.set(1);
        hurryup();
    }
    @Override
    public Integer recallPatient(Long roomId, String bedNo, String patId) {
        Integer updateNum = queueMapper.passedPatientReturn(roomId, bedNo, patId,
                QueueStatusEnum.PASSED.getStatus(), QueueStatusEnum.PASSED_RETURN.getStatus());
        startHurryUp();
        return updateNum;
    }
    @Override
    public Integer patientJump(String patId, Byte jumped) {
        Integer updateNum = queueMapper.queueJump(patId, QueueStatusEnum.WAITING.getStatus(), jumped);
        startHurryUp();
        return updateNum;
    }
    private void startHurryUp() {
        singleThreadExecutor.execute( () -> {
            hurryup();
        });
    }
}