yxh
昨天 fce96ef468291fb9a0e6a4d34ab371315e9485d4
jh-module-ecg/jh-module-ecg-biz/src/main/java/cn/lihu/jh/module/ecg/service/devrent/DevRentServiceImpl.java
@@ -1,31 +1,46 @@
package cn.lihu.jh.module.ecg.service.devrent;
import cn.lihu.jh.framework.common.pojo.CommonResult;
import cn.lihu.jh.framework.common.pojo.PageResult;
import cn.lihu.jh.framework.common.util.object.BeanUtils;
import cn.lihu.jh.framework.security.core.util.SecurityFrameworkUtils;
import cn.lihu.jh.module.ecg.controller.admin.devrent.vo.*;
import cn.lihu.jh.module.ecg.controller.admin.jobrecord.vo.JobRecordSaveReqVO;
import cn.lihu.jh.module.ecg.dal.dataobject.appointment.AppointmentDO;
import cn.lihu.jh.module.ecg.dal.dataobject.checktype.CheckTypeDO;
import cn.lihu.jh.module.ecg.dal.dataobject.devmanage.DeviceDO;
import cn.lihu.jh.module.ecg.dal.dataobject.devrent.DevRentDO;
import cn.lihu.jh.module.ecg.dal.dataobject.jobrecord.JobRecordDO;
import cn.lihu.jh.module.ecg.dal.dataobject.patient.PatDetails;
import cn.lihu.jh.module.ecg.dal.dataobject.queue.QueueDO;
import cn.lihu.jh.module.ecg.dal.mysql.appointment.AppointmentMapper;
import cn.lihu.jh.module.ecg.dal.mysql.checktype.CheckTypeMapper;
import cn.lihu.jh.module.ecg.dal.mysql.devmanage.DeviceMapper;
import cn.lihu.jh.module.ecg.dal.mysql.devrent.DevRentMapper;
import cn.lihu.jh.module.ecg.dal.mysql.jobrecord.JobRecordMapper;
import cn.lihu.jh.module.ecg.dal.mysql.queue.QueueMapper;
import cn.lihu.jh.module.ecg.enums.DevRentStateEnum;
import cn.lihu.jh.module.ecg.enums.DevStateEnum;
import cn.lihu.jh.module.ecg.enums.QueueStatusEnum;
import cn.lihu.jh.module.ecg.feign.FeeConfirmFeignService;
import cn.lihu.jh.module.ecg.service.queue.QueueServiceTxFunctions;
import cn.lihu.jh.module.ecg.webservice.WebServiceClient;
import cn.lihu.jh.module.infra.dal.dataobject.config.ConfigDO;
import cn.lihu.jh.module.infra.dal.mysql.config.ConfigMapper;
import cn.lihu.jh.module.infra.service.config.ConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import cn.lihu.jh.module.ecg.controller.admin.devrent.vo.*;
import cn.lihu.jh.module.ecg.dal.dataobject.devrent.DevRentDO;
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.devrent.DevRentMapper;
import javax.annotation.Resource;
import static cn.lihu.jh.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.lihu.jh.module.ecg.enums.ErrorCodeConstants.*;
@@ -37,7 +52,14 @@
 */
@Service
@Validated
@Slf4j
public class DevRentServiceImpl implements DevRentService {
    @Resource
    QueueServiceTxFunctions queueServiceTxFunctions;
    @Resource
    private FeeConfirmFeignService feeConfirmFeignService;
    @Resource
    private DevRentMapper devRentMapper;
@@ -48,6 +70,22 @@
    @Resource
    DeviceMapper deviceMapper;
    @Resource
    AppointmentMapper appointmentMapper;
    @Resource
    QueueMapper queueMapper;
    @Resource
    CheckTypeMapper checkTypeMapper;
    @Resource
    private ConfigService configService;
    @Autowired
    private WebServiceClient webServiceClient;
    @Override
    public CommonResult<Long> registerOperation(DevRentSaveReqVO createReqVO) {
        Long rent_id = createDevRent(createReqVO);
@@ -55,29 +93,210 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Long> routineFinishOperation(RoutineFinishReqVO routineFinishReqVO) {
        DevRentSaveReqVO createReqVO = BeanUtils.toBean(routineFinishReqVO, DevRentSaveReqVO.class);
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        String userNickname = SecurityFrameworkUtils.getLoginUserNickname();
        String userHisId = SecurityFrameworkUtils.getLoginUserHisId();
        // 在预约确认时 就进入 [dev_rent] 表了
        Long rent_id = createReqVO.getId();
        List<Integer> jobTypeList = new ArrayList<Integer>();
        jobTypeList.add(DevRentStateEnum.ROUTINE_FINISH.getState());
        jobTypeList.add(DevRentStateEnum.ROUTINE_CANCELLED.getState());
        JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(rent_id, userId, jobTypeList);
        if (null == jobRecordDO) {
            List<DevRentDO> devRentDOList = devRentMapper.selectByPatAndChecktypeAndState(createReqVO.getPatId(), createReqVO.getCheckType(), DevRentStateEnum.FREE.getState());
            if (devRentDOList.size() == 0) throw exception(DEV_INSTALL_NOT_APPOINTMENT);
            else if (devRentDOList.size() != 1) {
                throw exception(DEV_INSTALL_EXCEPTION);
            }
            rent_id = devRentDOList.get(0).getId();
            createReqVO.setId(rent_id);
            createReqVO.setState(DevRentStateEnum.ROUTINE_FINISH.getState());
            updateDevRent(createReqVO);
            JobRecordSaveReqVO jobRecordSaveReqVO = new JobRecordSaveReqVO();
            jobRecordSaveReqVO.setRentId(rent_id);
            jobRecordSaveReqVO.setDevId(createReqVO.getDevId());
            jobRecordSaveReqVO.setDocId(userId);
            jobRecordSaveReqVO.setDocName(userNickname);
            jobRecordSaveReqVO.setPatId(createReqVO.getPatId());
            jobRecordSaveReqVO.setPatName(createReqVO.getPatName());
            jobRecordSaveReqVO.setJobTime(createReqVO.getRentTime());
            jobRecordSaveReqVO.setJobType(DevRentStateEnum.ROUTINE_FINISH.getState());
            jobRecordSaveReqVO.setRemark(createReqVO.getRemark());
            jobRecordSaveReqVO.setSummary("");
            jobRecordSaveReqVO.setCheckType(createReqVO.getCheckType());
            JobRecordDO jobRecord = BeanUtils.toBean(jobRecordSaveReqVO, JobRecordDO.class);
            jobRecordMapper.insert(jobRecord);
        } else {
            // 撤销 放弃操作
            createReqVO.setState(DevRentStateEnum.ROUTINE_FINISH.getState());
            updateDevRent(createReqVO);
            jobRecordDO.setDevId(createReqVO.getDevId());
            jobRecordDO.setJobType(DevRentStateEnum.ROUTINE_FINISH.getState());
            jobRecordDO.setJobTime(createReqVO.getRentTime());
            jobRecordDO.setRemark(createReqVO.getRemark());
            jobRecordDO.setUpdater(String.valueOf(userId));
            jobRecordDO.setUpdateTime(LocalDateTime.now());
            jobRecordMapper.updateById(jobRecordDO);
        }
        // 更新..队列状态
        //queueMapper.updateBedQueueStatus(createReqVO.getRoomId(), createReqVO.getBedNo(),
        //        QueueStatusEnum.ONSTAGE.getStatus(), QueueStatusEnum.FINISH.getStatus());
        // 【缺陷修复】原实现:
        //   QueueDO firstOnStageQueueItem = queueMapper.getBedFirstQueueItemByStatus(
        //           roomId, bedNo, ONSTAGE);
        //   firstOnStageQueueItem.setStatus(FINISH);   // ← 取不到时为 null,直接 NPE
        //
        // 两个问题:
        //   1) NPE:该工位当前没有"就诊中(30)"患者时返回 null(例如医生先点了"放弃检查",
        //      cancelRoutineOperation 已把该患者置为 40),第 159 行空指针 → 整个事务回滚。
        //   2) 误伤:只按"工位 + 状态取 seq_num 最小的一条",若期间已叫了下一位患者,
        //      会把**下一位患者**误置为"已就诊(40)"。
        //
        // 修复策略(按可靠性从高到低):
        //   ① 优先按 patId + checkType 精确匹配(前端 submitForm 传的 formData 含这两个字段);
        //   ② 退化到"工位 + 状态 + 今日"取 update_time 最新的一条(比 seq_num 最小更贴合当前患者);
        //   ③ 仍取不到 → 记警告并**跳过队列更新**,而不是抛 NPE 让整个"检查完成"失败。
        //      此时设备归还、作业记录等仍会正常保存(数据不丢),仅队列状态未变。
        QueueDO onStageQueueItem = null;
        // ① 精确定位(用 Spring 的 StringUtils,避免额外依赖;
        //    本模块未引入 commons-lang3,勿用 org.apache.commons.lang3.StringUtils)
        if (org.springframework.util.StringUtils.hasText(createReqVO.getPatId())
                && null != createReqVO.getCheckType()) {
            onStageQueueItem = queueMapper.getBedQueueItemByPatAndStatus(
                    createReqVO.getRoomId(), createReqVO.getBedNo(),
                    createReqVO.getPatId(), createReqVO.getCheckType(),
                    QueueStatusEnum.ONSTAGE.getStatus());
        }
        // ② 退化:取该工位今日最近更新的一条"就诊中"
        if (null == onStageQueueItem) {
            onStageQueueItem = queueMapper.getBedLatestQueueItemByStatus(
                    createReqVO.getRoomId(), createReqVO.getBedNo(), QueueStatusEnum.ONSTAGE.getStatus());
            if (null != onStageQueueItem) {
                log.warn("[routineFinish] 未按 patId 命中,退化为按工位取最近就诊中患者. "
                                + "roomId={} bedNo={} reqPatId={} reqCheckType={} 实际patId={} checkType={}",
                        createReqVO.getRoomId(), createReqVO.getBedNo(), createReqVO.getPatId(),
                        createReqVO.getCheckType(), onStageQueueItem.getPatId(),
                        onStageQueueItem.getBookCheckType());
            }
        }
        // ③ 仍取不到:跳过队列更新,不抛异常(保证"检查完成"本身可用)
        if (null == onStageQueueItem) {
            log.warn("[routineFinish] 该工位无[就诊中]患者,跳过队列状态更新. roomId={} bedNo={} patId={} applyNo={}",
                    createReqVO.getRoomId(), createReqVO.getBedNo(),
                    createReqVO.getPatId(), createReqVO.getApplyNo());
        } else {
            // QueueStatusEnum.ONSTAGE  =>  QueueStatusEnum.FINISH
            onStageQueueItem.setStatus(QueueStatusEnum.FINISH.getStatus());
            queueMapper.updateById(onStageQueueItem);
            // 检查项目.亲和性 处理逻辑
            CheckTypeDO checkTypeDO = queueServiceTxFunctions.getCheckTypeItem(onStageQueueItem.getBookCheckType());
            if (null != checkTypeDO && null != checkTypeDO.getAffinityCheckTypes()
                    && checkTypeDO.getAffinityCheckTypes().length > 0) {
                procAffinityWhenRoutineFinish(onStageQueueItem.getPatId(), checkTypeDO.getAffinityCheckTypes());
            }
        }
        //检查完成后,此时检查登记写成了,需要将这个回写给集成平台(线上环境才回写)
        ConfigDO configDO = configService.getConfigByKey("ecg.back.data");
        if (configDO.getValue().equals("1")) {
            if (createReqVO.getCheckType() != 100 && createReqVO.getCheckType() != 1000 && createReqVO.getCheckType() != 1100 && createReqVO.getCheckType() != 1400 && createReqVO.getCheckType() != 1500) {
                AppointmentDO appointmentDO = appointmentMapper.getByApplyNo(createReqVO.getApplyNo());
                ApplicationTemplate app = new ApplicationTemplate();
                String action = "S0405";
                String message = app.getXML(createReqVO.getApplyNo(), "3", "检查登记", createReqVO.getOperater(), appointmentDO.getPatDeptCode(), appointmentDO.getPatDeptDesc(), appointmentDO.getPatWardCode(), appointmentDO.getPatWardDesc(), appointmentDO.getPatBedNo(), appointmentDO.getEpisodeId(), "" + appointmentDO.getBookSrc(), appointmentDO.getPatId(), appointmentDO.getPatName());
                String s = webServiceClient.callJHFWTYRK(action, "", message);
                log.info("routineFinishWebServiceClient的返回值为:{}", s);
                if (s.contains("更新成功")) {
                    //需要根据applyNo,将appiontment表里的status改成3
                    // 【诊断日志】同 AppointmentServiceImpl#handleAppointmentStatusUpdate:
                    // 打印日期对象类型与值,便于定位 register_date 被写成 00:00:00 的写入方。
                    java.util.Date regNow = new Date();
                    log.info("[devRentCreate][登记时间写入] applyNo={} 日期对象类型={} 值={}",
                            createReqVO.getApplyNo(), regNow.getClass().getName(),
                            new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(regNow));
                    appointmentMapper.updateStatusByApplyNo(createReqVO.getApplyNo(), "3", regNow);
                }
            } else {
                //检查类型是床边的或常规的,需要根据applyNo,将appiontment表里的status改成7
                appointmentMapper.updateStatusByApplyNo(createReqVO.getApplyNo(), "7", null);
            }
        }
        return CommonResult.success(rent_id);
    }
    /**
     * 领用完成 操作
     *
     * @param createReqVO 创建信息
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Long> readyOperation(DevRentSaveReqVO createReqVO) {
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        String userNickname = SecurityFrameworkUtils.getLoginUserNickname();
        // 在预约确认时 就进入 [dev_rent] 表了
        Long rent_id = createReqVO.getId();  // dev_rent 表的ID
        // 标注设备 已领取
        Integer ret = markDevRecieved(createReqVO.getDevId());
        Integer ret = markDevRecieved(createReqVO.getDevId(), rent_id, createReqVO.getPatDetails());
        if (null == ret || 0 == ret) {
            return CommonResult.error(DEVICE_NOT_EXISTS);
            throw exception(DEVICE_NOT_FREE);
        }
        Long rent_id = createReqVO.getId();
        if (null == rent_id) {
            List<DevRentDO>  devRentDOList = devRentMapper.selectByPatIdAndState(createReqVO.getPatId(), DevRentStateEnum.FREE.getState());
            if (devRentDOList.size() == 0)
                return CommonResult.error(DEV_INSTALL_NOT_APPOINTMENT);
        //设备领取完成后,此时检查登记写成了,需要将这个回写给集成平台(线上环境才回写)
        ConfigDO configDO = configService.getConfigByKey("ecg.back.data");
        if (configDO.getValue().equals("1")) {
            AppointmentDO appointmentDO = appointmentMapper.getByApplyNo(createReqVO.getApplyNo());
            ApplicationTemplate app = new ApplicationTemplate();
            String action = "S0405";
            String message = app.getXML(createReqVO.getApplyNo(), "3", "检查登记", createReqVO.getOperater(), appointmentDO.getPatDeptCode(), appointmentDO.getPatDeptDesc(), appointmentDO.getPatWardCode(), appointmentDO.getPatWardDesc(), appointmentDO.getPatBedNo(), appointmentDO.getEpisodeId(), "" + appointmentDO.getBookSrc(), appointmentDO.getPatId(), appointmentDO.getPatName());
            String s = webServiceClient.callJHFWTYRK(action, "", message);
            log.info("webservice的返回值为:{}", s);
            if (s.contains("更新成功")) {
                //需要根据applyNo,将appiontment表里的status改成3
                // 【诊断日志】同上:打印日期对象类型与值
                java.util.Date regNow2 = new Date();
                log.info("[devRentOther][登记时间写入] applyNo={} 日期对象类型={} 值={}",
                        createReqVO.getApplyNo(), regNow2.getClass().getName(),
                        new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(regNow2));
                appointmentMapper.updateStatusByApplyNo(createReqVO.getApplyNo(), "3", regNow2);
            }
        }
        List<Integer> jobTypeList = new ArrayList<Integer>();
        jobTypeList.add(DevRentStateEnum.RECEIVED.getState());
        jobTypeList.add(DevRentStateEnum.RECEIVED_CANCELLED.getState());
        JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(rent_id, userId, jobTypeList);
        if (null == jobRecordDO) {
            List<DevRentDO> devRentDOList = devRentMapper.selectByPatAndChecktypeAndState(createReqVO.getPatId(), createReqVO.getCheckType(), DevRentStateEnum.FREE.getState());
            if (devRentDOList.size() == 0) throw exception(DEV_INSTALL_NOT_APPOINTMENT);
            else if (devRentDOList.size() != 1) {
                return CommonResult.error(DEV_INSTALL_EXCEPTION);
                throw exception(DEV_INSTALL_EXCEPTION);
            }
            rent_id = devRentDOList.getFirst().getId();
            createReqVO.setId( rent_id );
            createReqVO.setState( DevRentStateEnum.READY.getState() );
            updateDevRent( createReqVO );
            rent_id = devRentDOList.get(0).getId();
            createReqVO.setId(rent_id);
            createReqVO.setState(DevRentStateEnum.RECEIVED.getState());
            updateDevRent(createReqVO);
            JobRecordSaveReqVO jobRecordSaveReqVO = new JobRecordSaveReqVO();
            jobRecordSaveReqVO.setRentId(rent_id);
@@ -87,57 +306,85 @@
            jobRecordSaveReqVO.setPatId(createReqVO.getPatId());
            jobRecordSaveReqVO.setPatName(createReqVO.getPatName());
            jobRecordSaveReqVO.setJobTime(createReqVO.getRentTime());
            jobRecordSaveReqVO.setJobType( DevRentStateEnum.READY.getState() );
            jobRecordSaveReqVO.setJobType(DevRentStateEnum.RECEIVED.getState());
            jobRecordSaveReqVO.setRemark(createReqVO.getRemark());
            jobRecordSaveReqVO.setSummary("");
            jobRecordSaveReqVO.setCheckType(createReqVO.getCheckType());
            JobRecordDO jobRecord = BeanUtils.toBean(jobRecordSaveReqVO, JobRecordDO.class);
            jobRecordMapper.insert(jobRecord);
        } else {
            // 撤销 放弃操作
            createReqVO.setState( DevRentStateEnum.READY.getState() );
            createReqVO.setState(DevRentStateEnum.RECEIVED.getState());
            updateDevRent( createReqVO );
            updateDevRent(createReqVO);
            JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(rent_id, userId);
            if (null != jobRecordDO) {
                jobRecordDO.setDevId(createReqVO.getDevId());
                jobRecordDO.setJobType( DevRentStateEnum.READY.getState() );
                jobRecordDO.setJobTime(createReqVO.getRentTime());
                jobRecordDO.setRemark(createReqVO.getRemark());
                jobRecordDO.setUpdater(String.valueOf(userId));
                jobRecordDO.setUpdateTime(LocalDateTime.now());
                jobRecordMapper.updateById(jobRecordDO);
            }
            jobRecordDO.setDevId(createReqVO.getDevId());
            jobRecordDO.setJobType(DevRentStateEnum.RECEIVED.getState());
            jobRecordDO.setJobTime(createReqVO.getRentTime());
            jobRecordDO.setRemark(createReqVO.getRemark());
            jobRecordDO.setUpdater(String.valueOf(userId));
            jobRecordDO.setUpdateTime(LocalDateTime.now());
            jobRecordMapper.updateById(jobRecordDO);
        }
        // 更新..队列状态
        //queueMapper.updateBedQueueStatus(createReqVO.getRoomId(), createReqVO.getBedNo(),
        //        QueueStatusEnum.ONSTAGE.getStatus(), QueueStatusEnum.RECEIVED.getStatus());
        // QueueStatusEnum.ONSTAGE  =>  QueueStatusEnum.RECEIVED
        QueueDO bedOnStageQueueItem = queueMapper.getBedFirstQueueItemByStatus(createReqVO.getRoomId(), createReqVO.getBedNo(), QueueStatusEnum.ONSTAGE.getStatus());
        CheckTypeDO checkTypeDO = queueServiceTxFunctions.getCheckTypeItem(bedOnStageQueueItem.getBookCheckType());
        bedOnStageQueueItem.setStatus(QueueStatusEnum.RECEIVED.getStatus());  // [已领用] 状态
        bedOnStageQueueItem.setAffinityItem(0); // 亲和标记 清除,在安装阶段重新亲和
        // 检查项目.亲和性 处理逻辑 , 更新 bedOnStageQueueItem
        if (checkTypeDO.getAffinityCheckTypes().length > 0) {
            procAffinityWhenReadyFinish(/*IN, OUT*/bedOnStageQueueItem, checkTypeDO.getAffinityCheckTypes());
        }
        queueMapper.updateById(bedOnStageQueueItem);
        return CommonResult.success(rent_id);
    }
    @Override
    @Transactional
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Long> installOperation(DevRentSaveReqVO createReqVO) {
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        String userNickname = SecurityFrameworkUtils.getLoginUserNickname();
        Long rent_id = createReqVO.getId();
        // 设置设备 使用中
        Integer ret = markDevInUse(createReqVO.getDevId());
        if (null == ret || 0 == ret) {
            return CommonResult.error(DEVICE_NOT_EXISTS);
        DevRentDO devRentDO = getDevRent(rent_id);
        // 对于已有领用,但是装机时使用的不是已领用设备,报错
        if (null != devRentDO.getDevId() && !createReqVO.getDevId().equals(devRentDO.getDevId())) {
            throw exception(DEVICE_NOT_PAT_RECEIVED);
        }
        Long rent_id = createReqVO.getId();
        if (null == rent_id) {
            List<DevRentDO>  devRentDOList = devRentMapper.selectByPatIdAndState(createReqVO.getPatId(), DevRentStateEnum.READY.getState());
            if (devRentDOList.size() == 0)
                return CommonResult.error(DEV_INSTALL_NOT_RECEIVED);
        // 设置设备 使用中
        Integer ret = markDevInUse(createReqVO.getDevId(), rent_id);
        if (null == ret || 0 == ret) {
            throw exception(DEVICE_NOT_RECEIVED);
        }
        // 检查 job_record 用于确认是否是第一次安装操作
        List<Integer> jobTypeList = new ArrayList<Integer>();
        jobTypeList.add(DevRentStateEnum.INSTALLED.getState());
        jobTypeList.add(DevRentStateEnum.INSTALL_CANCELLED.getState());
        JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(rent_id, userId, jobTypeList);
        // 第一次 安装操作
        if (null == jobRecordDO) {
            List<DevRentDO> devRentDOList = devRentMapper.selectByPatAndChecktypeAndState(createReqVO.getPatId(), createReqVO.getCheckType(), DevRentStateEnum.RECEIVED.getState());
            if (devRentDOList.size() == 0) throw exception(DEV_INSTALL_NOT_RECEIVED);
            else if (devRentDOList.size() != 1) {
                return CommonResult.error(DEV_INSTALL_EXCEPTION);
                throw exception(DEV_INSTALL_EXCEPTION);
            }
            rent_id = devRentDOList.getFirst().getId();
            createReqVO.setId( rent_id );
            createReqVO.setState( DevRentStateEnum.INSTALLED.getState() );
            updateDevRent( createReqVO );
            rent_id = devRentDOList.get(0).getId();
            createReqVO.setId(rent_id);
            createReqVO.setState(DevRentStateEnum.INSTALLED.getState());
            updateDevRent(createReqVO);
            JobRecordSaveReqVO jobRecordSaveReqVO = new JobRecordSaveReqVO();
            jobRecordSaveReqVO.setRentId(rent_id);
@@ -147,132 +394,170 @@
            jobRecordSaveReqVO.setPatId(createReqVO.getPatId());
            jobRecordSaveReqVO.setPatName(createReqVO.getPatName());
            jobRecordSaveReqVO.setJobTime(createReqVO.getRentTime());
            jobRecordSaveReqVO.setJobType( DevRentStateEnum.INSTALLED.getState() );
            jobRecordSaveReqVO.setJobType(DevRentStateEnum.INSTALLED.getState());
            jobRecordSaveReqVO.setRemark(createReqVO.getRemark());
            jobRecordSaveReqVO.setSummary("");
            jobRecordSaveReqVO.setCheckType(createReqVO.getCheckType());
            JobRecordDO jobRecord = BeanUtils.toBean(jobRecordSaveReqVO, JobRecordDO.class);
            jobRecordMapper.insert(jobRecord);
        } else {
            // 撤销 放弃操作
            createReqVO.setState( DevRentStateEnum.INSTALLED.getState() );
            createReqVO.setState(DevRentStateEnum.INSTALLED.getState());
            updateDevRent(createReqVO);
            updateDevRent( createReqVO );
            JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(rent_id, userId);
            if (null != jobRecordDO) {
                jobRecordDO.setDevId(createReqVO.getDevId());
                jobRecordDO.setJobType( DevRentStateEnum.INSTALLED.getState() );
                jobRecordDO.setJobTime(createReqVO.getRentTime());
                jobRecordDO.setRemark(createReqVO.getRemark());
                jobRecordDO.setUpdater(String.valueOf(userId));
                jobRecordDO.setUpdateTime(LocalDateTime.now());
                jobRecordMapper.updateById(jobRecordDO);
            }
            jobRecordDO.setDevId(createReqVO.getDevId());
            jobRecordDO.setJobType(DevRentStateEnum.INSTALLED.getState());
            jobRecordDO.setJobTime(createReqVO.getRentTime());
            jobRecordDO.setRemark(createReqVO.getRemark());
            jobRecordDO.setUpdater(String.valueOf(userId));
            jobRecordDO.setUpdateTime(LocalDateTime.now());
            jobRecordMapper.updateById(jobRecordDO);
        }
        // 更新..队列状态
        //queueMapper.updateBedQueueStatus(createReqVO.getRoomId(), createReqVO.getBedNo(),
        //        QueueStatusEnum.INSTALLING.getStatus(), QueueStatusEnum.FINISH.getStatus());
        // QueueStatusEnum.INSTALLING  =>  QueueStatusEnum.FINISH
        QueueDO bedInstallingQueueItem = queueMapper.getBedFirstQueueItemByStatus(createReqVO.getRoomId(), createReqVO.getBedNo(), QueueStatusEnum.INSTALLING.getStatus());
        bedInstallingQueueItem.setStatus(QueueStatusEnum.FINISH.getStatus());  // 默认状态 设置
        queueMapper.updateById(bedInstallingQueueItem);
        // 检查项目.亲和性 处理逻辑
        CheckTypeDO checkTypeDO = queueServiceTxFunctions.getCheckTypeItem(bedInstallingQueueItem.getBookCheckType());
        if (checkTypeDO.getAffinityCheckTypes().length > 0) {
            procAffinityWhenInstallFinish(bedInstallingQueueItem, checkTypeDO.getAffinityCheckTypes());
        }
        return CommonResult.success(rent_id);
    }
    @Override
    @Transactional
    @Transactional(rollbackFor = Exception.class)
    public Long dismantleOperation(DevRentSaveReqVO updateReqVO) {
        updateReqVO.setState( DevRentStateEnum.DISMANTLED.getState() );
        updateDevRent( updateReqVO );
        updateReqVO.setState(DevRentStateEnum.DISMANTLED.getState());
        updateDevRent(updateReqVO);
        Long rentId = updateReqVO.getId();
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        String userNickname = SecurityFrameworkUtils.getLoginUserNickname();
        // 设备恢复到空闲
        resumeDevFree( updateReqVO.getDevId() );
        markDevFree(updateReqVO.getDevId(), rentId);
        JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(updateReqVO.getId(), userId);
        if (jobRecordDO == null) {
        List<Integer> jobTypeList = new ArrayList<Integer>();
        jobTypeList.add(DevRentStateEnum.DISMANTLED.getState());
        JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(updateReqVO.getId(), userId, jobTypeList);
        if (null == jobRecordDO) {
            JobRecordSaveReqVO jobRecordSaveReqVO = new JobRecordSaveReqVO();
            jobRecordSaveReqVO.setRentId( updateReqVO.getId() );
            jobRecordSaveReqVO.setDevId( updateReqVO.getDevId() );
            jobRecordSaveReqVO.setDocId( userId );
            jobRecordSaveReqVO.setDocName( userNickname );
            jobRecordSaveReqVO.setPatId( updateReqVO.getPatId() );
            jobRecordSaveReqVO.setPatName( updateReqVO.getPatName() );
            jobRecordSaveReqVO.setJobTime( updateReqVO.getReturnTime() );
            jobRecordSaveReqVO.setJobType( DevRentStateEnum.DISMANTLED.getState() );
            jobRecordSaveReqVO.setRemark( updateReqVO.getRemark() );
            jobRecordSaveReqVO.setSummary( updateReqVO.getInterference() + " " + updateReqVO.getBaseline() + " " + updateReqVO.getDetachment() );
            jobRecordSaveReqVO.setRentId(updateReqVO.getId());
            jobRecordSaveReqVO.setDevId(updateReqVO.getDevId());
            jobRecordSaveReqVO.setDocId(userId);
            jobRecordSaveReqVO.setDocName(userNickname);
            jobRecordSaveReqVO.setPatId(updateReqVO.getPatId());
            jobRecordSaveReqVO.setPatName(updateReqVO.getPatName());
            jobRecordSaveReqVO.setJobTime(updateReqVO.getReturnTime());
            jobRecordSaveReqVO.setJobType(DevRentStateEnum.DISMANTLED.getState());
            jobRecordSaveReqVO.setRemark(updateReqVO.getRemark());
            jobRecordSaveReqVO.setSummary(updateReqVO.getInterference() + " " + updateReqVO.getBaseline() + " " + updateReqVO.getDetachment());
            jobRecordSaveReqVO.setCheckType(updateReqVO.getCheckType());
            JobRecordDO jobRecord = BeanUtils.toBean(jobRecordSaveReqVO, JobRecordDO.class);
            jobRecordMapper.insert(jobRecord);
        } else {
            jobRecordDO.setJobType( DevRentStateEnum.DISMANTLED.getState() );
            jobRecordDO.setJobTime( updateReqVO.getReturnTime() );
            jobRecordDO.setRemark( updateReqVO.getRemark() );
            jobRecordDO.setSummary( updateReqVO.getInterference() + " " + updateReqVO.getBaseline() + " " + updateReqVO.getDetachment() );
            jobRecordDO.setUpdater( String.valueOf(userId) );
            jobRecordDO.setUpdateTime( LocalDateTime.now() );
            jobRecordDO.setJobType(DevRentStateEnum.DISMANTLED.getState());
            jobRecordDO.setJobTime(updateReqVO.getReturnTime());
            jobRecordDO.setRemark(updateReqVO.getRemark());
            jobRecordDO.setSummary(updateReqVO.getInterference() + " " + updateReqVO.getBaseline() + " " + updateReqVO.getDetachment());
            jobRecordDO.setUpdater(String.valueOf(userId));
            jobRecordDO.setUpdateTime(LocalDateTime.now());
            jobRecordMapper.updateById(jobRecordDO);
        }
        return updateReqVO.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long dataEntryOperation(DevRentSaveReqVO updateReqVO) {
        updateReqVO.setState( DevRentStateEnum.DATAENTERED.getState() );
        updateDevRent( updateReqVO );
        updateReqVO.setState(DevRentStateEnum.DATAENTERED.getState());
        updateDevRent(updateReqVO);
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        String userNickname = SecurityFrameworkUtils.getLoginUserNickname();
        JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(updateReqVO.getId(), userId);
        if (jobRecordDO == null) {
        List<Integer> jobTypeList = new ArrayList<Integer>();
        jobTypeList.add(DevRentStateEnum.DATAENTERED.getState());
        JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(updateReqVO.getId(), userId, jobTypeList);
        if (null == jobRecordDO) {
            JobRecordSaveReqVO jobRecordSaveReqVO = new JobRecordSaveReqVO();
            jobRecordSaveReqVO.setRentId( updateReqVO.getId() );
            jobRecordSaveReqVO.setDevId( updateReqVO.getDevId() );
            jobRecordSaveReqVO.setDocId( userId );
            jobRecordSaveReqVO.setDocName( userNickname );
            jobRecordSaveReqVO.setPatId( updateReqVO.getPatId() );
            jobRecordSaveReqVO.setPatName( updateReqVO.getPatName() );
            jobRecordSaveReqVO.setJobTime( updateReqVO.getReturnTime() );
            jobRecordSaveReqVO.setJobType( DevRentStateEnum.DATAENTERED.getState() );
            jobRecordSaveReqVO.setRemark( updateReqVO.getRemark() );
            jobRecordSaveReqVO.setSummary( updateReqVO.getInterference() + " " + updateReqVO.getBaseline() + " " + updateReqVO.getDetachment() );
            jobRecordSaveReqVO.setRentId(updateReqVO.getId());
            jobRecordSaveReqVO.setDevId(updateReqVO.getDevId());
            jobRecordSaveReqVO.setDocId(userId);
            jobRecordSaveReqVO.setDocName(userNickname);
            jobRecordSaveReqVO.setPatId(updateReqVO.getPatId());
            jobRecordSaveReqVO.setPatName(updateReqVO.getPatName());
            jobRecordSaveReqVO.setJobTime(updateReqVO.getEntryTime());
            jobRecordSaveReqVO.setJobType(DevRentStateEnum.DATAENTERED.getState());
            jobRecordSaveReqVO.setRemark(updateReqVO.getRemark());
            jobRecordSaveReqVO.setSummary(updateReqVO.getInterference() + " " + updateReqVO.getBaseline() + " " + updateReqVO.getDetachment());
            jobRecordSaveReqVO.setCheckType(updateReqVO.getCheckType());
            JobRecordDO jobRecord = BeanUtils.toBean(jobRecordSaveReqVO, JobRecordDO.class);
            jobRecordMapper.insert(jobRecord);
        } else {
            jobRecordDO.setJobType( DevRentStateEnum.DATAENTERED.getState() );
            jobRecordDO.setJobTime( updateReqVO.getReturnTime() );
            jobRecordDO.setRemark( updateReqVO.getRemark() );
            jobRecordDO.setSummary( updateReqVO.getInterference() + " " + updateReqVO.getBaseline() + " " + updateReqVO.getDetachment() );
            jobRecordDO.setUpdater( String.valueOf(userId) );
            jobRecordDO.setUpdateTime( LocalDateTime.now() );
            jobRecordDO.setJobType(DevRentStateEnum.DATAENTERED.getState());
            jobRecordDO.setJobTime(updateReqVO.getEntryTime());
            jobRecordDO.setRemark(updateReqVO.getRemark());
            jobRecordDO.setSummary(updateReqVO.getInterference() + " " + updateReqVO.getBaseline() + " " + updateReqVO.getDetachment());
            jobRecordDO.setUpdater(String.valueOf(userId));
            jobRecordMapper.updateById(jobRecordDO);
        }
        return updateReqVO.getId();
    }
    // 常规检查 取消
    @Override
    @Transactional
    public CommonResult<Long> cancelOperation(DevCancelReqVO cancelReqVO) {
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Long> cancelRoutineOperation(DevCancelReqVO cancelReqVO) {
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        String userNickname = SecurityFrameworkUtils.getLoginUserNickname();
        // 若从 装机 改为 取消装机,恢复设备状态
        if (null != cancelReqVO.getDevId()) {
            resumeDevFree( cancelReqVO.getDevId() );
        }
        DevRentSaveReqVO createReqVO = BeanUtils.toBean(cancelReqVO, DevRentSaveReqVO.class);
        createReqVO.setDevId(null);
        createReqVO.setState( DevRentStateEnum.CANCELLED.getState() );
        createReqVO.setState(DevRentStateEnum.ROUTINE_CANCELLED.getState());
        Long rent_id = createReqVO.getId();
        if (null == rent_id) {
            List<DevRentDO>  devRentDOList = devRentMapper.selectByPatIdAndState(createReqVO.getPatId(), DevRentStateEnum.FREE.getState());
            if (devRentDOList.size() != 1)
                return CommonResult.error(DEV_INSTALL_EXIST);
        List<Integer> jobTypeList = new ArrayList<Integer>();
        jobTypeList.add(DevRentStateEnum.ROUTINE_FINISH.getState());
        jobTypeList.add(DevRentStateEnum.ROUTINE_CANCELLED.getState());
        JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(rent_id, userId, jobTypeList);
        if (null == jobRecordDO) {
            List<DevRentDO> devRentDOList = devRentMapper.selectByPatAndChecktypeAndState(createReqVO.getPatId(), createReqVO.getCheckType(), DevRentStateEnum.FREE.getState());
            if (devRentDOList.size() != 1) throw exception(ROUTINE_CHECK_EXIST);
            rent_id = devRentDOList.getFirst().getId();
            createReqVO.setId( rent_id );
            createReqVO.setState( DevRentStateEnum.CANCELLED.getState() );
            updateDevRent( createReqVO );
            rent_id = devRentDOList.get(0).getId();
            createReqVO.setId(rent_id);
            createReqVO.setState(DevRentStateEnum.ROUTINE_CANCELLED.getState());
            updateDevRent(createReqVO);
            ConfigDO configDO = configService.getConfigByKey("ecg.back.data");
            if (configDO.getValue().equals("1")) {
                if (cancelReqVO.getCheckType() != 100 && cancelReqVO.getCheckType() != 1000 && cancelReqVO.getCheckType() != 1100 && cancelReqVO.getCheckType() != 1400 && cancelReqVO.getCheckType() != 1500) {
                    AppointmentDO appointmentDO = appointmentMapper.getByApplyNo(createReqVO.getApplyNo());
                    ApplicationTemplate app = new ApplicationTemplate();
                    String action = "S0405";
                    String message = app.getXML(createReqVO.getApplyNo(), "4", "取消检查", createReqVO.getOperater(), appointmentDO.getPatDeptCode(), appointmentDO.getPatDeptDesc(), appointmentDO.getPatWardCode(), appointmentDO.getPatWardDesc(), appointmentDO.getPatBedNo(), appointmentDO.getEpisodeId(), "" + appointmentDO.getBookSrc(), appointmentDO.getPatId(), appointmentDO.getPatName());
                    String s = webServiceClient.callJHFWTYRK(action, "", message);
                    log.info("routineFinishWebServiceClient的返回值为:{}", s);
                    if (s.contains("更新成功")) {
                        //需要根据applyNo,将appiontment表里的status改成3
                        appointmentMapper.updateStatusByApplyNo(createReqVO.getApplyNo(), "4", null);
                    }
                } else {
                    //检查类型是床边的或常规的,需要根据applyNo,将appiontment表里的status改成7
                    appointmentMapper.updateStatusByApplyNo(createReqVO.getApplyNo(), "7", null);
                }
            }
            JobRecordSaveReqVO jobRecordSaveReqVO = new JobRecordSaveReqVO();
            jobRecordSaveReqVO.setRentId(rent_id);
@@ -281,64 +566,230 @@
            jobRecordSaveReqVO.setDocName(userNickname);
            jobRecordSaveReqVO.setPatId(createReqVO.getPatId());
            jobRecordSaveReqVO.setPatName(createReqVO.getPatName());
            jobRecordSaveReqVO.setJobType( DevRentStateEnum.CANCELLED.getState() );
            jobRecordSaveReqVO.setJobType(DevRentStateEnum.ROUTINE_CANCELLED.getState());
            jobRecordSaveReqVO.setJobTime(createReqVO.getRentTime());
            jobRecordSaveReqVO.setRemark(createReqVO.getRemark());
            jobRecordSaveReqVO.setSummary("");
            jobRecordSaveReqVO.setCheckType(createReqVO.getCheckType());
            JobRecordDO jobRecord = BeanUtils.toBean(jobRecordSaveReqVO, JobRecordDO.class);
            jobRecordMapper.insert(jobRecord);
        } else {
            updateDevRent( createReqVO );
            updateDevRent(createReqVO);
            JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(rent_id, userId);
            if (null != jobRecordDO) {
                jobRecordDO.setDevId(null);
                jobRecordDO.setJobType( DevRentStateEnum.CANCELLED.getState() );
                jobRecordDO.setJobTime(createReqVO.getRentTime());
                jobRecordDO.setRemark(createReqVO.getRemark());
                jobRecordDO.setUpdater(String.valueOf(userId));
                jobRecordDO.setUpdateTime(LocalDateTime.now());
                jobRecordMapper.updateById(jobRecordDO);
            jobRecordDO.setDevId(null);
            jobRecordDO.setJobType(DevRentStateEnum.ROUTINE_CANCELLED.getState());
            jobRecordDO.setJobTime(createReqVO.getRentTime());
            jobRecordDO.setRemark(createReqVO.getRemark());
            jobRecordDO.setUpdater(String.valueOf(userId));
            jobRecordDO.setUpdateTime(LocalDateTime.now());
            jobRecordMapper.updateById(jobRecordDO);
        }
        QueueDO firstOnStageQueueItem = queueMapper.getBedFirstQueueItemByApplyNo(createReqVO.getApplyNo(), QueueStatusEnum.ONSTAGE.getStatus());
        // QueueStatusEnum.ONSTAGE  =>  QueueStatusEnum.FINISH
        firstOnStageQueueItem.setStatus(QueueStatusEnum.FINISH.getStatus());  // 默认状态 设置
        firstOnStageQueueItem.setUpdateTime(LocalDateTime.now());
        queueMapper.updateById(firstOnStageQueueItem);
        return CommonResult.success(rent_id);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Long> cancelReadyOperation(DevCancelReqVO cancelReqVO) {
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        String userNickname = SecurityFrameworkUtils.getLoginUserNickname();
        Long rentId = cancelReqVO.getId();
        // 若从 装机 改为 取消装机,恢复设备状态
        if (null != cancelReqVO.getDevId()) {
            markDevFree(cancelReqVO.getDevId(), rentId);
        }
        DevRentSaveReqVO createReqVO = BeanUtils.toBean(cancelReqVO, DevRentSaveReqVO.class);
        createReqVO.setDevId(null);
        createReqVO.setState(DevRentStateEnum.RECEIVED_CANCELLED.getState());
        Long rent_id = createReqVO.getId();
        List<Integer> jobTypeList = new ArrayList<Integer>();
        jobTypeList.add(DevRentStateEnum.RECEIVED.getState());
        jobTypeList.add(DevRentStateEnum.RECEIVED_CANCELLED.getState());
        JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(rent_id, userId, jobTypeList);
        if (null == jobRecordDO) {
            List<DevRentDO> devRentDOList = devRentMapper.selectByPatAndChecktypeAndState(createReqVO.getPatId(), createReqVO.getCheckType(), DevRentStateEnum.FREE.getState());
            if (devRentDOList.size() != 1) throw exception(DEV_INSTALL_EXIST);
            rent_id = devRentDOList.get(0).getId();
            createReqVO.setId(rent_id);
            createReqVO.setState(DevRentStateEnum.RECEIVED_CANCELLED.getState());
            updateDevRent(createReqVO);
            ConfigDO configDO = configService.getConfigByKey("ecg.back.data");
            if (configDO.getValue().equals("1")) {
                AppointmentDO appointmentDO = appointmentMapper.getByApplyNo(createReqVO.getApplyNo());
                ApplicationTemplate app = new ApplicationTemplate();
                String action = "S0405";
                String message = app.getXML(createReqVO.getApplyNo(), "4", "取消检查", createReqVO.getOperater(), appointmentDO.getPatDeptCode(), appointmentDO.getPatDeptDesc(), appointmentDO.getPatWardCode(), appointmentDO.getPatWardDesc(), appointmentDO.getPatBedNo(), appointmentDO.getEpisodeId(), "" + appointmentDO.getBookSrc(), appointmentDO.getPatId(), appointmentDO.getPatName());
                String s = webServiceClient.callJHFWTYRK(action, "", message);
                log.info("routineFinishWebServiceClient的返回值为:{}", s);
                if (s.contains("更新成功")) {
                    //需要根据applyNo,将appiontment表里的status改成3
                    appointmentMapper.updateStatusByApplyNo(createReqVO.getApplyNo(), "4", null);
                }
            }
            JobRecordSaveReqVO jobRecordSaveReqVO = new JobRecordSaveReqVO();
            jobRecordSaveReqVO.setRentId(rent_id);
            jobRecordSaveReqVO.setDevId(null);
            jobRecordSaveReqVO.setDocId(userId);
            jobRecordSaveReqVO.setDocName(userNickname);
            jobRecordSaveReqVO.setPatId(createReqVO.getPatId());
            jobRecordSaveReqVO.setPatName(createReqVO.getPatName());
            jobRecordSaveReqVO.setJobType(DevRentStateEnum.RECEIVED_CANCELLED.getState());
            jobRecordSaveReqVO.setJobTime(createReqVO.getRentTime());
            jobRecordSaveReqVO.setRemark(createReqVO.getRemark());
            jobRecordSaveReqVO.setSummary("");
            jobRecordSaveReqVO.setCheckType(createReqVO.getCheckType());
            JobRecordDO jobRecord = BeanUtils.toBean(jobRecordSaveReqVO, JobRecordDO.class);
            jobRecordMapper.insert(jobRecord);
        } else {
            updateDevRent(createReqVO);
            jobRecordDO.setDevId(null);
            jobRecordDO.setJobType(DevRentStateEnum.RECEIVED_CANCELLED.getState());
            jobRecordDO.setJobTime(createReqVO.getRentTime());
            jobRecordDO.setRemark(createReqVO.getRemark());
            jobRecordDO.setUpdater(String.valueOf(userId));
            jobRecordDO.setUpdateTime(LocalDateTime.now());
            jobRecordMapper.updateById(jobRecordDO);
        }
        return CommonResult.success(rent_id);
    }
    @Override
    @Transactional
    public CommonResult<Long> loseOperation(DevRentSaveReqVO updateReqVO) {
        // 设备遗失时, 标注遗失
        markDevLost( updateReqVO.getDevId(), updateReqVO.getReturnTime().toLocalDate() );
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Long> cancelInstallOperation(DevCancelReqVO cancelReqVO) {
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        String userNickname = SecurityFrameworkUtils.getLoginUserNickname();
        Long rentId = cancelReqVO.getId();
        updateReqVO.setState( DevRentStateEnum.LOST.getState() );
        updateDevRent( updateReqVO );
        // 若从 装机 改为 取消装机,恢复设备状态
        if (null != cancelReqVO.getDevId()) {
            markDevFree(cancelReqVO.getDevId(), rentId);
        }
        DevRentSaveReqVO createReqVO = BeanUtils.toBean(cancelReqVO, DevRentSaveReqVO.class);
        createReqVO.setDevId(null);
        createReqVO.setState(DevRentStateEnum.INSTALL_CANCELLED.getState());
        Long rent_id = createReqVO.getId();
        List<Integer> jobTypeList = new ArrayList<Integer>();
        jobTypeList.add(DevRentStateEnum.INSTALLED.getState());
        jobTypeList.add(DevRentStateEnum.INSTALL_CANCELLED.getState());
        JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(rent_id, userId, jobTypeList);
        if (null == jobRecordDO) {
            List<DevRentDO> devRentDOList = devRentMapper.selectByPatAndChecktypeAndState(createReqVO.getPatId(), createReqVO.getCheckType(), DevRentStateEnum.RECEIVED.getState());
            if (devRentDOList.size() != 1) throw exception(DEV_INSTALL_EXIST);
            rent_id = devRentDOList.get(0).getId();
            createReqVO.setId(rent_id);
            createReqVO.setState(DevRentStateEnum.INSTALL_CANCELLED.getState());
            updateDevRent(createReqVO);
            ConfigDO configDO = configService.getConfigByKey("ecg.back.data");
            if (configDO.getValue().equals("1")) {
                AppointmentDO appointmentDO = appointmentMapper.getByApplyNo(createReqVO.getApplyNo());
                ApplicationTemplate app = new ApplicationTemplate();
                String action = "S0405";
                String message = app.getXML(createReqVO.getApplyNo(), "4", "取消检查", createReqVO.getOperater(), appointmentDO.getPatDeptCode(), appointmentDO.getPatDeptDesc(), appointmentDO.getPatWardCode(), appointmentDO.getPatWardDesc(), appointmentDO.getPatBedNo(), appointmentDO.getEpisodeId(), "" + appointmentDO.getBookSrc(), appointmentDO.getPatId(), appointmentDO.getPatName());
                String s = webServiceClient.callJHFWTYRK(action, "", message);
                log.info("routineFinishWebServiceClient的返回值为:{}", s);
                if (s.contains("更新成功")) {
                    //需要根据applyNo,将appiontment表里的status改成3
                    appointmentMapper.updateStatusByApplyNo(createReqVO.getApplyNo(), "4", null);
                }
            }
            JobRecordSaveReqVO jobRecordSaveReqVO = new JobRecordSaveReqVO();
            jobRecordSaveReqVO.setRentId(rent_id);
            jobRecordSaveReqVO.setDevId(null);
            jobRecordSaveReqVO.setDocId(userId);
            jobRecordSaveReqVO.setDocName(userNickname);
            jobRecordSaveReqVO.setPatId(createReqVO.getPatId());
            jobRecordSaveReqVO.setPatName(createReqVO.getPatName());
            jobRecordSaveReqVO.setJobType(DevRentStateEnum.INSTALL_CANCELLED.getState());
            jobRecordSaveReqVO.setJobTime(createReqVO.getRentTime());
            jobRecordSaveReqVO.setRemark(createReqVO.getRemark());
            jobRecordSaveReqVO.setSummary("");
            jobRecordSaveReqVO.setCheckType(createReqVO.getCheckType());
            JobRecordDO jobRecord = BeanUtils.toBean(jobRecordSaveReqVO, JobRecordDO.class);
            jobRecordMapper.insert(jobRecord);
        } else {
            updateDevRent(createReqVO);
            jobRecordDO.setDevId(null);
            jobRecordDO.setJobType(DevRentStateEnum.INSTALL_CANCELLED.getState());
            jobRecordDO.setJobTime(createReqVO.getRentTime());
            jobRecordDO.setRemark(createReqVO.getRemark());
            jobRecordDO.setUpdater(String.valueOf(userId));
            jobRecordDO.setUpdateTime(LocalDateTime.now());
            jobRecordMapper.updateById(jobRecordDO);
        }
        return CommonResult.success(rent_id);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Long> dismantleExceptionOperation(DevRentSaveReqVO updateReqVO) {
        DevStateEnum devStateEnum = null;
        DevRentStateEnum devRentStateEnum = null;
        if (updateReqVO.getState() == DevStateEnum.DAMAGED.getState()) {
            devStateEnum = DevStateEnum.DAMAGED;
            devRentStateEnum = DevRentStateEnum.DAMAGED;
        } else if (updateReqVO.getState() == DevStateEnum.LOST.getState()) {
            devStateEnum = DevStateEnum.LOST;
            devRentStateEnum = DevRentStateEnum.LOST;
        } else throw exception(DEV_DISMANTLE_EXCEPTION);
        // 设备遗失时, 标注遗失
        markDevDismantleException(updateReqVO.getDevId(), updateReqVO.getId(), updateReqVO.getReturnTime().toLocalDate(), devStateEnum);
        updateReqVO.setState(devRentStateEnum.getState());
        updateDevRent(updateReqVO);
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        String userNickname = SecurityFrameworkUtils.getLoginUserNickname();
        JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(updateReqVO.getId(), userId);
        if (jobRecordDO == null) {
        List<Integer> jobTypeList = new ArrayList<Integer>();
        jobTypeList.add(DevRentStateEnum.DAMAGED.getState());
        jobTypeList.add(DevRentStateEnum.LOST.getState());
        JobRecordDO jobRecordDO = jobRecordMapper.selectByRentIdDocId(updateReqVO.getId(), userId, jobTypeList);
        if (null == jobRecordDO) {
            JobRecordSaveReqVO jobRecordSaveReqVO = new JobRecordSaveReqVO();
            jobRecordSaveReqVO.setRentId( updateReqVO.getId() );
            jobRecordSaveReqVO.setDevId( updateReqVO.getDevId() );
            jobRecordSaveReqVO.setDocId( userId );
            jobRecordSaveReqVO.setDocName( userNickname );
            jobRecordSaveReqVO.setPatId( updateReqVO.getPatId() );
            jobRecordSaveReqVO.setPatName( updateReqVO.getPatName() );
            jobRecordSaveReqVO.setJobTime( updateReqVO.getReturnTime() );
            jobRecordSaveReqVO.setJobType( DevRentStateEnum.LOST.getState() );
            jobRecordSaveReqVO.setRemark( updateReqVO.getRemark() );
            jobRecordSaveReqVO.setSummary( updateReqVO.getInterference() + " " + updateReqVO.getBaseline() + " " + updateReqVO.getDetachment() );
            jobRecordSaveReqVO.setRentId(updateReqVO.getId());
            jobRecordSaveReqVO.setDevId(updateReqVO.getDevId());
            jobRecordSaveReqVO.setDocId(userId);
            jobRecordSaveReqVO.setDocName(userNickname);
            jobRecordSaveReqVO.setPatId(updateReqVO.getPatId());
            jobRecordSaveReqVO.setPatName(updateReqVO.getPatName());
            jobRecordSaveReqVO.setJobTime(updateReqVO.getReturnTime());
            jobRecordSaveReqVO.setJobType(devRentStateEnum.getState());
            jobRecordSaveReqVO.setRemark(updateReqVO.getRemark());
            jobRecordSaveReqVO.setSummary(updateReqVO.getInterference() + " " + updateReqVO.getBaseline() + " " + updateReqVO.getDetachment());
            jobRecordSaveReqVO.setCheckType(updateReqVO.getCheckType());
            JobRecordDO jobRecord = BeanUtils.toBean(jobRecordSaveReqVO, JobRecordDO.class);
            jobRecordMapper.insert(jobRecord);
        } else {
            jobRecordDO.setJobType( DevRentStateEnum.LOST.getState() );
            jobRecordDO.setJobTime( updateReqVO.getReturnTime() );
            jobRecordDO.setRemark( updateReqVO.getRemark() );
            jobRecordDO.setSummary( updateReqVO.getInterference() + " " + updateReqVO.getBaseline() + " " + updateReqVO.getDetachment() );
            jobRecordDO.setUpdater( String.valueOf(userId) );
            jobRecordDO.setUpdateTime( LocalDateTime.now() );
            jobRecordDO.setJobType(devRentStateEnum.getState());
            jobRecordDO.setJobTime(updateReqVO.getReturnTime());
            jobRecordDO.setRemark(updateReqVO.getRemark());
            jobRecordDO.setSummary(updateReqVO.getInterference() + " " + updateReqVO.getBaseline() + " " + updateReqVO.getDetachment());
            jobRecordDO.setUpdater(String.valueOf(userId));
            jobRecordDO.setUpdateTime(LocalDateTime.now());
            jobRecordMapper.updateById(jobRecordDO);
        }
@@ -347,8 +798,6 @@
    @Override
    public Long createDevRent(DevRentSaveReqVO createReqVO) {
        //
        // 插入
        DevRentDO devRent = BeanUtils.toBean(createReqVO, DevRentDO.class);
        devRentMapper.insert(devRent);
@@ -381,85 +830,278 @@
    @Override
    public DevRentDO getDevRent(Long id) {
        return devRentMapper.selectById(id);
        DevRentDO devRentDO = devRentMapper.selectById(id);
        fillPatientInfo(devRentDO);
        return devRentDO;
    }
    @Override
    public PageResult<DevRentDO> getDevRentPage(DevRentPageReqVO pageReqVO) {
        return devRentMapper.selectPage(pageReqVO);
        pageReqVO.setState(0);
        PageResult<DevRentDO> pageResult = devRentMapper.selectPage(pageReqVO);
        fillPatientInfo(pageResult.getList());
        return pageResult;
    }
    @Override
    public DevRentDO getInstalledOrCancelledRent(DevRentSearchReqVO searchReqVO) {
    public DevRentDO getExistingRent(DevRentSearchReqVO searchReqVO) {
        List<Integer> stateList = new ArrayList<>();
        stateList.add( DevRentStateEnum.INSTALLED.getState() );
        stateList.add( DevRentStateEnum.CANCELLED.getState() );
        searchReqVO.setStateList( stateList );
        return devRentMapper.getRentByState(searchReqVO);
        stateList.add(DevRentStateEnum.INSTALLED.getState());
        stateList.add(DevRentStateEnum.RECEIVED.getState());
        searchReqVO.setStateList(stateList);
        DevRentDO devRentDO = devRentMapper.getRentByState(searchReqVO);
        fillPatientInfo(devRentDO);
        return devRentDO;
    }
    @Override
    public DevRentDO getFreeRent(DevRentSearchReqVO searchReqVO) {
        List<Integer> stateList = new ArrayList<>();
        stateList.add(DevRentStateEnum.FREE.getState());
        searchReqVO.setStateList(stateList);
        DevRentDO devRentDO = devRentMapper.getRentByState(searchReqVO);
        fillPatientInfo(devRentDO);
        return devRentDO;
    }
    @Override
    public DevRentDO getReadyOrCancelledRent(DevRentSearchReqVO searchReqVO) {
        List<Integer> stateList = new ArrayList<>();
        stateList.add( DevRentStateEnum.READY.getState() );
        stateList.add( DevRentStateEnum.CANCELLED.getState() );
        searchReqVO.setStateList( stateList );
        return devRentMapper.getRentByState(searchReqVO);
        stateList.add(DevRentStateEnum.RECEIVED.getState());
        stateList.add(DevRentStateEnum.RECEIVED_CANCELLED.getState());
        searchReqVO.setStateList(stateList);
        DevRentDO devRentDO = devRentMapper.getRentByState(searchReqVO);
        fillPatientInfo(devRentDO);
        return devRentDO;
    }
    @Override
    public DevRentDO getDismantledRent(DevRentSearchReqVO searchReqVO) {
        List<Integer> stateList = new ArrayList<>();
        stateList.add( DevRentStateEnum.DISMANTLED.getState() );
        searchReqVO.setStateList( stateList );
        return devRentMapper.getRentByState(searchReqVO);
        stateList.add(DevRentStateEnum.DISMANTLED.getState());
        searchReqVO.setStateList(stateList);
        DevRentDO devRentDO = devRentMapper.getRentByState(searchReqVO);
        fillPatientInfo(devRentDO);
        return devRentDO;
    }
    private Integer markDevRecieved(String devId) {
    // ==================== 需求1:回填患者信息 18 项 ====================
    // dev_rent 表只存了 applyNo / patName / patDetails,
    // 其余患者信息(性别、出生日期、病区、床号、电话、来源、就诊卡号、
    // 登记时间、开单医师、开单日期、多耐药)都在 appointment 表。
    //
    // 参照 QueueServiceImpl#fillAppointmentInfo 的做法:查询后按 applyNo 回查预约单补齐。
    // 这样领用/装机/拆机/数据录入/检查记录 5 个界面才能展示完整患者信息。
    /**
     * 回填单个对象的患者信息(对象为 null 或 applyNo 为空时安全跳过)。
     */
    private void fillPatientInfo(DevRentDO devRentDO) {
        if (null == devRentDO || !org.springframework.util.StringUtils.hasText(devRentDO.getApplyNo())) {
            return;
        }
        AppointmentDO appointment = appointmentMapper.getByApplyNo(devRentDO.getApplyNo());
        if (null == appointment) {
            return;
        }
        applyAppointmentToDevRent(devRentDO, appointment);
    }
    /**
     * 批量回填(避免 N+1 之外的额外开销由调用方控制;此处逐条回查,
     * 与 QueueServiceImpl#fillAppointmentInfo 保持一致的做法)。
     */
    private void fillPatientInfo(List<DevRentDO> devRentDOList) {
        if (null == devRentDOList || devRentDOList.isEmpty()) {
            return;
        }
        devRentDOList.forEach(this::fillPatientInfo);
    }
    /**
     * 把预约单中的患者信息复制到 dev_rent 对象。
     * <p>
     * 注意:只覆盖**为空的**平铺字段?——不,这里直接覆盖:
     * 这些字段均标记为 {@code @TableField(exist=false)},不参与写库,
     * 本次查询返回的就是最新数据,不存在"覆盖掉用户刚录入的值"的风险。
     */
    private void applyAppointmentToDevRent(DevRentDO target, AppointmentDO source) {
        target.setPatGender(source.getPatGender());
        target.setPatBirthday(source.getPatBirthday());
        target.setRegisterDate(source.getRegisterDate());
        target.setDoctor(source.getDoctor());
        target.setDoctorTime(source.getDoctorTime());
        target.setPatSrc(source.getPatSrc());
        target.setPatMobile(source.getPatMobile());
        target.setPatPhone(source.getPatPhone());
        target.setPatWardDesc(source.getPatWardDesc());
        target.setPatBedNo(source.getPatBedNo());
        target.setMedicalCard(source.getMedicalCard());
        if (target.getDoctorAdviceNo() == null) target.setDoctorAdviceNo(source.getDoctorAdviceNo());
        target.setOutpatientNo(source.getOutpatientNo());
        target.setPatIdentityId(source.getPatIdentityId());
        target.setTolerance(source.getTolerance());
    }
    // 确费处理
    // 返回值:  0 成功  1 失败  2 不用确费
    @Override
    public Integer feeConfirm(Long rentId, String hisId, String userNickname, Boolean isFeeConfirmOrCancel) {
        DevRentDO devRentDO = devRentMapper.selectById(rentId);
        // 查询检查类型,确定是否需要确费
        CheckTypeDO checkTypeDO = checkTypeMapper.getCheckTypeByValue(devRentDO.getCheckType());
        if (0 == checkTypeDO.getExpenseRecognition()) {
            return 2;
        }
        Integer returnValue = 1;
        try {
            AppointmentDO appointmentDO = appointmentMapper.getByApplyNo(devRentDO.getApplyNo());
            returnValue = appointmentDO.getPaid() == 1 ? 0 : 1;
        } catch (RuntimeException ex) {
            returnValue = 1;
        }
        if (0 == returnValue) {
            devRentMapper.setPaid(rentId, isFeeConfirmOrCancel ? 1 : 0);
        }
        return returnValue;  // 0 成功  1 失败  2 不用确费
    }
    // 标记 设备已被领取
    private Integer markDevRecieved(String devId, Long rentId, PatDetails patDetails) {
        // 设备标注 已领取
        DeviceDO deviveDO = deviceMapper.getDeviceByDevId(devId);
        if (null != deviveDO) {
            deviveDO.setState( DevStateEnum.RECEIVED.getState() );
            return deviceMapper.updateById(deviveDO);
        DeviceDO deviceDO = deviceMapper.getDeviceByDevId(devId);
        if (null != deviceDO && DevStateEnum.FREE.isEqual(deviceDO.getState())) {
            deviceDO.setState(DevStateEnum.RECEIVED.getState());
            deviceDO.setRentId(rentId);
            deviceDO.setStateDate(LocalDate.now());
            deviceDO.setPatDetails(patDetails);
            return deviceMapper.updateById(deviceDO);
        }
        return 0;
    }
    private Integer markDevInUse(String devId) {
        // 设备标注 在使用
        DeviceDO deviveDO = deviceMapper.getDeviceByDevId(devId);
        if (null != deviveDO) {
            deviveDO.setState( DevStateEnum.INUSE.getState() );
            return deviceMapper.updateById(deviveDO);
    // 标记 设备已被使用
    private Integer markDevInUse(String devId, Long rentId) {
        DeviceDO deviceDO = deviceMapper.getDeviceByDevId(devId);
        if (null == deviceDO) return 0;
        if (DevStateEnum.RECEIVED.isEqual(deviceDO.getState()) && rentId.equals(deviceDO.getRentId())) {
            deviceDO.setState(DevStateEnum.INUSE.getState());
            deviceDO.setStateDate(LocalDate.now());
            return deviceMapper.updateById(deviceDO);
        }
        if (DevStateEnum.FREE.isEqual(deviceDO.getState())) {
            deviceDO.setState(DevStateEnum.INUSE.getState());
            deviceDO.setStateDate(LocalDate.now());
            deviceDO.setRentId(rentId);
            return deviceMapper.updateById(deviceDO);
        }
        return 0;
    }
    private Integer markDevLost(String devId, LocalDate localDate) {
    private Integer markDevDismantleException(String devId, Long rentId, LocalDate localDate, DevStateEnum devStateEnum) {
        // 设备标注遗失
        DeviceDO deviveDO = deviceMapper.getDeviceByDevId(devId);
        if (null != deviveDO) {
            deviveDO.setState( DevStateEnum.LOST.getState() );
            deviveDO.setLoseDate( localDate ); // createReqVO.getReturnTime().toLocalDate()
            return deviceMapper.updateById(deviveDO);
        DeviceDO deviceDO = deviceMapper.getDeviceByDevId(devId);
        if (null != deviceDO && DevStateEnum.INUSE.isEqual(deviceDO.getState()) && rentId.equals(deviceDO.getRentId())) {
            deviceDO.setState(devStateEnum.getState());
            deviceDO.setStateDate(localDate);
            return deviceMapper.updateById(deviceDO);
        }
        return 0;
    }
    private Integer resumeDevFree(String devId) {
    private Integer markDevFree(String devId, Long rentId) {
        // 设备标注遗失 恢复为空闲
        DeviceDO deviveDO = deviceMapper.getDeviceByDevId( devId );
        if (null != deviveDO) {
            deviveDO.setState( DevStateEnum.FREE.getState() );
            deviveDO.setLoseDate( null );
            return deviceMapper.updateById(deviveDO);
        DeviceDO deviceDO = deviceMapper.getDeviceByDevId(devId);
        if (null != deviceDO && rentId.equals(deviceDO.getRentId())) {
            deviceDO.setState(DevStateEnum.FREE.getState());
            deviceDO.setStateDate(LocalDate.now());
            deviceDO.setRentId(null);
            deviceDO.setPatDetails(null);
            return deviceMapper.updateById(deviceDO);
        }
        return 0;
    }
    private void procAffinityWhenRoutineFinish(String patId, Integer[] affinityCheckTypes) {
        // 如果存在 [亲和-排队] 亲和项,则选择 其中一个亲和项 接替其 处于 [就诊中]
        List<QueueDO> affinityAffinityWaitingItems = queueMapper.getCurPatGivenCheckTypesAndStatus(patId, affinityCheckTypes, QueueStatusEnum.AFFINITY.getStatus());
        if (!affinityAffinityWaitingItems.isEmpty()) {
            QueueDO tmpQueueDO = affinityAffinityWaitingItems.get(0);
            tmpQueueDO.setStatus(QueueStatusEnum.ONSTAGE.getStatus());
            queueMapper.updateById(tmpQueueDO);
        }
    }
    /**
     * 领用完成,主动寻求依附
     */
    private void procAffinityWhenReadyFinish(/*IN,OUT*/QueueDO bedOnStageQueueItem, Integer[] affinityCheckTypes) {
        // 如果存在 [安装中] [安装过号] [安装召回] 能被依附的检查项,则依附到其中一个检查项
        List<QueueDO> affinityInstallingItems = queueMapper.getCurPatGivenCheckTypesAndStatusList(bedOnStageQueueItem.getPatId(), affinityCheckTypes, List.of(QueueStatusEnum.INSTALLING.getStatus(), QueueStatusEnum.PASSED_INSTALL.getStatus(), QueueStatusEnum.RECALLED_INSTALL.getStatus()));
        if (!affinityInstallingItems.isEmpty()) {
            QueueDO affinityInstallingItem = affinityInstallingItems.get(0);
            bedOnStageQueueItem.setStatus(QueueStatusEnum.AFFINITY_INSTALL.getStatus()); // 亲和-领用
            bedOnStageQueueItem.setAffinityItem(1);
            bedOnStageQueueItem.setRoomId(affinityInstallingItem.getRoomId());
            bedOnStageQueueItem.setBedNo(affinityInstallingItem.getBedNo());  // 领用 到 安装 可能会在不同工位
        }
        // 如果存在 [亲和-排队] 亲和项,则选择 其中一个亲和项 接替其 处于 [就诊中]
        List<QueueDO> affinityAffinityWaitingItems = queueMapper.getCurPatGivenCheckTypesAndStatus(bedOnStageQueueItem.getPatId(), affinityCheckTypes, QueueStatusEnum.AFFINITY.getStatus());
        if (!affinityAffinityWaitingItems.isEmpty()) {
            QueueDO tmpQueueDO = affinityAffinityWaitingItems.get(0);
            tmpQueueDO.setStatus(QueueStatusEnum.ONSTAGE.getStatus());
            queueMapper.updateById(tmpQueueDO);
        }
    }
    // 如果存在 [亲和-领用] 亲和项,则选择 其中一个亲和项 接替其 处于 [安装中]
    private void procAffinityWhenInstallFinish(QueueDO bedInstallingQueueItem, Integer[] affinityCheckTypes) {
        List<QueueDO> affinityAffinityReceivedItems = queueMapper.getCurPatGivenCheckTypesAndStatus(bedInstallingQueueItem.getPatId(), affinityCheckTypes, QueueStatusEnum.AFFINITY_INSTALL.getStatus());
        if (!affinityAffinityReceivedItems.isEmpty()) {
            QueueDO tmpQueueDO = affinityAffinityReceivedItems.get(0);
            tmpQueueDO.setStatus(QueueStatusEnum.INSTALLING.getStatus());
            tmpQueueDO.setRoomId(bedInstallingQueueItem.getRoomId());
            tmpQueueDO.setBedNo(bedInstallingQueueItem.getBedNo()); // 从 [领用] 到 [安装], 可鞥在不同工位上操作
            queueMapper.updateById(tmpQueueDO);
        }
    }
    private String getHisCheckCode(Integer checkType) {
        if (100 == checkType) return "691133607";
        else if (200 == checkType) return "201605";
        else if (300 == checkType) return "200327";
        else if (400 == checkType) return "201652";
        else if (500 == checkType) return "502490914";
        else if (600 == checkType) return "419562119";
        else if (700 == checkType) return "201604";
        else if (800 == checkType) return "1202042";
        else if (900 == checkType) return "1202058";
        else if (1000 == checkType) return "1202065";
        else if (1100 == checkType) return "559542128";
        else if (1200 == checkType) return "590244511";
        else if (1300 == checkType) return "666454217";
        else if (1400 == checkType) return "720791490";
        else if (1500 == checkType) return "720792077";
        return "691133607";
    }
    String getPatientType(Integer patientSource) {
        if (1 == patientSource || 2 == patientSource) return "01";
        else if (3 == patientSource) return "03";
        else if (4 == patientSource) return "04";
        return "01";
    }
}