package com.ruoyi.task.api.contract; import com.ruoyi.common.core.service.VisitTaskCreateApi; 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 lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Collections; import java.util.List; import com.ruoyi.task.domain.entity.ServiceSubtaskEntity; import com.ruoyi.task.domain.ServiceSubtask; import com.ruoyi.task.domain.ServiceTask; import com.ruoyi.task.mapper.ServiceSubtaskMapper; import com.ruoyi.task.mapper.ServiceTaskMapper; /** * 【过渡实现·临时】{@link VisitTaskCreateApi} 在 smartor 侧的桥接。 * *

为什么需要它

* 出入院随访要"建随访任务",但按你的版图,任务创建将来是独立模块。 * 在它落地之前,先把现有的写库能力({@link ServiceSubtaskMapper} / * {@code IPatMedInhospService#persistSubtask})原样包一层, * 让 {@code ruoyi-inhospvisit} 现在就只依赖 {@link VisitTaskCreateApi} 这个接口。 *

* 将来动作:新模块建好后,把它的实现注册进容器, * 本类整体删除即可,{@code ruoyi-inhospvisit} 一行都不用改。 * *

⚠️ 本类必须保持"纯搬运"

* 这里不得加入去重、状态机、默认值补齐、合并更新等任何新逻辑 —— * 那些语义在 {@code ruoyi-inhospvisit} 的 {@code writeInSubTask} 里。 * 本类只做:字段搬运 + 转发现有 Mapper 调用。 * 逐字段对应关系由 {@code sql/_probe/verify_inhospvisit_equivalence.py}(A 档)与 * {@code verify_inhospvisit_semantics.py}(B 档口径断言)把关。 * *

逐方法来源(迁移前 {@code PatMedInhospServiceImpl},@ b25c3b51)

* * * * * * *
本类方法原实现位置
{@link #findPublishedLongTask(Long)}L686-728(查 ServiceTask,sendState=2)
{@link #insertSubtask(VisitSubtaskDraft)}L823 / L838 / L854 / L862 / L873
{@link #findPendingSubtasks(Long, Long)}L760-764、L1325-1328
{@link #updatePendingSubtask(VisitPendingSubtaskDto)}L782 / L791 / L812 / L836 / L1349
* * @see com.ruoyi.common.core.service.VisitTaskCreateApi */ @Slf4j @Component public class VisitTaskCreateApiImpl implements VisitTaskCreateApi { @Autowired private ServiceTaskMapper serviceTaskMapper; @Autowired private ServiceSubtaskMapper serviceSubtaskMapper; /** * 按 taskid 取「已发布的长期任务」。 *

* 原实现({@code PatMedInhospServiceImpl} L686-690): *

     * ServiceTask st = new ServiceTask();
     * st.setTaskid(taskid);
     * st.setSendState(2L);
     * List<ServiceTask> serviceTasks = serviceTaskMapper.selectServiceTaskList(st);
     * ServiceTask serviceTask = null;
     * if (CollectionUtils.isEmpty(serviceTasks)) { ...按 type 写不同 flag 后 return... }
     * else { serviceTask = serviceTasks.get(0); }
     * 
* 这里只负责"查 + 取第一条";"查不到时按 type 写哪个 flag"属于随访域, * 留在 {@code writeInSubTask} 里。 */ @Override public VisitLongTaskDto findPublishedLongTask(Long taskid) { ServiceTask query = new ServiceTask(); query.setTaskid(taskid); query.setSendState(2L); List tasks = serviceTaskMapper.selectServiceTaskList(query); if (CollectionUtils.isEmpty(tasks)) { return null; } return toLongTaskDto(tasks.get(0)); } @Override public boolean insertSubtask(VisitSubtaskDraft draft) { if (draft == null) { log.warn("【出入院随访·过渡桥】insertSubtask 收到 null 草稿,跳过"); return false; } ServiceSubtask entity = toSubtaskEntity(draft); // 转发现有过渡入口(等价于 serviceSubtaskMapper.insertServiceSubtask(entity) == 1) boolean ok = serviceSubtaskMapper.insertServiceSubtask(entity) == 1; // useGeneratedKeys 会把自增主键回填到 entity 上,回传给出参草稿, // 供"先 insert 再补字段回写"的那条链路使用(原实现也是复用同一个对象) draft.setId(entity.getId()); return ok; } @Override public List findPendingSubtasks(Long patid, Long taskid) { ServiceSubtaskEntity query = new ServiceSubtaskEntity(); query.setPatid(patid); query.setSendstate(2L); // 原实现在 dealInHospInfo(L1325-1328)里【不设 taskid】(按患者全量查), // 在 writeInSubTask(L760-764)里【设 taskid】。这里保持同样的可选语义。 query.setTaskid(taskid); List rows = serviceSubtaskMapper.selectServiceSubtaskList(query); if (CollectionUtils.isEmpty(rows)) { return Collections.emptyList(); } List out = new ArrayList<>(rows.size()); for (ServiceSubtask row : rows) { out.add(toPendingDto(row)); } return out; } @Override public boolean updatePendingSubtask(VisitPendingSubtaskDto pending) { if (pending == null || pending.getId() == null) { log.warn("【出入院随访·过渡桥】updatePendingSubtask 缺少 id,跳过。pending={}", pending); return false; } ServiceSubtask entity = new ServiceSubtask(); // 只映射"原实现会带过去"的字段: // 原实现传的是【查询出来的完整对象】+ 在内存里改的那几个字段。 // 查询出来的完整对象里,与本方法语义相关的只有 id/taskid/endtime/longSendTime/serviceType/orgid/sendstate/patid, // 其余字段值不变、写回也等于不写,故此处只带这些 + 调用方改过的 result/remark/updateBy/updateTime。 entity.setId(pending.getId()); entity.setTaskid(pending.getTaskid()); entity.setSendstate(pending.getSendstate()); entity.setResult(pending.getResult()); entity.setRemark(pending.getRemark()); entity.setUpdateBy(pending.getUpdateBy()); entity.setUpdateTime(pending.getUpdateTime()); entity.setIsVisitAgain(pending.getIsVisitAgain()); // finishtime:出入院侧从不设置(原样保留"不写"),门诊侧会设置 entity.setFinishtime(pending.getFinishtime()); return Boolean.TRUE.equals(serviceSubtaskMapper.updateServiceSubtask(entity)); } /** * 取任务的 {@code preachformDesc}。 *

* 原实现({@code PatMedInhospServiceImpl#addServiceSubtaskPreachform} L1063)用的是 * {@code serviceTaskMapper.selectServiceTaskByTaskid(taskid)} —— * 不带 sendState 条件,且查不到时返回 null。 * 这里逐字照搬,不复用 {@link #findPublishedLongTask}(那个带 {@code sendState=2})。 */ @Override public String findPreachformDesc(Long taskid) { if (taskid == null) { return null; } ServiceTask task = serviceTaskMapper.selectServiceTaskByTaskid(taskid); return task == null ? null : task.getPreachformDesc(); } /** * 是否存在「指定状态 + 指定服务类型」的子任务。 *

* 原实现(门诊 {@code selectPatMedOuthospList} L108-117):调 * {@code serviceSubtaskMapper.selectServiceSubtaskBySendstate(v)}, * 参数是 {@code ServiceSubtaskEntity},设了 {@code patid} / {@code sendstates} / {@code serviceType}。 * 这里逐字照搬,只把"非空"折叠成 {@code boolean}(调用方原本只用它做 if 判断)。 * *

⚠️ 不属于 {@link #findPendingSubtasks(Long, Long)}:那个固定 {@code sendstate=2} * 且按 taskid 过滤、查 {@code selectServiceSubtaskList},口径不同、不可互替。 */ @Override public boolean existsSubtaskWithStates(Long patid, java.util.List sendstates, String serviceType) { if (patid == null) { return false; } ServiceSubtaskEntity v = new ServiceSubtaskEntity(); v.setPatid(patid); v.setSendstates(sendstates); v.setServiceType(serviceType); List list = serviceSubtaskMapper.selectServiceSubtaskBySendstate(v); return !CollectionUtils.isEmpty(list); } // ========================================================================== // 字段搬运(逐字段对照,勿"顺手精简") // ========================================================================== private VisitLongTaskDto toLongTaskDto(ServiceTask t) { VisitLongTaskDto dto = new VisitLongTaskDto(); dto.setTaskid(t.getTaskid()); dto.setTaskName(t.getTaskName()); dto.setType(t.getType()); dto.setServiceType(t.getServiceType()); dto.setTemplateid(t.getTemplateid()); dto.setTemplatename(t.getTemplatename()); dto.setLibtemplateid(t.getLibtemplateid()); dto.setSendDay(t.getSendDay()); dto.setPreachform(t.getPreachform()); dto.setPreachformDesc(t.getPreachformDesc()); dto.setOrgid(t.getOrgid()); dto.setCampusid(t.getCampusid()); dto.setHosType(t.getHospType()); dto.setUpdateBy(t.getUpdateBy()); dto.setCreateBy(t.getCreateBy()); return dto; } /** * 草稿 → 实体。 *

* 逐个 setter 对照迁移前的 {@code boxedServiceSubtask} 与 {@code writeInSubTask}, * 少一个字段就会让对应列静默不更新(XML 里是 {@code }), * 所以这里刻意写全,不做精简。 */ private ServiceSubtask toSubtaskEntity(VisitSubtaskDraft d) { ServiceSubtask e = new ServiceSubtask(); e.setId(d.getId()); e.setTaskid(d.getTaskid()); e.setOrgid(d.getOrgid()); e.setCampusid(d.getCampusid()); e.setPatid(d.getPatid()); e.setSendname(d.getSendname()); e.setSfzh(d.getSfzh()); e.setPhone(d.getPhone()); e.setSex(d.getSex()); e.setAge(d.getAge()); e.setInhospid(d.getInhospid()); e.setNurseId(d.getNurseId()); e.setNurseName(d.getNurseName()); e.setDrcode(d.getDrcode()); e.setDrname(d.getDrname()); e.setManagementDoctor(d.getManagementDoctor()); e.setManagementDoctorCode(d.getManagementDoctorCode()); e.setDeptcode(d.getDeptcode()); e.setDeptname(d.getDeptname()); e.setLeavehospitaldistrictcode(d.getLeavehospitaldistrictcode()); e.setLeavehospitaldistrictname(d.getLeavehospitaldistrictname()); e.setLeaveicd10code(d.getLeaveicd10code()); e.setLeavediagname(d.getLeavediagname()); e.setDiagname(d.getDiagname()); e.setTemplateid(d.getTemplateid()); e.setTemplatename(d.getTemplatename()); e.setLibtemplateid(d.getLibtemplateid()); e.setStarttime(d.getStarttime()); e.setEndtime(d.getEndtime()); e.setLongSendTime(d.getLongSendTime()); e.setVisitTime(d.getVisitTime()); e.setSendstate(d.getSendstate()); e.setSubmit(d.getSubmit()); e.setServiceType(d.getServiceType()); e.setDiagType(d.getDiagType()); e.setHospType(d.getHospType()); e.setPreachform(d.getPreachform()); e.setVisitCount(d.getVisitCount()); e.setIsVisitAgain(d.getIsVisitAgain()); e.setVisitDeptCode(d.getVisitDeptCode()); e.setVisitDeptName(d.getVisitDeptName()); e.setTaskSituation(d.getTaskSituation()); e.setResult(d.getResult()); e.setRemark(d.getRemark()); e.setOperationItemId(d.getOperationItemId()); e.setCreateBy(d.getCreateBy()); e.setUpdateBy(d.getUpdateBy()); // ------------------------------------------------------------------ // 门急诊随访专用字段(OuthospSubtaskDraft)。 // // ⚠️ 用 instanceof 分流,而不是给 VisitSubtaskDraft 加字段: // 加了就必须在这里补映射,而补映射会让【出入院那条已验收的路径】多写列 // (insertServiceSubtask 是 39 个条件列,本来传 null 的列从此会被写进去)。 // 分流后:出入院传的是 VisitSubtaskDraft,下面整块不执行 —— 行为完全不变。 // 见 OuthospSubtaskDraft 的类注释。 // ------------------------------------------------------------------ if (d instanceof com.ruoyi.common.core.service.dto.OuthospSubtaskDraft) { com.ruoyi.common.core.service.dto.OuthospSubtaskDraft o = (com.ruoyi.common.core.service.dto.OuthospSubtaskDraft) d; // 只有父类没有的字段才在这里补映射(其余如 sendname/sfzh/phone/sex/age 父类已有) e.setHospno(o.getHospno()); e.setAdmindate(o.getAdmitdate()); e.setFinishtime(o.getFinishtime()); // ⚠️ 原实现硬编码 "1"(不读 serviceTask.getHospType()),此处保持一致 e.setHospType(o.getHospType()); } return e; } private VisitPendingSubtaskDto toPendingDto(ServiceSubtask s) { VisitPendingSubtaskDto dto = new VisitPendingSubtaskDto(); dto.setId(s.getId()); dto.setTaskid(s.getTaskid()); dto.setEndtime(s.getEndtime()); dto.setLongSendTime(s.getLongSendTime()); dto.setServiceType(s.getServiceType()); dto.setOrgid(s.getOrgid()); dto.setSendstate(s.getSendstate()); // finishtime:门诊侧停旧子任务时要读到它吗?原实现是"在查询出来的完整对象上改", // 而它要改的就是 finishtime 本身(设为 new Date()),所以这里带上查询值可保证 // "未改动的字段回写等于不写"这一语义成立。 dto.setFinishtime(s.getFinishtime()); // result / remark / updateBy / updateTime 刻意【不带过来】: // 它们原本就是 null(查询结果为 sendstate=2 的待执行记录), // 由调用方在"移出"时按原实现设置。 return dto; } }