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 org.springframework.stereotype.Service;
|
import org.springframework.validation.annotation.Validated;
|
|
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.*;
|
|
/**
|
* 排队 Service 实现类
|
*
|
* @author 芋道源码
|
*/
|
@Service
|
@Validated
|
public class QueueServiceImpl implements QueueService {
|
|
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<>();
|
ConcurrentHashMap<String, BedQueueBO > mapBedVsQueue = new ConcurrentHashMap<>();
|
|
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
|
|
|
@Override
|
public Integer createqueue(QueueSaveReqVO createReqVO) {
|
// 插入
|
QueueDO queue = BeanUtils.toBean(createReqVO, QueueDO.class);
|
queueMapper.insert(queue);
|
// 返回
|
return queue.getId();
|
}
|
|
@Override
|
public void updatequeue(QueueSaveReqVO updateReqVO) {
|
// 校验存在
|
validatequeueExists(updateReqVO.getId());
|
// 更新
|
QueueDO updateObj = BeanUtils.toBean(updateReqVO, QueueDO.class);
|
queueMapper.updateById(updateObj);
|
}
|
|
@Override
|
public void deletequeue(Integer id) {
|
// 校验存在
|
validatequeueExists(id);
|
// 删除
|
queueMapper.deleteById(id);
|
}
|
|
private void validatequeueExists(Integer id) {
|
if (queueMapper.selectById(id) == null) {
|
throw exception(QUEUE_NOT_EXISTS);
|
}
|
}
|
|
@Override
|
public QueueDO getqueue(Integer id) {
|
return queueMapper.selectById(id);
|
}
|
|
@Override
|
public PageResult<QueueDO> getqueuePage(QueuePageReqVO pageReqVO) {
|
return queueMapper.selectPage(pageReqVO);
|
}
|
|
/**
|
* 系统重启时,从DB同步工位的患者队列数据到 工位优先队列
|
*/
|
public void initQueue() {
|
priorityQueue.clear();
|
mapBedVsQueue.clear();
|
|
// 从DB 获取 工位列表
|
List<RoomDO> roomDOList = roomMapper.simpleRoomList();
|
List<BedQueueBO> bedQueueBOList = roomDOList.stream().map(item -> BeanUtils.toBean(item, BedQueueBO.class)).toList();
|
|
// 从DB 获取 队列中 就诊准备中人员统计 列表
|
List<Byte> queueStatusList = new ArrayList<>();
|
queueStatusList.add(QueueStatusEnum.READY.getStatus());
|
List<QueueStatisticDO> queueStatisticDOList = queueMapper.queueStatistic(queueStatusList);
|
|
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();
|
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);
|
});
|
|
Integer num = queueMapper.getMaxSeqNum();
|
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)
|
return;
|
|
int curQueueNum = bedQueueBO.queueNum.get();
|
if (curQueueNum > bedQueueBO.maxQueueNum)
|
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());
|
|
// 没有抢到排队患者
|
if (null == updateNum || 0 == updateNum) {
|
return;
|
}
|
|
curSeqNum.getAndIncrement();
|
|
// 可能已经【并发的】在 nextPatient 中改变了值
|
bedQueueBO.queueNum.incrementAndGet();
|
|
// 可能已经【并发的】在 nextPatient 中改变了优先队列顺序
|
priorityQueue.remove(bedQueueBO);
|
priorityQueue.offer(bedQueueBO);
|
}
|
}
|
|
/**
|
* 预约确认后的排队
|
* @param queueSaveReqVO
|
*/
|
@Override
|
public void queue(QueueSaveReqVO queueSaveReqVO) {
|
queueSaveReqVO.setStatus(QueueStatusEnum.WAITING.getStatus()); //排队中
|
QueueDO queue = BeanUtils.toBean(queueSaveReqVO, QueueDO.class);
|
queueMapper.insert(queue);
|
|
if (0 == openingFlag.get())
|
return;
|
|
singleThreadExecutor.execute( () -> {
|
hurryup();
|
});
|
}
|
|
private void nextPatient(Long roomId, String bedNo) {
|
// 从 DB 把 序号最小的 就诊准备中的人 设置为就诊中
|
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));
|
bo.queueNum.getAndDecrement(); // 可能已经【并发的】在 hurry-up 中改变了值
|
priorityQueue.remove(bo);
|
priorityQueue.offer(bo);
|
|
singleThreadExecutor.execute( () -> {
|
hurryup();
|
});
|
}
|
|
public void finishNextPatient(Long roomId, String bedNo) {
|
// 从 DB 把 就诊中的人 设置为就诊完成
|
Integer ret = queueMapper.updateQueueStatus(roomId, bedNo,
|
QueueStatusEnum.ONSTAGE.getStatus(), QueueStatusEnum.FINISH.getStatus());
|
|
nextPatient(roomId, bedNo);
|
}
|
|
public void passNextPatient(Long roomId, String bedNo) {
|
// 从 DB 把 就诊中的人 设置为过号
|
Integer ret = queueMapper.updateQueueStatus(roomId, bedNo,
|
QueueStatusEnum.ONSTAGE.getStatus(), QueueStatusEnum.PASSED.getStatus());
|
|
nextPatient(roomId, bedNo);
|
}
|
|
public List<QueueDO> getDoctorQueueByStatus(Long roomId, String bedNo, List<Byte> statusList) {
|
List<QueueDO> queueDOList = queueMapper.getDoctorQueueByStatus(roomId, bedNo, statusList);
|
return queueDOList;
|
}
|
|
public PatientStatisticVO getPatientStatistic(Long roomId, String bedNo) {
|
PatientStatisticVO patientStatisticVO = new PatientStatisticVO();
|
List<BedQueueStatisticDO> bedQueueStatisticDOList = queueMapper.bedQueueStatistic(roomId, bedNo);
|
bedQueueStatisticDOList.forEach(item -> {
|
if (QueueStatusEnum.READY.getStatus() == item.getStatus()) {
|
patientStatisticVO.setReadyNum(item.getTotalInStatus());
|
} else if (QueueStatusEnum.FINISH.getStatus() == item.getStatus()) {
|
patientStatisticVO.setFinishedNum(item.getTotalInStatus());
|
} else if (QueueStatusEnum.PASSED.getStatus() == item.getStatus()) {
|
patientStatisticVO.setPassedNum(item.getTotalInStatus());
|
}
|
});
|
|
List<Byte> statusList = new ArrayList<>();
|
statusList.add(QueueStatusEnum.WAITING.getStatus());
|
Integer num = queueMapper.statusStatistic(statusList);
|
patientStatisticVO.setQueuingNum(num);
|
|
return patientStatisticVO;
|
}
|
|
public void startBiz() {
|
openingFlag.set(1);
|
hurryup();
|
}
|
|
@Override
|
public Integer passedPatientReturn(Long roomId, String bedNo, String patId) {
|
Integer updateNum = queueMapper.passedPatientReturn(roomId, bedNo, patId,
|
QueueStatusEnum.PASSED.getStatus(), QueueStatusEnum.PASSED_RETURN.getStatus());
|
return updateNum;
|
}
|
|
@Override
|
public Integer queueJump(String patId, Byte jumped) {
|
Integer updateNum = queueMapper.queueJump(patId, QueueStatusEnum.WAITING.getStatus(), jumped);
|
return null;
|
}
|
|
}
|