package com.ruoyi.common.core.service;
|
|
import com.ruoyi.common.core.service.dto.VisitLongTaskDto;
|
import com.ruoyi.common.core.service.dto.VisitPendingSubtaskDto;
|
import com.ruoyi.common.core.service.dto.VisitSubtaskDraft;
|
|
import java.util.List;
|
|
/**
|
* 随访任务「创建 / 派发」能力的预留接口。
|
*
|
* <h3>为什么单独拆出这个接口</h3>
|
* 出入院随访原来把两件事混在一起:
|
* <ol>
|
* <li><b>随访编排</b> —— 按科室/病区/疾病/手术匹配长期任务、判断再入院该停谁(属 {@code ruoyi-inhospvisit});</li>
|
* <li><b>任务创建与落库</b> —— 查任务定义、写 {@code service_subtask}、终止旧任务(本接口)。</li>
|
* </ol>
|
* 你要求"任务创建将来单独拉一个模块,来调用这些接口",所以这里先把第 2 件事抽成接口:
|
* <ul>
|
* <li>接口定义在 <b>ruoyi-common</b> —— 这是唯一能同时满足
|
* 「{@code ruoyi-inhospvisit} 不依赖 smartor」+「smartor 依赖 {@code ruoyi-inhospvisit}」
|
* +「两者互相要调用」的位置。若把接口放 inhospvisit、实现放 smartor,
|
* 就会形成 {@code smartor -> inhospvisit -> smartor} 的循环依赖,Maven 直接构建失败。</li>
|
* <li>本次(第一批)由 <b>smartor 提供一个过渡实现</b>(逐行搬运现有写库能力,零逻辑改动)。</li>
|
* <li>将来「随访任务创建模块」建好时,<b>只需把该模块的实现注册进来</b>
|
* (实现方从 smartor 换成新模块),{@code ruoyi-inhospvisit} <b>一行都不用改</b>。</li>
|
* </ul>
|
* 这与满意度的 {@link ISatisfactionCategoryService} 是同一手法:接口在 ruoyi-common,
|
* 调用方用 {@code SpringUtils.getBean(...)} 按类型查找 + try/catch 降级。
|
*
|
* <h3>调用方务必降级</h3>
|
* 取不到实现(模块没打进应用 / 未启用 / 容器异常)时,调用方<b>必须按"不生成子任务"处理并打 WARN</b>,
|
* 绝不能让定时任务抛异常 —— 出入院随访挂了就是当天全院随访停摆。
|
*
|
* @see ISatisfactionCategoryService 同一手法的先例
|
*/
|
public interface VisitTaskCreateApi {
|
|
/**
|
* 按 taskid 取「已发布的长期任务」(原 {@code ServiceTaskMapper.selectServiceTaskList}
|
* 配 {@code sendState=2})。取不到时<b>返回 {@code null}</b>。
|
* <p>
|
* 迁移对应:{@code PatMedInhospServiceImpl#writeInSubTask} L686-728(取任务 + 任务不存在时的分支)。
|
*
|
* @param taskid 任务ID
|
* @return 任务快照;不存在或未发布返回 {@code null}
|
*/
|
VisitLongTaskDto findPublishedLongTask(Long taskid);
|
|
/**
|
* 落库一条随访子任务(原 {@code ServiceSubtaskMapper.insertServiceSubtask})。
|
* <p>
|
* 迁移对应:{@code PatMedInhospServiceImpl#writeInSubTask} 里的
|
* {@code insertServiceSubtask} 调用(L823 / L838 / L854 / L862 / L873)。
|
* <p>
|
* ⚠️ 实现必须保持原样:入参草稿怎么填就怎么落库,
|
* <b>不得在这里"顺手补默认值"或"顺手改状态"</b>,否则线上子任务内容会变。
|
* <p>
|
* ⚠️ <b>方法名刻意不叫 createSubtask</b>:它不是"新建一个业务对象",
|
* 而是"把一份已经组装好的草稿原样落库"。
|
* 将来「随访任务创建模块」接管时,<b>方法意图必须仍然是"原样落库"</b>,
|
* 不许在这一层加入去重/合并/状态机 —— 那些语义在 {@code ruoyi-inhospvisit} 里。
|
*
|
* @param draft 子任务草稿
|
* @return true = 落库成功(原实现判断 {@code insertServiceSubtask(...) == 1})
|
*/
|
boolean insertSubtask(VisitSubtaskDraft draft);
|
|
/**
|
* 把一条 {@link #findPendingSubtasks} 查出来的待执行子任务<b>原样回写</b>(整体回写)。
|
* <p>
|
* 迁移对应:{@code PatMedInhospServiceImpl#writeInSubTask} 里的
|
* {@code serviceSubtaskMapper.updateServiceSubtask(serviceSubtask1)}
|
* (L812 / L836)与 {@code #dealInHospInfo} 里的 L1349。
|
* <p>
|
* 原实现的做法是:先查完整对象 → 在内存里改几个字段
|
* ({@code sendstate=4L}、{@code result="error"}、{@code remark=...}、{@code updateBy}、{@code updateTime})
|
* → 把<b>整个对象</b>传给 {@code updateServiceSubtask}。
|
* 因为 {@code ServiceSubtaskMapper.xml} 的 {@code updateServiceSubtask}(L1060-1162)
|
* 是"每个字段各自 {@code <if test="xxx != null">}"、{@code where id = #{id}},
|
* 所以<b>只有非 null 字段生效</b>。
|
* <p>
|
* 因此本方法刻意接收<b>同一个 DTO</b>(调用方已经在上面改过要改的字段),
|
* 由实现把它映射回实体后整体回写 —— 这样"带了哪些字段过去"这件事与原实现逐字段一致,
|
* 不会因为"只传个 id + 几个字段"而丢掉原实现会带过去的其它字段。
|
*
|
* @param pending 从 {@link #findPendingSubtasks} 拿到并已改好字段的 DTO({@code id} 必须非空)
|
* @return true = 更新成功
|
*/
|
boolean updatePendingSubtask(VisitPendingSubtaskDto pending);
|
|
/**
|
* 查"某患者 + 某 taskid"下所有 {@code sendstate=2}(待执行)的子任务,用于再入院去重判定。
|
* <p>
|
* 迁移对应:{@code PatMedInhospServiceImpl#writeInSubTask} L760-764 与
|
* {@code #dealInHospInfo} L1325-1328 的
|
* {@code selectServiceSubtaskList(new ServiceSubtaskEntity(patid, sendstate=2, taskid))}。
|
*
|
* @param patid 患者档案ID
|
* @param taskid 任务ID;{@code dealInHospInfo} 那条链路传 {@code null}(按患者全量查)
|
* @return 待执行子任务摘要列表;无数据返回空列表,<b>不返回 null</b>
|
*/
|
List<VisitPendingSubtaskDto> findPendingSubtasks(Long patid, Long taskid);
|
|
/**
|
* 取任务的「执行方式明细」JSON 串(原 {@code serviceTask.preachformDesc})。
|
* <p>
|
* 迁移对应:{@code PatMedInhospServiceImpl#addServiceSubtaskPreachform} L1063-1068 ——
|
* 它先 {@code selectServiceTaskByTaskid(taskid)} 取到任务,再读 {@code getPreachformDesc()}。
|
* <p>
|
* <b>为什么不复用 {@link #findPublishedLongTask(Long)}</b>:后者带 {@code sendState=2} 条件
|
* (只要"已发布"的任务),而这里原实现用的是 {@code selectServiceTaskByTaskid}
|
* (<b>不带 sendState 条件</b>)—— 换成前者会改变"任务查得到/查不到"的判断。
|
* 所以刻意分开成两个方法。
|
*
|
* @param taskid 任务ID
|
* @return {@code preachformDesc};任务不存在或该字段为空时返回 {@code null}
|
*/
|
String findPreachformDesc(Long taskid);
|
|
/**
|
* 判断某患者是否存在「指定状态 + 指定服务类型」的随访子任务。
|
* <p>
|
* 只返回"有没有",不返回列表 —— 调用方(门诊列表富化)只用它来置
|
* {@code serverState="1"},不需要子任务明细。
|
*
|
* <h3>原实现对应</h3>
|
* 门诊 {@code PatMedOuthospServiceImpl#selectPatMedOuthospList} L108-117:
|
* <pre>
|
* ServiceSubtaskEntity v = new ServiceSubtaskEntity();
|
* v.setPatid(patMedOuthosp1.getPatid());
|
* v.setSendstates(Arrays.asList(1L, 2L, 3L));
|
* v.setServiceType("3");
|
* List<ServiceSubtask> list = serviceSubtaskMapper.selectServiceSubtaskBySendstate(v);
|
* if (!CollectionUtils.isEmpty(list)) patMedOuthosp1.setServerState("1");
|
* </pre>
|
*
|
* <h3>⚠️ 与 {@link #findPendingSubtasks(Long, Long)} 的区别(别混用)</h3>
|
* <table border="1">
|
* <tr><th></th><th>本方法</th><th>findPendingSubtasks</th></tr>
|
* <tr><td>过滤条件</td><td>{@code sendstate IN (...)} + {@code service_type}</td>
|
* <td>固定 {@code sendstate = 2}</td></tr>
|
* <tr><td>是否按 taskid</td><td><b>否</b>(跨该患者所有任务)</td><td>是(可选)</td></tr>
|
* <tr><td>底层 Mapper 方法</td><td>{@code selectServiceSubtaskBySendstate}</td>
|
* <td>{@code selectServiceSubtaskList}</td></tr>
|
* </table>
|
* 两者查的是<b>不同口径</b>,不能互相替代。
|
*
|
* @param patid 患者档案ID
|
* @param sendstates 子任务状态集合(门诊传 1/2/3);为空则退化为不按状态过滤
|
* @param serviceType 服务类型(门诊传 {@code "3"});为 null 则不过滤
|
* @return true = 至少存在一条
|
*/
|
boolean existsSubtaskWithStates(Long patid, java.util.List<Long> sendstates, String serviceType);
|
|
/**
|
* 判断某患者「同一次出入院记录 + 同一个任务」是否<b>已经生成过</b>子任务。
|
*
|
* <h3>为什么需要它(2026-10 线上问题)</h3>
|
* 出院随访扫描是按 {@code pat_med_inhosp.deptcheck_flag = '0'} 取患者的。
|
* 一旦标志位因为规则配置/异常没被改成 1 或 2,下一轮扫描会把同一个患者再处理一遍,
|
* 而 {@code writeInSubTask} 的"停旧"分支在 {@code endTime1 == endTime}(同一次出院)时
|
* 走的是 else —— <b>会再 insert 一条 sendstate=4 的停用行</b>,
|
* 于是同一个患者每次跑批都多一条,最终同一患者几十条子任务。
|
* <p>
|
* 这个方法是那道"最后防线":落库前按
|
* {@code patid + inhospid + taskid}(且 {@code del_flag = 0})判重,
|
* 已存在就不再加。口径是"同一次出院 + 同一个任务只应有一条子任务",
|
* <b>不跨住院记录</b>(再次入院换了 inhospid 仍会新建,这是原有语义)。
|
*
|
* @param patid 患者档案ID
|
* @param inhospid 出入院记录ID;为 {@code null} 时退化为只看 {@code patid + taskid}
|
* @param taskid 任务ID
|
* @return true = 已存在(不应再 insert)
|
*/
|
boolean existsSubtask(Long patid, Long inhospid, Long taskid);
|
}
|