package com.coffee.bus.service; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.LocalDateTimeUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.coffee.bus.entity.*; import com.coffee.bus.enums.DeviceAlarmEnum; import com.coffee.bus.enums.DeviceStatusEnum; import com.coffee.bus.enums.PatientAlarmEnum; import com.coffee.bus.hospital.HospitalManager; import com.coffee.bus.hospital.HospitalManagerRegister; import com.coffee.bus.hospital.his.HisScriptSession; import com.coffee.bus.hospital.his.HisScriptSessionManager; import com.coffee.bus.hospital.his.strategy.HisStrategyEnum; import com.coffee.bus.registry.device.DeviceRegistry; import com.coffee.bus.service.dto.*; import com.coffee.bus.mapper.BusPatientMapper; import com.coffee.bus.registry.patient.PatientOperator; import com.coffee.bus.registry.patient.PatientRegistry; import com.coffee.bus.utils.WsPublishUtils; import com.coffee.common.crud.BaseService; import com.coffee.common.enums.SexEnum; import com.coffee.common.exception.CustomException; import com.coffee.common.result.R; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.context.request.async.DeferredResult; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * @author lifang * @version 1.0.0 * @ClassName LocalBusHospitalService.java * @Description * @createTime 2022年03月19日 09:27:00 */ @Service @Slf4j public class LocalBusPatientService extends BaseService { @Autowired @Lazy private LocalBusClinicService clinicService; @Autowired @Lazy private PatientRegistry patientRegistry; @Autowired @Lazy private HospitalManagerRegister hospitalManagerRegister; @Autowired @Lazy private WsPublishUtils wsPublishUtils; @Autowired @Lazy private LocalBusInfusionHistoryService infusionHistoryService; @Autowired @Lazy private LocalBusPatientService patientService; @Autowired @Lazy private DeviceRegistry deviceRegistry; @Autowired @Lazy private LocalBusHospitalService hospitalService; private ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); @Override public void validateBeforeSave(BusPatientEntity entity) { if(entity.getGender()==null){ entity.setGender(SexEnum.UNKNOW); } if(entity.getAlarm()==null){ entity.setAlarm(PatientAlarmEnum.NONE); } } @Override public void validateBeforeUpdate(BusPatientEntity entity) { } @Override public void validateBeforeDelete(String id) { } @Override public void postSave(BusPatientEntity entity) { super.postSave(entity); //新增病人后推送主题,延迟推送,保证处理逻辑已全部完成 executorService.schedule(()->{ wsPublishUtils.publishPatientAdd(entity.getCode(),entity.getTenantId()); wsPublishUtils.publishMonitorTotalCount(entity.getTenantId()); } ,2, TimeUnit.SECONDS); } /** * 获取病人信息 * @param hospitalId 医院id * @param patientCode 病号 * @param sync 是否为同步操作 */ public DeferredResult> getPatientInfoFromHis(String hospitalId, String patientCode, long timeout,boolean sync){ BusHospitalEntity hospital = hospitalService.getById(hospitalId); HospitalManager hospitalManager = hospitalManagerRegister.get(hospitalId); HisScriptSession hisScriptSession = hospitalManager.getScriptSession(); HisStrategyEnum strategy = hospital.getStrategy(); if(null==strategy||HisStrategyEnum.NONE.equals(strategy)){ throw new CustomException("医院未对接his,请到【信息维护】->【HIS对接】配置his策略"); } log.info("医院【{}】,开始拉取病号【{}】信息,拉取方式【{}】",hospitalId,patientCode,sync?"同步":"异步"); return sync?hisScriptSession.syncGetPatientInfo(patientCode,timeout,TimeUnit.SECONDS):hisScriptSession.asyncGetPatientInfo(patientCode,timeout,TimeUnit.SECONDS,true); } /** * 描述: 对病人报警数量进行统计 * @author lifang * @date 2022/5/20 16:42 * @param tenantId * @param alarm * @return long */ public long patientAlarmCount(String tenantId,PatientAlarmEnum alarm){ return this.baseMapper.selectAlarmCount(tenantId,alarm.getValue()); } public long patientAlarmCount(PatientAlarmEnum alarm){ return patientAlarmCount(null,alarm); } public long monitorTotalCount(String tenantId){ return this.baseMapper.monitorTotalCount(tenantId); } /** * 获取给定医院下所有的挂载多个设备的病人信息 * */ public List repeatDevice() { List patientDeviceRepeats = this.baseMapper.selectRepeatDevice(); Map resultMap = new HashMap<>(); patientDeviceRepeats.forEach(deviceRepeat->{ PatientDeviceRepeatResult repeatResult = resultMap.computeIfAbsent(deviceRepeat.getCode()+deviceRepeat.getClinicId(),k-> PatientDeviceRepeatResult.of( deviceRepeat.getName(), deviceRepeat.getGender(), deviceRepeat.getCode(), deviceRepeat.getAge(), deviceRepeat.getWard(), deviceRepeat.getBedNo(), deviceRepeat.getClinicName(), deviceRepeat.getClinicId(), new ArrayList<>()) ); List deviceRunningSmallInfos = Optional.ofNullable(repeatResult.getDevices()).orElse(new ArrayList<>()); deviceRunningSmallInfos.add(PatientDeviceRepeatResult.DeviceRunningSmallInfo.of( deviceRepeat.getDeviceId(), deviceRepeat.getDeviceAlias(), deviceRepeat.getDeviceRunState(), deviceRepeat.getDeviceAlarm(), deviceRepeat.getInfusionStartTime(), deviceRepeat.isMaster() )); repeatResult.setDevices(deviceRunningSmallInfos); }); return new ArrayList<>(resultMap.values()); } /** * 获取无设备绑定的临床手术信息 * @return */ public List noneDevice() { return this.baseMapper.selectNoneDevice(); } public List selectAll(PatientMonitorQuery query) { Page page = new Page<>(0, 500, false); IPage result = this.baseMapper.selectMonitor(page,query); if(CollectionUtil.isNotEmpty(result.getRecords())){ result.getRecords().forEach(PatientMonitorResult::handleWarn); } return result.getRecords(); } /**t * 根据医院和住院号获取一个患者 * @param tenantId * @param patientCode * @return */ public BusPatientEntity getOneByHospitalAndPatientCode(String tenantId, String patientCode) { BusPatientEntity patient = this.getOne(new QueryWrapper().lambda() .eq(BusPatientEntity::getTenantId,tenantId) .eq(BusPatientEntity::getCode,patientCode)); // 如果患者不存在则新增一个患者 if (Objects.isNull(patient)){ patient = new BusPatientEntity(); patient.setTenantId(tenantId); patient.setCode(patientCode); this.save(patient); } return patient; } /** * * 病人手动更新当前监控的临床信息 * @param clinic */ @Transactional(rollbackFor = Exception.class) public void manualEdit(BusClinicEntity clinic) { BusClinicEntity originClinicInfo = clinicService.getById(clinic.getId()); if(originClinicInfo==null){ throw new CustomException("该住院号临床信息不存在,请刷新后重试"); } BusPatientEntity originPatientInfo = patientService.getById(originClinicInfo.getPatientId()); if(originPatientInfo==null){ throw new CustomException("该住院号信息不存在,请刷新后重试"); } //先更新手术信息 clinicService.updateById(clinic); //后更新病人信息 BusPatientEntity patient = BusPatientEntity.of(clinic); PatientOperator patientOperator = patientRegistry.getOperator(patient.getTenantId(), originPatientInfo.getOriginCode()); BusPatientEntity existPatient = this.getOne(new QueryWrapper().lambda().eq(BusPatientEntity::getCode, patient.getCode()).last("limit 1")); patient.setId(existPatient.getId()); this.updateById(patient); patientOperator.setClinicId(patient.getClinicId()); CompletableFuture.runAsync(()->{ wsPublishUtils.publishPatientMonitor(originPatientInfo.getId(),patient.getTenantId()); }); } /** * 根据病号查询临床监控记录 * @author lifang * @param patientId 病号id * @param tenantId 医院id * @return */ public PatientMonitorResult lookMonitorByPatientCode(String patientId,String tenantId) { try { return this.baseMapper.findByPatientId(tenantId, patientId); } catch (Exception e) { log.error("根据病号查询临床失败,",e.getMessage()); return null; } } /** * 描述: 设备状态数量统计 * @author lifang * @date 2022/5/8 21:52 * @param tenantId 医院id 用户请求时传输null * @return MonitorStatusStatsCountResult */ public MonitorStatusStatsCountResult statusStats(String tenantId) { PatientMonitorQuery query = new PatientMonitorQuery(); query.setTenantId(tenantId); List patientMonitorResults = this.selectAll(query); MonitorStatusStatsCountResult result = new MonitorStatusStatsCountResult(); if(CollectionUtil.isNotEmpty(patientMonitorResults)){ patientMonitorResults.forEach(monitor->{ //运行数量 if(!Boolean.TRUE.equals(monitor.getInfusionFinished())&& DeviceStatusEnum.Running.equals(monitor.getDeviceRunState())){ result.setRunningCount(result.getRunningCount()+1); } if((Boolean.TRUE.equals(monitor.getInfusionFinished()))|| (DeviceStatusEnum.Shutdown.equals(monitor.getDeviceRunState())||DeviceStatusEnum.NoSignal.equals(monitor.getDeviceRunState()))){ result.setWaitingFinish(result.getWaitingFinish()+1); } //报警数量 if(!Boolean.TRUE.equals(monitor.getInfusionFinished())&& monitor.getDeviceAlarm()!=null&&!monitor.getDeviceAlarm().equals(DeviceAlarmEnum.None)){ result.setAlarmCount(result.getAlarmCount()+1); } //提醒数量 if(!Boolean.TRUE.equals(monitor.getInfusionFinished())&& (Boolean.TRUE.equals(monitor.getWarnAnalgesicPoor()) ||Boolean.TRUE.equals(monitor.getWarnLowBattery()) ||Boolean.TRUE.equals(monitor.getWarnWillFinished()) ||monitor.getWarnFlow()!=null)){ result.setWarnCount(result.getWarnCount()+1); } }); } return result; } /** * 描述: 按照时间对输注监控进行统计 * @author lifang * @date 2022/5/8 22:40 * @param tenantId 医院id * @return MonitorTimeStatsCountResult */ public MonitorTimeStatsCountResult timeStats(String tenantId) { PatientMonitorQuery query = new PatientMonitorQuery(); query.setTenantId(tenantId); List patientMonitorResults = this.selectAll(query); MonitorTimeStatsCountResult result = new MonitorTimeStatsCountResult(); patientMonitorResults.forEach(monitor->{ Date startTime = monitor.getMonitorStartTime(); if(startTime==null){ return; } if (includeTimes(startTime, 0)) { result.setToday(result.getToday()+1); return; } if (includeTimes(startTime, -1)) { result.setOneDay(result.getOneDay()+1); return; } if (includeTimes(startTime, -2)) { result.setTwoDay(result.getTwoDay()+1); return; } if (includeTimes(startTime, -3)) { result.setThreeDay(result.getThreeDay()+1); return; } result.setBeyondThreeDay(result.getBeyondThreeDay()+1); }); return result; } /** * 描述: 判断所给时间是否在存在于某一时间段内 * @author lifang * @date 2022/5/8 22:47 * @param time 时间 * @param offset 时间偏移量,单位:天 以当天时间为基准进行偏移 * @return boolean */ private boolean includeTimes(Date time,int offset){ if(time==null){ return false; } LocalDateTime dateTime = LocalDateTime.now().plusDays(offset); LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(dateTime); LocalDateTime endTime = LocalDateTimeUtil.endOfDay(dateTime); return beginTime.toInstant(ZoneOffset.of("+8")).toEpochMilli()().lambda().eq(BusPatientEntity::getCode, formatCode)); if(patient==null){ throw new CustomException(String.format("住院号【%s】不存在,请刷新后重试",shiftConfig.getPatientCode())); } shiftConfig.setPatientId(patient.getId()); PatientOperator patientOperator = patientRegistry.getOperator(shiftConfig.getTenantId(), shiftConfig.getPatientCode()); List replicaDeviceIds = shiftConfig.getReplicaDeviceIds(); String masterDeviceId = shiftConfig.getMasterDeviceId(); BusInfusionHistoryEntity masterInfusion = infusionHistoryService.lastInfusion(masterDeviceId); //病患绑定主泵信息 this.update(new UpdateWrapper().lambda() .eq(BusPatientEntity::getCode,shiftConfig.getPatientCode()) .eq(BusPatientEntity::getTenantId,shiftConfig.getTenantId()) .set(BusPatientEntity::getInfusionId,masterInfusion.getId())); //刷新缓存信息 replicaDeviceIds.stream() .map(deviceRegistry::getOperator).forEach(deviceOperator->{ deviceOperator.setMaster(false); }); patientOperator.setBindDeviceId(shiftConfig.getMasterDeviceId()); } public BusPatientEntity findByFormatCode(String patientCode, String tenantId){ return this.getOne(new QueryWrapper().lambda().eq(BusPatientEntity::getCode,patientCode) .eq(StrUtil.isNotBlank(tenantId),BusPatientEntity::getTenantId,tenantId)); } public BusPatientEntity findByFormatCode(String patientCode){ return findByFormatCode(patientCode,null); } }