package com.ruoyi.inhospvisit.support; import com.ruoyi.common.core.service.dto.VisitSubtaskDraft; 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.VisitTaskRow; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.Calendar; import java.util.Date; /** * 随访子任务「组装器」—— 逐行搬自 {@code PatMedInhospServiceImpl} 的两个 boxed 方法。 * *
| 本类方法 | 原实现 | 行数 |
|---|---|---|
| {@link #boxedServiceSubtask} | {@code boxedServiceSubtask}(L1108-1222) | 115 |
| {@link #boxedServiceSubtaskForOp} | {@code boxedServiceSubtaskForOp}(L1225-1300) | 76 |
| {@link #addDays} | {@code addDays}(L663-671) | 9 |
| {@link #minusDay} | {@code minusDay}(L1309-1318) | 10 |
| {@link #updateCheckFlag} | {@code updateCheckFlag}(L1660-1684) | 25 |
* ⚠️ 这个值我第一版就抄错了一位(写成了 {@code ...1271011A1001})。 * 已逐字核对原实现出现过的 4 处: * {@code PatMedInhospServiceImpl} L798 / L1186(注释) / L1195 / L1338,均为 * {@code 47255004333112711A1001}。抄错会导致"景宁的白名单分支永远不生效"。 */ private static final String ORG_JINGNING = "47255004333112711A1001"; @Value("${spring.profiles.active}") private String active; /** * 组装院外随访(科室/病区/疾病/宣教/药品)的子任务草稿。 *
* 逐行对应 {@code boxedServiceSubtask(ServiceTask, PatMedInhosp, PatArchive, String, Integer)} L1108-1222。 * * @param serviceTask 长期任务 * @param inhosp 出入院记录 * @param archive 患者档案 * @param config {@code sys_config: visit.early.day} * @param type 0=药品宣教 / 1=离院科室 / 2=离院病区 / 3=疾病 / 4=入院科室 / 5=入院病区 * @param sideEffect 副作用回调(出院时间为空时要把标志位与备注写回库);可为 {@code null}(测试用) * @return 组装好的草稿;出院时间为空且 type∉{4,5} 时返回"空草稿"(对应原实现 {@code new ServiceSubtask()}) */ public VisitSubtaskDraft boxedServiceSubtask(VisitTaskRow serviceTask, VisitInhospRow inhosp, VisitArchiveRow archive, String config, Integer type, BoxedSideEffect sideEffect) { VisitSubtaskDraft d = new VisitSubtaskDraft(); // ---- 原 L1113:DtoConversionUtils 的整批拷贝(18 个字段靠它带过来)---- applyCarriedOverFieldsFromTask(d, serviceTask); // ---- 原 L1114-1118 ---- d.setTaskid(serviceTask.getTaskid()); d.setOrgid(serviceTask.getOrgid()); d.setCampusid(serviceTask.getCampusid()); if (StringUtils.isNotEmpty(serviceTask.getLibtemplateid())) { d.setLibtemplateid(Long.valueOf(serviceTask.getLibtemplateid())); } // ---- 原 L1119-1127 ---- d.setNurseId(inhosp.getNurseId()); d.setNurseName(inhosp.getNurseName()); d.setInhospid(inhosp.getInhospid()); // ⚠️ 这一行是【补的】,不是搬运: // 原 boxedServiceSubtask 从未设 hospno,而 ServiceSubtask.hospno 的语义是 // "就诊编号(对应的 outhospno、inhospno)"(见 ServiceSubtask 字段注释)。 // 结果就是出院链路的 service_subtask.hospno 一直是 NULL,与门诊链路不一致。 // 取值:pat_med_inhosp.inhospno(门诊侧取 pat_med_outhosp.outhospno,见 OuthospSubtaskDraft)。 d.setHospno(inhosp.getInhospno()); d.setDrcode(inhosp.getDrcode()); d.setDrname(inhosp.getDrname()); d.setDeptcode(inhosp.getLeaveldeptcode()); d.setDeptname(inhosp.getLeaveldeptname()); d.setLeavehospitaldistrictcode(inhosp.getLeavehospitaldistrictcode()); d.setLeavehospitaldistrictname(inhosp.getLeavehospitaldistrictname()); // ---- 原 L1128-1134:type 4/5 改用入院维度 ---- if (type != null && (type == 4 || type == 5)) { d.setDeptcode(inhosp.getDeptcode()); d.setDeptname(inhosp.getDeptname()); d.setLeavehospitaldistrictcode(inhosp.getHospitaldistrictcode()); d.setLeavehospitaldistrictname(inhosp.getHospitaldistrictname()); } // ---- 原 L1135-1146 ---- d.setLeaveicd10code(inhosp.getLeaveicd10code()); d.setLeavediagname(inhosp.getLeavediagname()); d.setDiagname(inhosp.getDiagname()); d.setTemplateid(serviceTask.getTemplateid()); d.setTemplatename(serviceTask.getTemplatename()); d.setPatid(archive.getId()); d.setSendname(archive.getName()); d.setSfzh(archive.getIdcardno()); d.setPhone(archive.getTelcode()); if (StringUtils.isBlank(archive.getTelcode())) { d.setPhone(archive.getRelativetelcode()); } d.setSex(archive.getSex()); d.setAge(archive.getAge()); // ---- 原 L1147-1163 ---- d.setSendstate(2L); d.setCampusid(inhosp.getCampusid()); d.setManagementDoctor(inhosp.getManagementDoctor()); d.setManagementDoctorCode(inhosp.getManagementDoctorCode()); d.setStarttime(inhosp.getStarttime()); d.setEndtime(inhosp.getEndtime()); d.setServiceType(serviceTask.getServiceType()); d.setPreachform(serviceTask.getPreachform()); d.setHospType("2"); if (type != null && (type == 4 || type == 5)) { d.setHospType("6"); } d.setCreateTime(new Date()); d.setUpdateTime(new Date()); d.setCreateBy(inhosp.getNurseName()); d.setLeavehospitaldistrictcode(inhosp.getLeavehospitaldistrictcode()); d.setLeavehospitaldistrictname(inhosp.getLeavehospitaldistrictname()); d.setUpdateBy(serviceTask.getUpdateBy()); d.setUpdateTime(new Date()); // 原 L1164-1165:如果是宣教就设置为7 // ⚠️ 原实现直接 getServiceType().equals("17"),serviceType 为 null 会 NPE —— 原样保留该风险 if (serviceTask.getServiceType() != null && SERVICE_TYPE_IN_HOSP_PUBLICITY.equals(serviceTask.getServiceType())) { d.setTaskSituation(7); } // ---- 原 L1166-1182:发送时间 ---- if (serviceTask.getSendDay() == null) { serviceTask.setSendDay(1L); } Date newDate; if (inhosp.getEndtime() != null) { newDate = addDays(inhosp.getEndtime(), serviceTask.getSendDay().intValue()); } else { // 如果等于4/5说明是入院宣教,入院不可能有 endTime if (type == null || (type != 4 && type != 5)) { // 原 L1174-1178:★副作用:写回 deptcheckFlag="2" + remark,然后返回【空草稿】 if (sideEffect != null) { sideEffect.onMissingOutHospTime(inhosp); } return new VisitSubtaskDraft(); } // 入院宣教,基本当天或隔天就要发出 newDate = addDays(new Date(), serviceTask.getSendDay().intValue()); } d.setLongSendTime(newDate); d.setVisitTime(newDate); // ---- 原 L1187-1203:通知类提前 N 天 ---- if (TASK_TYPE_NOTICE.equals(serviceTask.getType())) { d.setVisitTime(minusDay(newDate, StringUtils.isNotEmpty(config) ? Integer.parseInt(config) : 0)); if (inhosp.getFudate() != null) { d.setVisitTime(minusDay(inhosp.getFudate(), StringUtils.isNotEmpty(config) ? Integer.parseInt(config) : 0)); } else { // 判断是不是景宁 if (ORG_JINGNING.equals(inhosp.getOrgid())) { d.setVisitTime(null); // 不执行 d.setSendstate(4L); d.setRemark("景宁没有给随访时间,不发送通知短信"); } } } // ---- 原 L1204-1210:南华附一:入院出院同一天不随访 ---- if ("nhfy".equals(active) && inhosp.getStarttime() != null && inhosp.getEndtime() != null && daysBetweenByMillisecond(inhosp.getStarttime(), inhosp.getEndtime()) == 0) { d.setRemark("患者当天出院,不用随访"); d.setResult("error"); // 不执行 d.setSendstate(4L); } // ---- 原 L1212-1220:患者维度的"免随访"标记 ---- if (StringUtils.isNotEmpty(archive.getNotrequiredFlag()) && "1".equals(archive.getNotrequiredFlag())) { d.setRemark(archive.getNotrequiredreason()); d.setResult("error"); d.setTaskSituation(6); // 不执行 d.setSendstate(4L); } return d; } /** * 组装手术随访的子任务草稿。 *
* 逐行对应 {@code boxedServiceSubtaskForOp(ServiceTask, PatMedInhosp, PatMedOperationItem, PatArchive, String)} * L1225-1300。 *
* ⚠️ 与 {@link #boxedServiceSubtask} 的两处关键差异: *
* ⚠️ 这 18 个字段是"隐式"进子任务的:原实现没有一行 set 它们, * 全靠 DtoConversionUtils 的同名拷贝。搬迁后必须显式列出,否则值会丢。 * 清单由脚本从 {@code ServiceTask ∩ ServiceSubtask} 减去"被显式 set 过的"算出, * 并由 {@code sql/_probe/verify_inhospvisit_semantics.py} 断言。 */ private void applyCarriedOverFieldsFromTask(VisitSubtaskDraft d, VisitTaskRow t) { d.setDelFlag(t.getDelFlag()); d.setDiagType(t.getDiagType()); d.setEndOutHospTime(t.getEndOutHospTime()); d.setGuid(t.getGuid()); d.setIsupload(t.getIsupload()); d.setLeavehospitaldistrictcodes(t.getLeavehospitaldistrictcodes()); d.setLeaveldeptcodes(t.getLeaveldeptcodes()); d.setLibtemplatename(t.getLibtemplatename()); d.setPageNum(t.getPageNum()); d.setPageSize(t.getPageSize()); d.setPid(t.getPid() == null ? null : t.getPid().longValue()); d.setSendType(t.getSendType() == null ? null : String.valueOf(t.getSendType())); d.setStartOutHospTime(t.getStartOutHospTime()); d.setTaskDesc(t.getTaskDesc()); d.setTaskName(t.getTaskName()); d.setTextParam(t.getTextParam()); d.setType(t.getType()); d.setUploadTime(t.getUploadTime()); } // ========================================================================== // 工具方法(原样照抄,含已知缺陷) // ========================================================================== /** * 日期加天数(原 {@code addDays},L663-671)。{@code days} 为 null 时按 1 处理。 */ public Date addDays(Date date, Integer days) { if (days == null) { days = 1; } Calendar calendar = Calendar.getInstance(); calendar.setTime(date); calendar.add(Calendar.DAY_OF_MONTH, days); return calendar.getTime(); } /** * 减去相应天数(原 {@code minusDay},L1309-1318)。 *
* ⚠️ 已知缺陷,原样保留:原实现写死 {@code instant.minus(2, ChronoUnit.DAYS)}, * 完全忽略 {@code day} 入参。调用点传的是 {@code sys_config: visit.early.day}, * 但实际效果永远是"减 2 天"。 *
* 不修:修了会改变线上通知短信的发送时间,属可见行为变更。 */ public Date minusDay(Date originalDate, Integer day) { // 原样照抄:入参 day 被忽略,固定减 2 天 java.time.Instant instant = originalDate.toInstant(); java.time.Instant twoDaysAgoInstant = instant.minus(2, java.time.temporal.ChronoUnit.DAYS); return Date.from(twoDaysAgoInstant); } /** * 两个 {@code Date} 相差多少天(原 {@code DateUtils.differentDaysByMillisecond})。 *
* 原实现 {@code boxedServiceSubtask} L1205 直接调 {@code DateUtils.differentDaysByMillisecond}, * 该工具在 ruoyi-common,新模块可以直接用 —— 这里只是把调用点收敛到一处, * 便于探针断言"用的是同一个工具"。 */ private int daysBetweenByMillisecond(Date start, Date end) { return com.ruoyi.common.utils.DateUtils.differentDaysByMillisecond(start, end); } /** * 组装器的副作用回调(原 {@code boxedServiceSubtask} 出院时间为空时会顺手写库)。 *
* 抽成回调是为了让组装器不直接依赖 Mapper,保持"纯组装"可被逐行比对。 * 真正的写库由 {@code InhospVisitTaskService} 实现。 */ public interface BoxedSideEffect { /** * 出院时间为空时的回写:原 L1174-1176 *
* patMedInhosp1.setDeptcheckFlag("2");
* patMedInhosp1.setRemark(patMedInhosp1.getRemark() + "& 该患者疾病出院时间为空,出院记录ID为:" + patMedInhosp1.getInhospid());
* patMedInhospMapper.updatePatMedInhosp(patMedInhosp1);
*
*/
void onMissingOutHospTime(VisitInhospRow inhosp);
}
}