liusheng
8 天以前 34f78281cf395a24e1f3f1c3b861186ff8a03b7b
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
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
package com.ruoyi.inhospvisit.service;
 
import com.ruoyi.common.core.service.PublicityDataPort;
import com.ruoyi.common.core.service.VisitTaskCreateApi;
import com.ruoyi.common.core.service.dto.VisitInhospUpdateDto;
import com.ruoyi.common.core.service.dto.VisitPendingSubtaskDto;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.service.dto.VisitArchiveRow;
import com.ruoyi.inhospvisit.domain.row.VisitInhospRow;
import com.ruoyi.inhospvisit.domain.row.VisitTaskdeptRow;
import com.ruoyi.inhospvisit.domain.row.VisitTaskdiagRow;
import com.ruoyi.inhospvisit.mapper.VisitArchiveMapper;
import com.ruoyi.inhospvisit.mapper.VisitInhospMapper;
import com.ruoyi.inhospvisit.mapper.VisitTaskdeptMapper;
import com.ruoyi.inhospvisit.mapper.VisitTaskdiagMapper;
import com.ruoyi.inhospvisit.support.InhospFlagWriter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
 
import java.util.Date;
import java.util.List;
 
/**
 * 出院随访「编排器」—— 逐行搬自 {@code PatMedInhospServiceImpl}
 * 的编排层({@code addSubTaskByRule} + 3 个 {@code *ForPatient} + {@code dealInHospInfo})。
 *
 * <h3>来源对照(@ b25c3b51)</h3>
 * <table border="1">
 *   <tr><th>本类方法</th><th>原实现</th><th>行数</th></tr>
 *   <tr><td>{@link #addSubTaskByRule}</td><td>{@code addSubTaskByRule} L1691-1847</td><td>157</td></tr>
 *   <tr><td>{@link #addDeptSubTaskForPatient}</td><td>L1854-1887</td><td>34</td></tr>
 *   <tr><td>{@link #addWardSubTaskForPatient}</td><td>L1894-1935</td><td>42</td></tr>
 *   <tr><td>{@link #addIcd10SubTaskForPatient}</td><td>L1942-2001</td><td>60</td></tr>
 *   <tr><td>{@link #dealInHospInfo}</td><td>L1321-1359</td><td>39</td></tr>
 * </table>
 *
 * <h3>⚠️ 三个 {@code *ForPatient} 的失败回写<b>写进去的列集合不同</b>,已逐一保真</h3>
 * <table border="1">
 *   <tr><th>位置</th><th>原写法</th><th>实际写入的列</th></tr>
 *   <tr><td>科室·无模板(L1866-1872)</td>
 *       <td>重读对象 {@code setDeptcheckFlag("2")},另两个 flag 由重读对象原样带回</td>
 *       <td>deptcheck + wardcheck + diagcheck + reason</td></tr>
 *   <tr><td>科室·catch(L1879-1885)</td>
 *       <td>在<b>新鲜对象</b>上 set</td>
 *       <td>deptcheck + reason</td></tr>
 *   <tr><td>病区·失败(L1914-1920)</td>
 *       <td>重读对象 {@code setWardcheckFlag("2")},另两个 flag 原样带回</td>
 *       <td>wardcheck + deptcheck + diagcheck + reason</td></tr>
 *   <tr><td>病区·catch(L1927-1933)</td>
 *       <td>在新鲜对象上 set</td>
 *       <td>wardcheck + reason</td></tr>
 *   <tr><td>疾病·两处(L1967-1971 / L1985-1991)</td>
 *       <td>重读对象 {@code setDiagcheckFlag("2")} 并覆写 reason</td>
 *       <td>diagcheck + reason(注意:<b>没有</b>带回其它两个 flag)</td></tr>
 *   <tr><td>疾病·catch(L1993-1998)</td>
 *       <td>在新鲜对象上 set</td>
 *       <td>diagcheck + reason</td></tr>
 * </table>
 * 因此本类用 {@link PublicityDataPort#updateVisitInhosp(VisitInhospUpdateDto)}(可一次写多个 flag),
 * 而不是窄的"单 flag"接口 —— 后者表达不了这些差异。
 *
 * <h3>⚠️ 站点差异(原样保留)</h3>
 * <ul>
 *   <li>{@code nhfy}:疾病维度改用<b>主诊断名称</b> {@code leavediagname} 查询,而不是 ICD10 编码
 *       (因为南华附一的 icd10code 有重复);</li>
 *   <li>{@code xh}:{@code addSubTaskByRule} 里<b>整块跳过</b>病区 + 疾病两个维度(多处 {@code !xh} 判断)。</li>
 * </ul>
 */
@Slf4j
@Service
public class InhospTaskOrchestrator {
 
    /** 出院随访的服务类型 */
    private static final String SERVICE_TYPE_DISCHARGE = "2";
 
    /** 维度:科室 */
    private static final String DEPT_TYPE_DEPT = "1";
 
    /** 维度:病区 */
    private static final String DEPT_TYPE_WARD = "2";
 
    /** 站点:新华(跳过病区+疾病维度) */
    private static final String SITE_XH = "xh";
 
    /** 站点:南华附一(疾病维度用诊断名) */
    private static final String SITE_NHFY = "nhfy";
 
    @Value("${spring.profiles.active}")
    private String active;
 
    @Autowired
    private VisitInhospMapper inhospMapper;
 
    @Autowired
    private VisitArchiveMapper archiveMapper;
 
    @Autowired
    private VisitTaskdeptMapper taskdeptMapper;
 
    @Autowired
    private VisitTaskdiagMapper taskdiagMapper;
 
    @Autowired
    private InhospSubtaskWriter subtaskWriter;
 
    @Autowired
    private InhospFlagWriter flagWriter;
 
    @Autowired
    private ObjectProvider<PublicityDataPort> dataPortProvider;
 
    @Autowired
    private ObjectProvider<VisitTaskCreateApi> taskCreateApiProvider;
 
    // ==========================================================================
    // 1. 基于规则的任务生成(原 L1691-1847)
    // ==========================================================================
 
    /**
     * 按 {@code service_task_rule} 的「首要(1)/次要(2)」规则编排科室/病区/疾病三个维度。
     * <p>
     * 逐行对应原 {@code addSubTaskByRule(String config, List<PatMedInhosp> patMedInhosps)}。
     * <p>
     * ⚠️ 规则查询 {@code serviceTaskRuleMapper.selectServiceTaskRuleByDeptOrWard} 仍在 smartor,
     * 所以本方法接收<b>已查好的规则</b>(由调用方 {@code InhospVisitTaskService} 通过
     * {@code PublicityDataPort} 取——该端口已含"按科室/病区查规则"的能力,见下)。
     * 这样做是为了避免为"规则表"再加一个 Mapper 或端口。
     *
     * @param inhosp 待处理的出院患者
     * @param archive 患者档案
     * @param rule 已查好的规则;{@code null} = 未配置规则(全部生成)
     * @param config {@code sys_config: visit.early.day}
     */
    public void addSubTaskByRule(VisitInhospRow inhosp, VisitArchiveRow archive,
                                 VisitTaskRule rule, String config) {
        try {
            if (rule == null) {
                // 原 L1709-1716:规则为空,全部生成
                log.info("【基于规则】患者{}未配置规则,执行全部生成逻辑", inhosp.getInhospid());
                addDeptSubTaskForPatient(inhosp, archive, config);
                if (!SITE_XH.equals(active)) {
                    addWardSubTaskForPatient(inhosp, archive, config);
                    addIcd10SubTaskForPatient(inhosp, archive, config);
                }
                return;
            }
            // 原 L1717-1726:有规则,按规则执行
            log.info("【基于规则】患者{}查询到规则:deptRule={}, wradRule={}, diagRule={}",
                    inhosp.getInhospid(), rule.getDeptRule(), rule.getWradRule(), rule.getDiagRule());
 
            boolean deptIsPrimary = "1".equals(rule.getDeptRule());
            boolean wardIsPrimary = "1".equals(rule.getWradRule());
            boolean diagIsPrimary = "1".equals(rule.getDiagRule());
            boolean deptIsSecondary = "2".equals(rule.getDeptRule());
            boolean wardIsSecondary = "2".equals(rule.getWradRule());
            boolean diagIsSecondary = "2".equals(rule.getDiagRule());
 
            boolean hasPrimaryRule = deptIsPrimary || wardIsPrimary || diagIsPrimary;
 
            if (!hasPrimaryRule) {
                // 原 L1829-1837:没有首要规则,全部生成
                log.info("【基于规则】无首要规则,执行全部生成逻辑");
                addDeptSubTaskForPatient(inhosp, archive, config);
                if (!SITE_XH.equals(active)) {
                    addWardSubTaskForPatient(inhosp, archive, config);
                    addIcd10SubTaskForPatient(inhosp, archive, config);
                }
                return;
            }
 
            // 原 L1730-1754:先执行所有规则=1 的方法
            int deptGenCount = 0;
            int wardGenCount = 0;
            int diagGenCount = 0;
 
            if (deptIsPrimary) {
                log.info("【基于规则】执行科室首要规则");
                deptGenCount = addDeptSubTaskForPatient(inhosp, archive, config);
            }
            if (!SITE_XH.equals(active)) {
                if (wardIsPrimary) {
                    log.info("【基于规则】执行病区首要规则");
                    wardGenCount = addWardSubTaskForPatient(inhosp, archive, config);
                }
                if (diagIsPrimary) {
                    log.info("【基于规则】执行疾病首要规则");
                    diagGenCount = addIcd10SubTaskForPatient(inhosp, archive, config);
                }
            }
 
            int primaryGeneratedCount = deptGenCount + wardGenCount + diagGenCount;
            log.info("【基于规则】首要规则执行结果: deptGen={}, wardGen={}, diagGen={}, total={}",
                    deptGenCount, wardGenCount, diagGenCount, primaryGeneratedCount);
 
            String remark = inhosp.getRemark();
 
            if (primaryGeneratedCount > 0) {
                // 原 L1758-1796:至少一个首要规则成功
                log.info("【基于规则】首要规则有成功(count={}),将次要规则对应flag设为2", primaryGeneratedCount);
                if (deptIsSecondary) {
                    updateCheckFlag(inhosp, 1, "2", StringUtils.isNotEmpty(remark)
                            ? remark + ";首要规则已生成,次要规则跳过" : "首要规则已生成,次要规则跳过");
                }
                if (!SITE_XH.equals(active)) {
                    if (wardIsSecondary) {
                        updateCheckFlag(inhosp, 2, "2", StringUtils.isNotEmpty(remark)
                                ? remark + ";首要规则已生成,次要规则跳过" : "首要规则已生成,次要规则跳过");
                    }
                    if (diagIsSecondary) {
                        updateCheckFlag(inhosp, 3, "2", StringUtils.isNotEmpty(remark)
                                ? remark + ";首要规则已生成,次要规则跳过" : "首要规则已生成,次要规则跳过");
                    }
                }
                if (deptIsPrimary && deptGenCount > 0) {
                    updateCheckFlag(inhosp, 1, "1", StringUtils.isNotEmpty(remark)
                            ? remark + ";科室首要规则生成成功" : "科室首要规则生成成功");
                }
                if (!SITE_XH.equals(active)) {
                    if (wardIsPrimary && wardGenCount > 0) {
                        updateCheckFlag(inhosp, 2, "1", StringUtils.isNotEmpty(remark)
                                ? remark + ";病区首要规则生成成功" : "病区首要规则生成成功");
                    }
                    if (diagIsPrimary && diagGenCount > 0) {
                        updateCheckFlag(inhosp, 3, "1", StringUtils.isNotEmpty(remark)
                                ? remark + ";疾病首要规则生成成功" : "疾病首要规则生成成功");
                    }
                }
                if (deptIsPrimary && deptGenCount == 0) {
                    updateCheckFlag(inhosp, 1, "2", StringUtils.isNotEmpty(remark)
                            ? remark + ";科室首要规则生成失败" : "科室首要规则生成失败");
                }
                if (!SITE_XH.equals(active)) {
                    if (wardIsPrimary && wardGenCount == 0) {
                        updateCheckFlag(inhosp, 2, "2", StringUtils.isNotEmpty(remark)
                                ? remark + ";病区首要规则生成失败" : "病区首要规则生成失败");
                    }
                    if (diagIsPrimary && diagGenCount == 0) {
                        updateCheckFlag(inhosp, 3, "2", StringUtils.isNotEmpty(remark)
                                ? remark + ";疾病首要规则生成失败" : "疾病首要规则生成失败");
                    }
                }
            } else {
                // 原 L1797-1828:所有首要规则全部失败 → 把 =1 的置 2,然后执行次要规则
                log.info("【基于规则】首要规则全部失败,将=1的flag设为2,开始执行次要规则");
                if (deptIsPrimary) {
                    updateCheckFlag(inhosp, 1, "2", StringUtils.isNotEmpty(remark)
                            ? remark + ";科室首要规则全部失败" : "科室首要规则全部失败");
                }
                if (!SITE_XH.equals(active)) {
                    if (wardIsPrimary) {
                        updateCheckFlag(inhosp, 2, "2", StringUtils.isNotEmpty(remark)
                                ? remark + ";病区首要规则全部失败" : "病区首要规则全部失败");
                    }
                    if (diagIsPrimary) {
                        updateCheckFlag(inhosp, 3, "2", StringUtils.isNotEmpty(remark)
                                ? remark + ";疾病首要规则全部失败" : "疾病首要规则全部失败");
                    }
                }
                if (deptIsSecondary) {
                    log.info("【基于规则】执行科室次要规则");
                    addDeptSubTaskForPatient(inhosp, archive, config);
                }
                if (!SITE_XH.equals(active)) {
                    if (wardIsSecondary) {
                        log.info("【基于规则】执行病区次要规则");
                        addWardSubTaskForPatient(inhosp, archive, config);
                    }
                    if (diagIsSecondary) {
                        log.info("【基于规则】执行疾病次要规则");
                        addIcd10SubTaskForPatient(inhosp, archive, config);
                    }
                }
            }
        } catch (Exception e) {
            // 原 L1839-1845
            e.printStackTrace();
            log.error("【基于规则】患者{}处理异常:{}", inhosp.getInhospid(), e.getMessage());
            updateCheckFlag(inhosp, 1, "2", "基于规则处理异常");
        }
    }
 
    /**
     * 统一回写标志位 —— 委托 {@link InhospFlagWriter}(原 {@code updateCheckFlag})。
     */
    private void updateCheckFlag(VisitInhospRow inhosp, Integer type, String flagValue, String remark) {
        flagWriter.updateCheckFlag(inhosp, type, flagValue, remark);
    }
 
    // ==========================================================================
    // 2. 科室维度(原 L1854-1887)
    // ==========================================================================
 
    /**
     * 为单个患者生成科室维度的子任务。
     *
     * @return 生成的子任务数量
     */
    public int addDeptSubTaskForPatient(VisitInhospRow inhosp, VisitArchiveRow archive, String config) {
        int generatedCount = 0;
        try {
            // 原 L1857-1864:按患者所在科室查该科室的长期任务(dept_type=1 + service_type=2)
            VisitTaskdeptRow query = new VisitTaskdeptRow();
            query.setLongtask(1L);
            query.setDeptCode(inhosp.getLeaveldeptcode());
            query.setDeptType(DEPT_TYPE_DEPT);
            query.setServiceType(SERVICE_TYPE_DISCHARGE);
            query.setOrgid(inhosp.getOrgid());
            List<VisitTaskdeptRow> taskdepts = taskdeptMapper.selectList(query);
 
            if (CollectionUtils.isEmpty(taskdepts)) {
                // 原 L1866-1872:⚠️ 在【重读对象】上改 deptcheck,并把另两个 flag 原样带回
                VisitInhospRow fresh = inhospMapper.selectByInhospid(inhosp.getInhospid());
                VisitInhospUpdateDto u = new VisitInhospUpdateDto();
                u.setInhospid(inhosp.getInhospid());
                u.setDeptcheckFlag("2");
                u.setWardcheckFlag(fresh == null ? null : fresh.getWardcheckFlag());
                u.setDiagcheckFlag(fresh == null ? null : fresh.getDiagcheckFlag());
                // 原实现不判 null(fresh.getLongTaskReason() + "..."),原样保留
                u.setLongTaskReason((fresh == null ? null : fresh.getLongTaskReason())
                        + "该患者所在科室未配置离院长期任务;");
                updateInhosp(u);
            } else {
                for (VisitTaskdeptRow t : taskdepts) {
                    subtaskWriter.writeInSubTask(t.getTaskId(), true, inhosp, archive, 1, config);
                    generatedCount++;
                }
            }
        } catch (Exception e) {
            // 原 L1879-1885:⚠️ 在【新鲜对象】上 set —— 只写 deptcheck + reason
            e.printStackTrace();
            log.error("【科室任务】患者{}处理异常:{}", inhosp.getInhospid(), e.getMessage());
            VisitInhospUpdateDto u = new VisitInhospUpdateDto();
            u.setInhospid(inhosp.getInhospid());
            u.setDeptcheckFlag("2");
            u.setLongTaskReason("科室任务处理异常");
            updateInhosp(u);
        }
        return generatedCount;
    }
 
    // ==========================================================================
    // 3. 病区维度(原 L1894-1935)
    // ==========================================================================
 
    /**
     * 为单个患者生成病区维度的子任务。
     *
     * @return 生成的子任务数量
     */
    public int addWardSubTaskForPatient(VisitInhospRow inhosp, VisitArchiveRow archive, String config) {
        int generatedCount = 0;
        try {
            String errorreason = null;
            List<VisitTaskdeptRow> taskdeptList = null;
            // 原 L1899-1912
            if (!StringUtils.isEmpty(inhosp.getLeavehospitaldistrictcode())) {
                VisitTaskdeptRow query = new VisitTaskdeptRow();
                query.setLongtask(1L);
                query.setDeptCode(inhosp.getLeavehospitaldistrictcode());
                query.setDeptType(DEPT_TYPE_WARD);
                query.setServiceType(SERVICE_TYPE_DISCHARGE);
                query.setOrgid(inhosp.getOrgid());
                taskdeptList = taskdeptMapper.selectList(query);
                if (CollectionUtils.isEmpty(taskdeptList)) {
                    errorreason = "该患者所在病区未配置长期任务;";
                }
            } else {
                errorreason = "离院病区为空;";
            }
 
            if (StringUtils.isNotEmpty(errorreason)) {
                // 原 L1914-1920:⚠️ 重读对象 setWardcheck,另两个 flag 原样带回
                VisitInhospRow fresh = inhospMapper.selectByInhospid(inhosp.getInhospid());
                VisitInhospUpdateDto u = new VisitInhospUpdateDto();
                u.setInhospid(inhosp.getInhospid());
                u.setWardcheckFlag("2");
                u.setDeptcheckFlag(fresh == null ? null : fresh.getDeptcheckFlag());
                u.setDiagcheckFlag(fresh == null ? null : fresh.getDiagcheckFlag());
                // 原有判空(与科室维度那处不同),原样保留
                u.setLongTaskReason(StringUtils.isNotEmpty(fresh == null ? null : fresh.getLongTaskReason())
                        ? fresh.getLongTaskReason() + errorreason : errorreason);
                updateInhosp(u);
            } else {
                for (VisitTaskdeptRow t : taskdeptList) {
                    subtaskWriter.writeInSubTask(t.getTaskId(), true, inhosp, archive, 2, config);
                    generatedCount++;
                }
            }
        } catch (Exception e) {
            // 原 L1927-1933:新鲜对象 —— 只写 wardcheck + reason
            e.printStackTrace();
            log.error("【病区任务】患者{}处理异常:{}", inhosp.getInhospid(), e.getMessage());
            VisitInhospUpdateDto u = new VisitInhospUpdateDto();
            u.setInhospid(inhosp.getInhospid());
            u.setWardcheckFlag("2");
            u.setLongTaskReason("病区任务处理异常");
            updateInhosp(u);
        }
        return generatedCount;
    }
 
    // ==========================================================================
    // 4. 疾病维度(原 L1942-2001)
    // ==========================================================================
 
    /**
     * 为单个患者生成疾病维度的子任务。
     *
     * @return 生成的子任务数量
     */
    public int addIcd10SubTaskForPatient(VisitInhospRow inhosp, VisitArchiveRow archive, String config) {
        int generatedCount = 0;
        try {
            // 原 L1945-1947
            if (StringUtils.isEmpty(inhosp.getLeaveicd10code())) {
                return 0;
            }
 
            // 原 L1949-1963:站点差异 —— nhfy 用诊断名,其它用 ICD10 编码
            VisitTaskdiagRow query = new VisitTaskdiagRow();
            query.setLongtask(1L);
            if (SITE_NHFY.equals(active)) {
                query.setIcd10name(inhosp.getLeavediagname());
                if (StringUtils.isEmpty(inhosp.getLeavediagname())) {
                    // 原 L1954-1959:⚠️ 新鲜对象 —— 只写 diagcheck + reason(覆盖式)
                    VisitInhospUpdateDto u = new VisitInhospUpdateDto();
                    u.setInhospid(inhosp.getInhospid());
                    u.setDiagcheckFlag("2");
                    u.setLongTaskReason("该患者主诊断名称为空;");
                    updateInhosp(u);
                    return 0;
                }
            } else {
                query.setIcd10code(inhosp.getLeaveicd10code());
            }
            query.setOrgid(inhosp.getOrgid());
            List<VisitTaskdiagRow> taskdiags = taskdiagMapper.selectListByIcdName(query);
 
            if (CollectionUtils.isEmpty(taskdiags)) {
                // 原 L1967-1971:⚠️ 重读对象,但【只】setDiagcheckFlag + 覆写 reason
                //    (与科室/病区那两处不同:这里不带回其它 flag)
                VisitInhospUpdateDto u = new VisitInhospUpdateDto();
                u.setInhospid(inhosp.getInhospid());
                u.setDiagcheckFlag("2");
                u.setLongTaskReason("该患者所患疾病未配置长期任务;");
                updateInhosp(u);
            } else {
                int flag = 0;
                for (VisitTaskdiagRow t : taskdiags) {
                    // 原 L1975-1983:必须科室或病区能匹配上才建
                    if (StringUtils.isNotEmpty(t.getDeptCode())
                            && t.getDeptCode().equals(inhosp.getLeaveldeptcode())) {
                        subtaskWriter.writeInSubTask(t.getTaskId(), true, inhosp, archive, 3, config);
                        generatedCount++;
                        flag = 1;
                    } else if (StringUtils.isNotEmpty(t.getWardCode())
                            && t.getWardCode().equals(inhosp.getLeavehospitaldistrictcode())) {
                        subtaskWriter.writeInSubTask(t.getTaskId(), true, inhosp, archive, 3, config);
                        generatedCount++;
                        flag = 1;
                    }
                }
                if (flag == 0) {
                    // 原 L1985-1991
                    log.error("该科室或病区对于患者所患疾病未配置长期任务,患者:{}", inhosp.getInhospid());
                    VisitInhospUpdateDto u = new VisitInhospUpdateDto();
                    u.setInhospid(inhosp.getInhospid());
                    u.setDiagcheckFlag("2");
                    u.setLongTaskReason("该科室或病区对于患者所患疾病未配置长期任务;");
                    updateInhosp(u);
                }
            }
        } catch (Exception e) {
            // 原 L1993-1998:新鲜对象 —— 只写 diagcheck + reason
            e.printStackTrace();
            log.error("【疾病任务】患者{}处理异常:{}", inhosp.getInhospid(), e.getMessage());
            VisitInhospUpdateDto u = new VisitInhospUpdateDto();
            u.setInhospid(inhosp.getInhospid());
            u.setDiagcheckFlag("2");
            u.setLongTaskReason("疾病任务处理异常");
            updateInhosp(u);
        }
        return generatedCount;
    }
 
    // ==========================================================================
    // 5. 再入院处理(原 L1321-1359)
    // ==========================================================================
 
    /**
     * 再入院处理:新入院记录出现时,把该患者<b>旧的待执行随访</b>停掉。
     * <p>
     * 逐行对应原 {@code dealInHospInfo(List<PatMedInhosp> patMedInhospList)}。
     * <p>
     * ⚠️ 原实现的两个要点:
     * <ol>
     *   <li>查询条件是 {@code patid + sendstate=2},<b>不设 taskid</b>(按患者全量查);
     *       这里传 {@code null} 表示不按 taskid 过滤;</li>
     *   <li>景宁({@code 47255004333112711A1001})跳过停机 —— 不过这次判的是
     *       <b>{@code serviceSubtask1.getOrgid()}</b>(子任务的 orgid),不是入参患者的 orgid。</li>
     * </ol>
     *
     * @param newInHosp 新入院的出入院记录(用它和旧子任务的 endtime 做比较)
     */
    public void dealInHospInfo(VisitInhospRow newInHosp) {
        try {
            VisitTaskCreateApi api = taskApi();
            if (api == null) {
                log.warn("【出入院随访】VisitTaskCreateApi 不可用,跳过再入院处理。inhospid={}",
                        newInHosp.getInhospid());
                return;
            }
            // 原 L1324-1328:patid + sendstate=2(不设 taskid)
            List<VisitPendingSubtaskDto> pendingList =
                    api.findPendingSubtasks(newInHosp.getPatid(), null);
            if (CollectionUtils.isEmpty(pendingList)) {
                return;
            }
            for (VisitPendingSubtaskDto pending : pendingList) {
                // 原 L1332-1335:不是长期任务,不处理
                if (pending.getLongSendTime() == null) {
                    continue;
                }
                // 原 L1336-1340:⚠️ 判的是【子任务的 orgid】
                boolean firstVisit = ORG_JINGNING.equals(pending.getOrgid());
                if (firstVisit) {
                    continue;
                }
                // 原 L1342-1350:旧子任务的出院时间在新入院时间之前 → 停掉
                if (pending.getEndtime() != null && newInHosp.getStarttime() != null
                        && pending.getEndtime().before(newInHosp.getStarttime())) {
                    pending.setSendstate(4L);
                    pending.setIsVisitAgain(2);
                    pending.setRemark("患者再入院");
                    pending.setResult("error");
                    api.updatePendingSubtask(pending);
                }
            }
        } catch (Exception e) {
            // 原 L1355-1357
            log.error("入院数据信息出异常了:{},{}", newInHosp, e.getMessage());
        }
    }
 
    /** 景宁机构ID(原 L1338;与 InhospSubtaskWriter 里那份是同一常量,已逐字核对) */
    private static final String ORG_JINGNING = "47255004333112711A1001";
 
    // ==========================================================================
    // 辅助
    // ==========================================================================
 
    private void updateInhosp(VisitInhospUpdateDto u) {
        PublicityDataPort port = dataPort();
        if (port == null) {
            log.warn("【出入院随访】PublicityDataPort 不可用,无法写回标志位。inhospid={}", u.getInhospid());
            return;
        }
        port.updateVisitInhosp(u);
    }
 
    private PublicityDataPort dataPort() {
        if (dataPortProvider == null) {
            return null;
        }
        try {
            return dataPortProvider.stream().findFirst().orElse(null);
        } catch (Exception e) {
            log.warn("【出入院随访】获取 PublicityDataPort 失败:{}", e.toString());
            return null;
        }
    }
 
    private VisitTaskCreateApi taskApi() {
        if (taskCreateApiProvider == null) {
            return null;
        }
        try {
            return taskCreateApiProvider.stream().findFirst().orElse(null);
        } catch (Exception e) {
            log.warn("【出入院随访】获取 VisitTaskCreateApi 失败:{}", e.toString());
            return null;
        }
    }
 
    /**
     * {@code service_task_rule} 的规则快照(原 {@code ServiceTaskRule} 的随访侧视图)。
     * <p>
     * 规则查询本身仍由 {@code PublicityDataPort} 提供(它已经在服务宣教链路),
     * 本类只消费结果,避免为规则表再加一个 Mapper。
     */
    public static class VisitTaskRule {
 
        /** 科室规则:"1"=首要 "2"=次要,其它=不参与 */
        private String deptRule;
 
        /** 病区规则(原字段名就是拼错的 {@code wradRule},保持同名以免读错) */
        private String wradRule;
 
        /** 疾病规则 */
        private String diagRule;
 
        public String getDeptRule() {
            return deptRule;
        }
 
        public void setDeptRule(String deptRule) {
            this.deptRule = deptRule;
        }
 
        public String getWradRule() {
            return wradRule;
        }
 
        public void setWradRule(String wradRule) {
            this.wradRule = wradRule;
        }
 
        public String getDiagRule() {
            return diagRule;
        }
 
        public void setDiagRule(String diagRule) {
            this.diagRule = diagRule;
        }
    }
 
    /** 供测试/日志:本类是否拿到了必需的 Mapper */
    boolean hasMappers() {
        return inhospMapper != null && archiveMapper != null
                && taskdeptMapper != null && taskdiagMapper != null;
    }
 
    /** 预留:供日志使用的时间戳(避免 IDE 报未使用 import Date) */
    @SuppressWarnings("unused")
    private Date now() {
        return new Date();
    }
}