package com.smartor.common; import com.alibaba.fastjson2.JSON; import com.ruoyi.common.exception.base.BaseException; import com.ruoyi.common.utils.DateUtils; import com.smartor.domain.*; import com.smartor.mapper.ServiceSubtaskMapper; import com.smartor.mapper.ServiceSubtaskPreachformMapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Map; /** * 任务组(appltype=5)subtask 生成器 *

* 单一职责:根据 schedule + detailList 为每个患者派生 subtask。 * 同时服务两条路径: * (a) 前端手动勾选患者(insertOrUpdateTask) —— 旧 6 参入口; * (b) HIS 自动纳入新出院患者(PatMedInhospServiceImpl.writeInSubTask) —— 新 5 参入口。 * 两条路径统一从 baseSubtask 上取基准日字段,避免生成器与 PatTaskRelevance/ServiceTaskVO 耦合。 * * @author smartor * @date 2026-06-30 */ @Slf4j @Component public class ScheduleSubtaskBuilder { @Autowired private ServiceSubtaskMapper serviceSubtaskMapper; @Autowired private ServiceSubtaskPreachformMapper serviceSubtaskPreachformMapper; /** * 为单个患者派生 subtask(appltype=5)—— 解耦入口(自动纳入 & 手动勾选统一走这里)。 *

* 非循环:按 detailList 一次性铺平 N 条; * 循环 :按 schedule.currentLoopCount 生成当前轮 N 条,后续轮次由调度器滚动建。 * * @param serviceTask 主任务(已落库) * @param schedule 已落库的主计划 * @param detailList 明细列表(按 seq 升序) * @param baseSubtask 模板 subtask(已填充患者基础信息 + 基准日字段),方法内会深拷贝 * @param preachformList 发送方式(调用方已解析好,可为空) */ public void buildSubtasksForPatient(ServiceTask serviceTask, ServiceTaskSchedule schedule, List detailList, ServiceSubtask baseSubtask, List> preachformList) { if (CollectionUtils.isEmpty(detailList)) { throw new BaseException("任务组明细为空,无法生成 subtask, taskid=" + serviceTask.getTaskid()); } Date benchmark = resolveBenchmark(schedule, baseSubtask); long currentLoop = schedule.getCurrentLoopCount() == null ? 0L : schedule.getCurrentLoopCount(); for (ServiceTaskScheduleDetail detail : detailList) { ServiceSubtask subtask = copyFromBase(baseSubtask); // 回溯字段 subtask.setScheduleId(schedule.getId()); subtask.setScheduleDetailId(detail.getId()); subtask.setScheduleSeq(detail.getSeq()); subtask.setLoopCount(currentLoop); // 模板:detail 优先于 task if (detail.getTemplateid() != null) { subtask.setTemplateid(detail.getTemplateid()); subtask.setTemplatename(detail.getTemplatename()); } if (detail.getLibtemplateid() != null) { subtask.setLibtemplateid(detail.getLibtemplateid()); } // 时间:基准日 + dayOffset Date visitTime = addDays(benchmark, detail.getDayOffset()); subtask.setVisitTime(visitTime); subtask.setSenddate(visitTime); subtask.setLongSendTime(visitTime); // 状态:与原流程一致,新建后置为待发送 subtask.setSendstate(2L); subtask.setCreateTime(DateUtils.getNowDate()); subtask.setUpdateTime(DateUtils.getNowDate()); serviceSubtaskMapper.insertServiceSubtask(subtask); insertPreachform(serviceTask, preachformList, subtask); log.info("[任务组] taskid={} patid={} 生成 subtask id={} seq={} dayOffset={} visitTime={}", serviceTask.getTaskid(), baseSubtask.getPatid(), subtask.getId(), detail.getSeq(), detail.getDayOffset(), visitTime); } } /** * 兼容入口:前端手动勾选患者(insertOrUpdateTask)。 * 把 patTaskRelevance 的基准日字段回填到 baseSubtask 后,委托给解耦入口。 */ public void buildSubtasksForPatient(ServiceTask serviceTask, List> preachformList, ServiceTaskSchedule schedule, List scheduleDetailList, PatTaskRelevance patTaskRelevance, ServiceSubtask baseSubtask) { // 回填基准日字段(手动路径的 baseSubtask 由 VO 拼装,基准日字段可能未填全) if (baseSubtask.getStarttime() == null) { baseSubtask.setStarttime(patTaskRelevance.getInhosptime() != null ? patTaskRelevance.getInhosptime() : patTaskRelevance.getStarttime()); } if (baseSubtask.getEndtime() == null) { baseSubtask.setEndtime(patTaskRelevance.getEndtime()); } if (baseSubtask.getAdmindate() == null) { baseSubtask.setAdmindate(patTaskRelevance.getVisittime()); } buildSubtasksForPatient(serviceTask, schedule, scheduleDetailList, baseSubtask, preachformList); } /** * 根据 scheduleBenchmark 解析基准时间: * 0-自定义时间 1-入院时间 2-出院时间 3-就诊时间 4-入组时间 * 全部从 baseSubtask 取,取不到兜底当前时间。 */ private Date resolveBenchmark(ServiceTaskSchedule schedule, ServiceSubtask base) { Integer benchmark = schedule.getScheduleBenchmark() == null ? 0 : schedule.getScheduleBenchmark(); Date now = DateUtils.getNowDate(); switch (benchmark) { case 1: return base.getStarttime() != null ? base.getStarttime() : now; case 2: return base.getEndtime() != null ? base.getEndtime() : now; case 3: return base.getAdmindate() != null ? base.getAdmindate() : now; case 4: return base.getCreateTime() != null ? base.getCreateTime() : now; case 0: default: return schedule.getScheduleBenchmarkCustomTime() != null ? schedule.getScheduleBenchmarkCustomTime() : now; } } private Date addDays(Date base, int dayOffset) { Calendar c = Calendar.getInstance(); c.setTime(base); c.add(Calendar.DAY_OF_MONTH, dayOffset); return c.getTime(); } /** * 写 service_subtask_preachform,发送方式由调用方备好。 */ private void insertPreachform(ServiceTask serviceTask, List> pfList, ServiceSubtask subtask) { if (CollectionUtils.isEmpty(pfList)) { return; } for (Map map : pfList) { ServiceSubtaskPreachform pf = new ServiceSubtaskPreachform(); pf.setSort(ObjectUtils.isEmpty(map.get("sort")) ? 0 : Long.valueOf(map.get("sort").toString())); pf.setPreachform(ObjectUtils.isEmpty(map.get("preachform")) ? "" : map.get("preachform").toString()); pf.setCompensateTime(ObjectUtils.isEmpty(map.get("compensateTime")) ? "" : map.get("compensateTime").toString()); pf.setTaskid(serviceTask.getTaskid()); pf.setSubid(subtask.getId()); pf.setSendstate("1"); pf.setOrgid(serviceTask.getOrgid()); pf.setCreateTime(new Date()); serviceSubtaskPreachformMapper.insertServiceSubtaskPreachform(pf); } } /** * 深拷贝 baseSubtask 到一个新对象(每次 insert 后 id 会回写,必须独立实例)。 * 用 fastjson 做深拷贝,省一堆 set。 */ private ServiceSubtask copyFromBase(ServiceSubtask base) { String json = JSON.toJSONString(base); ServiceSubtask copy = JSON.parseObject(json, ServiceSubtask.class); // 主键、id 必须清掉,否则 useGeneratedKeys 仍会用上轮的 id 触发主键冲突 copy.setId(null); return copy; } }