eight
2024-09-02 3be2584eca33b24abfa773a4c5ae6f5c1de31d76
jh-module-ecg/jh-module-ecg-biz/src/main/java/cn/lihu/jh/module/ecg/service/queue/queueServiceImpl.java
@@ -1,28 +1,33 @@
package cn.lihu.jh.module.ecg.service.queue;
import cn.lihu.jh.module.ecg.dal.dataobject.queue.BedQueueStatisticDO;
import cn.lihu.jh.module.ecg.dal.dataobject.queue.QueueStatisticDO;
import cn.lihu.jh.module.ecg.dal.dataobject.room.RoomDO;
import cn.lihu.jh.module.ecg.dal.mysql.room.RoomMapper;
import cn.lihu.jh.module.ecg.enums.QueueStatusEnum;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import cn.lihu.jh.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.lihu.jh.module.ecg.dal.dataobject.room.RoomDO;
import cn.lihu.jh.framework.common.exception.ErrorCode;
import cn.lihu.jh.framework.common.pojo.CommonResult;
import cn.lihu.jh.module.ecg.controller.admin.room.vo.RoomRespVO;
import cn.lihu.jh.module.ecg.dal.dataobject.queue.BedQueueStatisticDO;
import cn.lihu.jh.module.ecg.dal.dataobject.queue.QueueStatisticDO;
import cn.lihu.jh.module.ecg.dal.mysql.room.RoomMapper;
import cn.lihu.jh.module.ecg.enums.BedStatusEnum;
import cn.lihu.jh.module.ecg.enums.QueueStatusEnum;
import cn.lihu.jh.module.ecg.controller.admin.queue.vo.*;
import cn.lihu.jh.module.ecg.dal.dataobject.queue.QueueDO;
import cn.lihu.jh.framework.common.pojo.PageResult;
import cn.lihu.jh.framework.common.util.object.BeanUtils;
import cn.lihu.jh.module.ecg.dal.mysql.queue.queueMapper;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import static cn.lihu.jh.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.lihu.jh.module.ecg.enums.ErrorCodeConstants.*;
import static cn.lihu.jh.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.lihu.jh.framework.common.pojo.CommonResult.error;
import static cn.lihu.jh.framework.common.pojo.CommonResult.success;
/**
 * 排队 Service 实现类
@@ -31,9 +36,8 @@
 */
@Service
@Validated
@Slf4j
public class QueueServiceImpl implements QueueService {
    final static Integer MAX_QUEUE_NUM = 3;
    @Resource
    private queueMapper queueMapper;
@@ -42,7 +46,6 @@
    private RoomMapper roomMapper;
    AtomicInteger openingFlag = new AtomicInteger(0);
    AtomicInteger curSeqNum = new AtomicInteger(0);
    PriorityBlockingQueue<BedQueueBO> priorityQueue = new PriorityBlockingQueue<>();
@@ -50,6 +53,7 @@
    ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    Integer queueReadyMax = 0;
    @Override
    public Integer createqueue(QueueSaveReqVO createReqVO) {
@@ -78,30 +82,350 @@
    }
    @Override
    public Integer bedControl(Long roomId, String bedNo, Integer status) {
        // 营业期间不能关闭工位
        if ( status == 0 && openingFlag.get() == 1 )
            return 301;
    public ErrorCode startBedOpen(Long roomId, String bedNo) {
        Future<ErrorCode> future = singleThreadExecutor.submit( new BedOpenCallable(this, roomId, bedNo));
        // 当前状态
        roomMapper.select
        // DB update
        Integer updateNum = roomMapper.setBedStatus(roomId, bedNo, status);
        if ( null==updateNum || 0 == updateNum )
            return 310;
        if ( status == 10 ) {
            BedQueueBO bedQueueBO = new BedQueueBO();
            bedQueueBO.setRoomId(roomId);
            bedQueueBO.setBedNo(bedNo);
            bedQueueBO.setMaxQueueNum(MAX_QUEUE_NUM);
            bedQueueBO.setQueueNum(new AtomicInteger(0));
            priorityQueue.offer(bedQueueBO);
            mapBedVsQueue.put(String.format("%09d%s", bedQueueBO.roomId, bedQueueBO.bedNo), bedQueueBO);
        try {
            ErrorCode ret = future.get();
            return ret;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return 0;
        System.out.println("startBedOpen ========");
        return ECG_INNER_ERROR;
    }
    @Override
    public ErrorCode startBedClose(Long roomId, String bedNo) {
        Future<ErrorCode> future = singleThreadExecutor.submit( new BedCloseCallable(this, roomId, bedNo));
        try {
            ErrorCode ret = future.get();
            return ret;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("startBedClose ========");
        return ECG_INNER_ERROR;
    }
    @Override
    public ErrorCode startBedDoctorPause(Long roomId, String bedNo, Long docId, String docName) {
        Future<ErrorCode> future = singleThreadExecutor.submit(
            new BedDoctorPauseCallable(this, roomId, bedNo, docId, docName)
        );
        try {
            ErrorCode ret = future.get();
            return ret;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("startBedDoctorPause ========");
        return ECG_INNER_ERROR;
    }
    @Override
    public ErrorCode startBedDoctorResume(Long roomId, String bedNo, Long docId, String docName) {
        Future<ErrorCode> future = singleThreadExecutor.submit(
            new BedDoctorResumeCallable(this, roomId, bedNo, docId, docName)
        );
        try {
            ErrorCode ret = future.get();
            return ret;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("startBedDoctorResume ========");
        return ECG_INNER_ERROR;
    }
    @Override
    public ErrorCode startBedDoctorOn(Long roomId, String bedNo, Long docId, String docName) {
        Future<ErrorCode> future = singleThreadExecutor.submit(
            new BedDoctorOnCallable(this, roomId, bedNo, docId, docName)
        );
        try {
            ErrorCode ret = future.get();
            return ret;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("startBedDoctorOn ========");
        return ECG_INNER_ERROR;
    }
    @Override
    public ErrorCode startBedDoctorOff(Long roomId, String bedNo, Long docId, String docName) {
        Future<ErrorCode> future = singleThreadExecutor.submit(
            new BedDoctorOffCallable(this, roomId, bedNo, docId, docName)
        );
        try {
            ErrorCode ret = future.get();
            return ret;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("startBedDoctorOff ========");
        return ECG_INNER_ERROR;
    }
    @Override
    public ErrorCode startNextPatient(Long roomId, String bedNo) {
        Future<ErrorCode> future = singleThreadExecutor.submit(
                new BedDoctorNextPatientCallable(this, roomId, bedNo)
        );
        try {
            ErrorCode ret = future.get();
            return ret;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("startNextPatient ========");
        return ECG_INNER_ERROR;
    }
    @Override
    public void startHurryUp() {
        singleThreadExecutor.execute( () -> {
            hurryup();
        });
    }
    @Override
    public void startBedReload() {
        singleThreadExecutor.execute( () -> {
            bedReload();
            hurryup();
            monitorInfo();
        });
    }
    @Override
    public Integer getOpeningFlag() {
        return openingFlag.get();
    }
    /**
     * 已关闭 或者 关闭中,可以开通工位
     * @param roomId
     * @param bedNo
     * @return
     */
    @Override
    public ErrorCode bedOpen(Long roomId, String bedNo) {
        BedQueueBO bedQueueBO2 = mapBedVsQueue.get(String.format("%09d%s", roomId, bedNo));
        if (null != bedQueueBO2) {
            log.error("bedOpen mapBedVsQueue has existed. " + roomId + " " + bedNo);
            return QUEUE_BED_EXIST;
        }
        // DB update
        List statusList = new ArrayList<BedStatusEnum>();
        statusList.add(BedStatusEnum.CLOSED);
        Integer updateNum = roomMapper.setBedOpeningOpening(roomId, bedNo, BedStatusEnum.OPENING, statusList);
        if ( null==updateNum || 0 == updateNum ) {
            log.error("bedOpen DB invalid status. " + roomId + " " + bedNo);
            return ROOM_INVALID_STATUS;
        }
        List<Byte> queueStatusList = new ArrayList<>();
        queueStatusList.add(QueueStatusEnum.READY.getStatus());
        List<QueueDO> queueDOList = queueMapper.getDoctorQueueByStatus(roomId, bedNo, queueStatusList);
        // 新增工位 优先队列
        BedQueueBO bedQueueBO = new BedQueueBO();
        bedQueueBO.setRoomId(roomId);
        bedQueueBO.setBedNo(bedNo);
        bedQueueBO.setMaxQueueNum(queueReadyMax);
        bedQueueBO.setQueueNum(new AtomicInteger(queueDOList.size()));
        bedQueueBO.setStatus(BedStatusEnum.OPENING.getStatus());
        priorityQueue.offer(bedQueueBO);
        mapBedVsQueue.put(String.format("%09d%s", roomId, bedNo), bedQueueBO);
        return GlobalErrorCodeConstants.SUCCESS;
    }
    @Override
    public ErrorCode bedClose(Long roomId, String bedNo) {
        BedQueueBO bedQueueBO = mapBedVsQueue.get(String.format("%09d%s", roomId, bedNo));
        if (null == bedQueueBO) {
            log.error("bedClose mapBedVsQueue DONOT existed. " + roomId + " " + bedNo);
            return QUEUE_BED_NOT_EXIST;
        }
        // 除了判断 准备候诊中 的人数,还需要 判断 过号的人数
        //if (bedQueueBO.getQueueNum().get() >0)
        //    return QUEUE_HAVE_PATIENT;
        List<Byte> queueStatusList = new ArrayList<>();
        queueStatusList.add(QueueStatusEnum.READY.getStatus());
        queueStatusList.add(QueueStatusEnum.PASSED.getStatus());
        queueStatusList.add(QueueStatusEnum.PASSED_RETURN.getStatus());
        List<QueueDO>  queueDOList = queueMapper.getDoctorQueueByStatus(roomId, bedNo, queueStatusList);
        if (queueDOList.size() > 0)
            return QUEUE_HAVE_PATIENT;
        // DB update
        List statusList = new ArrayList<BedStatusEnum>();
        statusList.add(BedStatusEnum.OPENING);
        statusList.add(BedStatusEnum.DOCTOR_ON);
        statusList.add(BedStatusEnum.PAUSE);
        Integer updateNum = roomMapper.setBedOpeningClosed(roomId, bedNo,
                BedStatusEnum.CLOSED, statusList);
        if ( null==updateNum || 0 == updateNum ) {
            log.error("bedClose DB invalid status. " + roomId + " " + bedNo);
            return ROOM_INVALID_STATUS;
        }
        mapBedVsQueue.remove(String.format("%09d%s", roomId, bedNo));
        priorityQueue.remove(bedQueueBO);
        return GlobalErrorCodeConstants.SUCCESS;
    }
    @Override
    public CommonResult<RoomRespVO> getRoom(Long roomId, String bedNo, Long docId) {
        RoomDO roomDO = roomMapper.getRoom(roomId, bedNo, docId);
        if (null == roomDO) {
            return error(ROOM_NOT_SIT);
        }
        BedQueueBO bedQueueBO = mapBedVsQueue.get(String.format("%09d%s", roomId, bedNo));
        if (null == bedQueueBO) {
            log.error("getRoom mapBedVsQueue DONOT existed. " + roomId + " " + bedNo);
            return error(QUEUE_BED_NOT_EXIST);
        }
        RoomRespVO roomRespVO = BeanUtils.toBean(roomDO, RoomRespVO.class);
        return success(roomRespVO);
    }
    @Override
    public ErrorCode bedDoctorPause(Long roomId, String bedNo, Long docId, String docName) {
        BedQueueBO bedQueueBO = mapBedVsQueue.get(String.format("%09d%s", roomId, bedNo));
        if (null == bedQueueBO) {
            log.error("bedDoctorPause mapBedVsQueue DONOT existed. " + roomId + " " + bedNo);
            return QUEUE_BED_NOT_EXIST;
        }
        // DB update
        List statusList = new ArrayList<BedStatusEnum>();
        statusList.add(BedStatusEnum.DOCTOR_ON);
        Integer updateNum = roomMapper.setBedDoctorStatus(roomId, bedNo, docId, BedStatusEnum.PAUSE, statusList);
        if ( null==updateNum || 0 == updateNum ) {
            log.error("bedDoctorPause DB invalid status. " + roomId + " " + bedNo);
            return ROOM_INVALID_STATUS;
        }
        bedQueueBO.setStatus(BedStatusEnum.PAUSE.getStatus());
        priorityQueue.remove(bedQueueBO);
        return GlobalErrorCodeConstants.SUCCESS;
    }
    @Override
    public ErrorCode bedDoctorResume(Long roomId, String bedNo, Long docId, String docName) {
        BedQueueBO bedQueueBO = mapBedVsQueue.get(String.format("%09d%s", roomId, bedNo));
        if (null == bedQueueBO || !bedQueueBO.getStatus().equals(BedStatusEnum.PAUSE.getStatus())) {
            log.error("bedDoctorResume mapBedVsQueue DONOT existed OR NOT Paused. " + roomId + " " + bedNo);
            return QUEUE_BED_NOT_EXIST;
        }
        // DB update
        List statusList = new ArrayList<BedStatusEnum>();
        statusList.add(BedStatusEnum.PAUSE);
        Integer updateNum = roomMapper.setBedDoctorStatus(roomId, bedNo, docId,
                BedStatusEnum.DOCTOR_ON, statusList);
        if ( null==updateNum || 0 == updateNum ) {
            log.error("bedDoctorResume DB invalid status. " + roomId + " " + bedNo);
            return ROOM_INVALID_STATUS;
        }
        bedQueueBO.setStatus(BedStatusEnum.DOCTOR_ON.getStatus());
        if (!priorityQueue.contains(bedQueueBO)) {
            priorityQueue.offer(bedQueueBO);
        } else {
            log.error("bedDoctorResume priorityQueue still exist!. " + roomId + " " + bedNo);
            return ECG_INNER_ERROR;
        }
        return GlobalErrorCodeConstants.SUCCESS;
    }
    @Override
    public ErrorCode bedDoctorOn(Long roomId, String bedNo, Long docId, String docName) {
        BedQueueBO bedQueueBO = mapBedVsQueue.get(String.format("%09d%s", roomId, bedNo));
        if (null == bedQueueBO) {
            log.error("bedDoctorOn mapBedVsQueue DONOT existed. " + roomId + " " + bedNo);
            return QUEUE_BED_NOT_EXIST;
        }
        // DB update
        List statusList = new ArrayList<BedStatusEnum>();
        statusList.add(BedStatusEnum.OPENING);
        Integer updateNum = roomMapper.setBedDoctorOn(roomId, bedNo, docId, docName, BedStatusEnum.DOCTOR_ON, statusList);
        if ( null==updateNum || 0 == updateNum ) {
            log.error("bedDoctorOn DB invalid status. " + roomId + " " + bedNo);
            return ROOM_INVALID_STATUS;
        }
        bedQueueBO.setStatus(BedStatusEnum.DOCTOR_ON.getStatus());
        return GlobalErrorCodeConstants.SUCCESS;
    }
    @Override
    public ErrorCode bedDoctorOff(Long roomId, String bedNo, Long docId, String docName) {
        BedQueueBO bedQueueBO = mapBedVsQueue.get(String.format("%09d%s", roomId, bedNo));
        if (null == bedQueueBO) {
            log.error("bedDoctorOff mapBedVsQueue DONOT existed. " + roomId + " " + bedNo);
            return QUEUE_BED_NOT_EXIST;
        }
        // DB update
        List statusList = new ArrayList<BedStatusEnum>();
        statusList.add(BedStatusEnum.DOCTOR_ON);
        statusList.add(BedStatusEnum.PAUSE);
        Integer updateNum = roomMapper.setBedDoctorOff(roomId, bedNo, docId, BedStatusEnum.OPENING, statusList);
        if ( null==updateNum || 0 == updateNum ) {
            log.error("bedDoctorOff DB invalid status. " + roomId + " " + bedNo);
            return ROOM_INVALID_STATUS;
        }
        // 如果是在 PAUSE 状态离座的,需要重新加回 优先队列
        if (bedQueueBO.getStatus().equals(BedStatusEnum.PAUSE.getStatus())) {
            if (!priorityQueue.contains(bedQueueBO)) {
                priorityQueue.offer(bedQueueBO);
            } else {
                log.error("bedDoctorResume priorityQueue still exist!. " + roomId + " " + bedNo);
                return ECG_INNER_ERROR;
            }
        }
        bedQueueBO.setStatus(BedStatusEnum.OPENING.getStatus());
        return GlobalErrorCodeConstants.SUCCESS;
    }
    private void validatequeueExists(Integer id) {
@@ -125,12 +449,20 @@
     * 1. 每天开诊前 从DB同步工位的患者队列数据到 工位优先队列
     * 2. 服务运维重启时
     */
    public void initBedQueueAndSeqNumFromDB() {
    @Override
    public ErrorCode bedReload() {
        priorityQueue.clear();
        mapBedVsQueue.clear();
        // 清除非当天的排队人员
        queueMapper.clearQueue();
        // 从DB 获取 工位列表
        List<RoomDO> roomDOList = roomMapper.simpleRoomList();
        List<BedStatusEnum> bedStatusEnumList = new ArrayList<BedStatusEnum>();
        bedStatusEnumList.add(BedStatusEnum.OPENING);
        bedStatusEnumList.add(BedStatusEnum.DOCTOR_ON);
        bedStatusEnumList.add(BedStatusEnum.PAUSE);
        List<RoomDO> roomDOList = roomMapper.simpleRoomList(bedStatusEnumList);
        List<BedQueueBO> bedQueueBOList = roomDOList.stream().map(item -> BeanUtils.toBean(item, BedQueueBO.class)).toList();
        // 从DB 获取 队列中 就诊准备中人员统计 列表
@@ -139,19 +471,23 @@
        List<QueueStatisticDO> queueStatisticDOList = queueMapper.queueStatistic(queueStatusList);
        bedQueueBOList.forEach(item -> {
            item.maxQueueNum = MAX_QUEUE_NUM;
            item.maxQueueNum = queueReadyMax;
            Optional<QueueStatisticDO> queueStatisticDOOptional = queueStatisticDOList.stream().filter(it->it.getRoomId()==item.roomId && it.getBedNo().equals(item.getBedNo())).findFirst();
            int queueNum = queueStatisticDOOptional.isPresent() ? queueStatisticDOOptional.get().getTotalInStatus() : 0;
            if ( MAX_QUEUE_NUM < queueNum )
            if ( queueReadyMax < 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);
            if (BedStatusEnum.PAUSE.getStatus() != item.getStatus()) {
                priorityQueue.offer(item);
            }
        });
        Integer num = queueMapper.getMaxSeqNum();
        curSeqNum = new AtomicInteger(null == num ? 0 : num);
        return GlobalErrorCodeConstants.SUCCESS;
    }
    /**
@@ -166,7 +502,7 @@
        for (BedQueueBO bedQueueBO : mapBedVsQueue.values()) {
            while (bedQueueBO.queueNum.get() < bedQueueBO.maxQueueNum) {
                // 查看 当前工位 是否有过号-回来的患者
                Integer updateNum = queueMapper.procPassedReturnPatient(
                Integer updateNum = queueMapper.queuePassedReturnPatient(
                        bedQueueBO.getRoomId(),
                        bedQueueBO.getRoomName(),
                        bedQueueBO.getBedNo(),
@@ -235,20 +571,17 @@
        QueueDO queue = BeanUtils.toBean(queueSaveReqVO, QueueDO.class);
        queueMapper.insert(queue);
        if (0 == openingFlag.get())
            return;
        startHurryUp();
    }
    private void nextPatient(Long roomId, String bedNo) {
    @Override
    public ErrorCode nextPatient(Long roomId, String bedNo) {
        // 从 DB 把 序号最小的 就诊准备中的人 设置为就诊中
        Integer updateNum = queueMapper.updateQueueStatus(roomId, bedNo,
                QueueStatusEnum.READY.getStatus(), QueueStatusEnum.ONSTAGE.getStatus());
        // 该工位 没有 就诊准备中 人员
        if (null == updateNum || 0 == updateNum) {
            return;
            return QUEUE_NOT_READY_PATIENT;
        }
        // 优先队列中 该工位 就诊准备中人的数量 减一
@@ -257,7 +590,7 @@
        priorityQueue.remove(bo);
        priorityQueue.offer(bo);
        startHurryUp();
        return GlobalErrorCodeConstants.SUCCESS;
    }
    public void finishNextPatient(Long roomId, String bedNo) {
@@ -265,7 +598,7 @@
        Integer ret =  queueMapper.updateQueueStatus(roomId, bedNo,
                QueueStatusEnum.ONSTAGE.getStatus(), QueueStatusEnum.FINISH.getStatus());
        nextPatient(roomId, bedNo);
        startNextPatient(roomId, bedNo);
    }
    public void passNextPatient(Long roomId, String bedNo) {
@@ -273,7 +606,7 @@
        Integer ret =  queueMapper.updateQueueStatus(roomId, bedNo,
                QueueStatusEnum.ONSTAGE.getStatus(), QueueStatusEnum.PASSED.getStatus());
        nextPatient(roomId, bedNo);
        startNextPatient(roomId, bedNo);
    }
    public List<QueueDO> getDoctorQueueByStatus(Long roomId, String bedNo, List<Byte> statusList) {
@@ -302,26 +635,27 @@
        return patientStatisticVO;
    }
    @Override
    public void setQueueReadyMax(Integer max) {
        queueReadyMax = max;
    }
    public void startBiz() {
        if (1 == openingFlag.get())
            return;
        // 清除非当天的排队人员
        queueMapper.clearQueue();
        initBedQueueAndSeqNumFromDB();
        openingFlag.set(1);
        hurryup();
        startBedReload();
    }
    public void closeBiz() {
        openingFlag.set(0);
        startBedReload();
    }
    @Override
    public Integer recallPatient(Long roomId, String bedNo, String patId) {
        Integer updateNum = queueMapper.passedPatientReturn(roomId, bedNo, patId,
        Integer updateNum = queueMapper.recallPassedPatient(roomId, bedNo, patId,
                QueueStatusEnum.PASSED.getStatus(), QueueStatusEnum.PASSED_RETURN.getStatus());
        startHurryUp();
        return updateNum;
@@ -334,11 +668,10 @@
        return updateNum;
    }
    private void startHurryUp() {
        singleThreadExecutor.execute( () -> {
            hurryup();
        });
    @Override
    public void monitorInfo() {
        log.info("map " + mapBedVsQueue.size() + " priority " + priorityQueue.size()
                 + " opening " + openingFlag.get());
    }
}