package cn.lihu.jh.module.ecg.service.room;
|
|
import java.util.*;
|
import javax.annotation.Resource;
|
|
import cn.lihu.jh.framework.common.exception.ErrorCode;
|
import cn.lihu.jh.framework.common.exception.enums.GlobalErrorCodeConstants;
|
import cn.lihu.jh.framework.common.pojo.PageResult;
|
import cn.lihu.jh.module.ecg.utils.IpUtils;
|
import cn.lihu.jh.module.ecg.controller.admin.room.vo.MonitorInfoVO;
|
import cn.lihu.jh.module.ecg.controller.admin.room.vo.RoomSeatRespVO;
|
import cn.lihu.jh.module.ecg.dal.dataobject.room.RoomDO;
|
import cn.lihu.jh.module.ecg.dal.dataobject.queue.QueueDO;
|
import cn.lihu.jh.module.ecg.dal.dataobject.room.RoomProfile;
|
import cn.lihu.jh.module.ecg.enums.RoomCallingScreenEnum;
|
import cn.lihu.jh.module.ecg.service.queue.QueueService;
|
import cn.lihu.jh.module.system.api.oauth2.OAuth2TokenApi;
|
import org.springframework.stereotype.Service;
|
import org.springframework.validation.annotation.Validated;
|
|
import cn.lihu.jh.module.ecg.controller.admin.room.vo.RoomPageReqVO;
|
import cn.lihu.jh.module.ecg.controller.admin.room.vo.RoomSaveReqVO;
|
import cn.lihu.jh.module.ecg.controller.admin.room.vo.RoomSelectBedRespVO;
|
import cn.lihu.jh.module.ecg.dal.mysql.room.RoomMapper;
|
import cn.lihu.jh.module.ecg.enums.BedStatusEnum;
|
import cn.lihu.jh.framework.common.util.object.BeanUtils;
|
import lombok.extern.slf4j.Slf4j;
|
|
import static cn.lihu.jh.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.lihu.jh.module.ecg.enums.ErrorCodeConstants.ROOM_INVALID_STATUS;
|
import static cn.lihu.jh.module.ecg.enums.ErrorCodeConstants.ROOM_NOT_EXISTS;
|
import static cn.lihu.jh.module.ecg.enums.ErrorCodeConstants.ROOM_NOT_OWNER;
|
|
/**
|
* 诊室和诊疗床 Service 实现类
|
*
|
* @author 芋道源码
|
*/
|
@Service
|
@Validated
|
@Slf4j
|
public class RoomServiceImpl implements RoomService {
|
|
@Resource
|
private RoomMapper roomMapper;
|
|
@Resource
|
private OAuth2TokenApi oAuth2TokenApi;
|
|
@Resource
|
QueueService queueService;
|
|
/** 【主/围观】按患者反查工位做主人校验时需要读 queue 行 */
|
@Resource
|
private cn.lihu.jh.module.ecg.dal.mysql.queue.QueueMapper queueMapper;
|
|
@Override
|
public Integer createRoom(RoomSaveReqVO createReqVO) {
|
// 插入
|
RoomDO room = BeanUtils.toBean(createReqVO, RoomDO.class);
|
roomMapper.insert(room);
|
// 返回
|
return room.getId();
|
}
|
|
@Override
|
public void updateRoom(RoomSaveReqVO updateReqVO) {
|
// 校验存在
|
validateRoomExists(updateReqVO.getId());
|
// 更新
|
RoomDO updateObj = BeanUtils.toBean(updateReqVO, RoomDO.class);
|
roomMapper.updateById(updateObj);
|
}
|
|
@Override
|
public void deleteRoom(Integer id) {
|
// 校验存在
|
validateRoomExists(id);
|
// 删除
|
roomMapper.deleteById(id);
|
}
|
|
private void validateRoomExists(Integer id) {
|
if (roomMapper.selectById(id) == null) {
|
throw exception(ROOM_NOT_EXISTS);
|
}
|
}
|
|
@Override
|
public RoomDO getRoom(Integer id) {
|
return roomMapper.selectById(id);
|
}
|
|
@Override
|
public RoomProfile getRoomByIP(String ip, Long roomId) {
|
List<RoomDO> roomDOList = null;
|
if (null != roomId) {
|
roomDOList = roomMapper.selectList(RoomDO::getRoomId, roomId);
|
} else {
|
roomDOList = roomMapper.queueByIp(ip);
|
}
|
|
RoomProfile roomProfile = new RoomProfile();
|
roomProfile.setBedNum( 0 );
|
roomProfile.setCallingScreenType( RoomCallingScreenEnum.NONE.getCallingScreenType() );
|
|
for (RoomDO roomDO : roomDOList) {
|
roomProfile.setRoomId(roomDO.getRoomId());
|
roomProfile.setRoomName(roomDO.getRoomName());
|
roomProfile.setBedNum( roomProfile.getBedNum() + 1);
|
|
// 0 检查 1 领用 2 装机
|
|
// 计算 诊间屏类型
|
if (roomProfile.getCallingScreenType() == RoomCallingScreenEnum.NONE.getCallingScreenType()) {
|
if ( roomDO.getOpType() == 0 ) {
|
roomProfile.setCallingScreenType(RoomCallingScreenEnum.CHECK_ONLY.getCallingScreenType());
|
} else if ( roomDO.getOpType() == 1 ) {
|
roomProfile.setCallingScreenType( RoomCallingScreenEnum.RECEIVE_ONLY.getCallingScreenType());
|
} else if ( roomDO.getOpType() == 2 ) {
|
roomProfile.setCallingScreenType( RoomCallingScreenEnum.INSTALL_ONLY.getCallingScreenType());
|
}
|
} else if (roomProfile.getCallingScreenType() == RoomCallingScreenEnum.CHECK_ONLY.getCallingScreenType()) {
|
if ( roomDO.getOpType() == 0 || roomDO.getOpType() == 1 ) {
|
// do nothing
|
} else if ( roomDO.getOpType() == 2 ) {
|
roomProfile.setCallingScreenType( RoomCallingScreenEnum.CHECK_INSTALL.getCallingScreenType());
|
}
|
} else if (roomProfile.getCallingScreenType() == RoomCallingScreenEnum.RECEIVE_ONLY.getCallingScreenType()) {
|
if ( roomDO.getOpType() == 0 || roomDO.getOpType() == 1 ) {
|
// do nothing
|
} else if ( roomDO.getOpType() == 2 ) {
|
roomProfile.setCallingScreenType( RoomCallingScreenEnum.RECEIVE_INSTALL.getCallingScreenType());
|
}
|
} else if (roomProfile.getCallingScreenType() == RoomCallingScreenEnum.INSTALL_ONLY.getCallingScreenType()) {
|
if ( roomDO.getOpType() == 0 ) {
|
roomProfile.setCallingScreenType( RoomCallingScreenEnum.CHECK_INSTALL.getCallingScreenType());
|
} else if ( roomDO.getOpType() == 1 ) {
|
roomProfile.setCallingScreenType( RoomCallingScreenEnum.RECEIVE_INSTALL.getCallingScreenType());
|
} else if ( roomDO.getOpType() == 2 ) {
|
// do nothing
|
}
|
} else if (roomProfile.getCallingScreenType() == RoomCallingScreenEnum.RECEIVE_INSTALL.getCallingScreenType()) {
|
// do nothing
|
} else if (roomProfile.getCallingScreenType() == RoomCallingScreenEnum.CHECK_INSTALL.getCallingScreenType()) {
|
// do nothing
|
}
|
}
|
|
return roomProfile;
|
}
|
|
@Override
|
public PageResult<RoomDO> getRoomPage(RoomPageReqVO pageReqVO) {
|
return roomMapper.selectPage(pageReqVO);
|
}
|
|
/**
|
* 「选择工位模式」- 按本机IP匹配工位。
|
* <p>排序(决定自动入座选哪一个):① 本人已占用(刷新/重进直接恢复)→ ② 空闲且工位号最小
|
* → ③ 被他人占用(前端会提示"该IP对应工位已被XX占用",让人工处理)。</p>
|
*/
|
@Override
|
public List<RoomSelectBedRespVO.RoomSelectBedItem> matchBedsByIp(String ip, Long docId) {
|
String normalized = IpUtils.normalize(ip);
|
if (!IpUtils.isBindableIp(normalized)) {
|
return Collections.emptyList();
|
}
|
List<RoomDO> list = roomMapper.queueByIp(normalized);
|
if (list == null || list.isEmpty()) {
|
return Collections.emptyList();
|
}
|
List<RoomSelectBedRespVO.RoomSelectBedItem> items = new ArrayList<>(list.size());
|
for (RoomDO roomDO : list) {
|
RoomSelectBedRespVO.RoomSelectBedItem item = new RoomSelectBedRespVO.RoomSelectBedItem();
|
item.setId(roomDO.getId());
|
item.setRoomId(roomDO.getRoomId());
|
item.setRoomName(roomDO.getRoomName());
|
item.setBedNo(roomDO.getBedNo());
|
item.setIp(roomDO.getIp());
|
item.setDocId(roomDO.getDocId());
|
item.setDocName(roomDO.getDocName());
|
item.setOpType(roomDO.getOpType());
|
item.setCheckTypes(roomDO.getCheckTypes());
|
item.setStatus(roomDO.getStatus());
|
item.setOccupied(roomDO.getDocId() != null);
|
item.setMine(docId != null && docId.equals(roomDO.getDocId()));
|
items.add(item);
|
}
|
// 排序:本人已占用(0) → 空闲(1) → 被他人占用(2);同档按工位号
|
items.sort(Comparator
|
.comparingInt((RoomSelectBedRespVO.RoomSelectBedItem i) -> {
|
if (Boolean.TRUE.equals(i.getMine())) return 0;
|
if (!Boolean.TRUE.equals(i.getOccupied())) return 1;
|
return 2;
|
})
|
.thenComparing(RoomSelectBedRespVO.RoomSelectBedItem::getBedNo,
|
Comparator.nullsLast(Comparator.naturalOrder())));
|
return items;
|
}
|
|
@Override
|
public Integer bindIpToBed(Integer id, String ip) {
|
// 回环/未指定/非法地址绑上去没有意义(127.x、::1、0.0.0.0),还会让后续匹配失效
|
if (!IpUtils.isBindableIp(ip)) {
|
return 0;
|
}
|
Integer rows = roomMapper.updateIpById(id, IpUtils.normalize(ip));
|
log.info("[bindIpToBed][把本机IP绑定到工位] id={} ip={} 影响行数={}", id, ip, rows);
|
return rows;
|
}
|
|
@Override
|
public List<RoomDO> listIpBindings(String ip, Integer excludeId) {
|
if (!IpUtils.isBindableIp(ip)) {
|
return Collections.emptyList();
|
}
|
List<RoomDO> list = roomMapper.listByIpExcludeId(IpUtils.normalize(ip), excludeId);
|
return list == null ? Collections.emptyList() : list;
|
}
|
|
@Override
|
public RoomSeatRespVO autoSeatByIp(String ip, Long docId, String docName) {
|
RoomSeatRespVO resp = new RoomSeatRespVO();
|
String clientIp = IpUtils.normalize(ip);
|
resp.setClientIp(clientIp);
|
|
// ① 本机IP不可用(回环 / IPv6 / 未指定 / 非法 / **服务器自身地址**)→ 不能作为工位标识
|
if (!IpUtils.isBindableIp(clientIp)) {
|
// 区分"识别到的是服务器自身地址"与其它不可用情况:前者现场最常见(在服务器上/远程桌面里访问),
|
// 提示语要能直接指向解决办法,而不是让排查的人以为是代码坏了。
|
resp.setReason(IpUtils.isLocalAddress(clientIp) ? "ip_is_server" : "ip_invalid");
|
log.info("[autoSeatByIp] 本机IP不可用,不自动入座. clientIp={} docId={} 是服务器自身地址={}",
|
clientIp, docId, IpUtils.isLocalAddress(clientIp));
|
return resp;
|
}
|
|
// 已按「本人已占用 → 空闲 → 被他人占用」排序
|
List<RoomSelectBedRespVO.RoomSelectBedItem> beds = matchBedsByIp(clientIp, docId);
|
if (beds.isEmpty()) {
|
// 该 IP 没有对应任何工位:工位没录 IP(现场多为 0.0.0.0),或工位已关闭
|
resp.setReason("nomatch");
|
log.info("[autoSeatByIp] 该IP没有匹配到工位(工位未录IP / 已关闭). clientIp={} docId={}", clientIp, docId);
|
return resp;
|
}
|
|
// ② 已经是我的工位 → 幂等恢复(刷新/重进/上次没正常离座都走这条)
|
RoomSelectBedRespVO.RoomSelectBedItem mine = beds.stream()
|
.filter(b -> Boolean.TRUE.equals(b.getMine())).findFirst().orElse(null);
|
if (null != mine) {
|
return doSeat(mine.getId(), mine.getRoomId(), mine.getBedNo(), docId, docName, resp, "mine");
|
}
|
|
// ③ 空闲工位:**恰好一个**才自动入座
|
List<RoomSelectBedRespVO.RoomSelectBedItem> free = beds.stream()
|
.filter(b -> !Boolean.TRUE.equals(b.getOccupied())).toList();
|
if (free.size() == 1) {
|
return doSeat(free.get(0).getId(), free.get(0).getRoomId(), free.get(0).getBedNo(),
|
docId, docName, resp, "free");
|
}
|
if (free.size() > 1) {
|
// 这台机器对应多个空闲工位 → 坐错的风险大于便利,交人工点选
|
resp.setReason("multi");
|
log.info("[autoSeatByIp] 该IP对应 {} 个空闲工位,无法确定是哪一个,交人工选择. clientIp={}", free.size(), clientIp);
|
return resp;
|
}
|
|
// ④ 全被他人占用 → **不自动挤掉**,但把候选工位与占用人带回去,
|
// 供前端弹「该工位由 xxx 占用,是否接管?」(本需求:接班医生自助接管)。
|
RoomSelectBedRespVO.RoomSelectBedItem occupied = beds.get(0);
|
resp.setReason("occupied");
|
resp.setOwnerName(occupied.getDocName());
|
// 【本需求】补齐候选工位信息:前端凭这些字段调 /clinic/room/take-over-bed
|
resp.setId(occupied.getId());
|
resp.setRoomId(occupied.getRoomId());
|
resp.setRoomName(occupied.getRoomName());
|
resp.setBedNo(occupied.getBedNo());
|
resp.setIp(occupied.getIp());
|
resp.setDocId(occupied.getDocId());
|
resp.setDocName(occupied.getDocName());
|
resp.setCheckTypes(occupied.getCheckTypes());
|
resp.setOpType(occupied.getOpType());
|
log.info("[autoSeatByIp] 该IP对应的工位已被他人占用,等待前端确认是否接管. clientIp={} 工位={}/{} 占用人={}({})",
|
clientIp, occupied.getRoomId(), occupied.getBedNo(), occupied.getDocName(), occupied.getDocId());
|
return resp;
|
}
|
|
/** 执行入座并回填结果(内部方法,供 autoSeatByIp 各分支复用) */
|
private RoomSeatRespVO doSeat(Integer bedId, Long roomId, String bedNo, Long docId, String docName,
|
RoomSeatRespVO resp, String branch) {
|
// 传 bedId:入座优先按主键定位,避免 bed_no 与库内值有肉眼难辨差异时更新 0 行
|
ErrorCode ec = queueService.startBedDoctorOn(roomId, bedNo, docId, docName, bedId);
|
if (!GlobalErrorCodeConstants.SUCCESS.equals(ec)) {
|
resp.setReason("error");
|
log.warn("[autoSeatByIp] 入座未生效({}). roomId={} bedNo={} docId={}", branch, roomId, bedNo, docId);
|
return resp;
|
}
|
fillSeatInfo(resp, roomMapper.getRoom(roomId, bedNo));
|
resp.setSeated(true);
|
log.info("[autoSeatByIp] 自动入座成功({}). docId={} roomId={} bedNo={}", branch, docId, roomId, bedNo);
|
return resp;
|
}
|
|
@Override
|
public RoomSeatRespVO getMySeat(Long docId) {
|
RoomSeatRespVO resp = new RoomSeatRespVO();
|
if (null == docId) {
|
return resp;
|
}
|
List<RoomDO> beds = roomMapper.getRoomByDocId(docId);
|
if (null == beds || beds.isEmpty()) {
|
return resp;
|
}
|
if (beds.size() > 1) {
|
// 【本需求·一人多工位】值班医生可同时占多个工位,这里不再是异常场景;
|
// 本接口只为兼容旧前端/其它调用(返回第一个),完整列表用 getMySeats()。
|
log.info("[getMySeat] 该医生占用了 {} 个工位,本接口返回第一个(完整列表见 /my-seats). docId={}",
|
beds.size(), docId);
|
}
|
fillSeatInfo(resp, beds.get(0));
|
resp.setSeated(true);
|
return resp;
|
}
|
|
@Override
|
public List<RoomSeatRespVO> getMySeats(Long docId) {
|
List<RoomSeatRespVO> result = new ArrayList<>();
|
if (null == docId) {
|
return result;
|
}
|
// 只取「医生在座 / 医生暂停」且未删除的工位(排除历史残留)
|
List<Integer> statusList = new ArrayList<>();
|
statusList.add(BedStatusEnum.DOCTOR_ON.getStatus());
|
statusList.add(BedStatusEnum.DOCTOR_PAUSE.getStatus());
|
List<RoomDO> beds = roomMapper.getMyActiveBeds(docId, statusList);
|
if (null == beds || beds.isEmpty()) {
|
return result;
|
}
|
for (RoomDO bed : beds) {
|
RoomSeatRespVO resp = new RoomSeatRespVO();
|
fillSeatInfo(resp, bed);
|
resp.setSeated(true);
|
result.add(resp);
|
}
|
return result;
|
}
|
|
/**
|
* 【本需求·接班自助接管】把工位从"别人占用"改为"当前登录人占用"。
|
*
|
* <p>这是"登出不释放工位"场景下,接班医生立即用上工位的唯一入口(不需要管理员)。
|
* 原占用人那台电脑会在下一次 5s 轮询收到「请重新就坐」并转只读(现有逻辑)。
|
*
|
* <p>「强制清空 → 我入座」在同一事务内完成,避免中间态被第三方抢到。
|
*/
|
@Override
|
@org.springframework.transaction.annotation.Transactional(rollbackFor = Exception.class)
|
public void takeOverBed(Long roomId, String bedNo) {
|
Long userId = cn.lihu.jh.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId();
|
String userNickname = cn.lihu.jh.framework.security.core.util.SecurityFrameworkUtils.getLoginUserNickname();
|
if (null == userId) {
|
throw exception(ROOM_NOT_OWNER);
|
}
|
if (null == roomId || null == bedNo || bedNo.isEmpty()) {
|
throw exception(ROOM_NOT_EXISTS);
|
}
|
RoomDO bed = roomMapper.getRoom(roomId, bedNo);
|
if (null == bed) {
|
throw exception(ROOM_NOT_EXISTS);
|
}
|
|
// ① 本来就属于我 → 幂等成功
|
if (null != bed.getDocId() && bed.getDocId().equals(userId)) {
|
log.info("[takeOverBed] 工位本就属于当前登录人,无需接管. 工位={}/{} docId={}", roomId, bedNo, userId);
|
return;
|
}
|
|
// ② 空闲 → 直接入座("接管"等价于"坐上去",省掉前端再点一次入座)
|
if (null == bed.getDocId()) {
|
ErrorCode onRet = queueService.startBedDoctorOn(roomId, bedNo, userId, userNickname, bed.getId());
|
if (!GlobalErrorCodeConstants.SUCCESS.equals(onRet)) {
|
log.warn("[takeOverBed] 空闲工位入座失败. 工位={}/{} ret={}", roomId, bedNo, onRet);
|
throw exception(ROOM_INVALID_STATUS);
|
}
|
return;
|
}
|
|
// ③ 被他人占用 → 审计日志 + 强制清空 + 立即入座
|
log.warn("[takeOverBed][接班接管] 工位={}/{} 原占用人={}({}) → 接管人={}({})",
|
roomId, bedNo, bed.getDocName(), bed.getDocId(), userNickname, userId);
|
ErrorCode offRet = queueService.startBedDoctorOffByAdmin(roomId, bedNo);
|
if (!GlobalErrorCodeConstants.SUCCESS.equals(offRet)) {
|
log.warn("[takeOverBed] 接管时清空原占用人失败. 工位={}/{} ret={}", roomId, bedNo, offRet);
|
throw exception(ROOM_INVALID_STATUS);
|
}
|
ErrorCode onRet = queueService.startBedDoctorOn(roomId, bedNo, userId, userNickname, bed.getId());
|
if (!GlobalErrorCodeConstants.SUCCESS.equals(onRet)) {
|
log.warn("[takeOverBed] 接管后入座失败. 工位={}/{} ret={}", roomId, bedNo, onRet);
|
throw exception(ROOM_INVALID_STATUS);
|
}
|
log.info("[takeOverBed][接班接管] 完成. 工位={}/{} 新占用人={}({})", roomId, bedNo, userNickname, userId);
|
}
|
|
@Override
|
public void assertBedOwner(Long roomId, String bedNo) {
|
Long userId = cn.lihu.jh.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId();
|
RoomDO bed = (null == roomId || null == bedNo || bedNo.isEmpty())
|
? null : roomMapper.getRoom(roomId, bedNo);
|
if (null == bed) {
|
log.warn("[assertBedOwner] 工位不存在,拒绝写操作. roomId={} bedNo={} docId={}", roomId, bedNo, userId);
|
throw exception(ROOM_NOT_OWNER);
|
}
|
if (null == bed.getDocId() || !bed.getDocId().equals(userId)) {
|
log.warn("[assertBedOwner] 非工位主人,拒绝写操作(围观模式). roomId={} bedNo={} 我={} 主人={}({})",
|
roomId, bedNo, userId, bed.getDocName(), bed.getDocId());
|
throw exception(ROOM_NOT_OWNER);
|
}
|
}
|
|
@Override
|
public void assertBedOwnerByPatient(String patId, Integer checkType) {
|
QueueDO queue = resolvePatientBedQueue(patId, checkType);
|
if (null == queue) {
|
log.warn("[assertBedOwnerByPatient] 找不到该患者当前所在工位,拒绝写操作. patId={} checkType={}",
|
patId, checkType);
|
throw exception(ROOM_NOT_OWNER);
|
}
|
assertBedOwner(queue.getRoomId(), queue.getBedNo());
|
}
|
|
@Override
|
public void assertBedOwnerByPatientSoft(String patId, Integer checkType) {
|
QueueDO queue = resolvePatientBedQueue(patId, checkType);
|
if (null == queue) {
|
// 确费/取消确费常在流程末端调用,患者可能已不在工位队列上;
|
// 一律拒绝会误伤正常业务,这里只记警告放行(前端围观模式下按钮已禁用)。
|
log.warn("[assertBedOwnerByPatientSoft] 找不到该患者当前所在工位,放行(仅记警告). patId={} checkType={}",
|
patId, checkType);
|
return;
|
}
|
assertBedOwner(queue.getRoomId(), queue.getBedNo());
|
}
|
|
@Override
|
public void assertBedOwnerIs(Long roomId, String bedNo, Long ownerDocId) {
|
RoomDO bed = (null == roomId || null == bedNo || bedNo.isEmpty())
|
? null : roomMapper.getRoom(roomId, bedNo);
|
if (null == bed || null == bed.getDocId() || !bed.getDocId().equals(ownerDocId)) {
|
log.warn("[assertBedOwnerIs] 工位当前主人与请求的 ownerDocId 不一致,拒绝. roomId={} bedNo={} 请求={} 实际={}",
|
roomId, bedNo, ownerDocId, null == bed ? null : bed.getDocId());
|
throw exception(ROOM_NOT_OWNER);
|
}
|
}
|
|
/** 按「患者 + 检查项目」找当前所在工位的那条队列行;找不到工位返回 null */
|
private QueueDO resolvePatientBedQueue(String patId, Integer checkType) {
|
if (null == patId) {
|
return null;
|
}
|
QueueDO queue = queueMapper.getQueueByPatId(patId, checkType);
|
if (null == queue || null == queue.getRoomId() || null == queue.getBedNo()
|
|| queue.getBedNo().trim().isEmpty()) {
|
return null;
|
}
|
return queue;
|
}
|
|
@Override
|
public Map<String, Object> diagnoseBed(Integer id, Long roomId, String bedNo) {
|
Map<String, Object> data = new LinkedHashMap<>();
|
String condition = String.format("id=%s, roomId=%s, bedNo=[%s](len=%s)",
|
id, roomId, bedNo, null == bedNo ? null : bedNo.length());
|
data.put("查询条件", condition);
|
|
List<Map<String, Object>> rows = roomMapper.selectBedRawState(id, roomId, bedNo);
|
data.put("匹配到的行数", rows.size());
|
data.put("行明细", rows);
|
|
String verdict;
|
if (rows.isEmpty()) {
|
verdict = "库里【查不到】这个工位(按 id 与 room_id+TRIM(bed_no) 都没匹配到)→ "
|
+ "按 roomId+bedNo 入座必然更新 0 行、报“诊室和诊疗床状态异常”。"
|
+ "新版已支持按 id 入座;若仍失败,请把本结果发回。";
|
} else {
|
Map<String, Object> row = rows.get(0);
|
Integer rowStatus = toInt(row.get("status"));
|
Integer rowDeleted = toInt(row.get("deleted"));
|
Integer bedNoLen = toInt(row.get("bed_no_len"));
|
Object rowDocId = row.get("doc_id");
|
if (null != rowDeleted && 0 != rowDeleted) {
|
verdict = "该工位在库里是【已删除】(deleted=1) → 入座不会命中,请恢复数据或重建工位。";
|
} else if (null != bedNoLen && null != bedNo && bedNoLen != bedNo.length()) {
|
verdict = String.format("库里 bed_no 的长度(%d)与请求值(%d)不一致 → 请求里的工位编号与库内不同"
|
+ "(常见是结尾空格/全角),HEX=%s。新版已改为按 id 入座,可绕开该问题。",
|
bedNoLen, bedNo.length(), row.get("bed_no_hex"));
|
} else if (null == rowStatus || (10 != rowStatus && 20 != rowStatus && 30 != rowStatus)) {
|
verdict = "该工位状态=" + rowStatus + "(0=已关闭);不在可入座状态(可入座的为 10/20/30),"
|
+ "请先在开诊设置/监控页把它开通。";
|
} else if (null != rowDocId) {
|
verdict = "该工位当前的主人是【" + row.get("doc_name") + "】(doc_id=" + rowDocId + "):"
|
+ "若这就是你本人,说明上次没正常离座 —— 新版已支持幂等恢复入座;"
|
+ "若是别人,则不能入座(不挤掉正在操作的人)。";
|
} else {
|
verdict = "该工位当前【可入座】。若之前仍报“状态异常”,请把本结果连同那次请求参数一起发回。";
|
}
|
}
|
data.put("诊断结论", verdict);
|
log.info("[diagnoseBed][工位状态诊断] {} → {}", condition, verdict);
|
return data;
|
}
|
|
/** 兼容 bit(1)/Boolean/Number 的取值(诊断接口用) */
|
private Integer toInt(Object v) {
|
if (null == v) {
|
return null;
|
}
|
if (v instanceof Boolean) {
|
return ((Boolean) v) ? 1 : 0;
|
}
|
if (v instanceof Number) {
|
return ((Number) v).intValue();
|
}
|
try {
|
return Integer.valueOf(String.valueOf(v).trim());
|
} catch (Exception e) {
|
return null;
|
}
|
}
|
|
/** 把工位字段拷进响应体 */
|
private void fillSeatInfo(RoomSeatRespVO resp, RoomDO bed) {
|
if (null == bed) {
|
return;
|
}
|
resp.setId(bed.getId());
|
resp.setRoomId(bed.getRoomId());
|
resp.setRoomName(bed.getRoomName());
|
resp.setBedNo(bed.getBedNo());
|
resp.setIp(bed.getIp());
|
resp.setStatus(bed.getStatus());
|
resp.setDocId(bed.getDocId());
|
resp.setDocName(bed.getDocName());
|
resp.setCheckTypes(bed.getCheckTypes());
|
resp.setOpType(bed.getOpType());
|
}
|
|
@Override
|
public List<RoomDO> simpleRoomList() {
|
List<BedStatusEnum> bedStatusEnumList = new ArrayList<BedStatusEnum>();
|
bedStatusEnumList.add(BedStatusEnum.OPENING);
|
bedStatusEnumList.add(BedStatusEnum.DOCTOR_ON);
|
bedStatusEnumList.add(BedStatusEnum.DOCTOR_PAUSE);
|
return roomMapper.simpleRoomList(bedStatusEnumList);
|
}
|
|
@Override
|
public void resetRoom(Boolean needCloseBed) {
|
queueService.startResetRoom(needCloseBed);
|
}
|
|
@Override
|
public MonitorInfoVO getMonitorInfo() {
|
return queueService.getMonitorInfo();
|
}
|
}
|