| | |
| | | @Autowired |
| | | private IServicePatientTempService iServicePatientTempService; |
| | | |
| | | @Autowired |
| | | private IHNGatherPatArchiveService ihnGatherPatArchiveService; |
| | | // ⚠️ 2026-10 移除:原 `IHNGatherPatArchiveService ihnGatherPatArchiveService` 字段。 |
| | | // 它在本类里**只**被 dealHisData 的河南分支使用(死字段检查:改前全文件仅 1 处使用), |
| | | // 该分支已随方法体搬进 ruoyi-datacollection,故字段连带删除 —— |
| | | // 留着它会让人误以为 smartor 的河南采集还有本类这个使用方 |
| | | // (真正的使用方已是数据采集模块经契约 HisGatherApi 调用的 Bridge)。 |
| | | |
| | | @Autowired |
| | | private IServiceSLTDHealthcareRecordService serviceSLTDHealthcareRecordService; |
| | |
| | | @Autowired |
| | | private IServiceSubtaskRecordService serviceSubtaskRecordService; |
| | | |
| | | @Autowired |
| | | IIcd10Service iIcd10Service; |
| | | // ⚠️ 2026-10 移除:原 `IIcd10Service iIcd10Service` 字段。 |
| | | // 它在本类里**只**被 dealHisData 的"疾病字典增量采集"一行调用,已随方法体搬进 |
| | | // ruoyi-datacollection(经契约 HisGatherApi#gatherIcd10 调回 smartor 的实现)。 |
| | | |
| | | @Autowired |
| | | IPatArchiveService patArchiveService; |
| | |
| | | @Autowired |
| | | private com.ruoyi.publicity.api.PublicityFacade publicityFacade; |
| | | |
| | | @Value("${appid}") |
| | | private String appid; |
| | | /** |
| | | * 数据采集模块的<b>常驻门面</b>。 |
| | | * <p> |
| | | * ⚠️ 2026-10 模块化:本类的 {@code dealHisData(String)} 方法体(连同私有帮手 |
| | | * {@code collectSegmentData})已整体搬进 {@code ruoyi-datacollection}, |
| | | * 本类只保留定时任务入口(sys_job 的 invoke_target 是 {@code ryTask.dealHisData('...')}, |
| | | * 入口名不能变)。站点分支、时间窗口、分段回溯、日志文案都在模块里,一行未改。 |
| | | * <p> |
| | | * 注入门面而不是模块内部 Service:门面<b>永远存在</b>,且只有门面判 |
| | | * {@code sys_config: datacollection.enabled} 开关。 |
| | | */ |
| | | @Autowired |
| | | private com.ruoyi.datacollection.api.DataCollectionFacade dataCollectionFacade; |
| | | |
| | | // ⚠️ 2026-10 删除:原 `@Value("${appid}") private String appid;` |
| | | // 它是「新华微信公众号推送」的遗留字段(提交 8590ef6a 引入,只被 |
| | | // xinHuaWXReq.setAppid(appid) 用过)。该推送逻辑已搬进 ruoyi-message |
| | | // (公众号凭据改由 sys_config.wx.mp.config.* 下发),本类里已无引用; |
| | | // 但它的 @Value 没有默认值,而 application-nhfy.yml / -xh.yml / -ls.yml |
| | | // 都没定义 appid 这个 key,这些 profile 启动时会直接报 |
| | | // `Could not resolve placeholder 'appid'`。留着是定时炸弹,故删除。 |
| | | |
| | | @Value("${server.port}") |
| | | private String port; |
| | |
| | | * HIS数据采集 |
| | | * 定时任务:采集患者信息、出院、门诊数据 |
| | | * todayZeroStr 当前的时间 例如:2026-05-06 00:00:00 |
| | | * <p> |
| | | * ⚠️ <b>2026-10 模块化</b>:方法体(连同私有帮手 {@code collectSegmentData})已整体搬进 |
| | | * {@code ruoyi-datacollection},实现见 |
| | | * {@code com.ruoyi.datacollection.service.impl.HisDataCollectionServiceImpl}。 |
| | | * 这里只保留<b>定时任务入口</b>:sys_job 的 invoke_target 仍是 |
| | | * {@code ryTask.dealHisData('...')},入口名不能变,所以本方法是<b>纯转发</b>。 |
| | | * <p> |
| | | * 关闭数据采集模块({@code sys_config: datacollection.enabled=false})时门面会抛 |
| | | * {@link com.ruoyi.datacollection.api.DataCollectionModuleDisabledException}; |
| | | * 本入口捕获后只打一条 WARN —— 那是"模块被主动关掉",不是采集失败, |
| | | * 不该让定时任务记成执行异常(否则运维会看到一片红色失败记录)。 |
| | | */ |
| | | 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); |
| | | |
| | | if (active.trim().equals("xh")) { |
| | | try { |
| | | // 采集入院数据 |
| | | log.info("【dealHisData】新华开始采集入院数据"); |
| | | ixhGatherPatArchiveService.getInHospDataGather("0", yesterdayZero, todayZero); |
| | | log.info("【dealHisData】新华入院数据采集完成"); |
| | | |
| | | // 采集出院数据 |
| | | log.info("【dealHisData】新华开始采集出院数据"); |
| | | ixhGatherPatArchiveService.getInHospDataGather("1", yesterdayZero, todayZero); |
| | | log.info("【dealHisData】新华出院数据采集完成"); |
| | | |
| | | } catch (Exception e) { |
| | | log.error("【dealHisData】新华数据采集异常", e); |
| | | } |
| | | } else if (active.trim().equals("hn")) { |
| | | try { |
| | | // 河南数据采集 |
| | | HnDataGatherVO hnDataGatherVO = new HnDataGatherVO(); |
| | | log.info("【dealHisData】河南开始采集数据"); |
| | | Date startTime = Date.from(yesterdayZero.atZone(ZoneId.systemDefault()).toInstant()); |
| | | Date endTime = Date.from(todayZero.atZone(ZoneId.systemDefault()).toInstant()); |
| | | |
| | | hnDataGatherVO.setStartTime(startTime); |
| | | hnDataGatherVO.setEndTime(endTime); |
| | | ihnGatherPatArchiveService.hnDataGather(hnDataGatherVO); |
| | | log.info("【dealHisData】河南结束采集数据"); |
| | | } catch (Exception e) { |
| | | log.error("【dealHisData】河南数据采集异常", e); |
| | | } |
| | | } else if (active.trim().equals("sltd") || active.trim().equals("nhfy")) { |
| | | //省立同德与华南附一用同一套采集方法(都是来未来提供) |
| | | |
| | | try { |
| | | //获取是否需要采集用户、部门信息 |
| | | SysConfig config = new SysConfig(); |
| | | config.setConfigKey("sltd.gather.sysuser"); |
| | | config.setOrgid("20001001"); |
| | | List<SysConfig> sysConfigs = configService.selectConfigList(config); |
| | | |
| | | //获取省立同德要采集的院区 |
| | | SysConfig cf = new SysConfig(); |
| | | cf.setConfigKey("sltd.gather.campusid"); |
| | | cf.setOrgid("20001001"); |
| | | List<SysConfig> sysConfigcampusids = configService.selectConfigList(cf); |
| | | |
| | | List<Long> campusidList = new ArrayList<>(); |
| | | if (CollectionUtils.isNotEmpty(sysConfigcampusids)) { |
| | | String[] split = sysConfigcampusids.get(0).getConfigValue().split(","); |
| | | campusidList = Arrays.stream(split).map(Long::valueOf).collect(Collectors.toList()); |
| | | } else { |
| | | log.error("SysConfig中的院区ID为空!"); |
| | | return; |
| | | } |
| | | |
| | | //configValue = 1 不需要采集 configValue = 2需要采集 |
| | | if (CollectionUtils.isNotEmpty(sysConfigs) && sysConfigs.get(0).getConfigValue().equals("2")) { |
| | | log.info("【dealHisData】省立同德“部门信息”开始采集数据"); |
| | | ServiceSLTDDeptReqVO serviceSLTDDeptReqVO = new ServiceSLTDDeptReqVO(); |
| | | serviceSLTDDeptReqVO.setOrgId("20001001"); |
| | | serviceSLTDDeptReqVO.setCampusIds(campusidList); |
| | | Boolean aBoolean1 = serviceSLTDHealthcareRecordService.queryDeptWardAreaInfoList(serviceSLTDDeptReqVO); |
| | | log.info("【dealHisData】省立同德“部门信息”结束采集数据:{}", aBoolean1); |
| | | |
| | | Boolean aBoolean = false; |
| | | for (Long cid : campusidList) { |
| | | log.info("【dealHisData】省立同德“用户信息”开始采集数据,院区:{}", cid); |
| | | ServiceSLTDInhospReqVO reqVO = new ServiceSLTDInhospReqVO(); |
| | | reqVO.setOrgId("20001001"); |
| | | reqVO.setCampusId(cid); |
| | | ArrayList<String> statusList = new ArrayList<>(); |
| | | statusList.add("1"); |
| | | reqVO.setStatusList(statusList); |
| | | aBoolean = serviceSLTDHealthcareRecordService.queryHospUserInfoList(reqVO); |
| | | } |
| | | log.info("【dealHisData】省立同德“用户信息”结束采集数据:{}", aBoolean); |
| | | //疾病字典采集 |
| | | iIcd10Service.queryAddIcd10(); |
| | | } |
| | | |
| | | // 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); |
| | | } |
| | | |
| | | // 省立同德数据采集(非回溯模式才执行) |
| | | 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.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.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"); |
| | | //预出院 |
| | | list2.add("FH0109.25"); |
| | | serviceSLTDInhospReqVO.setStatusList(list2); |
| | | // log.info("【dealHisData】省立同德“出院”采集数据入参:{}", serviceSLTDInhospReqVO); |
| | | serviceSLTDHealthcareRecordService.queryHealthcareRecordList(serviceSLTDInhospReqVO); |
| | | log.info("【dealHisData】省立同德“出院”束采集数据,院区:{}", cid); |
| | | } |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | log.error("【dealHisData】省立同德数据采集异常", e); |
| | | } |
| | | dataCollectionFacade.dealHisData(todayZeroStr); |
| | | } catch (com.ruoyi.datacollection.api.DataCollectionModuleDisabledException e) { |
| | | log.warn("【dealHisData】{}(本轮定时采集跳过)", e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 分段采集数据(预入院、入院、出院) |
| | | * 用于nhfy/sltd回溯采集,每次最多查询7天数据 |
| | | * 在院患者"科室 / 病区 / 床位"刷新(南华附一,每天一次)。 |
| | | * <p> |
| | | * 在院患者会转科、转病区、换床,而 {@code pat_med_inhosp} 的位置字段只在入院时写过一次, |
| | | * 之后不会自己更新 —— 这里每天按患者查一遍 HIS 里<b>该患者当前的在院信息</b> |
| | | * ({@code dtcQueryHealthcareRecordList},与入院采集是同一个接口,按 |
| | | * {@code healthcareRecordNo} 单查 + {@code statusList=["FH0109.24"]}), |
| | | * 与库内比对后只把变化的字段写回去。实现与判定口径见 |
| | | * {@code com.ruoyi.datacollection.service.impl.InhospDeptRefreshServiceImpl}。 |
| | | * <p> |
| | | * 与 {@link #dealHisData(String)} 同样的降级口径:模块被关掉( |
| | | * {@code sys_config: datacollection.enabled=false})时门面抛 |
| | | * {@link com.ruoyi.datacollection.api.DataCollectionModuleDisabledException}, |
| | | * 这里只打一条 WARN —— 那是"模块被主动关掉",不该记成定时任务执行异常。 |
| | | * <p> |
| | | * sys_job 的 invoke_target 为 {@code ryTask.refreshInhospDeptInfo()}, |
| | | * 建议每天 03:30(避开 01:00 入院采集与 02:00 出院处理),并发策略选"禁止并发"。 |
| | | * |
| | | * @param cid 院区ID |
| | | * @param startDateStr 日期开始 yyyy-MM-dd |
| | | * @param endDateStr 日期结束 yyyy-MM-dd |
| | | * @return 本次实际更新的在院记录条数(0 = 无变化 / 站点不匹配 / 模块被关闭) |
| | | */ |
| | | 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); |
| | | public int refreshInhospDeptInfo() { |
| | | try { |
| | | serviceSLTDHealthcareRecordService.queryHealthcareRecordList(reqVO); |
| | | } catch (Exception e) { |
| | | // 单类数据失败不再向上抛:否则一段失败会让本次回溯后面的所有段都不执行 |
| | | log.error("【dealHisData】预入院采集失败,跳过该类继续。院区:{},时间段:{} ~ {}", cid, startDateStr, endDateStr, e); |
| | | return dataCollectionFacade.refreshInhospDeptInfo(); |
| | | } catch (com.ruoyi.datacollection.api.DataCollectionModuleDisabledException e) { |
| | | log.warn("【refreshInhospDeptInfo】{}(本轮在院信息刷新跳过)", e.getMessage()); |
| | | return 0; |
| | | } |
| | | 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); |
| | | } |
| | | |
| | | /** |