liusheng
6 天以前 cc479e6a056785103996cc7ea67dbd5faa3e800e
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
package com.ruoyi.outpatientvisit.api;
 
import com.ruoyi.outpatientvisit.config.OuthospVisitProperties;
import com.ruoyi.outpatientvisit.config.OuthospVisitSwitch;
import com.ruoyi.outpatientvisit.support.OuthospPartitionMaintainer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
/**
 * 门急诊随访模块对外门面(<b>常驻 Bean</b>)。
 * <p>
 * 这是其它模块访问门急诊随访能力的<b>唯一入口</b>。与
 * {@code InhospVisitFacade} / {@code PublicityFacade} 同一套设计与理由:
 * <ul>
 *   <li><b>永远存在</b>(不用 {@code @ConditionalOnProperty})—— 调用方({@code RyTask}
 *       定时任务)的 {@code @Autowired} 在任何开关状态下都<b>不会启动失败</b>;</li>
 *   <li>开关在 {@code sys_config} 的 {@code outpatientvisit.enabled},由
 *       {@link OuthospVisitSwitch} <b>运行时</b>读取(10 秒进程内缓存),改完无需重启;</li>
 *   <li>关闭时返回安全值并打 WARN,<b>不抛异常</b>,保证调用方主流程照常完成。</li>
 * </ul>
 *
 * <h3>关于"分表维护"是否该受开关管辖</h3>
 * {@link #createOuthospQuarterTable()} 决定<b>不受</b> {@code outpatientvisit.enabled} 约束,
 * 理由与出入院模块的基础数据读写一致:
 * <ul>
 *   <li>它建的是<b>物理分表 + 存储过程</b>,是数据基础设施,不是"生成随访";</li>
 *   <li>关掉随访生成却让分表不建,会导致新周期的门诊记录写进不存在的表 ——
 *       那是数据故障,与"今天不发随访"完全不是一回事;</li>
 *   <li>而且它本身已有独立的开关:{@code sys_config: outhosp.split.mode}
 *       没配就整块不执行(见 {@link OuthospPartitionMaintainer})。</li>
 * </ul>
 */
@Slf4j
@Component
public class OuthospVisitFacade {
 
    private final OuthospVisitProperties properties;
 
    /**
     * 运行时开关({@code sys_config} 优先,{@link OuthospVisitProperties} 兜底)
     */
    private final OuthospVisitSwitch outhospVisitSwitch;
 
    /**
     * 分表维护与存储过程重建(必存在;它是本模块自己的组件,没有条件装配)。
     */
    private final OuthospPartitionMaintainer partitionMaintainer;
 
    /**
     * 门诊随访编排(扫未处理门诊记录 → 匹配长期任务 → 写子任务)。
     * <p>
     * ⚠️ 用 {@code ObjectProvider} 惰性取:容器里若没有它也不该让门面装配失败。
     */
    private final ObjectProvider<com.ruoyi.outpatientvisit.service.OuthospVisitTaskService> taskServiceProvider;
 
    /**
     * Spring 使用的构造器。
     * <p>
     * ⚠️ <b>必须显式标注 {@code @Autowired}</b>:本类有两个公开构造器
     * (另一个是给测试/非 Spring 环境用的),若不标注,Spring 会因为"构造器不唯一"
     * 而退回查找无参构造器,从而在启动时报 {@code No default constructor found}。
     * —— 这是满意度模块<b>实机踩到</b>的缺陷,此处刻意保留同样的写法与注释。
     */
    @Autowired
    public OuthospVisitFacade(OuthospVisitProperties properties,
                              OuthospVisitSwitch outhospVisitSwitch,
                              OuthospPartitionMaintainer partitionMaintainer,
                              ObjectProvider<com.ruoyi.outpatientvisit.service.OuthospVisitTaskService> taskServiceProvider) {
        this.properties = properties;
        this.outhospVisitSwitch = outhospVisitSwitch;
        this.partitionMaintainer = partitionMaintainer;
        this.taskServiceProvider = taskServiceProvider;
    }
 
    /**
     * 供<b>测试/非 Spring 环境</b>直接构造。
     * <p>
     * 分表维护器与编排服务都不传:测试构造下不提供建表与随访编排能力
     * (调用会打 WARN 并安全返回),保证开关校验类测试不必拉起数据库。
     */
    public OuthospVisitFacade(OuthospVisitProperties properties, String sysConfigValue) {
        this(properties, new OuthospVisitSwitch(properties, sysConfigValue), null, null);
    }
 
    /**
     * 取运行时开关(供测试断言 / 拦截器复用)
     */
    public OuthospVisitSwitch getOuthospVisitSwitch() {
        return outhospVisitSwitch;
    }
 
    /**
     * 门诊随访模块是否可用(开关打开 <b>且</b> 组件在位)
     */
    public boolean isEnabled() {
        return outhospVisitSwitch.isEnabled();
    }
 
    /**
     * 门诊分表维护 + 存储过程重建(供 {@code RyTask#createOuthospQuarterTable} 调用)。
     *
     * <h3>⚠️ 刻意<b>不</b>判 {@code outpatientvisit.enabled}</h3>
     * 见类注释:它建的是物理分表与存储过程,属数据基础设施;
     * 它自己的开关是 {@code sys_config: outhosp.split.mode}。
     * 若把它也套上随访开关,关闭随访会让新周期分表不建 → 门诊记录写不进表。
     *
     * @return true = 已执行(不代表"建了新表",周期表已存在时也算已执行);
     *         false = 组件不可用(非 Spring 构造),已打 WARN
     */
    public boolean createOuthospQuarterTable() {
        if (partitionMaintainer == null) {
            log.warn("【门急诊随访】分表维护器不可用(非 Spring 构造或装配缺失),createOuthospQuarterTable 跳过。");
            return false;
        }
        partitionMaintainer.createOuthospQuarterTable();
        return true;
    }
 
    /**
     * 门诊随访编排(供 {@code PatMedOuthospServiceImpl#dealOutpatientInfo} 与
     * {@code RyTask} 调用)。
     *
     * <h3>⚠️ 本方法<b>受</b> {@code outpatientvisit.enabled} 管辖</h3>
     * 与 {@link #createOuthospQuarterTable()} 相反:那是"建分表/存储过程"的数据基础设施;
     * 本方法才是"生成门诊随访"的业务动作 —— 关掉开关就该停下。
     * 关闭时返回 {@code 0} 并打 WARN(不抛异常,保证定时任务主流程不被拖垮)。
     *
     * @return 关闭/不可用时返回 0;正常时返回编排结果(原实现固定返回 1)
     */
    public Integer dealOutpatientInfo() {
        if (!isEnabled()) {
            log.warn("【门急诊随访】模块未启用(参数配置 outpatientvisit.enabled=false),dealOutpatientInfo 跳过。");
            return 0;
        }
        if (taskServiceProvider == null) {
            log.warn("【门急诊随访】编排服务不可用(非 Spring 构造),dealOutpatientInfo 跳过。");
            return 0;
        }
        com.ruoyi.outpatientvisit.service.OuthospVisitTaskService svc =
                taskServiceProvider.stream().findFirst().orElse(null);
        if (svc == null) {
            log.warn("【门急诊随访】容器里没有 OuthospVisitTaskService,dealOutpatientInfo 跳过。");
            return 0;
        }
        return svc.dealOutpatientInfo();
    }
}