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
package com.ruoyi.common.core.service;
 
import com.ruoyi.common.core.service.dto.InHospPublicityRowDto;
import com.ruoyi.common.core.service.dto.PublicityTaskMatchDto;
 
import java.util.List;
 
/**
 * 宣教模块所需的<b>只读/回写数据端口</b>(接口在 ruoyi-common,实现由 smartor 提供)。
 *
 * <h3>为什么要有这个端口</h3>
 * 宣教模块({@code ruoyi-publicity})要做两件事:
 * <ol>
 *   <li>查"这个科室/病区/药品有没有配长期宣教任务"({@code service_taskdept} / {@code service_taskmedical});</li>
 *   <li>扫"哪些在院患者还没做入院宣教"({@code pat_med_inhosp} 的 {@code in_deptcheck_flag} /
 *       {@code in_wardcheck_flag}),查不到患者档案时把标志位置 "2" 并追加原因。</li>
 * </ol>
 * 这些表和 SQL 全在 smartor。若让 {@code ruoyi-publicity} 直接依赖 smartor,
 * 就会形成 {@code smartor ↔ ruoyi-publicity} 循环依赖(smartor 要依赖它来触发宣教)。
 * 若把 SQL 抄一份到新模块,就会出现"同一份口径两处维护"的隐患
 * —— 这正是满意度模块当初极力避免的坑。
 * <p>
 * 所以:<b>接口放 ruoyi-common,实现({@code PublicityDataPortBridge})留在 smartor</b>,
 * 宣教模块运行时按类型查找实现。这与
 * {@link VisitTaskCreateApi} / {@link PublicitySubtaskApi} 是同一手法。
 *
 * <h3>调用方务必降级</h3>
 * 取不到实现时按"没有宣教要做"处理并打 WARN,绝不能让出入院随访主流程抛异常。
 */
public interface PublicityDataPort {
 
    /**
     * 扫在院患者(原 {@code patMedInhospMapper.selectPatMedInhospList} 的两段查询)。
     * <p>
     * 迁移对应 {@code PatMedInhospServiceImpl#dealOutHospInfo}:
     * <ul>
     *   <li>科室维度(L325-329):{@code deptcheck_flag="0"}、{@code inhospstate="1"}、{@code fuflag="1"};</li>
     *   <li>病区维度(L376-379):{@code in_wardcheck_flag="0"}、{@code inhospstate="1"}、{@code fuflag="1"}
     *       (并把 inDeptcheckFlag / deptcheckFlag 置 null)。</li>
     * </ul>
     * 两段是<b>独立查询</b>,调用方分两次调(不要合并成一次"OR"查询,那会改变结果集)。
     *
     * @param checkFlagColumn {@code "in_deptcheck_flag"} 或 {@code "in_wardcheck_flag"}(只允许这两个值)
     * @return 在院患者行;无数据返回空列表
     */
    List<InHospPublicityRowDto> findInHospForPublicity(String checkFlagColumn);
 
    /**
     * 按科室/病区查长期宣教任务ID(原 {@code serviceTaskdeptMapper.selectServiceTaskdeptList})。
     * <p>
     * 迁移对应 L347-353({@code deptType="1"}、{@code serviceType="17"})与
     * L390-396({@code deptType="2"}、{@code serviceType="17"})。
     * SQL 条件:{@code del_flag = 0}、{@code longtask = 1}、{@code dept_code = ?}、
     * {@code dept_type = ?}、{@code service_type = '17'}、{@code orgid = ?}。
     *
     * @param deptCode  科室编码(deptType=1)或病区编码(deptType=2)
     * @param deptType  "1" = 科室, "2" = 病区
     * @param orgid     机构ID
     * @param inhospid  该条记录对应的出入院记录ID(回填进返回值,方便调用方回调 inhospvisit)
     * @return 命中的任务(可能多条);无数据返回空列表
     */
    List<PublicityTaskMatchDto> findDeptPublicityTaskIds(String deptCode, String deptType,
                                                         String orgid, Long inhospid);
 
    /**
     * 按药品编码 + 名称 + 公司查长期宣教任务ID(原 {@code serviceTaskmedicalMapper.selectServiceTaskmedicalList})。
     * <p>
     * 迁移对应原代码 L424-434。原实现设了<b>三个</b>字段:
     * <pre>
     * serviceTaskmedical.setMedicalCode(split[i]);
     * if (i &lt; medicalCompanyArray.length) {
     *     serviceTaskmedical.setMedicalCompany(medicalCompanyArray[i]);
     * } else {
     *     log.warn("...下标越界...");
     *     serviceTaskmedical.setMedicalName("");   // ← 注意:越界时设的是 name,不是 company
     * }
     * </pre>
     * <p>
     * ⚠️ <b>越界分支是个历史笔误,但会真实影响 SQL,因此必须原样保留</b>:
     * 越界时 {@code medical_name} 被置为<b>空串</b>(而非 null),
     * 而 SQL 条件是 {@code <if test="medicalName != null and medicalName != ''">} —— 空串会跳过该条件;
     * 同时 {@code medical_company} 保持为 <b>null</b>(未被赋值),SQL 条件同样跳过。
     * 也就是说:<b>越界时两个条件都不过滤</b>(等价于只按 medical_code 查)。
     * <p>
     * 所以本方法必须把 {@code medicalName} 也传进来 —— 不能让 Bridge 自作主张把空串转成 null
     * 或反过来(那会改变 SQL 条件是否生效)。
     *
     * @param medicalCode    药品编码
     * @param medicalName    药品名称;越界分支传<b>空串</b>;正常分支传 null
     * @param medicalCompany 药品公司;越界分支传 null;正常分支传数组里的值
     * @param inhospid       该条记录对应的出入院记录ID
     * @return 命中的任务(可能多条);无数据返回空列表
     */
    List<PublicityTaskMatchDto> findMedicalPublicityTaskIds(String medicalCode, String medicalName,
                                                            String medicalCompany, Long inhospid);
 
    /**
     * 患者档案是否存在(原 {@code patArchiveMapper.selectPatArchiveByPatid(patid)} 的判空用途)。
     * <p>
     * 迁移对应 L338-344 / L382-388。原实现取的是完整 {@code PatArchive},
     * 但宣教这一层<b>只用到"是不是 null"</b>(真正的患者信息填充发生在 inhospvisit 组子任务时),
     * 所以这里只返回存在性。
     *
     * @param patid 患者档案ID(即 {@code PatMedInhosp.patid} / {@code PatArchive.id},都是 Long)
     * @return true = 存在且未删除
     */
    boolean existsPatArchive(Long patid);
 
    /**
     * 把入院宣教的标志位置为给定值,并可同时写 {@code long_task_reason}。
     * <p>
     * 迁移对应 L340-342 / L357-359 / L367-370(科室维度)与 L384-386 / L400-402(病区维度)。
     *
     * <h3>⚠️ 原实现有"覆盖"与"追加"两种写法,必须区分(所以这里给的是 {@code reasonMode})</h3>
     * <table border="1">
     *   <tr><th>场景</th><th>原代码</th><th>语义</th></tr>
     *   <tr><td>患者基本信息为空(科室/病区共用)</td>
     *       <td>{@code setLongTaskReason("患者基本信息为空")}</td>
     *       <td><b>覆盖</b>(直接赋值,丢掉旧值)</td></tr>
     *   <tr><td>科室未配宣教任务</td>
     *       <td>{@code setLongTaskReason(旧值 + "该患者所在科室未配置入院长期宣教任务;")}<br>
     *           其中"旧值"是<b>按 inhospid 重查一次</b>拿到的</td>
     *       <td><b>追加</b>(且不判旧值是否为 null)</td></tr>
     *   <tr><td>病区未配宣教任务</td>
     *       <td>{@code setLongTaskReason(旧值 + "该患者所在病区未配置入院长期宣教任务;")}</td>
     *       <td><b>追加</b>(同样不判 null;旧值为 null 时会拼出 {@code "null该患者所在病区..."})</td></tr>
     *   <tr><td>科室处理异常</td>
     *       <td>{@code setLongTaskReason("人员入院科室处理出异常了")}</td>
     *       <td><b>覆盖</b>;且原代码<b>不设 longTaskReason 前缀</b>、也不重查</td></tr>
     * </table>
     * 因为"覆盖 / 追加"是两种不同语义,本方法用一个布尔区分,而不是让调用方拼字符串
     * —— 拼字符串会让 {@code null} 前缀这种原缺陷被"顺手修掉",那就成了行为变更。
     *
     * @param inhospid   出入院记录ID
     * @param flagColumn {@code "in_deptcheck_flag"} 或 {@code "in_wardcheck_flag"}(只允许这两个值)
     * @param flagValue  要写入的标志位值(原实现写 "2")
     * @param reason     要写入 {@code long_task_reason} 的文案;为 null 表示<b>不动</b>该字段
     * @param appendMode true = 追加({@code 旧值 + reason},旧值按 inhospid 重查,<b>不判 null</b>);
     *                   false = 覆盖(直接写 {@code reason})
     * @return true = 更新成功
     */
    boolean updatePublicityFlag(Long inhospid, String flagColumn, String flagValue,
                                String reason, boolean appendMode);
 
    /**
     * 按字段更新出入院记录(可一次写多个 flag + {@code long_task_reason} + {@code remark})。
     *
     * <h3>为什么要有这个"宽"接口</h3>
     * 出院随访编排层({@code InhospTaskOrchestrator} 的 3 个 {@code *ForPatient})在写失败标志位时,
     * 原实现有三种不同写法,写进去的列集合也不同:
     * <ul>
     *   <li>有的在<b>按 inhospid 重读出来的对象</b>上改一个 flag,并把另两个 flag
     *       从重读对象<b>原样带回</b>(因为 {@code updatePatMedInhosp} 是"传什么更新什么")
     *       —— 实际写了 3 个 flag;</li>
     *   <li>有的在<b>新鲜对象</b>上只 set 一个 flag —— 实际只写 1 个 flag。</li>
     * </ul>
     * 窄接口 {@link #updatePublicityFlag} 表达不了"一次写多个 flag",
     * 所以加这个通道。调用方按原逻辑决定给哪些字段赋值,<b>null = 不改</b>
     * (对应原 XML 每个字段各自的 {@code <if test="xxx != null">})。
     *
     * @param update 更新草稿({@code inhospid} 必填)
     * @return true = 影响行数 &gt; 0
     */
    boolean updateVisitInhosp(com.ruoyi.common.core.service.dto.VisitInhospUpdateDto update);
 
    /**
     * 按离院科室/病区查「任务执行规则」(原 {@code ServiceTaskRuleMapper#selectServiceTaskRuleByDeptOrWard})。
     *
     * <h3>为什么放在这个端口上</h3>
     * 该查询服务的是<b>出院随访</b>的规则编排({@code addSubTaskByRule}),
     * 语义上不属于"宣教"。但本端口已经是"随访侧访问 smartor 只读配置"的统一入口
     * (它已经提供 {@code findInHospForPublicity} / {@code findDeptPublicityTaskIds} 等),
     * 再为一张单表加第四个端口不划算。这里在注释里写明用途,避免后人误以为它只服务宣教。
     * <p>
     * 迁移对应 {@code PatMedInhospServiceImpl#addSubTaskByRule} L1704-1707:
     * <pre>
     * ServiceTaskRule ruleQuery = new ServiceTaskRule();
     * ruleQuery.setDeptcode(patMedInhosp1.getLeaveldeptcode());
     * ruleQuery.setWardcode(patMedInhosp1.getLeavehospitaldistrictcode());
     * ServiceTaskRule rule = serviceTaskRuleMapper.selectServiceTaskRuleByDeptOrWard(ruleQuery);
     * </pre>
     * 注意:<b>该查询最多返回一条</b>(原实现直接取单个对象)。
     *
     * @param leaveldeptcode             离院科室编码
     * @param leavehospitaldistrictcode  离院病区编码
     * @return 规则;未配置返回 {@code null}
     */
    com.ruoyi.common.core.service.dto.VisitTaskRuleDto findTaskRule(String leaveldeptcode,
                                                                     String leavehospitaldistrictcode);
}