package com.ruoyi.datacollection.service.impl; import com.ruoyi.common.core.service.HisGatherApi; import com.ruoyi.common.core.service.dto.SltdGatherReq; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.datacollection.service.IHisDataCollectionService; import com.ruoyi.system.domain.SysConfig; import com.ruoyi.system.service.ISysConfigService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.stream.Collectors; /** * HIS 数据采集调度编排实现 —— * 由 {@code ruoyi-quartz} 的 {@code RyTask#dealHisData} + {@code collectSegmentData} 原样搬入。 * *

搬运纪律(本类刻意"零改动")

* * *

它不管什么

* 真正的采集动作(HTTP 取数、入库)不在本类:那里仍在 smartor,经 * {@link HisGatherApi} 契约调用,后续分批搬进本模块。 * * @see com.ruoyi.datacollection.api.DataCollectionFacade 对外门面 */ @Slf4j @Service public class HisDataCollectionServiceImpl implements IHisDataCollectionService { /** * 省立同德 / 南华附一的机构ID(原实现里的字面量 {@code "20001001"}) */ private static final String SLTD_ORG_ID = "20001001"; /** * 当时站点标识(原 {@code RyTask} 的 {@code @Value("${spring.profiles.active}")}) */ @Value("${spring.profiles.active}") private String active; /** * HIS 采集能力契约(当前实现在 smartor:纯转发到既有 Service)。 *

* ⚠️ 刻意直接注入而不是 {@code ObjectProvider} 惰性取: * 采集实现是数据链路的必需件,"取不到就静默不采"正是最危险的失败形态, * 这里让它启动期就暴露(宁可启动失败,也不要静默少数据)。 */ @Autowired private HisGatherApi hisGatherApi; /** * 参数配置服务(ruoyi-system 的 API):读 {@code sltd.gather.sysuser} / {@code sltd.gather.campusid} */ @Autowired private ISysConfigService configService; /** * HIS数据采集 * 定时任务:采集患者信息、出院、门诊数据 * * @param todayZeroStr 当前的时间 例如:2026-05-06 00:00:00 */ @Override 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】新华开始采集入院数据"); hisGatherApi.gatherXhInhospRecords("0", yesterdayZero, todayZero); log.info("【dealHisData】新华入院数据采集完成"); // 采集出院数据 log.info("【dealHisData】新华开始采集出院数据"); hisGatherApi.gatherXhInhospRecords("1", yesterdayZero, todayZero); log.info("【dealHisData】新华出院数据采集完成"); } catch (Exception e) { log.error("【dealHisData】新华数据采集异常", e); } } else if (active.trim().equals("hn")) { try { // 河南数据采集 log.info("【dealHisData】河南开始采集数据"); Date startTime = Date.from(yesterdayZero.atZone(ZoneId.systemDefault()).toInstant()); Date endTime = Date.from(todayZero.atZone(ZoneId.systemDefault()).toInstant()); hisGatherApi.gatherHnRecords(startTime, endTime); 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(SLTD_ORG_ID); List sysConfigs = configService.selectConfigList(config); //获取省立同德要采集的院区 SysConfig cf = new SysConfig(); cf.setConfigKey("sltd.gather.campusid"); cf.setOrgid(SLTD_ORG_ID); List sysConfigcampusids = configService.selectConfigList(cf); List campusidList = new ArrayList<>(); if (sysConfigcampusids != null && !sysConfigcampusids.isEmpty()) { 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 (sysConfigs != null && !sysConfigs.isEmpty() && sysConfigs.get(0).getConfigValue().equals("2")) { log.info("【dealHisData】省立同德“部门信息”开始采集数据"); SltdGatherReq serviceSLTDDeptReqVO = new SltdGatherReq(); serviceSLTDDeptReqVO.setOrgId(SLTD_ORG_ID); serviceSLTDDeptReqVO.setCampusIds(campusidList); Boolean aBoolean1 = hisGatherApi.gatherSltdDeptWardArea(serviceSLTDDeptReqVO); log.info("【dealHisData】省立同德“部门信息”结束采集数据:{}", aBoolean1); Boolean aBoolean = false; for (Long cid : campusidList) { log.info("【dealHisData】省立同德“用户信息”开始采集数据,院区:{}", cid); SltdGatherReq reqVO = new SltdGatherReq(); reqVO.setOrgId(SLTD_ORG_ID); reqVO.setCampusId(cid); ArrayList statusList = new ArrayList<>(); statusList.add("1"); reqVO.setStatusList(statusList); aBoolean = hisGatherApi.gatherSltdHospUser(reqVO); } log.info("【dealHisData】省立同德“用户信息”结束采集数据:{}", aBoolean); //疾病字典采集 hisGatherApi.gatherIcd10(); } // 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")); SltdGatherReq serviceSLTDInhospReqVO = new SltdGatherReq(); serviceSLTDInhospReqVO.setOrgId(SLTD_ORG_ID); 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 list = new ArrayList<>(); list.add("FH0109.22"); list.add("FH0109.23"); list.add("FH0109.53"); serviceSLTDInhospReqVO.setStatusList(list); // log.info("【dealHisData】省立同德“预入院”采集数据入参:{}", serviceSLTDInhospReqVO); hisGatherApi.gatherSltdHealthcareRecords(serviceSLTDInhospReqVO); log.info("【dealHisData】省立同德“预入院”結束采集数据,院区:{}", cid); log.info("【dealHisData】省立同德“入院”开始采集数据,院区:{}", cid); serviceSLTDInhospReqVO.setOrgId(SLTD_ORG_ID); 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 listStr = new ArrayList<>(); listStr.add("FH0109.24"); serviceSLTDInhospReqVO.setStatusList(listStr); // log.info("【dealHisData】省立同德“入院”采集数据入参:{}", serviceSLTDInhospReqVO); hisGatherApi.gatherSltdHealthcareRecords(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 list2 = new ArrayList<>(); // list2.add("FH0108.02"); //出院 list2.add("FH0109.27"); //预出院 list2.add("FH0109.25"); serviceSLTDInhospReqVO.setStatusList(list2); // log.info("【dealHisData】省立同德“出院”采集数据入参:{}", serviceSLTDInhospReqVO); hisGatherApi.gatherSltdHealthcareRecords(serviceSLTDInhospReqVO); log.info("【dealHisData】省立同德“出院”束采集数据,院区:{}", cid); } } } 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); SltdGatherReq reqVO = new SltdGatherReq(); reqVO.setOrgId(SLTD_ORG_ID); reqVO.setCampusId(cid); reqVO.setEncounterTimeStart(startDateStr); reqVO.setEncounterTimeEnd(endDateStr); List preAdmitStatus = new ArrayList<>(); preAdmitStatus.add("FH0109.22"); preAdmitStatus.add("FH0109.23"); preAdmitStatus.add("FH0109.53"); reqVO.setStatusList(preAdmitStatus); log.info("【dealHisData】预入院采集数据入参:{}", reqVO); try { hisGatherApi.gatherSltdHealthcareRecords(reqVO); } catch (Exception e) { // 单类数据失败不再向上抛:否则一段失败会让本次回溯后面的所有段都不执行 log.error("【dealHisData】预入院采集失败,跳过该类继续。院区:{},时间段:{} ~ {}", cid, startDateStr, endDateStr, e); } log.info("【dealHisData】预入院終了采集数据,院区:{}", cid); // 入院 log.info("【dealHisData】入院开始采集数据,院区:{},时间段:{} ~ {}", cid, startDateStr, endDateStr); reqVO = new SltdGatherReq(); reqVO.setOrgId(SLTD_ORG_ID); reqVO.setCampusId(cid); reqVO.setStartHeadTime(startDateStr); reqVO.setStartTailTime(endDateStr); List admitStatus = new ArrayList<>(); admitStatus.add("FH0109.24"); reqVO.setStatusList(admitStatus); log.info("【dealHisData】入院采集数据入参:{}", reqVO); try { hisGatherApi.gatherSltdHealthcareRecords(reqVO); } catch (Exception e) { // 单类数据失败不再向上抛:否则一段失败会让本次回溯后面的所有段都不执行 log.error("【dealHisData】入院采集失败,跳过该类继续。院区:{},时间段:{} ~ {}", cid, startDateStr, endDateStr, e); } log.info("【dealHisData】入院終了采集数据,院区:{}", cid); // 出院 log.info("【dealHisData】出院开始采集数据,院区:{},时间段:{} ~ {}", cid, startDateStr, endDateStr); reqVO = new SltdGatherReq(); reqVO.setOrgId(SLTD_ORG_ID); reqVO.setCampusId(cid); reqVO.setPreOutHospitalHeadDate(startDateStr); reqVO.setPreOutHospitalTailDate(endDateStr); List dischargeStatus = new ArrayList<>(); //出院 dischargeStatus.add("FH0109.27"); //预出院 dischargeStatus.add("FH0109.25"); reqVO.setStatusList(dischargeStatus); log.info("【dealHisData】出院采集数据入参:{}", reqVO); try { hisGatherApi.gatherSltdHealthcareRecords(reqVO); } catch (Exception e) { // 单类数据失败不再向上抛:否则一段失败会让本次回溯后面的所有段都不执行 log.error("【dealHisData】出院采集失败,跳过该类继续。院区:{},时间段:{} ~ {}", cid, startDateStr, endDateStr, e); } log.info("【dealHisData】出院结束采集数据,院区:{}", cid); } }