liusheng
4 天以前 0bbbd746e8c39d91ad7b47a4e36e29ab972dc98c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
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 数据采集调度编排实现 ——
 * <b>由 {@code ruoyi-quartz} 的 {@code RyTask#dealHisData} + {@code collectSegmentData} 原样搬入</b>。
 *
 * <h3>搬运纪律(本类刻意"零改动")</h3>
 * <ul>
 *   <li>站点分支({@code xh} / {@code hn} / {@code sltd}-{@code nhfy})、时间窗口算法、
 *       30 天分段回溯的段长与顺序、每个采集动作的先后、异常吞掉的范围、日志文案
 *       —— 全部与搬运前一致;</li>
 *   <li>唯一的形式变化:内部 VO({@code ServiceSLTDInhospReqVO} / {@code ServiceSLTDDeptReqVO} /
 *       {@code HnDataGatherVO})换成契约 DTO {@link SltdGatherReq} / 两个 {@code Date},
 *       由 smartor 侧 Bridge 逐字段搬回原 VO;字段设置顺序与"显式置 null"都保留;
 *   <li>{@code CollectionUtils.isNotEmpty(...)} 换成等价的 {@code list != null && !list.isEmpty()}
 *       (避免为一次判空给模块引 commons-collections4);</li>
 *   <li>{@code sys_config} 的读法<b>刻意保持</b> {@code selectConfigList(条件)} + 取第 0 行
 *       (含原实现的 LIKE 匹配与"不排序取首行"语义),没有改成 {@code selectConfigByKey}
 *       —— 那是"顺手优化",会改变查询语义,回归时无法逐条比对。</li>
 * </ul>
 *
 * <h3>它不管什么</h3>
 * 真正的采集动作(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)。
     * <p>
     * ⚠️ 刻意<b>直接注入</b>而不是 {@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<SysConfig> sysConfigs = configService.selectConfigList(config);
 
                //获取省立同德要采集的院区
                SysConfig cf = new SysConfig();
                cf.setConfigKey("sltd.gather.campusid");
                cf.setOrgid(SLTD_ORG_ID);
                List<SysConfig> sysConfigcampusids = configService.selectConfigList(cf);
 
                List<Long> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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);
    }
}