| | |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.google.gson.Gson; |
| | | import com.ruoyi.common.core.domain.AjaxResult; |
| | | import com.ruoyi.common.core.domain.entity.SysDept; |
| | | import com.ruoyi.common.core.domain.entity.SysUser; |
| | | import com.ruoyi.common.core.redis.RedisCache; |
| | | import com.ruoyi.common.dx.MessageSend; |
| | | import com.ruoyi.common.enums.MsgLSEnum; |
| | | import com.ruoyi.common.core.service.IDingTalkService; |
| | | import com.ruoyi.common.enums.ServiceFromEnum; |
| | | import com.ruoyi.common.enums.WxGZHEnum; |
| | | import com.ruoyi.common.utils.HttpUtil; |
| | | import com.ruoyi.common.utils.OkHttpExample; |
| | | import com.ruoyi.common.utils.RSAPublicKeyExample; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.utils.*; |
| | | import com.ruoyi.common.utils.http.HttpUtils; |
| | | import com.ruoyi.common.utils.sms.smsUtils; |
| | | import com.ruoyi.message.SendResult; |
| | | import com.ruoyi.message.sms.SmsSendRequest; |
| | | import com.ruoyi.message.sms.SmsSenderFactory; |
| | | import com.ruoyi.message.sms.provider.xh.XinHuaSmsSender; |
| | | import com.ruoyi.message.wechat.WechatSendRequest; |
| | | import com.ruoyi.message.wechat.WechatSenderFactory; |
| | | import com.ruoyi.quartz.constant.MzGatherConstant; |
| | | import com.ruoyi.quartz.enums.MzGatherParamEnum; |
| | | import com.ruoyi.quartz.enums.MzGatherSlotEnum; |
| | | import com.ruoyi.quartz.enums.MzGatherStatusEnum; |
| | | import com.ruoyi.quartz.service.ICollectHISService; |
| | | import com.ruoyi.quartz.util.CreateDataTable; |
| | | import com.ruoyi.system.domain.SysConfig; |
| | | import com.ruoyi.system.mapper.SysConfigMapper; |
| | | import com.ruoyi.system.service.ISysConfigService; |
| | | import com.smartor.common.LSHospTokenUtil; |
| | | import com.ruoyi.system.service.ISysDeptService; |
| | | import com.ruoyi.system.service.ISysUserService; |
| | | import com.ruoyi.message.sms.provider.sltd.ws.MessageSend; |
| | | import com.smartor.common.QwenLLMUtil; |
| | | import com.smartor.domain.*; |
| | | import com.smartor.domain.VO.ServiceSubtaskDetailTraceVO; |
| | | import com.smartor.domain.entity.ServiceSubtaskEntity; |
| | | import com.smartor.mapper.*; |
| | | import com.smartor.service.*; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Paths; |
| | | import java.security.MessageDigest; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.LocalTime; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.*; |
| | | import java.util.concurrent.CompletableFuture; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.hutool.core.convert.Convert.toHex; |
| | | |
| | | /** |
| | | * 定时任务调度测试 |
| | |
| | | @Autowired |
| | | private ISysConfigService configService; |
| | | |
| | | @Autowired |
| | | private IDingTalkService dingTalkService; |
| | | |
| | | @Autowired |
| | | private ISysUserService userService; |
| | | |
| | | |
| | | @Value("${localIP}") |
| | | private String localIP; |
| | | |
| | | @Value("${xhsmsPath}") |
| | | private String xhsmsPath; |
| | | |
| | | @Value("${xhsmsAccount}") |
| | | private String xhsmsAccount; |
| | | |
| | | @Value("${xhsmsPwd}") |
| | | private String xhsmsPwd; |
| | | |
| | | @Value("${pub_key}") |
| | | private String pub_key; |
| | |
| | | private IServiceSubtaskRecordService serviceSubtaskRecordService; |
| | | |
| | | @Autowired |
| | | IBaseSmsaccountService baseSmsaccountService; |
| | | IIcd10Service iIcd10Service; |
| | | |
| | | @Autowired |
| | | IPatArchiveService patArchiveService; |
| | | |
| | | @Autowired |
| | | ISysDeptService sysDeptService; |
| | | |
| | | @Autowired |
| | | IServiceSubtaskDetailTraceService serviceSubtaskDetailTraceService; |
| | | |
| | | @Autowired |
| | | IPatMedOuthospService patMedOuthospService; |
| | |
| | | @Autowired |
| | | ICollectHISService icollectHis; |
| | | |
| | | @Autowired |
| | | CreateDataTable createDataTable; |
| | | |
| | | |
| | | @Value("${appid}") |
| | | private String appid; |
| | |
| | | @Value("${server.port}") |
| | | private String port; |
| | | |
| | | /** |
| | | * 消息发送方工厂(短信/微信公众号)。 |
| | | * <p> |
| | | * 原来短信与公众号的发送逻辑在本类里用 {@code if (active.equals("xx"))} 硬分支了约 300 行, |
| | | * 现已收口到 {@code ruoyi-message} 模块,这里只负责按 {@code active} 路由。 |
| | | */ |
| | | @Autowired |
| | | private SmsSenderFactory smsSenderFactory; |
| | | |
| | | @Autowired |
| | | private WechatSenderFactory wechatSenderFactory; |
| | | |
| | | @Value("${spring.profiles.active}") |
| | | private String active; |
| | | @Autowired |
| | | private SysConfigMapper sysConfigMapper; |
| | | |
| | | @Autowired |
| | | private IServiceTaskScheduleService serviceTaskScheduleService; |
| | | |
| | | public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) { |
| | | System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i)); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 定时发送SLTD满意度异常信息 |
| | | */ |
| | | public void sendSLTDMYDExceptInfo() { |
| | | //1.获取发送人工号 |
| | | ServiceSubtaskDetailTraceVO subtaskDetailTraceVO = new ServiceSubtaskDetailTraceVO(); |
| | | subtaskDetailTraceVO.setHandleFlag("0"); |
| | | List<ServiceSubtaskDetailTrace> serviceSubtaskDetailTraces = serviceSubtaskDetailTraceService.selectServiceSubtaskDetailTtraceList(subtaskDetailTraceVO); |
| | | // 把 dutyDeptPersonCode 整理到 list 中(如果多个人用逗号分割,则展开并去重) |
| | | List<String> dutyDeptPersonCodeList = serviceSubtaskDetailTraces.stream().map(ServiceSubtaskDetailTrace::getDutyDeptPersonCode).filter(StringUtils::isNotBlank).flatMap(code -> Arrays.stream(code.split(","))).map(String::trim).filter(StringUtils::isNotBlank).distinct() // 去重 |
| | | .collect(Collectors.toList()); |
| | | // 获取“省立同德满意度异常内容提醒" |
| | | SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("sltd.myd.send.except.contont"); |
| | | String exceptContent = sysConfig.getConfigValue(); |
| | | |
| | | // 获取“省立同德满意度异常内容提醒测试人工号” |
| | | String testPerson = sysConfigMapper.checkConfigKeyUnique("sltd.myd.send.test.person").getConfigValue(); |
| | | if (StringUtils.isNotBlank(testPerson)) { |
| | | String[] split = testPerson.split(","); |
| | | if (CollectionUtils.isEmpty(dutyDeptPersonCodeList)) dutyDeptPersonCodeList = Arrays.asList(split); |
| | | else dutyDeptPersonCodeList.addAll(Arrays.asList(split)); |
| | | } |
| | | |
| | | if (CollectionUtils.isNotEmpty(dutyDeptPersonCodeList)) { |
| | | for (String dutyDeptPersonCode : dutyDeptPersonCodeList) { |
| | | log.info("【sendSLTDMYDExceptInfo】发送人工号:{}", dutyDeptPersonCode); |
| | | String data = MessageSend.sendMsg(exceptContent, "7", dutyDeptPersonCode, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 定时发送异常信息-景宁 |
| | | */ |
| | | public void sendExceptInfo() throws Exception { |
| | | //1.获取发送人工号 |
| | | ServiceSubtaskDetailTraceVO subtaskDetailTraceVO = new ServiceSubtaskDetailTraceVO(); |
| | | subtaskDetailTraceVO.setHandleFlag("0"); |
| | | List<ServiceSubtaskDetailTrace> serviceSubtaskDetailTraces = |
| | | serviceSubtaskDetailTraceService.selectServiceSubtaskDetailTtraceList(subtaskDetailTraceVO); |
| | | // 获取“异常内容提醒" |
| | | SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("send.except.content"); |
| | | String exceptContent = sysConfig.getConfigValue(); |
| | | |
| | | // 获取“内容提醒测试人工号” |
| | | SysConfig testPersonConfig = sysConfigMapper.checkConfigKeyUnique("send.test.person"); |
| | | String testPerson = ObjectUtils.isNotEmpty(testPersonConfig) ? testPersonConfig.getConfigValue() : ""; |
| | | if(CollectionUtils.isNotEmpty(serviceSubtaskDetailTraces)){ |
| | | for (ServiceSubtaskDetailTrace serviceSubtaskDetailTrace : serviceSubtaskDetailTraces) { |
| | | List<String> dutyDeptPersonCodeList = new ArrayList<>(); |
| | | // 把 dutyDeptPersonCode 整理到 list 中(如果多个人用逗号分割,则展开并去重) |
| | | String dutyDeptPersonCodeStr = serviceSubtaskDetailTrace.getDutyDeptPersonCode(); |
| | | if(StringUtils.isNotEmpty(dutyDeptPersonCodeStr)){ |
| | | String[] dutyDeptPersonCodeSplit = dutyDeptPersonCodeStr.split(","); |
| | | if (CollectionUtils.isEmpty(dutyDeptPersonCodeList)) { |
| | | dutyDeptPersonCodeList = new ArrayList<>(Arrays.asList(dutyDeptPersonCodeSplit)); |
| | | } else { |
| | | dutyDeptPersonCodeList.addAll(Arrays.asList(dutyDeptPersonCodeSplit)); |
| | | } |
| | | |
| | | } |
| | | |
| | | if (StringUtils.isNotBlank(testPerson)) { |
| | | String[] split = testPerson.split(","); |
| | | if (CollectionUtils.isEmpty(dutyDeptPersonCodeList)) { |
| | | dutyDeptPersonCodeList = new ArrayList<>(Arrays.asList(split)); |
| | | } else { |
| | | dutyDeptPersonCodeList.addAll(Arrays.asList(split)); |
| | | } |
| | | } |
| | | |
| | | if (CollectionUtils.isNotEmpty(dutyDeptPersonCodeList)) { |
| | | for (String dutyDeptPersonCode : dutyDeptPersonCodeList) { |
| | | log.info("【sendExceptInfo】发送人工号:{}", dutyDeptPersonCode); |
| | | SysUser sysUser = new SysUser(); |
| | | SysUser sysUserVo = new SysUser(); |
| | | sysUserVo.setUserName(dutyDeptPersonCode); |
| | | sysUserVo.setOrgid(serviceSubtaskDetailTrace.getOrgid()); |
| | | List<SysUser> users = userService.getUserList(sysUserVo); |
| | | String phoneNumber = ""; |
| | | if(ObjectUtils.isNotEmpty(users)){ |
| | | sysUser = users.get(0); |
| | | phoneNumber = sysUser.getPhonenumber(); |
| | | }else { |
| | | continue; |
| | | } |
| | | |
| | | if(StringUtils.isEmpty(phoneNumber)){ |
| | | continue; |
| | | } |
| | | String dutyDeptPerson = dingTalkService.getDingTalkUserByMobile(phoneNumber); |
| | | if(StringUtils.isNotBlank(dutyDeptPerson)){ |
| | | String context = TemplateUtils.render(exceptContent, serviceSubtaskDetailTrace); |
| | | dingTalkService.sendDingTalkText(context, dutyDeptPerson); |
| | | //更新状态 |
| | | serviceSubtaskDetailTrace.setHandleFlag("1"); |
| | | serviceSubtaskDetailTrace.setUpdateTime(new Date()); |
| | | serviceSubtaskDetailTraceService.updateServiceSubtaskDetailTtrace(serviceSubtaskDetailTrace); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 定时处理外链的数据 |
| | | */ |
| | | public void runServiceSubTaskAnswer() { |
| | | serviceSubtaskAnswerService.saveQuestionAnswer(null, 0L); |
| | | } |
| | | |
| | | /** |
| | | * 获取预约停诊记录信息 |
| | | * |
| | | */ |
| | | public void getAppointRecordInfo() { |
| | | LocalDateTime tomorrowStart = LocalDate.now().plusDays(1).atStartOfDay(); // 明天 00:00:00 |
| | | LocalDateTime tomorrowEnd = tomorrowStart.plusDays(1).minusSeconds(1); // 明天 23:59:59 |
| | | ixhGatherPatArchiveService.getAppointRecordInfo("2", tomorrowStart, tomorrowEnd); |
| | | } |
| | | |
| | | /** |
| | | * 每月15号和月底,查询并处理 HIS 数据(同德,南华) |
| | | */ |
| | | public void dealHisDataHalfMonth() { |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
| | | String formattedNow = now.format(formatter); |
| | | dealHisData(formattedNow); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * HIS数据采集 |
| | | * 定时任务:采集患者信息、出院、门诊数据 |
| | | * todayZeroStr 当前的时间 例如:2026-05-06 00:00:00 |
| | | */ |
| | | public void dealHisData() { |
| | | public void dealHisData(String todayZeroStr) { |
| | | log.info("【dealHisData】开始执行HIS数据采集任务"); |
| | | // 获取昨天0点到今天0点的时间范围 |
| | | LocalDateTime todayZero = LocalDateTime.now().with(LocalTime.MIN); |
| | | if (StringUtils.isNotBlank(todayZeroStr)) { |
| | | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
| | | todayZero = LocalDateTime.parse(todayZeroStr, formatter); |
| | | } |
| | | LocalDateTime yesterdayZero = todayZero.minusDays(1); |
| | | log.info("【dealHisData】采集时间范围:{} ~ {}", yesterdayZero, todayZero); |
| | | |
| | |
| | | } catch (Exception e) { |
| | | log.error("【dealHisData】河南数据采集异常", e); |
| | | } |
| | | } else if (active.trim().equals("sltd")) { |
| | | } else if (active.trim().equals("sltd") || active.trim().equals("nhfy")) { |
| | | //省立同德与华南附一用同一套采集方法(都是来未来提供) |
| | | |
| | | try { |
| | | //获取是否需要采集用户、部门信息 |
| | | SysConfig config = new SysConfig(); |
| | |
| | | aBoolean = serviceSLTDHealthcareRecordService.queryHospUserInfoList(reqVO); |
| | | } |
| | | log.info("【dealHisData】省立同德“用户信息”结束采集数据:{}", aBoolean); |
| | | //疾病字典采集 |
| | | iIcd10Service.queryAddIcd10(); |
| | | } |
| | | |
| | | // 省立同德数据采集 |
| | | for (Long cid : campusidList) { |
| | | log.debug("【dealHisData】省立同德“预入院”开始采集数据,院区:{}", cid); |
| | | String yesterday = LocalDate.now().minusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); |
| | | String nowTime = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); |
| | | ServiceSLTDInhospReqVO serviceSLTDInhospReqVO = new ServiceSLTDInhospReqVO(); |
| | | serviceSLTDInhospReqVO.setOrgId("20001001"); |
| | | serviceSLTDInhospReqVO.setCampusId(cid); |
| | | serviceSLTDInhospReqVO.setEncounterTimeStart(nowTime); |
| | | serviceSLTDInhospReqVO.setEncounterTimeEnd(nowTime); |
| | | List<String> list = new ArrayList<>(); |
| | | // list.add("FH0108.02"); |
| | | list.add("FH0109.22"); |
| | | list.add("FH0109.23"); |
| | | list.add("FH0109.53"); |
| | | serviceSLTDInhospReqVO.setStatusList(list); |
| | | log.debug("【dealHisData】省立同德“预入院”采集数据入参:{}", serviceSLTDInhospReqVO); |
| | | serviceSLTDHealthcareRecordService.queryHealthcareRecordList(serviceSLTDInhospReqVO); |
| | | log.debug("【dealHisData】省立同德“预入院”結束采集数据,院区:{}", cid); |
| | | // nhfy/sltd回溯30天分段采集判断(每段最多4天) |
| | | boolean isBackfill = (active.trim().equals("nhfy") || active.trim().equals("sltd")) && StringUtils.isNotBlank(todayZeroStr); |
| | | if (isBackfill) { |
| | | DateTimeFormatter dateFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd"); |
| | | LocalDate segStart = todayZero.toLocalDate().minusDays(30); |
| | | LocalDate segEndDate = todayZero.toLocalDate(); |
| | | int segCount = 0; |
| | | while (!segStart.isAfter(segEndDate)) { |
| | | LocalDate segEnd = segStart.plusDays(3); |
| | | if (segEnd.isAfter(segEndDate)) { |
| | | segEnd = segEndDate; |
| | | } |
| | | segCount++; |
| | | log.info("【dealHisData】{}回溯第{}段采集,日期范围:{} ~ {}", active.trim(), segCount, segStart.format(dateFmt), segEnd.format(dateFmt)); |
| | | for (Long cid : campusidList) { |
| | | collectSegmentData(cid, segStart.format(dateFmt), segEnd.format(dateFmt)); |
| | | } |
| | | segStart = segEnd.plusDays(1); |
| | | } |
| | | log.info("【dealHisData】{}回溯采集完成,共{}段", active.trim(), segCount); |
| | | } |
| | | |
| | | log.debug("【dealHisData】省立同德“入院”开始采集数据,院区:{}", cid); |
| | | serviceSLTDInhospReqVO.setOrgId("20001001"); |
| | | serviceSLTDInhospReqVO.setCampusId(cid); |
| | | serviceSLTDInhospReqVO.setEncounterTimeStart(null); |
| | | serviceSLTDInhospReqVO.setEncounterTimeEnd(null); |
| | | serviceSLTDInhospReqVO.setStartHeadTime(nowTime); |
| | | serviceSLTDInhospReqVO.setStartTailTime(nowTime); |
| | | List<String> listStr = new ArrayList<>(); |
| | | // list.add("FH0108.02"); |
| | | listStr.add("FH0109.26"); |
| | | serviceSLTDInhospReqVO.setStatusList(listStr); |
| | | log.debug("【dealHisData】省立同德“入院”采集数据入参:{}", serviceSLTDInhospReqVO); |
| | | serviceSLTDHealthcareRecordService.queryHealthcareRecordList(serviceSLTDInhospReqVO); |
| | | log.debug("【dealHisData】省立同德“入院”結束采集数据,院区:{}", cid); |
| | | // 省立同德数据采集(非回溯模式才执行) |
| | | if (!isBackfill) { |
| | | for (Long cid : campusidList) { |
| | | log.info("【dealHisData】省立同德预入院开始采集数据,院区:{}", cid); |
| | | String nowTime = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); |
| | | ServiceSLTDInhospReqVO serviceSLTDInhospReqVO = new ServiceSLTDInhospReqVO(); |
| | | serviceSLTDInhospReqVO.setOrgId("20001001"); |
| | | serviceSLTDInhospReqVO.setCampusId(cid); |
| | | serviceSLTDInhospReqVO.setEncounterTimeStart(nowTime); |
| | | serviceSLTDInhospReqVO.setEncounterTimeEnd(nowTime); |
| | | if (active.trim().equals("nhfy")) { |
| | | serviceSLTDInhospReqVO.setEncounterTimeStart(yesterdayZero.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); |
| | | serviceSLTDInhospReqVO.setEncounterTimeEnd(todayZero.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); |
| | | } |
| | | List<String> list = new ArrayList<>(); |
| | | list.add("FH0109.22"); |
| | | list.add("FH0109.23"); |
| | | list.add("FH0109.53"); |
| | | serviceSLTDInhospReqVO.setStatusList(list); |
| | | // log.info("【dealHisData】省立同德“预入院”采集数据入参:{}", serviceSLTDInhospReqVO); |
| | | serviceSLTDHealthcareRecordService.queryHealthcareRecordList(serviceSLTDInhospReqVO); |
| | | log.info("【dealHisData】省立同德“预入院”結束采集数据,院区:{}", cid); |
| | | |
| | | log.debug("【dealHisData】省立同德“门急诊”开始采集数据,院区:{}", cid); |
| | | serviceSLTDInhospReqVO.setStartHeadTime(null); |
| | | serviceSLTDInhospReqVO.setStartTailTime(null); |
| | | serviceSLTDInhospReqVO.setEncounterTimeStart(nowTime); |
| | | serviceSLTDInhospReqVO.setEncounterTimeEnd(nowTime); |
| | | serviceSLTDInhospReqVO.setStatusList(null); |
| | | List<String> list1 = new ArrayList<>(); |
| | | list1.add("FH0108.01"); |
| | | list1.add("FH0108.03"); |
| | | serviceSLTDInhospReqVO.setHealthcareRecordTypeList(list1); |
| | | log.debug("【dealHisData】省立同德“门急诊”采集数据入参:{}", serviceSLTDInhospReqVO); |
| | | serviceSLTDHealthcareRecordService.queryHealthcareRecordList(serviceSLTDInhospReqVO); |
| | | log.debug("【dealHisData】省立同德“门急诊”束采集数据,院区:{}", cid); |
| | | log.info("【dealHisData】省立同德“入院”开始采集数据,院区:{}", cid); |
| | | serviceSLTDInhospReqVO.setOrgId("20001001"); |
| | | serviceSLTDInhospReqVO.setCampusId(cid); |
| | | serviceSLTDInhospReqVO.setEncounterTimeStart(null); |
| | | serviceSLTDInhospReqVO.setEncounterTimeEnd(null); |
| | | serviceSLTDInhospReqVO.setStartHeadTime(nowTime); |
| | | serviceSLTDInhospReqVO.setStartTailTime(nowTime); |
| | | if (active.trim().equals("nhfy")) { |
| | | serviceSLTDInhospReqVO.setStartHeadTime(yesterdayZero.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); |
| | | serviceSLTDInhospReqVO.setStartTailTime(todayZero.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); |
| | | } |
| | | List<String> listStr = new ArrayList<>(); |
| | | listStr.add("FH0109.24"); |
| | | serviceSLTDInhospReqVO.setStatusList(listStr); |
| | | // log.info("【dealHisData】省立同德“入院”采集数据入参:{}", serviceSLTDInhospReqVO); |
| | | serviceSLTDHealthcareRecordService.queryHealthcareRecordList(serviceSLTDInhospReqVO); |
| | | log.info("【dealHisData】省立同德“入院”結束采集数据,院区:{}", cid); |
| | | |
| | | log.debug("【dealHisData】省立同德“出院”开始采集数据,院区:{}", cid); |
| | | serviceSLTDInhospReqVO.setStartHeadTime(null); |
| | | serviceSLTDInhospReqVO.setStartTailTime(null); |
| | | serviceSLTDInhospReqVO.setEncounterTimeStart(null); |
| | | serviceSLTDInhospReqVO.setEncounterTimeEnd(null); |
| | | serviceSLTDInhospReqVO.setPreOutHospitalHeadDate(nowTime); |
| | | serviceSLTDInhospReqVO.setPreOutHospitalTailDate(nowTime); |
| | | serviceSLTDInhospReqVO.setHealthcareRecordTypeList(null); |
| | | List<String> list2 = new ArrayList<>(); |
| | | log.info("【dealHisData】省立同德“出院”开始采集数据,院区:{}", cid); |
| | | serviceSLTDInhospReqVO.setStartHeadTime(null); |
| | | serviceSLTDInhospReqVO.setStartTailTime(null); |
| | | serviceSLTDInhospReqVO.setEncounterTimeStart(null); |
| | | serviceSLTDInhospReqVO.setEncounterTimeEnd(null); |
| | | serviceSLTDInhospReqVO.setPreOutHospitalHeadDate(nowTime); |
| | | serviceSLTDInhospReqVO.setPreOutHospitalTailDate(nowTime); |
| | | if (active.trim().equals("nhfy")) { |
| | | serviceSLTDInhospReqVO.setPreOutHospitalHeadDate(yesterdayZero.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); |
| | | serviceSLTDInhospReqVO.setPreOutHospitalTailDate(todayZero.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); |
| | | } |
| | | serviceSLTDInhospReqVO.setHealthcareRecordTypeList(null); |
| | | List<String> list2 = new ArrayList<>(); |
| | | // list2.add("FH0108.02"); |
| | | list2.add("FH0109.27"); |
| | | serviceSLTDInhospReqVO.setStatusList(list2); |
| | | log.debug("【dealHisData】省立同德“出院”采集数据入参:{}", serviceSLTDInhospReqVO); |
| | | serviceSLTDHealthcareRecordService.queryHealthcareRecordList(serviceSLTDInhospReqVO); |
| | | log.debug("【dealHisData】省立同德“出院”束采集数据,院区:{}", cid); |
| | | //出院 |
| | | list2.add("FH0109.27"); |
| | | //预出院 |
| | | list2.add("FH0109.25"); |
| | | serviceSLTDInhospReqVO.setStatusList(list2); |
| | | // log.info("【dealHisData】省立同德“出院”采集数据入参:{}", serviceSLTDInhospReqVO); |
| | | serviceSLTDHealthcareRecordService.queryHealthcareRecordList(serviceSLTDInhospReqVO); |
| | | log.info("【dealHisData】省立同德“出院”束采集数据,院区:{}", cid); |
| | | } |
| | | } |
| | | // SysConfig sysConfig = sysConfigs.get(0); |
| | | // sysConfig.setConfigValue(nowTime); |
| | | // configService.updateConfig(sysConfig); |
| | | |
| | | } catch (Exception e) { |
| | | log.error("【dealHisData】省立同德数据采集异常", e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 分段采集数据(预入院、入院、出院) |
| | | * 用于nhfy/sltd回溯采集,每次最多查询7天数据 |
| | | * |
| | | * @param cid 院区ID |
| | | * @param startDateStr 日期开始 yyyy-MM-dd |
| | | * @param endDateStr 日期结束 yyyy-MM-dd |
| | | */ |
| | | private void collectSegmentData(Long cid, String startDateStr, String endDateStr) { |
| | | // // 预入院 |
| | | log.info("【dealHisData】预入院开始采集数据,院区:{},时间段:{} ~ {}", cid, startDateStr, endDateStr); |
| | | ServiceSLTDInhospReqVO reqVO = new ServiceSLTDInhospReqVO(); |
| | | reqVO.setOrgId("20001001"); |
| | | reqVO.setCampusId(cid); |
| | | reqVO.setEncounterTimeStart(startDateStr); |
| | | reqVO.setEncounterTimeEnd(endDateStr); |
| | | List<String> preAdmitStatus = new ArrayList<>(); |
| | | preAdmitStatus.add("FH0109.22"); |
| | | preAdmitStatus.add("FH0109.23"); |
| | | preAdmitStatus.add("FH0109.53"); |
| | | reqVO.setStatusList(preAdmitStatus); |
| | | log.info("【dealHisData】预入院采集数据入参:{}", reqVO); |
| | | try { |
| | | serviceSLTDHealthcareRecordService.queryHealthcareRecordList(reqVO); |
| | | } catch (Exception e) { |
| | | // 单类数据失败不再向上抛:否则一段失败会让本次回溯后面的所有段都不执行 |
| | | log.error("【dealHisData】预入院采集失败,跳过该类继续。院区:{},时间段:{} ~ {}", cid, startDateStr, endDateStr, e); |
| | | } |
| | | log.info("【dealHisData】预入院終了采集数据,院区:{}", cid); |
| | | |
| | | // 入院 |
| | | log.info("【dealHisData】入院开始采集数据,院区:{},时间段:{} ~ {}", cid, startDateStr, endDateStr); |
| | | reqVO = new ServiceSLTDInhospReqVO(); |
| | | reqVO.setOrgId("20001001"); |
| | | reqVO.setCampusId(cid); |
| | | reqVO.setStartHeadTime(startDateStr); |
| | | reqVO.setStartTailTime(endDateStr); |
| | | List<String> admitStatus = new ArrayList<>(); |
| | | admitStatus.add("FH0109.24"); |
| | | reqVO.setStatusList(admitStatus); |
| | | log.info("【dealHisData】入院采集数据入参:{}", reqVO); |
| | | try { |
| | | serviceSLTDHealthcareRecordService.queryHealthcareRecordList(reqVO); |
| | | } catch (Exception e) { |
| | | // 单类数据失败不再向上抛:否则一段失败会让本次回溯后面的所有段都不执行 |
| | | log.error("【dealHisData】入院采集失败,跳过该类继续。院区:{},时间段:{} ~ {}", cid, startDateStr, endDateStr, e); |
| | | } |
| | | log.info("【dealHisData】入院終了采集数据,院区:{}", cid); |
| | | |
| | | // 出院 |
| | | log.info("【dealHisData】出院开始采集数据,院区:{},时间段:{} ~ {}", cid, startDateStr, endDateStr); |
| | | reqVO = new ServiceSLTDInhospReqVO(); |
| | | reqVO.setOrgId("20001001"); |
| | | reqVO.setCampusId(cid); |
| | | reqVO.setPreOutHospitalHeadDate(startDateStr); |
| | | reqVO.setPreOutHospitalTailDate(endDateStr); |
| | | List<String> dischargeStatus = new ArrayList<>(); |
| | | //出院 |
| | | dischargeStatus.add("FH0109.27"); |
| | | //预出院 |
| | | dischargeStatus.add("FH0109.25"); |
| | | reqVO.setStatusList(dischargeStatus); |
| | | log.info("【dealHisData】出院采集数据入参:{}", reqVO); |
| | | try { |
| | | serviceSLTDHealthcareRecordService.queryHealthcareRecordList(reqVO); |
| | | } catch (Exception e) { |
| | | // 单类数据失败不再向上抛:否则一段失败会让本次回溯后面的所有段都不执行 |
| | | log.error("【dealHisData】出院采集失败,跳过该类继续。院区:{},时间段:{} ~ {}", cid, startDateStr, endDateStr, e); |
| | | } |
| | | log.info("【dealHisData】出院结束采集数据,院区:{}", cid); |
| | | } |
| | | |
| | | /** |
| | | * HIS省立同德门急诊数据采集(每半小时一轮,按天全量刷新 + 分页断点续采) |
| | | * <p> |
| | | * HIS 侧门急诊数据只能按天查,每轮拉到的都是当天全量(到晚上一万多条),这个取数成本改不了; |
| | | * 能改的是处理成本:当天数据里绝大多数上一轮已经入库,靠"整页批量查重 + 已入库直接跳过", |
| | | * 每轮真正需要做档案处理和入库的只有新出现的那几百条,一万多条也能在很短时间内跑完。 |
| | | * <p> |
| | | * 每一轮做三件事: |
| | | * <ol> |
| | | * <li>今天的整轮刷新:第 1 页翻到最后一页,只处理新记录;</li> |
| | | * <li>昨天的收尾采集:每天只做一次,兜住 23:xx 之后才写入 HIS 的记录和跨零点的数据;</li> |
| | | * <li>手工回溯:任务入参给了日期时,把那天补采一次(同一天只补一次,不会每轮重复补)。</li> |
| | | * </ol> |
| | | * 每轮最多跑 {@link MzGatherParamEnum#BUDGET_MINUTES}(默认 20 分钟),到点保存页码断点正常退出,下一轮从该页继续; |
| | | * 每采完一页都会落一次断点,进程被杀也不会从头重扫。 |
| | | * <p> |
| | | * 失败一律不阻断采集进度: |
| | | * <ul> |
| | | * <li>数据层失败(转换/档案/入库)落 pat_med_outhosp_error 表交人工处理;</li> |
| | | * <li>接口层失败(网络超时、来未来返回非 200)没有数据可落表,只写 error 日志(含院区、日期、页码、接口入参), |
| | | * 跳过该页继续往后采,本轮结束时把断点留在第一个失败页;因为每轮都是整天从头全量刷新, |
| | | * 被跳过的页下一轮必然重新采到,不会漏;</li> |
| | | * <li>连续 {@link MzGatherConstant#MAX_CONSECUTIVE_PAGE_FAIL} 页接口失败判定为接口整体不可用,本轮提前结束(避免空转几十页), |
| | | * 断点留在第一个失败页,下一轮再试。</li> |
| | | * </ul> |
| | | * |
| | | * @param todayZeroStr 可选,手工指定要一次性回溯的日期,支持 yyyy-MM-dd HH:mm:ss 或 yyyy-MM-dd;为空时只刷新今天+收尾昨天 |
| | | */ |
| | | public void dealHisDataMZ(String todayZeroStr) { |
| | | log.info("【dealHisDataMZ】门急诊数据采集开始,入参:{}", todayZeroStr); |
| | | // 南华附一的先不采门急诊数据 |
| | | if (!"sltd".equals(active.trim())) { |
| | | log.info("【dealHisDataMZ】当前环境 {} 不采集门急诊数据,跳过", active.trim()); |
| | | return; |
| | | } |
| | | |
| | | long budgetMinutes = getMzConfigLong(MzGatherParamEnum.BUDGET_MINUTES); |
| | | long pageSize = getMzConfigLong(MzGatherParamEnum.PAGE_SIZE); |
| | | long maxPage = getMzConfigLong(MzGatherParamEnum.MAX_PAGE); |
| | | |
| | | LocalDateTime now = LocalDateTime.now().withNano(0); |
| | | LocalDateTime deadline = now.plusMinutes(budgetMinutes); |
| | | |
| | | // 获取省立同德要采集的院区 |
| | | SysConfig cf = new SysConfig(); |
| | | cf.setConfigKey("sltd.gather.campusid"); |
| | | cf.setOrgid(MzGatherConstant.ORG_ID); |
| | | List<SysConfig> sysConfigcampusids = configService.selectConfigList(cf); |
| | | if (CollectionUtils.isEmpty(sysConfigcampusids) || StringUtils.isBlank(sysConfigcampusids.get(0).getConfigValue())) { |
| | | log.error("【dealHisDataMZ】SysConfig中的院区ID为空!"); |
| | | return; |
| | | } |
| | | List<Long> campusidList = Arrays.stream(sysConfigcampusids.get(0).getConfigValue().split(",")) |
| | | .map(String::trim).filter(StringUtils::isNotBlank).map(Long::valueOf).collect(Collectors.toList()); |
| | | log.info("【dealHisDataMZ】院区:{},单轮预算:{}分钟,每页:{}条", campusidList, budgetMinutes, pageSize); |
| | | |
| | | for (Long cid : campusidList) { |
| | | if (LocalDateTime.now().isAfter(deadline)) { |
| | | log.warn("【dealHisDataMZ】本轮时间预算已用尽,剩余院区留到下一轮继续,院区:{}", cid); |
| | | break; |
| | | } |
| | | // 防止上一轮还没跑完就被下一轮叠着跑(预算配置过大、或手工触发时兜底) |
| | | String lockKey = "mzRunning:" + cid; |
| | | if (Boolean.TRUE.equals(redisCache.hasKey(lockKey))) { |
| | | log.warn("【dealHisDataMZ】该院区上一轮采集尚未结束,本轮跳过,院区:{}(断点已保留,下一轮继续)", cid); |
| | | continue; |
| | | } |
| | | redisCache.setCacheObject(lockKey, MzGatherConstant.MINUTE_FMT.format(now), budgetMinutes * 2, TimeUnit.MINUTES); |
| | | try { |
| | | collectOutpatientDaily(cid, todayZeroStr, pageSize, maxPage, deadline); |
| | | } catch (Exception e) { |
| | | log.error("【dealHisDataMZ】门急诊采集异常,院区:{},断点已保留,下一轮从断点继续", cid, e); |
| | | } finally { |
| | | redisCache.deleteObject(lockKey); |
| | | } |
| | | } |
| | | log.info("【dealHisDataMZ】门急诊采集本轮结束,耗时:{}秒", |
| | | java.time.Duration.between(now, LocalDateTime.now()).getSeconds()); |
| | | } |
| | | |
| | | /** |
| | | * 采集某个院区:先补一次性回溯日期,再做昨天的收尾采集,最后整轮刷新今天 |
| | | */ |
| | | private void collectOutpatientDaily(Long cid, String backfillParam, long pageSize, long maxPage, |
| | | LocalDateTime deadline) { |
| | | String cursorKey = "mzDayCursor:" + cid; |
| | | MzDayCursor cursor = loadMzDayCursor(cursorKey); |
| | | LocalDate today = LocalDate.now(); |
| | | LocalDate yesterday = today.minusDays(1); |
| | | |
| | | // 手工入参指定的一次性回溯:同一个日期只补一次,避免定时任务每轮重复补 |
| | | LocalDateTime backfillTime = parseMzStartTime(backfillParam); |
| | | if (backfillTime != null) { |
| | | LocalDate backfillDay = backfillTime.toLocalDate(); |
| | | if (backfillDay.toString().equals(cursor.backfillDone)) { |
| | | log.info("【dealHisDataMZ】入参日期 {} 已完成回溯,跳过,院区:{}", backfillDay, cid); |
| | | } else if (backfillDay.equals(today)) { |
| | | // 入参就是今天,交给下面的整轮刷新处理即可 |
| | | cursor.backfillDone = backfillDay.toString(); |
| | | saveMzDayCursor(cursorKey, cursor); |
| | | } else { |
| | | cursor.backfillDay = backfillDay.toString(); |
| | | cursor.backfillPage = 1L; |
| | | saveMzDayCursor(cursorKey, cursor); |
| | | log.info("【dealHisDataMZ】待回溯日期:{},院区:{}", backfillDay, cid); |
| | | } |
| | | } |
| | | |
| | | // 1) 一次性回溯 |
| | | if (cursor.backfillDay != null) { |
| | | LocalDate day = LocalDate.parse(cursor.backfillDay); |
| | | MzGatherStatusEnum status = runDayPass(cid, cursorKey, cursor, MzGatherSlotEnum.BACKFILL, day, |
| | | cursor.backfillPage, pageSize, maxPage, deadline); |
| | | if (status != MzGatherStatusEnum.DONE) { |
| | | log.warn("【dealHisDataMZ】回溯日期 {} 本轮未完成({}),断点已保留,院区:{}", day, status.getDesc(), cid); |
| | | return; |
| | | } |
| | | cursor.backfillDone = cursor.backfillDay; |
| | | cursor.backfillDay = null; |
| | | cursor.backfillPage = 1L; |
| | | saveMzDayCursor(cursorKey, cursor); |
| | | } |
| | | |
| | | // 2) 昨天收尾采集:每天一次,兜住 23:xx 之后才写进 HIS 的记录和跨零点的数据 |
| | | if (!yesterday.toString().equals(cursor.finalDay)) { |
| | | MzGatherStatusEnum status = runDayPass(cid, cursorKey, cursor, MzGatherSlotEnum.FINAL, yesterday, |
| | | cursor.finalPage, pageSize, maxPage, deadline); |
| | | if (status != MzGatherStatusEnum.DONE) { |
| | | log.warn("【dealHisDataMZ】昨日收尾采集 {} 本轮未完成({}),断点已保留,院区:{}", yesterday, status.getDesc(), cid); |
| | | return; |
| | | } |
| | | cursor.finalDay = yesterday.toString(); |
| | | cursor.finalPage = 1L; |
| | | saveMzDayCursor(cursorKey, cursor); |
| | | } |
| | | |
| | | // 3) 今天的整轮刷新 |
| | | if (!today.toString().equals(cursor.day)) { |
| | | cursor.day = today.toString(); |
| | | cursor.page = 1L; |
| | | saveMzDayCursor(cursorKey, cursor); |
| | | } |
| | | MzGatherStatusEnum status = runDayPass(cid, cursorKey, cursor, MzGatherSlotEnum.TODAY, today, |
| | | cursor.page, pageSize, maxPage, deadline); |
| | | if (status == MzGatherStatusEnum.DONE) { |
| | | // 整轮刷新完成,页码归位,下一轮重新从第一页整轮刷新 |
| | | cursor.page = 1L; |
| | | saveMzDayCursor(cursorKey, cursor); |
| | | log.info("【dealHisDataMZ】{} 门急诊整轮刷新完成,院区:{}", today, cid); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 全量采集某一天(分页直到采完),每采完一页立即落一次断点 |
| | | * |
| | | * @param slot 断点槽位(今天整轮刷新 / 昨日收尾 / 手工回溯),决定中断页码记到哪个字段 |
| | | * @return {@link MzGatherStatusEnum} 采集完成 / 预算用尽 / 接口连续失败提前结束 |
| | | */ |
| | | private MzGatherStatusEnum runDayPass(Long cid, String cursorKey, MzDayCursor cursor, MzGatherSlotEnum slot, |
| | | LocalDate day, long startPage, long pageSize, long maxPage, |
| | | LocalDateTime deadline) { |
| | | String startStr = day.format(MzGatherConstant.DAY_FMT); |
| | | String endStr = day.plusDays(1).format(MzGatherConstant.DAY_FMT); |
| | | |
| | | ServiceSLTDInhospReqVO reqVO = new ServiceSLTDInhospReqVO(); |
| | | reqVO.setOrgId(MzGatherConstant.ORG_ID); |
| | | reqVO.setCampusId(cid); |
| | | reqVO.setStartHeadTime(null); |
| | | reqVO.setStartTailTime(null); |
| | | reqVO.setStatusList(null); |
| | | // 接口只支持按天查,起止都传日期 |
| | | reqVO.setEncounterTimeStart(startStr); |
| | | reqVO.setEncounterTimeEnd(endStr); |
| | | List<String> typeList = new ArrayList<>(); |
| | | typeList.add("FH0108.01"); // 门诊 |
| | | typeList.add("FH0108.03"); // 急诊 |
| | | reqVO.setHealthcareRecordTypeList(typeList); |
| | | reqVO.setSize(pageSize); |
| | | |
| | | long page = startPage < 1L ? 1L : startPage; |
| | | long total = 0L; |
| | | long pageCount = 0L; |
| | | // 接口层失败跳过的页数、第一个失败页、连续失败页数 |
| | | long skippedPages = 0L; |
| | | long firstFailPage = -1L; |
| | | int consecutiveFail = 0; |
| | | while (true) { |
| | | if (LocalDateTime.now().isAfter(deadline)) { |
| | | // 本轮有跳过的页时,断点回到第一个失败页,保证下一轮先把这些页补回来 |
| | | long resumePage = (skippedPages > 0 && firstFailPage > 0) ? firstFailPage : page; |
| | | saveMzDayPage(cursorKey, cursor, slot, resumePage); |
| | | log.warn("【dealHisDataMZ】本轮时间预算用尽,{} 本轮已采{}页{}条{},下次从第{}页继续,院区:{}", |
| | | day, pageCount, total, skippedLog(skippedPages), resumePage, cid); |
| | | return MzGatherStatusEnum.BUDGET_UP; |
| | | } |
| | | reqVO.setCurrent(page); |
| | | log.info("【dealHisDataMZ】采集门急诊数据,院区:{},日期:{},第{}页", cid, day, page); |
| | | |
| | | List<ServiceSLTDInhospResDTO> pageData = null; |
| | | Exception lastError = null; |
| | | for (int attempt = 1; attempt <= MzGatherConstant.PAGE_RETRY_TIMES; attempt++) { |
| | | try { |
| | | pageData = serviceSLTDHealthcareRecordService.queryHealthcareRecordList(reqVO); |
| | | lastError = null; |
| | | break; |
| | | } catch (Exception e) { |
| | | lastError = e; |
| | | log.error("【dealHisDataMZ】门急诊第{}页第{}次采集失败,院区:{},日期:{}", page, attempt, cid, day, e); |
| | | if (attempt < MzGatherConstant.PAGE_RETRY_TIMES) { |
| | | try { |
| | | Thread.sleep(MzGatherConstant.PAGE_RETRY_SLEEP_MS); |
| | | } catch (InterruptedException ie) { |
| | | Thread.currentThread().interrupt(); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | if (lastError != null) { |
| | | // 接口层失败(网络超时 / 来未来返回非200)没有数据可以落表,只写 error 日志,然后跳过该页继续往后采,不阻断进度。 |
| | | // 每轮都是整天从头全量刷新,被跳过的页下一轮必然重新采到,所以不会漏数据。 |
| | | skippedPages++; |
| | | if (firstFailPage < 0) { |
| | | firstFailPage = page; |
| | | } |
| | | consecutiveFail++; |
| | | log.error("【dealHisDataMZ】接口层采集失败,已记 error 日志并跳过该页,本轮继续往后采;" |
| | | + "院区:{},日期:{},第{}页,接口入参:{},异常:{}", |
| | | cid, day, page, new Gson().toJson(reqVO), lastError.getMessage(), lastError); |
| | | if (consecutiveFail >= MzGatherConstant.MAX_CONSECUTIVE_PAGE_FAIL) { |
| | | // 连续多页接口失败,判定来未来接口整体不可用,本轮提前结束(不再空转后面几十页),断点留在第一个失败页 |
| | | log.error("【dealHisDataMZ】连续{}页接口失败,判定来未来接口不可用,本轮提前结束;院区:{},日期:{},断点保留在第{}页", |
| | | consecutiveFail, cid, day, firstFailPage); |
| | | saveMzDayPage(cursorKey, cursor, slot, firstFailPage); |
| | | return MzGatherStatusEnum.FAILED; |
| | | } |
| | | page++; |
| | | continue; |
| | | } |
| | | consecutiveFail = 0; |
| | | |
| | | if (CollectionUtils.isEmpty(pageData)) { |
| | | log.info("【dealHisDataMZ】{} 第{}页无数据,采集结束,院区:{},本轮{}页共{}条{}", |
| | | day, page, cid, pageCount, total, skippedLog(skippedPages)); |
| | | return MzGatherStatusEnum.DONE; |
| | | } |
| | | pageCount++; |
| | | total += pageData.size(); |
| | | log.info("【dealHisDataMZ】{} 第{}页采集{}条,院区:{}", day, page, pageData.size(), cid); |
| | | if (pageData.size() < pageSize) { |
| | | log.info("【dealHisDataMZ】{} 采集结束,院区:{},本轮{}页共{}条{}", day, cid, pageCount, total, skippedLog(skippedPages)); |
| | | return MzGatherStatusEnum.DONE; |
| | | } |
| | | page++; |
| | | if (page > maxPage) { |
| | | log.error("【dealHisDataMZ】{} 已翻到第{}页,超过单日上限{}页,按采完处理,院区:{}", day, page, maxPage, cid); |
| | | return MzGatherStatusEnum.DONE; |
| | | } |
| | | // 每采完一页立即落断点:进程被重启/强杀也能从这一页继续,不从头重扫,也不会漏 |
| | | saveMzDayPage(cursorKey, cursor, slot, page); |
| | | } |
| | | } |
| | | |
| | | /** 按槽位保存中断页码 */ |
| | | private void saveMzDayPage(String cursorKey, MzDayCursor cursor, MzGatherSlotEnum slot, long page) { |
| | | if (slot == MzGatherSlotEnum.FINAL) { |
| | | cursor.finalPage = page; |
| | | } else if (slot == MzGatherSlotEnum.BACKFILL) { |
| | | cursor.backfillPage = page; |
| | | } else { |
| | | cursor.page = page; |
| | | } |
| | | saveMzDayCursor(cursorKey, cursor); |
| | | } |
| | | |
| | | /** 日志里提示接口层失败跳过了几页(下一轮整轮刷新会重新采到) */ |
| | | private String skippedLog(long skippedPages) { |
| | | return skippedPages > 0L ? ",接口失败跳过" + skippedPages + "页(下一轮整轮刷新会重新采到)" : ""; |
| | | } |
| | | |
| | | /** 读取 sys_config 里的门急诊采集参数(机构 orgid=20001001),取不到或超范围时用枚举里的默认值 */ |
| | | private long getMzConfigLong(MzGatherParamEnum param) { |
| | | try { |
| | | SysConfig config = new SysConfig(); |
| | | config.setConfigKey(param.getKey()); |
| | | config.setOrgid(MzGatherConstant.ORG_ID); |
| | | List<SysConfig> configs = configService.selectConfigList(config); |
| | | if (CollectionUtils.isNotEmpty(configs) && StringUtils.isNotBlank(configs.get(0).getConfigValue())) { |
| | | long value = Long.parseLong(configs.get(0).getConfigValue().trim()); |
| | | if (value >= param.getMin() && value <= param.getMax()) { |
| | | return value; |
| | | } |
| | | log.warn("【dealHisDataMZ】配置{}的值{}超出范围[{},{}],使用默认值{}", |
| | | param.getKey(), value, param.getMin(), param.getMax(), param.getDefaultValue()); |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("【dealHisDataMZ】读取配置{}失败,使用默认值{}", param.getKey(), param.getDefaultValue(), e); |
| | | } |
| | | return param.getDefaultValue(); |
| | | } |
| | | |
| | | /** 解析任务入参(要一次性回溯的日期),支持 yyyy-MM-dd HH:mm:ss 与 yyyy-MM-dd */ |
| | | private LocalDateTime parseMzStartTime(String text) { |
| | | LocalDateTime time = parseMzTimeQuiet(text); |
| | | if (time == null && StringUtils.isNotBlank(text)) { |
| | | log.warn("【dealHisDataMZ】入参 {} 无法解析为日期,按未指定处理", text); |
| | | } |
| | | return time; |
| | | } |
| | | |
| | | /** 静默解析日期,支持 yyyy-MM-dd HH:mm:ss 与 yyyy-MM-dd,格式不符返回 null(不刷日志) */ |
| | | private LocalDateTime parseMzTimeQuiet(String text) { |
| | | if (StringUtils.isBlank(text)) { |
| | | return null; |
| | | } |
| | | String value = text.trim(); |
| | | try { |
| | | return LocalDateTime.parse(value, MzGatherConstant.MINUTE_FMT); |
| | | } catch (Exception ignore) { |
| | | // 兼容只传日期的情况,落到下面按日期解析 |
| | | } |
| | | try { |
| | | return LocalDate.parse(value).atStartOfDay(); |
| | | } catch (Exception ignore) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** 保存门急诊采集断点(各轮次中断的页码) */ |
| | | private void saveMzDayCursor(String cursorKey, MzDayCursor cursor) { |
| | | JSONObject json = new JSONObject(); |
| | | json.put("day", cursor.day); |
| | | json.put("page", cursor.page); |
| | | json.put("finalDay", cursor.finalDay); |
| | | json.put("finalPage", cursor.finalPage); |
| | | json.put("backfillDay", cursor.backfillDay); |
| | | json.put("backfillPage", cursor.backfillPage); |
| | | json.put("backfillDone", cursor.backfillDone); |
| | | json.put("updateTime", MzGatherConstant.MINUTE_FMT.format(LocalDateTime.now().withNano(0))); |
| | | redisCache.setCacheObject(cursorKey, json.toJSONString(), MzGatherConstant.CACHE_TTL_HOURS, TimeUnit.HOURS); |
| | | } |
| | | |
| | | /** 读取门急诊采集断点,读不到或解析失败时返回空断点(等价于从第一页开始整轮刷新) */ |
| | | private MzDayCursor loadMzDayCursor(String cursorKey) { |
| | | MzDayCursor cursor = new MzDayCursor(); |
| | | try { |
| | | String json = redisCache.getCacheObject(cursorKey); |
| | | if (StringUtils.isBlank(json)) { |
| | | return cursor; |
| | | } |
| | | JSONObject obj = JSONObject.parseObject(json); |
| | | cursor.day = obj.getString("day"); |
| | | cursor.page = obj.getLongValue("page"); |
| | | cursor.finalDay = obj.getString("finalDay"); |
| | | cursor.finalPage = obj.getLongValue("finalPage"); |
| | | cursor.backfillDay = obj.getString("backfillDay"); |
| | | cursor.backfillPage = obj.getLongValue("backfillPage"); |
| | | cursor.backfillDone = obj.getString("backfillDone"); |
| | | if (cursor.page < 1L) { |
| | | cursor.page = 1L; |
| | | } |
| | | if (cursor.finalPage < 1L) { |
| | | cursor.finalPage = 1L; |
| | | } |
| | | if (cursor.backfillPage < 1L) { |
| | | cursor.backfillPage = 1L; |
| | | } |
| | | return cursor; |
| | | } catch (Exception e) { |
| | | log.warn("【dealHisDataMZ】读取断点{}失败,按空断点处理", cursorKey, e); |
| | | return new MzDayCursor(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 门急诊采集断点:每天一轮全量刷新,这里分别记录三个轮次中断在第几页 |
| | | * (页码为 1 表示该轮次下一轮从第一页整轮开始) |
| | | */ |
| | | private static class MzDayCursor { |
| | | /** 今天的整轮刷新 */ |
| | | private String day; |
| | | private long page = 1L; |
| | | /** 昨天的收尾采集 */ |
| | | private String finalDay; |
| | | private long finalPage = 1L; |
| | | /** 待做的一次性回溯 */ |
| | | private String backfillDay; |
| | | private long backfillPage = 1L; |
| | | /** 已经补过的回溯日期,避免定时任务每轮重复补同一天 */ |
| | | private String backfillDone; |
| | | } |
| | | |
| | | /** |
| | | * 同德药品数据采集 |
| | | */ |
| | | |
| | | public void dealHisDataMedication() { |
| | | final long OUTP_PAGE_SIZE = 500L; |
| | | ServiceSLTDInhospReqVO serviceSLTDInhospReqVO = new ServiceSLTDInhospReqVO(); |
| | | serviceSLTDInhospReqVO.setOrgId("20001001"); |
| | | Long outpPage = 1L; |
| | | int totalCount = 0; // 统计总共采集的药品数量 |
| | | |
| | | while (true) { |
| | | serviceSLTDInhospReqVO.setCurrent(outpPage); |
| | | serviceSLTDInhospReqVO.setSize(OUTP_PAGE_SIZE); |
| | | log.info("【dealHisData】省立同德\"药品数据\"采集第{}页数据", outpPage); |
| | | |
| | | try { |
| | | Integer fetchedCount = serviceSLTDHealthcareRecordService.queryMedicationItemList(serviceSLTDInhospReqVO); |
| | | log.info("【dealHisData】省立同德\"药品数据\"第{}页采集到{}条记录", outpPage, fetchedCount); |
| | | |
| | | totalCount += fetchedCount != null ? fetchedCount : 0; |
| | | |
| | | // 如果返回的数据量小于 pageSize,说明已经是最后一页了,退出循环 |
| | | if (fetchedCount == null || fetchedCount < OUTP_PAGE_SIZE) { |
| | | log.info("【dealHisData】省立同德\"药品数据\"采集完成,总页数:{}, 总计{}条记录", outpPage, totalCount); |
| | | break; |
| | | } |
| | | |
| | | outpPage++; |
| | | } catch (Exception e) { |
| | | log.error("【dealHisData】省立同德\"药品数据\"第{}页采集失败", outpPage, e); |
| | | // 出错时也可以选择退出或继续,这里选择退出避免无限重试 |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | public void dealHisDataSLTD() { |
| | | log.info("【dealHisData】开始执行HIS数据采集任务"); |
| | | |
| | | try { |
| | | LocalDate startDate = LocalDate.of(2025, 11, 1); |
| | | LocalDate endDate = LocalDate.of(2026, 2, 27); |
| | | |
| | | // 省立同德数据采集 |
| | | // String nowTime = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); |
| | | for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) { |
| | | String nowTime = date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); |
| | | ServiceSLTDInhospReqVO serviceSLTDInhospReqVO = new ServiceSLTDInhospReqVO(); |
| | | serviceSLTDInhospReqVO.setOrgId("20001001"); |
| | | serviceSLTDInhospReqVO.setCampusId(30001002L); |
| | | log.info("【dealHisData】省立同德“门急诊”开始采集数据,院区:{}", "30001002"); |
| | | serviceSLTDInhospReqVO.setStartHeadTime(null); |
| | | serviceSLTDInhospReqVO.setStartTailTime(null); |
| | | serviceSLTDInhospReqVO.setEncounterTimeStart(nowTime); |
| | | serviceSLTDInhospReqVO.setEncounterTimeEnd(nowTime); |
| | | serviceSLTDInhospReqVO.setStatusList(null); |
| | | List<String> list1 = new ArrayList<>(); |
| | | list1.add("FH0108.01"); |
| | | list1.add("FH0108.03"); |
| | | serviceSLTDInhospReqVO.setHealthcareRecordTypeList(list1); |
| | | log.info("【dealHisData】省立同德“门急诊”采集数据入参:{}", serviceSLTDInhospReqVO); |
| | | serviceSLTDHealthcareRecordService.queryHealthcareRecordList(serviceSLTDInhospReqVO); |
| | | log.info("【dealHisData】省立同德“门急诊”束采集数据,院区:{}", "30001002"); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("【dealHisData】省立同德数据采集异常", e); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | |
| | | public void dealHisData2() { |
| | | //将患者信息、出入院数据全部采集 |
| | | // 指定的开始日期 |
| | | LocalDate endDate = LocalDate.of(2025, 3, 3); |
| | | LocalDate endDate = LocalDate.of(2026, 7, 6); |
| | | // 当前日期 |
| | | LocalDate currentDate = LocalDate.now(); |
| | | // 定义日期格式 |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 处理患者信息,进入子任务表(任务组) |
| | | */ |
| | | public void dealOutHospInfoBySchedule() { |
| | | String config = configService.selectConfigByKey("visit.early.day"); |
| | | if (port.equals("8095")) { |
| | | //出院表 |
| | | iPatMedInhospService.dealOutHospInfoBySchedule(config); |
| | | //门诊表(新华的暂时不做门诊随访) |
| | | // if (!active.equals("xh")) { |
| | | // //iPatMedOuthospService.dealOutpatientInfo(); |
| | | // } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 处理手术信息,进入子任务表 |
| | |
| | | public void dealOperationInfo() { |
| | | if (port.equals("8095")) iPatMedOperationService.dealOperationInfo(); |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 门诊分表定时任务:从 sys_config 读取分表模式,按模式建表并刷新视图 v_pat_med_outhosp |
| | | * 配置 key: outhosp.split.mode |
| | | * quarter -> 按季度,表名示例:pat_med_outhosp_20260103 |
| | | * year -> 按年,表名示例:pat_med_outhosp_2026 |
| | | * month -> 按月,表名示例:pat_med_outhosp_202601 |
| | | * 查询不到配置则不分表 |
| | | */ |
| | | public void createOuthospQuarterTable() { |
| | | log.info("【createOuthospQuarterTable】开始执行门诊分表任务"); |
| | | createDataTable.createOuthospQuarterTable(); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | |
| | | /** |
| | | * |
| | | * 长期任务发送 |
| | | */ |
| | | public void longTaskSend() { |
| | | //获取任务信息 |
| | |
| | | commonTaskcallMQ.setSendType("2"); |
| | | |
| | | //通过任务ID拿到患者信息,并且随访时间得是今天之前的 |
| | | ServiceSubtaskVO serviceSubtaskVO = new ServiceSubtaskVO(); |
| | | ServiceSubtaskEntity serviceSubtaskVO = new ServiceSubtaskEntity(); |
| | | serviceSubtaskVO.setTaskid(commonTaskcallMQ.getTaskid()); |
| | | serviceSubtaskVO.setSendstate(2L); |
| | | serviceSubtaskVO.setIsVisitAgain(1); |
| | | serviceSubtaskVO.setVisitTime(new Date()); |
| | | List<ServiceSubtask> selectServiceSubtaskList = serviceSubtaskMapper.queryServiceSubtaskList(serviceSubtaskVO); |
| | | for (ServiceSubtask serviceSubtask : selectServiceSubtaskList) { |
| | | sfHandlle(serviceSubtask); |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | |
| | | * 处理补偿任务 |
| | | */ |
| | | public void compensateTask() { |
| | | //获取到sendstate=3 并且 visit_time为今天的subtask |
| | | ServiceSubtaskVO serviceSubtaskVO = new ServiceSubtaskVO(); |
| | | serviceSubtaskVO.setSendstate(3L); |
| | | serviceSubtaskVO.setVisitTime(new Date()); |
| | | List<ServiceSubtask> serviceSubtaskList = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskVO); |
| | | //获取到sendstate=3、1 并且 visit_time为小于等于今天的subtask |
| | | List<Long> list = Arrays.asList(3L, 1L); |
| | | ServiceSubtaskEntity serviceSubtaskVO = new ServiceSubtaskEntity(); |
| | | List<ServiceSubtask> serviceSubtaskList = new ArrayList<>(); |
| | | for (Long sendstate : list) { |
| | | serviceSubtaskVO.setSendstate(sendstate); |
| | | serviceSubtaskVO.setVisitTime(new Date()); |
| | | List<ServiceSubtask> compensateServiceSubtaskList = serviceSubtaskMapper.getCompensateServiceSubtaskList(serviceSubtaskVO); |
| | | serviceSubtaskList.addAll(compensateServiceSubtaskList); |
| | | } |
| | | for (ServiceSubtask serviceSubtask : serviceSubtaskList) { |
| | | //根据当前的执行方式,获取下一种执行方式 |
| | | ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform(); |
| | |
| | | |
| | | //2判断一下,当前的sort是不是等于需要执行的总个数,如果等于的话,说明是最后一个,直接将servuce_subtask的状态改成5,执行失败就行了 |
| | | Long cs = currentSort.get(); |
| | | if (cs.equals(serviceSubtaskPreachforms.size())) { |
| | | serviceSubtask.setSendstate(5L); |
| | | |
| | | if (cs.equals(serviceSubtaskPreachforms.size()) |
| | | && isSubtaskDeadline(serviceSubtask, serviceSubtaskPreachforms)) { |
| | | serviceSubtask.setSendstate(7L); |
| | | serviceSubtask.setRemark("处理补偿任务,当前处理最后补偿,全部执行失败(超时)"); |
| | | |
| | | serviceSubtaskMapper.updateServiceSubtask(serviceSubtask); |
| | | //修改发送方式的状态为失败 |
| | | serviceSubtaskPreachform.setSendstate("5"); |
| | | serviceSubtaskPreachform.setId(id.get()); |
| | | |
| | | serviceSubtaskPreachformMapper.updateServiceSubtaskPreachform(serviceSubtaskPreachform); |
| | | continue; |
| | | } |
| | | |
| | | //3.不是最后一个,获取到下一个执行方式(因为都是在今天执行,那就直接发出去就完了) |
| | | sfHandlle(serviceSubtask); |
| | | |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 设置失败任务默认值,并将失败任务重新置为成功 |
| | | * <p> |
| | | * *@param failDay (失败天数:距离当前日期失败天数) |
| | | * |
| | | * @return |
| | | */ |
| | | public void setFailSubTaskDefalutVal() { |
| | | String failDay = configService.selectConfigByKey("hosp.default.fail.day"); |
| | | if (StringUtils.isNotEmpty(failDay)) iServiceSubtaskService.setFailSubTaskDefalutVal(Long.valueOf(failDay)); |
| | | } |
| | | |
| | | |
| | |
| | | * @param date |
| | | * @return |
| | | */ |
| | | public boolean isDateToday(Date date) { |
| | | private boolean isDateToday(Date date) { |
| | | if (date == null) { |
| | | return false; |
| | | } |
| | |
| | | return !localDate.isAfter(today); |
| | | } |
| | | |
| | | public void sfHandlle(ServiceSubtask serviceSubtask) { |
| | | /** |
| | | * 随访-补偿处理 |
| | | * |
| | | * @param serviceSubtask |
| | | */ |
| | | private void sfHandlle(ServiceSubtask serviceSubtask) { |
| | | //这里需要根据身份证去重(给微信小程序的固定任务用的) |
| | | List<ServiceSubtask> serviceSubtaskListFilter = new ArrayList<>(); |
| | | ServiceTask ivrTask1 = serviceTaskMapper.selectServiceTaskByTaskid(serviceSubtask.getTaskid()); |
| | |
| | | String sendPreachform = getSendPreachform(serviceSubtaskPreachform, serviceSubtask); |
| | | if (sendPreachform.equals("-1") || serviceSubtask.getSendstate() == 6 || serviceSubtask.getSendstate() == 5 || serviceSubtask.getSendstate() == 4) { |
| | | //说明已经全部遍历完了,当前的执行方式序号是最后一个了 或者 该患者已经随访结束(成功或失败了或不执行) |
| | | log.error("说明已经全部遍历完了,当前的执行方式序号是最后一个了 或者 该患者已经随访结束(成功或失败了或不执行)"); |
| | | return; |
| | | } |
| | | //先判断患者是不是还要发送 |
| | |
| | | if (!Objects.isNull(patArchive) && patArchive.getNotrequiredFlag() == "1") { |
| | | //不需要发送 |
| | | serviceSubtask.setResult("患者不需要随访"); |
| | | serviceSubtask.setRemark(patArchive.getNotrequiredreason()); |
| | | serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + "," + "患者不需要随访," + patArchive.getNotrequiredreason() : "患者不需要随访," + patArchive.getNotrequiredreason()); |
| | | |
| | | serviceSubtask.setSendstate(4L); |
| | | iServiceSubtaskService.insertServiceSubtask(serviceSubtask); |
| | | setSuccessPreachForm(serviceSubtask, "-1", "不需要执行", "6"); |
| | |
| | | serviceOutPath.setParam3(ivrTask1.getTaskName()); |
| | | serviceOutPath.setParam6(subId); |
| | | serviceOutPath.setCreateTime(new Date()); |
| | | serviceOutPath.setOrgid(serviceSubtask.getOrgid()); |
| | | iServiceOutPathService.insertServiceOutPath(serviceOutPath); |
| | | String url = ""; |
| | | String format = String.format("%03X", serviceOutPath.getId()); |
| | | serviceOutPath.setRadix(format); |
| | | serviceOutPath.setUpdateTime(new Date()); |
| | | iServiceOutPathService.updateServiceOutPath(serviceOutPath); |
| | | //先判断一下发的是不是宣教 |
| | | if (!"3".equals(serviceSubtask.getType())) { |
| | | sendMagParam.setPhone(serviceSubtask.getPhone()); |
| | | sendMagParam.setUrl(localIP + ":" + req_path + "/wt?p=" + format); |
| | | url = StringUtils.isNotEmpty(req_path) ? localIP + ":" + req_path + "/wt?p=" + format : localIP + "/wt?p=" + format; |
| | | sendMagParam.setUrl(url); |
| | | //如果type是语音随访的话(说明补偿发送方式中有电话随访的方式,这里的外链就地址只能用/sf) |
| | | if (serviceSubtask.getType().equals("1")) |
| | | sendMagParam.setUrl(localIP + ":" + req_path + "/sf?p=" + format); |
| | | sendMagParam.setContent("您好,邀请您填写出院调查表,请点击" + sendMagParam.getUrl() + "填写。感谢您配合!"); |
| | | url = StringUtils.isNotEmpty(req_path) ? localIP + ":" + req_path + "/sf?p=" + format : localIP + "/sf?p=" + format; |
| | | sendMagParam.setUrl(url); |
| | | |
| | | // SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("wj.sms.template"); |
| | | SysConfig sysConfig = new SysConfig(); |
| | | if (StringUtils.isNotEmpty(serviceSubtask.getServiceType()) && serviceSubtask.getServiceType().equals("6")) { |
| | | sysConfig = sysConfigMapper.checkConfigKeyUnique("zymyd.sms.template"); |
| | | } else if (StringUtils.isNotEmpty(serviceSubtask.getServiceType()) && serviceSubtask.getServiceType().equals("14")) { |
| | | sysConfig = sysConfigMapper.checkConfigKeyUnique("mzmyd.sms.template"); |
| | | } else { |
| | | sysConfig = sysConfigMapper.checkConfigKeyUnique("wj.sms.template"); |
| | | } |
| | | if (!Objects.isNull(sysConfig)) { |
| | | String smsContemt = sysConfig.getConfigValue(); |
| | | // sendMagParam.setContent("您好,邀请您填写出院调查表,请点击" + sendMagParam.getUrl() + "填写。感谢您配合!"); |
| | | sendMagParam.setContent(smsContemt.replace("${wjurl}", sendMagParam.getUrl())); |
| | | } |
| | | } else { |
| | | HeLibrary heLibrary = heLibraryMapper.selectHeLibraryById(serviceSubtask.getLibtemplateid()); |
| | | if (heLibrary.getHetype().equals("1")) { |
| | | sendMagParam.setPhone(serviceSubtask.getPhone()); |
| | | sendMagParam.setUrl(localIP + ":" + req_path + "/xj?p=" + format); |
| | | if (active.equals("ls") || active.equals("sltd")) { |
| | | sendMagParam.setContent("您好,邀请您填写出院宣教调查表,请点击" + sendMagParam.getUrl() + "查看。感谢您配合!"); |
| | | } else if (active.equals("xh")) { |
| | | sendMagParam.setContent("【新华医院】您好,邀请您填写出院宣教调查表,请点击" + sendMagParam.getUrl() + "查看。感谢您配合!"); |
| | | url = StringUtils.isNotEmpty(req_path) ? localIP + ":" + req_path + "/xj?p=" + format : localIP + "/xj?p=" + format; |
| | | |
| | | sendMagParam.setUrl(url); |
| | | |
| | | SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("xj.sms.template"); |
| | | if (!Objects.isNull(sysConfig)) { |
| | | String smsContemt = sysConfig.getConfigValue(); |
| | | // sendMagParam.setContent("您好,邀请您填写出院宣教调查表,请点击" + sendMagParam.getUrl() + "查看。感谢您配合!"); |
| | | sendMagParam.setContent(smsContemt.replace("${xjurl}", sendMagParam.getUrl())); |
| | | |
| | | } |
| | | } else if (heLibrary.getHetype().equals("2")) { |
| | | //通知 |
| | | sendMagParam.setPhone(serviceSubtask.getPhone()); |
| | | sendMagParam.setContent(heLibrary.getPreachcontent()); |
| | | // 替换文本内容中的变量 |
| | | String content = getObject(serviceSubtask, heLibrary.getPreachcontent()); |
| | | sendMagParam.setContent(content); |
| | | } |
| | | |
| | | } |
| | | String isSuccess = null; |
| | | |
| | | serviceOutPath.setRadix(format); |
| | | serviceOutPath.setUpdateTime(new Date()); |
| | | serviceOutPath.setUrl(url); |
| | | iServiceOutPathService.updateServiceOutPath(serviceOutPath); |
| | | //短信发送:各站点对接方式差异较大(报文/签名/token/返回判定都不同), |
| | | //已收口到 ruoyi-message 模块,这里只负责组装入参并路由 |
| | | log.info("active的值为:{}", active); |
| | | if (active.equals("xh")) { |
| | | Map<String, String> req = new HashMap<>(); |
| | | req.put("phone", sendMagParam.getPhone()); |
| | | req.put("content", sendMagParam.getContent()); |
| | | isSuccess = HttpUtil.postJsonRequest(xhsmsPath, new Gson().toJson(req)); |
| | | } else if (active.equals("ls")) { |
| | | // String url = configService.selectConfigByKey("token.360", serviceSubtask.getOrgid()); |
| | | // //丽水的短信发送方式 |
| | | // Map<String, String> map = MsgLSEnum.getAddressByCode(serviceSubtask.getOrgid()); |
| | | SysConfig config = new SysConfig(); |
| | | config.setConfigKey("ls.msg.info"); |
| | | config.setOrgid(serviceSubtask.getOrgid()); |
| | | List<SysConfig> sysConfigs = configService.selectConfigList(config); |
| | | Map<String, String> msgInfo = new HashMap<>(); |
| | | if (CollectionUtils.isNotEmpty(sysConfigs)) { |
| | | String configValue = sysConfigs.get(0).getConfigValue(); |
| | | ObjectMapper objectMapper = new ObjectMapper(); |
| | | if (StringUtils.isNotEmpty(configValue)) { |
| | | msgInfo = objectMapper.readValue(configValue, Map.class); |
| | | } |
| | | } |
| | | log.info("-----------map的值为:{}", msgInfo); |
| | | String token = LSHospTokenUtil.getToken(serviceSubtask.getOrgid(), msgInfo.get("tokenUrl")); |
| | | if (ObjectUtils.isNotEmpty(msgInfo) && StringUtils.isEmpty(msgInfo.get("address"))) { |
| | | setSuccessPreachForm(serviceSubtask, sendPreachform, "短信发送失败,该机构没有配置短信地址, 时间:" + now , "5"); |
| | | return; |
| | | } |
| | | log.info("【sfHandlle】丽水短信发送,地址:{},参数:{},机构ID:{}", msgInfo.get("address"), sendMagParam, serviceSubtask.getOrgid()); |
| | | String dxCode = getDXCode(msgInfo.get("address"), sendMagParam.getPhone(), sendMagParam.getContent(), msgInfo.get("sendPersonId"), msgInfo.get("sendPersonName"), MsgLSEnum.getHeaderByCode(msgInfo), token); |
| | | log.info("【sfHandlle】丽水短信发送结果:{}", dxCode); |
| | | ObjectMapper objectMapper = new ObjectMapper(); |
| | | Map<String, Object> textParam = objectMapper.readValue(dxCode, Map.class); |
| | | String code = textParam.get("Code").toString(); |
| | | if (code.equals("0")) { |
| | | isSuccess = "true"; |
| | | } |
| | | } else if (active.equals("sltd")) { |
| | | log.info("sltd进来了吗?{} ,---sendMagParam.getContent()的参数为:{}", active, sendMagParam.getContent()); |
| | | //省立同德的短信发送方式 |
| | | String data = MessageSend.sendMsg(sendMagParam.getContent(), "6", sendMagParam.getPhone(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))); |
| | | if (StringUtils.isNotEmpty(data) && data.contains("result=0")) { |
| | | isSuccess = "true"; |
| | | } |
| | | } else if (active.equals("hzszlyy")) {//市一医院 |
| | | String content = sendMagParam.getContent().replace("【新华医院】", ""); |
| | | String result = smsUtils.sendSms(xhsmsPath, xhsmsAccount, xhsmsPwd, sendMagParam.getPhone(), content); |
| | | //这里需要根据result返回的值去判断,是否成功,然后将值赋值给isSuccess |
| | | if (StringUtils.isNotEmpty(result) && result.contains("处理成功")) { |
| | | isSuccess = "true"; |
| | | } |
| | | |
| | | } |
| | | SendResult smsSendResult = sendSmsByActive(serviceSubtask, sendMagParam); |
| | | String isSuccess = smsSendResult.asLegacyFlag(); |
| | | //任务发送记录 |
| | | ServiceSubtaskRecord serviceSubtaskRecord = new ServiceSubtaskRecord(); |
| | | serviceSubtaskRecord.setTaskid(serviceSubtask.getTaskid().toString()); |
| | | serviceSubtaskRecord.setSubtaskId(serviceSubtask.getId()); |
| | | serviceSubtaskRecord.setUuid(UUID.randomUUID().toString()); |
| | | serviceSubtaskRecord.setTasktype(serviceSubtask.getType()); |
| | | serviceSubtaskRecord.setPreachform("4"); |
| | |
| | | serviceSubtask.setSendstate(3L); |
| | | if (StringUtils.isNotEmpty(isSuccess) && isSuccess.equals("true")) { |
| | | serviceSubtask.setResult("success"); |
| | | serviceSubtask.setRemark("短信发送成功"); |
| | | serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + ",短信发送成功" : "短信发送成功"); |
| | | setSuccessPreachForm(serviceSubtask, sendPreachform, "短信发送成功", "2"); |
| | | } else { |
| | | serviceSubtask.setResult("error"); |
| | | serviceSubtask.setRemark("短信发送失败" + now); |
| | | serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + ",短信发送失败" + now : "短信发送失败" + now); |
| | | setSuccessPreachForm(serviceSubtask, sendPreachform, "短信发送失败" + now, "5"); |
| | | } |
| | | } catch (Exception e) { |
| | |
| | | if (dateToday) { |
| | | try { |
| | | String patid = rsaPublicKeyExample.encryptedData(serviceSubtask.getPatid().toString(), pub_key); |
| | | String subId = rsaPublicKeyExample.encryptedData(serviceSubtask.getId().toString(), pub_key); |
| | | String url = null; |
| | | url = localIP + ":" + req_path + "/outsideChainwt?param1=" + taskId + "¶m2=" + patid + "¶m3=" + URLEncoder.encode(ivrTask1.getTaskName(), StandardCharsets.UTF_8.toString()) + "¶m5=false"; |
| | | url = localIP + ":" + req_path + "/outsideChainwt?param1=" + taskId + "¶m2=" + patid + "¶m3=" + URLEncoder.encode(ivrTask1.getTaskName(), StandardCharsets.UTF_8.toString()) + "¶m5=false" + "$param6=" + subId; |
| | | |
| | | //获取微信公众号请求信息根据机构ID |
| | | List<String> wxqqxx = WxGZHEnum.getDescByCode(patArchive.getOrgid()); |
| | | if (CollectionUtils.isEmpty(wxqqxx) || wxqqxx.size() < 4) { |
| | | setSuccessPreachForm(serviceSubtask, sendPreachform, "【公众号】该机构的公众号配置信息不全,无法通过公众号发送", "4"); |
| | | return; |
| | | } |
| | | ServiceOutPath serviceOutPath = new ServiceOutPath(); |
| | | serviceOutPath.setParam1(taskId); |
| | | serviceOutPath.setParam2(patid); |
| | | serviceOutPath.setParam3(ivrTask1.getTaskName()); |
| | | serviceOutPath.setParam6(subId); |
| | | serviceOutPath.setCreateTime(new Date()); |
| | | serviceOutPath.setOrgid(serviceSubtask.getOrgid()); |
| | | iServiceOutPathService.insertServiceOutPath(serviceOutPath); |
| | | String format = String.format("%03X", serviceOutPath.getId()); |
| | | serviceOutPath.setRadix(format); |
| | | serviceOutPath.setUpdateTime(new Date()); |
| | | serviceOutPath.setUrl(url); |
| | | iServiceOutPathService.updateServiceOutPath(serviceOutPath); |
| | | |
| | | if (StringUtils.isEmpty(patArchive.getPatidHis())) { |
| | | setSuccessPreachForm(serviceSubtask, sendPreachform, "【公众号】公众号发送失败,his系统的患者id为空", "5"); |
| | | return; |
| | | } |
| | | String wxCode = ""; |
| | | if (active.equals("hzszlyy")) {//市一医院 |
| | | String urlTemp = "http://192.200.54.14:5003/sfjk/SendMessage?sfzh=" + serviceSubtask.getSfzh() + "&title=您好,邀请您填写出院调查表,请点击填写。&content=您好,邀请您填写出院调查表,请点击填写。&phone=" + patArchive.getTelcode() + "&url=" + url + "&key=ff76f8904f5f32b5ee1739e8ea46e60g"; |
| | | HttpUtils.sendGet(urlTemp); |
| | | } else { |
| | | wxCode = getWXCode(serviceSubtask.getSfzh(), url, serviceSubtask.getTaskName(), serviceSubtask.getTaskDesc(), patArchive.getTelcode(), serviceSubtask.getSendname(), patArchive.getPatidHis(), wxqqxx); |
| | | } |
| | | //微信公众号发送:市一与新华是两套完全不同的网关与判定标准, |
| | | //机构配置校验、HIS患者ID校验都已收口到 ruoyi-message 的发送方实现里 |
| | | SendResult wechatSendResult = sendWechatByActive(serviceSubtask, patArchive, url); |
| | | Boolean wxCodeSuccess = wechatSendResult.isOk(); |
| | | |
| | | Map<String, Object> map = JSONObject.parseObject(wxCode, Map.class); |
| | | //任务发送记录 |
| | | ServiceSubtaskRecord serviceSubtaskRecord = new ServiceSubtaskRecord(); |
| | | serviceSubtaskRecord.setTaskid(serviceSubtask.getTaskid().toString()); |
| | |
| | | serviceSubtaskRecord.setPreachform("4"); |
| | | serviceSubtaskRecord.setStartTime(System.currentTimeMillis()); |
| | | serviceSubtaskRecord.setResult("success"); |
| | | if (!map.isEmpty() && (Boolean) map.get("succ") == false) serviceSubtaskRecord.setResult("error"); |
| | | if (!wxCodeSuccess) serviceSubtaskRecord.setResult("error"); |
| | | serviceSubtaskRecordService.insertServiceSubtaskRecord(serviceSubtaskRecord); |
| | | |
| | | //发送完成后,修改状态 |
| | | serviceSubtask.setSendstate(3L); |
| | | if ((Boolean) map.get("succ") == true) { |
| | | if (wxCodeSuccess) { |
| | | serviceSubtask.setResult("success"); |
| | | setSuccessPreachForm(serviceSubtask, sendPreachform, "【公众号】公众号发送成功", "2"); |
| | | } else { |
| | | log.error("【sfHandlle】公众号发送失败,患者信息:{},错误信息:{}", serviceSubtask, map.get("msg")); |
| | | log.error("【sfHandlle】公众号发送失败,患者信息:{},错误信息:{}", serviceSubtask, wechatSendResult.getMessage()); |
| | | setSuccessPreachForm(serviceSubtask, sendPreachform, "【公众号】公众号发送失败", "5"); |
| | | } |
| | | } catch (Exception e) { |
| | |
| | | } |
| | | } |
| | | } else if (descByCode.equals("人工")) { |
| | | setSuccessPreachForm(serviceSubtask, sendPreachform, "人工待执行", "2"); |
| | | setSuccessPreachForm(serviceSubtask, sendPreachform, "人工发送成功", "2"); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 按 {@code active}(站点)路由到对应的短信发送方。 |
| | | * <p> |
| | | * 各站点差异见 {@code ruoyi-message} 模块各实现类;本方法只做「入参组装 + 路由 + 日志」。 |
| | | * <p> |
| | | * ⚠️ 新华({@code xh})站点仍走「旧实现(发送空报文)」,与原 {@code RyTask} 行为一字不差, |
| | | * 确认网关报文格式后应改为 {@code XinHuaSmsSender#sendForm}。详见该类的 TODO 说明。 |
| | | */ |
| | | private SendResult sendSmsByActive(ServiceSubtask serviceSubtask, SendMagParam sendMagParam) { |
| | | SmsSendRequest request = SmsSendRequest.of(sendMagParam.getPhone(), sendMagParam.getContent(), serviceSubtask.getOrgid()); |
| | | //市一区分湖滨/吴山两套短信签名 |
| | | if ("hzszlyy".equals(active)) { |
| | | request.setCampus(resolveShiyiCampus(serviceSubtask)); |
| | | } |
| | | if ("xh".equals(active)) { |
| | | XinHuaSmsSender xinHuaSmsSender = (XinHuaSmsSender) smsSenderFactory.get(active); |
| | | return xinHuaSmsSender.sendJsonLegacy(request); |
| | | } |
| | | return smsSenderFactory.send(active, request); |
| | | } |
| | | |
| | | /** |
| | | * 按 {@code active}(站点)路由到对应的微信公众号发送方。 |
| | | * <p> |
| | | * ⚠️ 新华公众号必须传患者信息(姓名 + HIS 患者ID),与原 |
| | | * {@code getWXCode(...)} 的入参保持一一对应。 |
| | | */ |
| | | private SendResult sendWechatByActive(ServiceSubtask serviceSubtask, PatArchive patArchive, String url) { |
| | | WechatSendRequest request = new WechatSendRequest(); |
| | | request.setOrgid(patArchive.getOrgid()); |
| | | request.setIdCard(serviceSubtask.getSfzh()); |
| | | request.setUrl(url); |
| | | request.setTitle(serviceSubtask.getTaskName()); |
| | | request.setContent(serviceSubtask.getTaskDesc()); |
| | | request.setPhone(patArchive.getTelcode()); |
| | | request.setPatientName(serviceSubtask.getSendname()); |
| | | request.setPatidHis(patArchive.getPatidHis()); |
| | | return wechatSenderFactory.send(active, request); |
| | | } |
| | | |
| | | /** |
| | | * 查询患者所在院区(1=湖滨,其他=吴山)。 |
| | | * <p> |
| | | * TODO 待确认:原实现并未把 {@code sysDeptService.selectDeptList(...)} 的返回值赋给 |
| | | * {@code sysDepts},因此 {@code sysDepts} 恒为空列表、{@code campusid} 恒为 {@code "2"}, |
| | | * 湖滨院区实际上永远走不到 {@code sendSms1}。本次重构<b>保持该行为不变</b>, |
| | | * 请在业务确认后再决定是否修成 {@code sysDepts = sysDeptService.selectDeptList(...)}。 |
| | | */ |
| | | private String resolveShiyiCampus(ServiceSubtask serviceSubtask) { |
| | | SysDept sysDeptVo = new SysDept(); |
| | | List<SysDept> sysDepts = new ArrayList<>(); |
| | | if (ObjectUtils.isNotEmpty(serviceSubtask.getDeptcode())) { |
| | | sysDeptVo.setDeptType("1"); |
| | | sysDeptVo.setOrgid(serviceSubtask.getOrgid()); |
| | | sysDeptVo.setDeptCode(serviceSubtask.getDeptcode()); |
| | | sysDeptService.selectDeptList(sysDeptVo); |
| | | } else if (ObjectUtils.isNotEmpty(serviceSubtask.getLeavehospitaldistrictcode())) { |
| | | sysDeptVo.setDeptType("2"); |
| | | sysDeptVo.setOrgid(serviceSubtask.getOrgid()); |
| | | sysDeptVo.setDeptCode(serviceSubtask.getLeavehospitaldistrictcode()); |
| | | sysDeptService.selectDeptList(sysDeptVo); |
| | | } |
| | | SysDept sysDept = CollectionUtils.isNotEmpty(sysDepts) ? sysDepts.get(0) : new SysDept(); |
| | | return ObjectUtils.isNotEmpty(sysDept) ? sysDept.getCampusid() : "2"; |
| | | } |
| | | |
| | | |
| | |
| | | |
| | | // 情况1:currentPreachform为空,取sort为1的preachform |
| | | if (StringUtils.isEmpty(serviceSubtask.getCurrentPreachform())) { |
| | | return serviceSubtaskPreachforms.stream().filter(item -> item.getSort() == 1L).map(ServiceSubtaskPreachform::getPreachform).findFirst().orElse(null); |
| | | String firstPreachform = serviceSubtaskPreachforms.stream().filter(item -> item.getSort() == 1L).map(ServiceSubtaskPreachform::getPreachform).findFirst().orElse(null); |
| | | |
| | | //更新一下当前发送时间 |
| | | if (StringUtils.isNotEmpty(firstPreachform)) { |
| | | serviceSubtaskPreachform.setPreachform(firstPreachform); |
| | | serviceSubtaskPreachform.setCurrentSendTime(new Date()); |
| | | serviceSubtaskPreachformMapper.updateSSPByCondition(serviceSubtaskPreachform); |
| | | } |
| | | return firstPreachform; |
| | | } |
| | | |
| | | // 情况2:currentPreachform不为空,找下一个sort |
| | |
| | | serviceSubtaskMapper.updateServiceSubtask(serviceSubtask); |
| | | return "-1"; |
| | | } |
| | | |
| | | // 当sort等于列表size时,并且所有sendstate都不等于9,则返回-1 |
| | | if (currentSort.get().equals((long) serviceSubtaskPreachforms.size())) { |
| | | if (currentSort.get().equals((long) serviceSubtaskPreachforms.size()) |
| | | && isSubtaskDeadline(serviceSubtask, serviceSubtaskPreachforms)) { |
| | | if (allSendStateNot1AndNot9) { |
| | | //说明全发送失败了 |
| | | serviceSubtask.setSendstate(5L); |
| | | serviceSubtask.setSendstate(7L); |
| | | serviceSubtask.setFinishtime(null); |
| | | log.error("getSendPreachform方法 检查是否所有sendstate不等于9, 全失败了"); |
| | | serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + "," + "所有执行方式均未获取结果,任务失败(超时)" : "所有执行方式均未获取结果,任务失败(超时)"); |
| | | serviceSubtaskMapper.updateServiceSubtask(serviceSubtask); |
| | | //修改发送状态 |
| | | setFail(serviceSubtaskPreachforms, serviceSubtask); |
| | |
| | | |
| | | // 当sort等于列表size时,下一个sort为-1 |
| | | Long nextSort = currentSort.get() + 1; |
| | | |
| | | //如果当前的发送方式不为空,并且开始准备要用下一种方式发送了,那当前的肯定没有发送成功,需要设置失败 |
| | | // (这里主要是,人工如果没有随访,然后到公众号的时候,就不会再去管人工是否成功,人工的状态会一直卡在2的状态上) |
| | | if (StringUtils.isNotEmpty(serviceSubtask.getCurrentPreachform())) { |
| | | setFail(serviceSubtaskPreachforms, serviceSubtask); |
| | | } |
| | | |
| | | // 找到下一个sort对应的preachform |
| | | return serviceSubtaskPreachforms.stream().filter(item -> nextSort.equals(item.getSort())).map(ServiceSubtaskPreachform::getPreachform).findFirst().orElse("-1"); |
| | | } |
| | | String nextPreachform = serviceSubtaskPreachforms.stream().filter(item -> nextSort.equals(item.getSort())).map(ServiceSubtaskPreachform::getPreachform).findFirst().orElse("-1"); |
| | | |
| | | //更新一下当前发送时间 |
| | | if (StringUtils.isNotEmpty(nextPreachform)) { |
| | | serviceSubtaskPreachform.setCurrentSendTime(new Date()); |
| | | serviceSubtaskPreachform.setPreachform(nextPreachform); |
| | | serviceSubtaskPreachformMapper.updateSSPByCondition(serviceSubtaskPreachform); |
| | | } |
| | | return nextPreachform; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | |
| | | ServiceSubtaskPreachform subtaskPreachform = new ServiceSubtaskPreachform(); |
| | | subtaskPreachform.setSendstate("5"); |
| | | subtaskPreachform.setId(id.get()); |
| | | subtaskPreachform.setRemark("当前发送方式在规定时间内未成功获取结果"); |
| | | return serviceSubtaskPreachformMapper.updateServiceSubtaskPreachform(subtaskPreachform); |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | |
| | | private String getDXCode(String address, String ShouJiHM, String FaSongNR, String FaSongRID, String FaSongRXM, Map<String, Object> headerMap, String token) { |
| | | Map<String, Object> map = new LinkedHashMap<>(); |
| | | |
| | | Map<String, Object> YeWuXX = new LinkedHashMap<>(); |
| | | Map<String, Object> DuanXinXX = new LinkedHashMap<>(); |
| | | DuanXinXX.put("ShouJiHM", ShouJiHM); |
| | | DuanXinXX.put("FaSongNR", FaSongNR); |
| | | DuanXinXX.put("FaSongRID", FaSongRID); |
| | | DuanXinXX.put("FaSongRXM", FaSongRXM); |
| | | YeWuXX.put("DuanXinXX", DuanXinXX); |
| | | |
| | | map.put("XiaoXiTou", headerMap); |
| | | map.put("YeWuXX", YeWuXX); |
| | | |
| | | String body = new Gson().toJson(map); |
| | | |
| | | // String result = HttpUtils.sendPost(address, body); |
| | | String result = null; |
| | | try { |
| | | result = OkHttpExample.sendPostRequest(address, body, token); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | log.info("【getDXCode】短信发送结果:{}", result); |
| | | |
| | | JSONObject jsonObject = JSONObject.parseObject(result); |
| | | String code = (String) jsonObject.toString(); |
| | | return code; |
| | | } |
| | | |
| | | |
| | | private String getWXCode(String idcard, String url, String taskName, String taskDesc, String phone, String setPatientname, String setPatientid, List<String> wxqqxx) { |
| | | XinHuaWXReq xinHuaWXReq = new XinHuaWXReq(); |
| | | xinHuaWXReq.setIdcard(idcard); |
| | | xinHuaWXReq.setUrl(url); |
| | | xinHuaWXReq.setPatientid(setPatientid); |
| | | xinHuaWXReq.setPatientname(setPatientname); |
| | | xinHuaWXReq.setMobile(phone); |
| | | xinHuaWXReq.setMedcardno(null); |
| | | xinHuaWXReq.setTitlename(taskName); |
| | | xinHuaWXReq.setContent(taskDesc); |
| | | xinHuaWXReq.setAppid(wxqqxx.get(1)); |
| | | log.info("【getWXCode】微信公众号请求参数:{}", xinHuaWXReq); |
| | | String body = new Gson().toJson(xinHuaWXReq); |
| | | String encode = encode(wxqqxx.get(2).concat(body)); |
| | | Map<String, String> headerMap = new HashMap<>(); |
| | | headerMap.put("sign", encode); |
| | | String result = HttpUtils.sendPostByHeader(wxqqxx.get(3), body, headerMap); |
| | | |
| | | JSONObject jsonObject = JSONObject.parseObject(result); |
| | | String code = (String) jsonObject.toString(); |
| | | return code; |
| | | } |
| | | |
| | | |
| | | public String encode(String arg) { |
| | | if (arg == null) { |
| | | arg = ""; |
| | | } |
| | | MessageDigest md5 = null; |
| | | try { |
| | | md5 = MessageDigest.getInstance("MD5"); |
| | | md5.update(arg.getBytes("UTF-8")); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return toHex(md5.digest()); |
| | | } |
| | | // --------------------------------------------------------------------- |
| | | // 说明:原 getDXCode / getWXCode / encode 三个私有方法已随发送方一起收口到 |
| | | // ruoyi-message 模块(LishuiSmsSender / XinHuaMpApi),此处已删除,避免重复实现。 |
| | | // --------------------------------------------------------------------- |
| | | |
| | | public void collectHIS() { |
| | | try { |
| | |
| | | } |
| | | } |
| | | |
| | | public void collectHISYesterday() { |
| | | try { |
| | | // 获取昨天0点到今天0点的时间范围 |
| | | LocalDateTime todayZero = LocalDateTime.now().with(LocalTime.MIN); |
| | | LocalDateTime yesterdayZero = todayZero.minusDays(1); |
| | | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
| | | String startTime = yesterdayZero.format(formatter); |
| | | String endTime = todayZero.format(formatter); |
| | | // HIS数据采集 |
| | | log.info("【collectHISYesterday】HIS开始采集数据"); |
| | | // 立即返回,后台异步执行 |
| | | CompletableFuture.runAsync(() -> { |
| | | ichService.syncMedInhosp(startTime, endTime); |
| | | }); |
| | | log.info("【collectHISYesterday】HIS结束采集数据"); |
| | | } catch (Exception e) { |
| | | log.error("【collectHISYesterday】HIS数据采集异常", e); |
| | | } |
| | | } |
| | | |
| | | public void collectHISDaysBefore(Long daysBefore) { |
| | | try { |
| | | // 获取daysBefore 0点到daysBefore-1 0点的时间范围 |
| | | LocalDateTime todayZero = LocalDateTime.now().with(LocalTime.MIN); |
| | | LocalDateTime daysBeforeZeroStart = todayZero.minusDays(daysBefore); |
| | | LocalDateTime daysBeforeZeroEnd = todayZero.minusDays(daysBefore-1L); |
| | | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
| | | String startTime = daysBeforeZeroStart.format(formatter); |
| | | String endTime = daysBeforeZeroEnd.format(formatter); |
| | | // HIS数据采集 |
| | | log.info("【collectHISDaysBefore】HIS开始采集数据: startTime:{}, endtime:{}",startTime, endTime); |
| | | // 立即返回,后台异步执行 |
| | | CompletableFuture.runAsync(() -> { |
| | | ichService.syncMedInhosp(startTime, endTime); |
| | | }); |
| | | log.info("【collectHISDaysBefore】HIS结束采集数据: startTime:{}, endtime:{}",startTime, endTime); |
| | | } catch (Exception e) { |
| | | log.error("【collectHISDaysBefore】HIS数据采集异常", e); |
| | | } |
| | | } |
| | | |
| | | public Boolean setSuccessPreachForm(ServiceSubtask serviceSubtask, String preachform, String remark, String failSendstate) { |
| | | if (preachform.equals("-1")) { |
| | | //说明都不需要执行了 |
| | |
| | | serviceSubtaskPreachform.setTaskid(serviceSubtask.getTaskid()); |
| | | serviceSubtaskPreachform.setOrgid(serviceSubtask.getOrgid()); |
| | | serviceSubtaskPreachform.setSendstate(failSendstate); |
| | | serviceSubtaskPreachform.setCurrentSendTime(new Date()); |
| | | serviceSubtaskPreachformMapper.updateSSPByCondition(serviceSubtaskPreachform); |
| | | return true; |
| | | } |
| | | |
| | | |
| | | ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform(); |
| | | serviceSubtaskPreachform.setSubid(serviceSubtask.getId()); |
| | |
| | | //电话的未发送状态,需要查2,因为,数据可能卡在队列里了; (没进队列,电话状态还是1) |
| | | if (preachform.equals("3") && StringUtils.isNotEmpty(failSendstate) && Long.valueOf(failSendstate) > 2) |
| | | serviceSubtaskPreachform.setSendstate("2"); |
| | | //人工的未发送状态,需要查2 |
| | | if (preachform.equals("1") && StringUtils.isNotEmpty(failSendstate) && Long.valueOf(failSendstate) > 2) |
| | | serviceSubtaskPreachform.setSendstate("2"); |
| | | |
| | | // //人工的未发送状态,需要查2 |
| | | // if (preachform.equals("1") && StringUtils.isNotEmpty(failSendstate) && Long.valueOf(failSendstate) > 2) |
| | | // serviceSubtaskPreachform.setSendstate("2"); |
| | | log.error("-----serviceSubtaskPreachform的入参是?{}", serviceSubtaskPreachform); |
| | | List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform); |
| | | if (CollectionUtils.isNotEmpty(serviceSubtaskPreachforms)) { |
| | | ServiceSubtaskPreachform serviceSubtaskPreachform1 = serviceSubtaskPreachforms.get(0); |
| | |
| | | serviceSubtaskPreachform1.setRemark(remark); |
| | | serviceSubtaskPreachformMapper.updateServiceSubtaskPreachform(serviceSubtaskPreachform1); |
| | | |
| | | //如果当前的preachform已经是最后一个了,那直接将serviceSubtask的sendstate状态改成5就行了(全失败了) |
| | | if (serviceSubtaskPreachform1.getSort() == sspCount.size()) { |
| | | //如果当前的preachform已经是最后一个了,那直接将serviceSubtask的sendstate状态改成7就行了(全超时了) |
| | | if (serviceSubtaskPreachform1.getSort() == sspCount.size() |
| | | && isSubtaskDeadline(serviceSubtask, serviceSubtaskPreachforms)) { |
| | | if (failSendstate.equals("4") || failSendstate.equals("5")) { |
| | | serviceSubtask.setCurrentPreachform(preachform); |
| | | serviceSubtask.setSendstate(5L); |
| | | serviceSubtask.setSendstate(7L); |
| | | log.error("setSuccessPreachForm方法,当前的preachform已经是最后一个了,全部执行失败"); |
| | | serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + "," + "所有发送方式均未获取结果,任务失败(超时)" : "所有发送方式均未获取结果,任务失败(超时)"); |
| | | //serviceSubtask.setRemark("setSuccessPreachForm方法,当前的preachform已经是最后一个了,全部执行失败"); |
| | | serviceSubtaskMapper.updateServiceSubtask(serviceSubtask); |
| | | return true; |
| | | } |
| | |
| | | log.error("【定时任务中该患者没有查询到属于他的发送方式,subid:{},prechform:{},orgid:{}】", serviceSubtask.getId(), preachform, serviceSubtask.getOrgid()); |
| | | return false; |
| | | } |
| | | |
| | | //将下一次的随访时间与当前执行方式记录一下 |
| | | serviceSubtask.setVisitTime(getNextVisitTime(serviceSubtask.getId(), serviceSubtask.getTaskid(), serviceSubtask.getVisitTime())); |
| | | serviceSubtask.setCurrentPreachform(preachform); |
| | | serviceSubtask.setSendstate(3L); |
| | | |
| | | if (serviceSubtask.getType().equals("3") || serviceSubtask.getType().equals("4")) { |
| | | //如果是宣教或通知,并且发送成功,则直接将serviceSubtask状态改成6L(这个患者是没有题做的,不会再触发接口进行状态修改) |
| | | if (failSendstate.equals("2")) { |
| | | serviceSubtask.setSendstate(6L); |
| | | } |
| | | } |
| | | serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + "," + remark : remark); |
| | | serviceSubtaskMapper.updateServiceSubtask(serviceSubtask); |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * 通配符变量替换 |
| | | * |
| | | * @param serviceSubtask |
| | | * @param scriptContent |
| | | * @return |
| | | */ |
| | | private String getObject(ServiceSubtask serviceSubtask, String scriptContent) { |
| | | Map<String, Map<String, String>> param = getParam(serviceSubtask.getTaskid()); |
| | | for (Map<String, String> map : param.values()) { |
| | | for (String key : map.keySet()) { |
| | | scriptContent = scriptContent.replace(key, StringUtils.isNotEmpty(map.get(key)) ? map.get(key) : ""); |
| | | } |
| | | } |
| | | if (StringUtils.isEmpty(scriptContent)) { |
| | | return scriptContent; |
| | | } |
| | | scriptContent = scriptContent.replace("${name}", StringUtils.isNotEmpty(serviceSubtask.getSendname()) ? serviceSubtask.getSendname() : ""); |
| | | scriptContent = scriptContent.replace("${dzz}", StringUtils.isNotEmpty(serviceSubtask.getAddr()) ? serviceSubtask.getAddr() : ""); |
| | | scriptContent = scriptContent.replace("${phone}", StringUtils.isNotEmpty(serviceSubtask.getTelcode()) ? serviceSubtask.getTelcode() : ""); |
| | | |
| | | return scriptContent; |
| | | } |
| | | |
| | | /** |
| | | * 获取任务里的通配符 |
| | | * |
| | | * @param taskId |
| | | * @return |
| | | */ |
| | | private Map<String, Map<String, String>> getParam(Long taskId) { |
| | | ServiceTask serviceTask = serviceTaskMapper.selectServiceTaskByTaskid(taskId); |
| | | ObjectMapper objectMapper = new ObjectMapper(); |
| | | Map<String, Map<String, String>> serviceTaskMap = null; |
| | | try { |
| | | serviceTaskMap = objectMapper.readValue(serviceTask.getTextParam(), Map.class); |
| | | } catch (JsonProcessingException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return serviceTaskMap; |
| | | } |
| | | |
| | | public int qwenLLMTest(String questionText, String voiceText, String value, String regexText) { |
| | | int result = QwenLLMUtil.matchRegex(questionText, voiceText, value, regexText); |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 任务组(appltype=5)首轮纳入:扫描新出院患者,按 schedule 绑定的维度匹配任务组, |
| | | * 为命中的患者按计划明细一次性生成第 0 轮 N 条 subtask,并置 schedulecheck_flag=1。 |
| | | * 后续轮次由 {@link #rollTaskGroupCycle()} 负责。 |
| | | * <p> |
| | | * 与主扫描 dealOutHospInfo 分离,互不干扰。建议每 30 分钟执行一次。 |
| | | */ |
| | | public void dealTaskGroupOutHosp() { |
| | | log.info("【dealTaskGroupOutHosp】开始扫描任务组新出院患者"); |
| | | if (!port.equals("8095")) { |
| | | return; |
| | | } |
| | | try { |
| | | String config = configService.selectConfigByKey("visit.early.day"); |
| | | iPatMedInhospService.dealOutHospInfoBySchedule(config); |
| | | log.info("【dealTaskGroupOutHosp】任务组首轮纳入扫描完成"); |
| | | } catch (Exception e) { |
| | | log.error("【dealTaskGroupOutHosp】任务组扫描异常", e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 任务组(appltype=5)循环任务滚动调度。 |
| | | * <p> |
| | | * 扫描所有 schedule_style=1 的计划,若本轮 subtask 全部 finalize(sendstate ∉ 1/2/3), |
| | | * 则克隆本轮患者到下一轮,推进 current_loop_count 与 visit_time。 |
| | | * <p> |
| | | * 建议每小时或每半小时执行一次,避免轮次积压。 |
| | | */ |
| | | public void rollTaskGroupCycle() { |
| | | log.info("【rollTaskGroupCycle】开始扫描循环任务组"); |
| | | try { |
| | | int rolled = serviceTaskScheduleService.rollCycleTasks(); |
| | | log.info("【rollTaskGroupCycle】本次推进 {} 个计划", rolled); |
| | | } catch (Exception e) { |
| | | log.error("【rollTaskGroupCycle】滚动调度异常", e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 随访任务是否超时 |
| | | * @param serviceSubtask |
| | | * @param serviceSubtaskPreachforms |
| | | * @return |
| | | */ |
| | | public boolean isSubtaskDeadline(ServiceSubtask serviceSubtask, List<ServiceSubtaskPreachform> serviceSubtaskPreachforms){ |
| | | if (ObjectUtils.isNotEmpty(serviceSubtask)) { |
| | | //只有出院随访和专病随访有超时 |
| | | if(StringUtils.isNotEmpty(serviceSubtask.getServiceType()) && |
| | | !serviceSubtask.getServiceType().equals("2") && !serviceSubtask.getServiceType().equals("13")){ |
| | | return true; |
| | | } |
| | | } |
| | | if (Objects.isNull(serviceSubtask) || CollectionUtils.isEmpty(serviceSubtaskPreachforms)) { |
| | | return false; |
| | | } |
| | | // 基准时刻:出院时间 + 发送日(sendDay 缺省按 1),并兜底避免 NPE/脏数据 |
| | | ServiceTask serviceTask = serviceTaskMapper.selectServiceTaskByTaskid(serviceSubtask.getTaskid()); |
| | | int sendDay = serviceTask != null && serviceTask.getSendDay() != null ? serviceTask.getSendDay().intValue() : 1; |
| | | Date deadline = DateUtils.addDays(serviceSubtask.getEndtime(), sendDay); |
| | | int sumCompensateTime = CollectionUtils.isEmpty(serviceSubtaskPreachforms) ? 0 : serviceSubtaskPreachforms.stream() |
| | | .mapToInt(x -> x.getCompensateTime() == null ? 0 : Integer.parseInt(x.getCompensateTime())) |
| | | .sum(); |
| | | sumCompensateTime = sumCompensateTime + 1; |
| | | // 只补偿最后一步自身的补偿时间(天),脏值一律按 0 兜底 |
| | | deadline = DateUtils.addDays(deadline, sumCompensateTime); |
| | | // 出院(发送)时间 + 本步补偿时间 小于当前时间 -> 超时 |
| | | return deadline.getTime() < new Date().getTime(); |
| | | } |
| | | |
| | | /** |
| | | * 处理超时随访任务 |
| | | * @return |
| | | */ |
| | | public void dealSubtaskDeadline(String orgid, Integer deadlineDay){ |
| | | //获取任务信息 |
| | | ServiceTask st = new ServiceTask(); |
| | | st.setDelFlag("0"); |
| | | st.setLongTask(1); |
| | | st.setNexttaskflag("1"); |
| | | st.setOrgid(orgid); |
| | | List<ServiceTask> serviceTasks = serviceTaskMapper.selectServiceTaskList(st); |
| | | for (ServiceTask serviceTask : serviceTasks) { |
| | | deadlineDay = -deadlineDay; |
| | | Date deadline = DateUtils.addDays(new Date(), deadlineDay); |
| | | //通过任务ID拿到患者信息,并且随访时间得是今天之前的 |
| | | ServiceSubtaskEntity serviceSubtaskVO = new ServiceSubtaskEntity(); |
| | | serviceSubtaskVO.setOrgid(orgid); |
| | | serviceSubtaskVO.setSendstate(7L); |
| | | //只扫描首次随访的 |
| | | serviceSubtaskVO.setVisitCount(1); |
| | | serviceSubtaskVO.setTaskid(serviceTask.getTaskid()); |
| | | serviceSubtaskVO.setEndOutHospTime(deadline); |
| | | List<ServiceSubtask> serviceSubtaskList |
| | | = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskVO); |
| | | log.info("【dealSubtaskDeadline】获取到{}个超时任务, taskid={}", serviceSubtaskList.size(), serviceTask.getTaskid()); |
| | | for (ServiceSubtask serviceSubtask : serviceSubtaskList) { |
| | | PatMedInhosp patMedInhospVo = new PatMedInhosp(); |
| | | patMedInhospVo.setOrgid(serviceSubtask.getOrgid()); |
| | | patMedInhospVo.setInhospid(serviceSubtask.getInhospid()); |
| | | List<PatMedInhosp> patMedInhospList = iPatMedInhospService.selectPatMedInhospList(patMedInhospVo); |
| | | PatMedInhosp patMedInhosp = new PatMedInhosp(); |
| | | if (!patMedInhospList.isEmpty()) { |
| | | patMedInhosp = patMedInhospList.get(0); |
| | | }else { |
| | | continue; |
| | | } |
| | | if(ObjectUtils.isEmpty(serviceTask.getNexttaskid())){ |
| | | continue; |
| | | } |
| | | ServiceTask nextServiceTask = serviceTaskMapper.selectServiceTaskByTaskid(serviceTask.getNexttaskid()); |
| | | PatArchive patArchive = patArchiveService.selectPatArchiveByPatid(serviceSubtask.getPatid()); |
| | | String config = configService.selectConfigByKey("visit.early.day"); |
| | | Integer type = 1; |
| | | |
| | | //新增再次随访任务 |
| | | ServiceSubtask serviceSubtaskAgain = iPatMedInhospService.boxedServiceSubtask(nextServiceTask, |
| | | patMedInhosp, patArchive, config, type); |
| | | serviceSubtaskAgain.setId(null); |
| | | serviceSubtaskAgain.setUpid(serviceSubtask.getId().intValue()); |
| | | serviceSubtaskAgain.setVisitCount(serviceSubtask.getVisitCount() + 1); |
| | | serviceSubtaskAgain.setSendstate(2L); |
| | | serviceSubtaskAgain.setCreateTime(new Date()); |
| | | serviceSubtaskAgain.setUpdateTime(new Date()); |
| | | serviceSubtaskAgain.setVisitType(serviceSubtask.getVisitType()); |
| | | serviceSubtaskAgain.setVisitDeptCode(serviceSubtask.getVisitDeptCode()); |
| | | serviceSubtaskAgain.setVisitDeptName(serviceSubtask.getVisitDeptName()); |
| | | int i = serviceSubtaskMapper.insertServiceSubtask(serviceSubtaskAgain); |
| | | if (i == 1) { |
| | | //再次随访任务创建成功后,原任务改为失败状态 |
| | | serviceSubtask.setSendstate(5L); |
| | | serviceSubtaskMapper.updateServiceSubtask(serviceSubtask); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |