ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
@@ -8,6 +8,7 @@
import com.google.gson.Gson;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.service.IDingTalkService;
import com.ruoyi.common.dx.MessageSend;
@@ -16,13 +17,14 @@
import com.ruoyi.common.enums.WxGZHEnum;
import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.common.utils.sms.smsUtils;
import com.ruoyi.common.utils.sms.SmsUtils;
import com.ruoyi.quartz.service.ICollectHISService;
import com.ruoyi.quartz.util.CreateDataTable;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysUserService;
import com.smartor.common.LSHospTokenUtil;
import com.smartor.common.MtSubmitSmUtil;
import com.smartor.common.QwenLLMUtil;
@@ -108,6 +110,9 @@
    @Autowired
    private IDingTalkService dingTalkService;
    @Autowired
    private ISysUserService userService;
    @Value("${localIP}")
@@ -283,31 +288,67 @@
        subtaskDetailTraceVO.setHandleFlag("0");
        List<ServiceSubtaskDetailTrace> serviceSubtaskDetailTraces =
                serviceSubtaskDetailTraceService.selectServiceSubtaskDetailTtraceList(subtaskDetailTraceVO);
        // 把 dutyDeptPersonCode 整理到 list 中(如果多个人用逗号分割,则展开并去重)
        List<String> dutyDeptPersonCodeList = serviceSubtaskDetailTraces.stream()
                .map(ServiceSubtaskDetailTrace::getDutyDeptPersonCode)
                .filter(StringUtils::isNotBlank)
                .flatMap(code -> Arrays.stream(code.split(",")))
                .map(String::trim)
                .filter(StringUtils::isNotBlank).distinct()  // 去重
                .collect(Collectors.toList());
        // 获取“异常内容提醒"
        SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("send.except.content");
        String exceptContent = sysConfig.getConfigValue();
        // 获取“内容提醒测试人工号”
        String testPerson = sysConfigMapper.checkConfigKeyUnique("send.test.person").getConfigValue();
        if (StringUtils.isNotBlank(testPerson)) {
            String[] split = testPerson.split(",");
            if (CollectionUtils.isEmpty(dutyDeptPersonCodeList)) dutyDeptPersonCodeList = Arrays.asList(split);
            else dutyDeptPersonCodeList.addAll(Arrays.asList(split));
        }
        SysConfig testPersonConfig = sysConfigMapper.checkConfigKeyUnique("send.test.person");
        String testPerson = ObjectUtils.isNotEmpty(testPersonConfig) ? testPersonConfig.getConfigValue() : "";
        if(CollectionUtils.isNotEmpty(serviceSubtaskDetailTraces)){
            for (ServiceSubtaskDetailTrace serviceSubtaskDetailTrace : serviceSubtaskDetailTraces) {
                List<String> dutyDeptPersonCodeList = new ArrayList<>();
                        // 把 dutyDeptPersonCode 整理到 list 中(如果多个人用逗号分割,则展开并去重)
                String dutyDeptPersonCodeStr = serviceSubtaskDetailTrace.getDutyDeptPersonCode();
                if(StringUtils.isNotEmpty(dutyDeptPersonCodeStr)){
                    String[] dutyDeptPersonCodeSplit = dutyDeptPersonCodeStr.split(",");
                    if (CollectionUtils.isEmpty(dutyDeptPersonCodeList)) {
                        dutyDeptPersonCodeList = new ArrayList<>(Arrays.asList(dutyDeptPersonCodeSplit));
                    } else {
                        dutyDeptPersonCodeList.addAll(Arrays.asList(dutyDeptPersonCodeSplit));
                    }
        if (CollectionUtils.isNotEmpty(dutyDeptPersonCodeList)) {
            for (String dutyDeptPersonCode : dutyDeptPersonCodeList) {
                log.info("【sendExceptInfo】发送人工号:{}", dutyDeptPersonCode);
                String dutyDeptPerson = dingTalkService.getDingTalkUserByMobile(dutyDeptPersonCode);
                dingTalkService.sendDingTalkText(exceptContent, dutyDeptPerson);
                }
                if (StringUtils.isNotBlank(testPerson)) {
                    String[] split = testPerson.split(",");
                    if (CollectionUtils.isEmpty(dutyDeptPersonCodeList)) {
                        dutyDeptPersonCodeList = new ArrayList<>(Arrays.asList(split));
                    } else {
                        dutyDeptPersonCodeList.addAll(Arrays.asList(split));
                    }
                }
                if (CollectionUtils.isNotEmpty(dutyDeptPersonCodeList)) {
                    for (String dutyDeptPersonCode : dutyDeptPersonCodeList) {
                        log.info("【sendExceptInfo】发送人工号:{}", dutyDeptPersonCode);
                        SysUser sysUser = new SysUser();
                        SysUser sysUserVo = new SysUser();
                        sysUserVo.setUserName(dutyDeptPersonCode);
                        sysUserVo.setOrgid(serviceSubtaskDetailTrace.getOrgid());
                        List<SysUser> users = userService.getUserList(sysUserVo);
                        String phoneNumber = "";
                        if(ObjectUtils.isNotEmpty(users)){
                            sysUser = users.get(0);
                            phoneNumber = sysUser.getPhonenumber();
                        }else {
                            continue;
                        }
                        if(StringUtils.isEmpty(phoneNumber)){
                            continue;
                        }
                        String dutyDeptPerson = dingTalkService.getDingTalkUserByMobile(phoneNumber);
                        if(StringUtils.isNotBlank(dutyDeptPerson)){
                            String context = TemplateUtils.render(exceptContent, serviceSubtaskDetailTrace);
                            dingTalkService.sendDingTalkText(context, dutyDeptPerson);
                            //更新状态
                            serviceSubtaskDetailTrace.setHandleFlag("1");
                            serviceSubtaskDetailTrace.setUpdateTime(new Date());
                            serviceSubtaskDetailTraceService.updateServiceSubtaskDetailTtrace(serviceSubtaskDetailTrace);
                        }
                    }
                }
            }
        }
    }
@@ -882,6 +923,13 @@
            serviceSubtaskList.addAll(compensateServiceSubtaskList);
        }
        for (ServiceSubtask serviceSubtask : serviceSubtaskList) {
            //currentPreachform为空,说明该子任务还没有执行过任何一种发送方式(sendstate=1,属于首次补偿),
            //这里直接交给sfHandlle处理,它会按sort=1的第一种发送方式发送,与getSendPreachform中的处理保持一致
            if (StringUtils.isEmpty(serviceSubtask.getCurrentPreachform())) {
                log.warn("【compensateTask】subid:{},currentPreachform为空,按第一种发送方式补偿处理", serviceSubtask.getId());
                sfHandlle(serviceSubtask);
                continue;
            }
            //根据当前的执行方式,获取下一种执行方式
            ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform();
            serviceSubtaskPreachform.setTaskid(serviceSubtask.getTaskid());
@@ -952,6 +1000,24 @@
        LocalDate today = LocalDate.now();
        // 如果日期早于或等于今天,返回 true
        return !localDate.isAfter(today);
    }
    /**
     * 按机构查询配置值(同一 key 在不同机构下可配置不同值)
     *
     * @param configKey 配置key
     * @param orgid     机构ID
     * @return 配置值,未配置返回 null
     */
    private String getConfigValue(String configKey, String orgid) {
        SysConfig query = new SysConfig();
        query.setConfigKey(configKey);
        query.setOrgid(orgid);
        List<SysConfig> sysConfigs = configService.selectConfigList(query);
        if (CollectionUtils.isNotEmpty(sysConfigs)) {
            return sysConfigs.get(0).getConfigValue();
        }
        return null;
    }
    /**
@@ -1069,18 +1135,17 @@
                    serviceOutPath.setCreateTime(new Date());
                    serviceOutPath.setOrgid(serviceSubtask.getOrgid());
                    iServiceOutPathService.insertServiceOutPath(serviceOutPath);
                    String url = "";
                    String format = String.format("%03X", serviceOutPath.getId());
                    serviceOutPath.setRadix(format);
                    serviceOutPath.setUpdateTime(new Date());
                    iServiceOutPathService.updateServiceOutPath(serviceOutPath);
                    //先判断一下发的是不是宣教
                    if (!"3".equals(serviceSubtask.getType())) {
                        sendMagParam.setPhone(serviceSubtask.getPhone());
                        sendMagParam.setUrl(StringUtils.isNotEmpty(req_path) ? localIP + ":" + req_path + "/wt?p=" + format : localIP + "/wt?p=" + format);
                        url = StringUtils.isNotEmpty(req_path) ? localIP + ":" + req_path + "/wt?p=" + format : localIP + "/wt?p=" + format;
                        sendMagParam.setUrl(url);
                        //如果type是语音随访的话(说明补偿发送方式中有电话随访的方式,这里的外链就地址只能用/sf)
                        if (serviceSubtask.getType().equals("1"))
                            sendMagParam.setUrl(StringUtils.isNotEmpty(req_path) ? localIP + ":" + req_path + "/sf?p=" + format : localIP + "/sf?p=" + format);
                            url = StringUtils.isNotEmpty(req_path) ? localIP + ":" + req_path + "/sf?p=" + format : localIP + "/sf?p=" + format;
                            sendMagParam.setUrl(url);
//                        SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("wj.sms.template");
                        SysConfig sysConfig = new SysConfig();
@@ -1091,7 +1156,7 @@
                        } else {
                            sysConfig = sysConfigMapper.checkConfigKeyUnique("wj.sms.template");
                        }
                        if (!Objects.isNull(sysConfig)) {
                        if (!Objects.isNull(sysConfig) && StringUtils.isNotEmpty(sysConfig.getConfigValue())) {
                            String smsContemt = sysConfig.getConfigValue();
//                            sendMagParam.setContent("您好,邀请您填写出院调查表,请点击" + sendMagParam.getUrl() + "填写。感谢您配合!");
                            sendMagParam.setContent(smsContemt.replace("${wjurl}", sendMagParam.getUrl()));
@@ -1100,10 +1165,12 @@
                        HeLibrary heLibrary = heLibraryMapper.selectHeLibraryById(serviceSubtask.getLibtemplateid());
                        if (heLibrary.getHetype().equals("1")) {
                            sendMagParam.setPhone(serviceSubtask.getPhone());
                            sendMagParam.setUrl(StringUtils.isNotEmpty(req_path) ? localIP + ":" + req_path + "/xj?p=" + format : localIP + "/xj?p=" + format);
                            url = StringUtils.isNotEmpty(req_path) ? localIP + ":" + req_path + "/xj?p=" + format : localIP + "/xj?p=" + format;
                            sendMagParam.setUrl(url);
                            SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("xj.sms.template");
                            if (!Objects.isNull(sysConfig)) {
                            if (!Objects.isNull(sysConfig) && StringUtils.isNotEmpty(sysConfig.getConfigValue())) {
                                String smsContemt = sysConfig.getConfigValue();
//                              sendMagParam.setContent("您好,邀请您填写出院宣教调查表,请点击" + sendMagParam.getUrl() + "查看。感谢您配合!");
                                sendMagParam.setContent(smsContemt.replace("${xjurl}", sendMagParam.getUrl()));
@@ -1118,6 +1185,18 @@
                        }
                    }
                    //内容为空说明短信模板未配置或宣教/通知内容为空,直接按发送失败处理,避免后续发送时出现空指针
                    if (StringUtils.isEmpty(sendMagParam.getContent())) {
                        log.error("【sfHandlle】长期任务短信内容为空,无法发送,subid:{},taskid:{},type:{},机构ID:{}", serviceSubtask.getId(), serviceSubtask.getTaskid(), serviceSubtask.getType(), serviceSubtask.getOrgid());
                        setSuccessPreachForm(serviceSubtask, sendPreachform, "长期任务短信发送失败:短信内容为空(短信模板未配置或内容为空), 时间: " + now, "5");
                        return;
                    }
                    serviceOutPath.setRadix(format);
                    serviceOutPath.setUpdateTime(new Date());
                    serviceOutPath.setUrl(url);
                    iServiceOutPathService.updateServiceOutPath(serviceOutPath);
                    String isSuccess = null;
                    log.info("active的值为:{}", active);
                    if (active.equals("xh")) {
@@ -1149,33 +1228,48 @@
//                        String url = configService.selectConfigByKey("token.360", serviceSubtask.getOrgid());
//                        //丽水的短信发送方式
//                        Map<String, String> map = MsgLSEnum.getAddressByCode(serviceSubtask.getOrgid());
                        SysConfig config = new SysConfig();
                        config.setConfigKey("ls.msg.info");
                        config.setOrgid(serviceSubtask.getOrgid());
                        List<SysConfig> sysConfigs = configService.selectConfigList(config);
                        Map<String, String> msgInfo = new HashMap<>();
                        if (CollectionUtils.isNotEmpty(sysConfigs)) {
                            String configValue = sysConfigs.get(0).getConfigValue();
                        if ("47226079133110211G1001".equals(serviceSubtask.getOrgid())) {
                            // 妇幼中国移动云MAS,不依赖 ls.msg.info 与 token
                            SmsUtils.YunMasConfig yunMasConfig = SmsUtils.YunMasConfig.of(getConfigValue("ls.msg.yunmas", serviceSubtask.getOrgid()));
                            SmsUtils.SmsResult smsResult = SmsUtils.sendYunMasSms(yunMasConfig, sendMagParam.getPhone(), sendMagParam.getContent());
                            log.info("【sfHandlle】云MAS短信发送结果:ok={},rspcod={},msgGroup={},message={}",
                                    smsResult.isOk(), smsResult.getRspcod(), smsResult.getMsgGroup(), smsResult.getMessage());
                            if (smsResult.isOk()) {
                                isSuccess = "true";
                            } else {
                                log.error("【sfHandlle】云MAS短信发送失败,subid:{},手机号:{},原因:{}",
                                        serviceSubtask.getId(), sendMagParam.getPhone(), smsResult.getMessage());
                            }
                        } else {
                            SysConfig config = new SysConfig();
                            config.setConfigKey("ls.msg.info");
                            config.setOrgid(serviceSubtask.getOrgid());
                            List<SysConfig> sysConfigs = configService.selectConfigList(config);
                            Map<String, String> msgInfo = new HashMap<>();
                            if (CollectionUtils.isNotEmpty(sysConfigs)) {
                                String configValue = sysConfigs.get(0).getConfigValue();
                                ObjectMapper objectMapper = new ObjectMapper();
                                if (StringUtils.isNotEmpty(configValue)) {
                                    msgInfo = objectMapper.readValue(configValue, Map.class);
                                }
                            }
                            log.info("-----------map的值为:{}", msgInfo);
                            if (ObjectUtils.isEmpty(msgInfo) || StringUtils.isEmpty(msgInfo.get("address"))) {
                                setSuccessPreachForm(serviceSubtask, sendPreachform, "短信发送失败,该机构没有配置短信地址, 时间:" + now, "5");
                                return;
                            }
                            String token = LSHospTokenUtil.getToken(serviceSubtask.getOrgid(), msgInfo.get("tokenUrl"));
                            log.info("【sfHandlle】丽水短信发送,地址:{},参数:{},机构ID:{}", msgInfo.get("address"), sendMagParam, serviceSubtask.getOrgid());
                            String dxCode = getDXCode(msgInfo.get("address"), sendMagParam.getPhone(), sendMagParam.getContent(), msgInfo.get("sendPersonId"), msgInfo.get("sendPersonName"), MsgLSEnum.getHeaderByCode(msgInfo), token);
                            log.info("【sfHandlle】丽水短信发送结果:{}", dxCode);
                            ObjectMapper objectMapper = new ObjectMapper();
                            if (StringUtils.isNotEmpty(configValue)) {
                                msgInfo = objectMapper.readValue(configValue, Map.class);
                            Map<String, Object> textParam = objectMapper.readValue(dxCode, Map.class);
                            String code = textParam.get("Code").toString();
                            if (code.equals("0")) {
                                isSuccess = "true";
                            }
                        }
                        log.info("-----------map的值为:{}", msgInfo);
                        String token = LSHospTokenUtil.getToken(serviceSubtask.getOrgid(), msgInfo.get("tokenUrl"));
                        if (ObjectUtils.isNotEmpty(msgInfo) && StringUtils.isEmpty(msgInfo.get("address"))) {
                            setSuccessPreachForm(serviceSubtask, sendPreachform, "短信发送失败,该机构没有配置短信地址, 时间:" + now, "5");
                            return;
                        }
                        log.info("【sfHandlle】丽水短信发送,地址:{},参数:{},机构ID:{}", msgInfo.get("address"), sendMagParam, serviceSubtask.getOrgid());
                        String dxCode = getDXCode(msgInfo.get("address"), sendMagParam.getPhone(), sendMagParam.getContent(), msgInfo.get("sendPersonId"), msgInfo.get("sendPersonName"), MsgLSEnum.getHeaderByCode(msgInfo), token);
                        log.info("【sfHandlle】丽水短信发送结果:{}", dxCode);
                        ObjectMapper objectMapper = new ObjectMapper();
                        Map<String, Object> textParam = objectMapper.readValue(dxCode, Map.class);
                        String code = textParam.get("Code").toString();
                        if (code.equals("0")) {
                            isSuccess = "true";
                        }
                    } else if (active.equals("sltd") || active.equals("nhfy")) {
                        log.info("sltd进来了吗?{}   ,---sendMagParam.getContent()的参数为:{}", active, sendMagParam.getContent());
                        if (active.equals("sltd")) {
@@ -1223,13 +1317,13 @@
                        String campusid = ObjectUtils.isNotEmpty(sysDept) ? sysDept.getCampusid() : "2";
                        //湖滨单独发送方式
                        if ("1".equals(campusid)) {
                            String result = smsUtils.sendSms1(xhsmsPath, xhsmsAccount, xhsmsPwd, sendMagParam.getPhone(), content);
                            String result = SmsUtils.sendSms1(xhsmsPath, xhsmsAccount, xhsmsPwd, sendMagParam.getPhone(), content);
                            //这里需要根据result返回的值去判断,是否成功,然后将值赋值给isSuccess
                            if (StringUtils.isNotEmpty(result) && result.contains("处理成功")) {
                                isSuccess = "true";
                            }
                        } else {
                            String result = smsUtils.sendSms(xhsmsPath, xhsmsAccount, xhsmsPwd, sendMagParam.getPhone(), content);
                            String result = SmsUtils.sendSms(xhsmsPath, xhsmsAccount, xhsmsPwd, sendMagParam.getPhone(), content);
                            //这里需要根据result返回的值去判断,是否成功,然后将值赋值给isSuccess
                            if (StringUtils.isNotEmpty(result) && result.contains("处理成功")) {
                                isSuccess = "true";
@@ -1291,6 +1385,7 @@
                    String format = String.format("%03X", serviceOutPath.getId());
                    serviceOutPath.setRadix(format);
                    serviceOutPath.setUpdateTime(new Date());
                    serviceOutPath.setUrl(url);
                    iServiceOutPathService.updateServiceOutPath(serviceOutPath);
                    //获取微信公众号请求信息根据机构ID
@@ -1310,7 +1405,7 @@
                    //市一医院
                    if (active.equals("hzszlyy")) {
                        //处理中文乱码问题
                        wxCode = smsUtils.sendChat(url, patArchive.getTelcode(), serviceSubtask.getSfzh());
                        wxCode = SmsUtils.sendChat(url, patArchive.getTelcode(), serviceSubtask.getSfzh());
                    } else {
                        wxCode = getWXCode(serviceSubtask.getSfzh(), url, serviceSubtask.getTaskName(), serviceSubtask.getTaskDesc(), patArchive.getTelcode(), serviceSubtask.getSendname(), patArchive.getPatidHis(), wxqqxx);
                    }
@@ -1700,14 +1795,15 @@
     * @return
     */
    private String getObject(ServiceSubtask serviceSubtask, String scriptContent) {
        //内容为空时直接返回,避免下面的replace出现空指针
        if (StringUtils.isEmpty(scriptContent)) {
            return scriptContent;
        }
        Map<String, Map<String, String>> param = getParam(serviceSubtask.getTaskid());
        for (Map<String, String> map : param.values()) {
            for (String key : map.keySet()) {
                scriptContent = scriptContent.replace(key, StringUtils.isNotEmpty(map.get(key)) ? map.get(key) : "");
            }
        }
        if (StringUtils.isEmpty(scriptContent)) {
            return scriptContent;
        }
        scriptContent = scriptContent.replace("${name}", StringUtils.isNotEmpty(serviceSubtask.getSendname()) ? serviceSubtask.getSendname() : "");
        scriptContent = scriptContent.replace("${dzz}", StringUtils.isNotEmpty(serviceSubtask.getAddr()) ? serviceSubtask.getAddr() : "");
@@ -1811,7 +1907,16 @@
    /**
     * 处理超时随访任务
     * @return
     * <p>
     * 扫描「出院满 deadlineDay 天仍未完成」的首次随访任务(sendstate=7 + visit_count=1),
     * 按当前任务的 nexttaskid 生成再次随访任务并转到随访中心。
     * <p>
     * 注意:截止时间必须在循环外计算一次。历史实现把 {@code deadlineDay = -deadlineDay}
     * 写在循环体内,每轮取反一次,导致偶数轮用「今天 + deadlineDay」做截止时间,
     * 把出院不满 28 天的超时任务也扫了进来。
     *
     * @param orgid       机构ID
     * @param deadlineDay 出院满多少天算超时(正数,如 28)
     */
    public void dealSubtaskDeadline(String orgid, Integer deadlineDay){
        //获取任务信息
@@ -1821,9 +1926,17 @@
        st.setNexttaskflag("1");
        st.setOrgid(orgid);
        List<ServiceTask> serviceTasks = serviceTaskMapper.selectServiceTaskList(st);
        // 截止时间只算一次:出院时间 <= 今天 - deadlineDay
        int days = Math.abs(ObjectUtils.isEmpty(deadlineDay) ? 28 : deadlineDay);
        Date deadline = DateUtils.addDays(new Date(), -days);
        log.info("【dealSubtaskDeadline】开始扫描, orgid={}, day={}, deadline={}, 任务数={}",
                orgid, days, deadline, serviceTasks.size());
        for (ServiceTask serviceTask : serviceTasks) {
            deadlineDay = -deadlineDay;
            Date deadline = DateUtils.addDays(new Date(), deadlineDay);
            if (ObjectUtils.isEmpty(serviceTask.getNexttaskid())) {
                continue;
            }
            //通过任务ID拿到患者信息,并且随访时间得是今天之前的
            ServiceSubtaskEntity serviceSubtaskVO = new ServiceSubtaskEntity();
            serviceSubtaskVO.setOrgid(orgid);
@@ -1832,45 +1945,20 @@
            serviceSubtaskVO.setVisitCount(1);
            serviceSubtaskVO.setTaskid(serviceTask.getTaskid());
            serviceSubtaskVO.setEndOutHospTime(deadline);
            List<ServiceSubtask> serviceSubtaskList
                    = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskVO);
            log.info("【dealSubtaskDeadline】获取到{}个超时任务, taskid={}", serviceSubtaskList.size(), serviceTask.getTaskid());
            for (ServiceSubtask serviceSubtask : serviceSubtaskList) {
                PatMedInhosp patMedInhospVo = new PatMedInhosp();
                patMedInhospVo.setOrgid(serviceSubtask.getOrgid());
                patMedInhospVo.setInhospid(serviceSubtask.getInhospid());
                List<PatMedInhosp> patMedInhospList = iPatMedInhospService.selectPatMedInhospList(patMedInhospVo);
                PatMedInhosp patMedInhosp = new PatMedInhosp();
                if (!patMedInhospList.isEmpty()) {
                    patMedInhosp = patMedInhospList.get(0);
                }else {
                    continue;
                }
                if(ObjectUtils.isEmpty(serviceTask.getNexttaskid())){
                    continue;
                }
                ServiceTask nextServiceTask = serviceTaskMapper.selectServiceTaskByTaskid(serviceTask.getNexttaskid());
                PatArchive patArchive = patArchiveService.selectPatArchiveByPatid(serviceSubtask.getPatid());
                String config = configService.selectConfigByKey("visit.early.day");
                Integer type = 1;
                //新增再次随访任务
                ServiceSubtask serviceSubtaskAgain = iPatMedInhospService.boxedServiceSubtask(nextServiceTask,
                        patMedInhosp, patArchive, config, type);
                serviceSubtaskAgain.setId(null);
                serviceSubtaskAgain.setUpid(serviceSubtask.getId().intValue());
                serviceSubtaskAgain.setVisitCount(serviceSubtask.getVisitCount() + 1);
                serviceSubtaskAgain.setSendstate(2L);
                serviceSubtaskAgain.setCreateTime(new Date());
                serviceSubtaskAgain.setUpdateTime(new Date());
                serviceSubtaskAgain.setVisitType(serviceSubtask.getVisitType());
                serviceSubtaskAgain.setVisitDeptCode(serviceSubtask.getVisitDeptCode());
                serviceSubtaskAgain.setVisitDeptName(serviceSubtask.getVisitDeptName());
                int i = serviceSubtaskMapper.insertServiceSubtask(serviceSubtaskAgain);
                if (i == 1) {
                    //再次随访任务创建成功后,原任务改为失败状态
                    serviceSubtask.setSendstate(5L);
                    serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                try {
                    // 生成逻辑统一收口到 service:锁 + 幂等 + 事务 + 发送方式明细初始化
                    String result = iServiceSubtaskService.addSubTaskAgainByDeadline(serviceSubtask);
                    log.info("【dealSubtaskDeadline】生成再次随访, subId={}, taskid={}, 结果={}",
                            serviceSubtask.getId(), serviceTask.getTaskid(), result);
                } catch (Exception e) {
                    // 单条失败不影响其他患者
                    log.error("【dealSubtaskDeadline】生成再次随访失败, subId=" + serviceSubtask.getId()
                            + ", taskid=" + serviceTask.getTaskid(), e);
                }
            }
        }