陈昶聿
11 小时以前 2d3b575ed96eee7ccd5b324baa3135d2236934ac
【市一】1、登录服务问题处理
2、电话挂断问题处理
3、查询报错问题处理
已修改6个文件
373 ■■■■■ 文件已修改
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-hzszlyy.yml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/aspectj/SetOrgIdAspect.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/resources/mapper/inhospvisit/PatMedInhospMapper.xml 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java 245 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
@@ -231,10 +231,12 @@
        SysUser user = SecurityUtils.getLoginUser().getUser();
        //显示所有院区权限
        String allCampus = sysConfigService.selectConfigByKey("sys.authority.campus.all");
        if (StringUtils.isNotEmpty(allCampus)) {
            allCampus = "," + allCampus + ",";
            if (allCampus.contains("," + user.getUserName() + ",")) {
                user.setCampusid(null);
        if (StringUtils.isNotEmpty(allCampus) && StringUtils.isNotEmpty(user.getUserName())) {
            for (String item : allCampus.split("[,,;;\\s]+")) {
                if (user.getUserName().equals(item.trim())) {
                    user.setCampusid(null);
                    break;
                }
            }
        }
ruoyi-admin/src/main/resources/application-hzszlyy.yml
@@ -164,7 +164,7 @@
#  指定asr回调的url路径(本地)
ASRCallBackPath: http://192.169.129.198:8095/smartor/serviceSubtask/phoneCallBackYQ
#挂断IP(本地)
hangup: http://192.169.129.198:8089/hangup
hangup: http://192.169.129.198:8091/hangup
#fs所使用的阿里的app_key(本地)
app_key_yq: ZurNHpaQLq6P55YS
ruoyi-common/src/main/java/com/ruoyi/common/aspectj/SetOrgIdAspect.java
@@ -42,12 +42,19 @@
            {
                String orgid = loginUser.getUser().getOrgid();
                String campusid = loginUser.getUser().getCampusid();
                boolean allCampusUser = false;
                //显示所有院区权限
                String allCampus = sysConfigService.selectConfigByKey("sys.authority.campus.all");
                if(StringUtils.isNotEmpty(allCampus)){
                    allCampus = "," + allCampus + ",";
                    if (allCampus.contains("," + loginUser.getUser().getUserName() + ",")) {
                        campusid = null;
                if (StringUtils.isNotEmpty(allCampus)) {
                    String userName = loginUser.getUser().getUserName();
                    if (StringUtils.isNotEmpty(userName)) {
                        for (String item : allCampus.split("[,,;;\\s]+")) {
                            if (userName.equals(item.trim())) {
                                campusid = null;
                                allCampusUser = true;
                                break;
                            }
                        }
                    }
                }
                
@@ -76,17 +83,22 @@
                        }
                        
                        // 如果需要同时设置campusid
                        if (setOrgId.withCampusId() && campusid != null)
                        if (setOrgId.withCampusId())
                        {
                            String campusFieldName = setOrgId.campusField();
                            // 检查字段是否已经有值,如果有且skipIfPresent为true,则跳过设置
                            Object currentValueCampus = getFieldValue(param, campusFieldName);
                            if (setOrgId.skipIfPresent() && currentValueCampus != null && !currentValueCampus.toString().isEmpty()) {
                                // 如果字段已经有值且skipIfPresent为true,则跳过设置
                                log.debug("字段 {} 已有值: {},跳过设置", campusFieldName, currentValueCampus);
                            } else {
                                setFieldValue(param, campusFieldName, campusid);
                            if (allCampusUser) {
                                // 全院区权限必须覆盖请求里可能带进来的 campusid,
                                // 否则前端传 campusid=8 会重新把院区条件加回去。
                                setFieldValue(param, campusFieldName, null);
                            } else if (campusid != null) {
                                // 检查字段是否已经有值,如果有且skipIfPresent为true,则跳过设置
                                Object currentValueCampus = getFieldValue(param, campusFieldName);
                                if (setOrgId.skipIfPresent() && currentValueCampus != null && !currentValueCampus.toString().isEmpty()) {
                                    // 如果字段已经有值且skipIfPresent为true,则跳过设置
                                    log.debug("字段 {} 已有值: {},跳过设置", campusFieldName, currentValueCampus);
                                } else {
                                    setFieldValue(param, campusFieldName, campusid);
                                }
                            }
                        }
                    }
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java
@@ -4,6 +4,7 @@
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.service.IConfigService;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
@@ -13,6 +14,7 @@
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@@ -28,6 +30,7 @@
 *
 * @author ruoyi
 */
@Slf4j
@Component
public class TokenService {
    // 令牌自定义标识
@@ -51,6 +54,9 @@
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private IConfigService configService;
    /**
     * 获取用户身份信息
     *
@@ -66,6 +72,8 @@
                String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
                String userKey = getTokenKey(uuid);
                LoginUser user = redisCache.getCacheObject(userKey);
                // 统一在会话解析层处理「全院区权限」,避免各业务接口直接读 user.campusid 时绕过配置。
                applyCampusAuthority(user);
                return user;
            } catch (Exception e) {
            }
@@ -74,6 +82,41 @@
    }
    /**
     * 应用全院区权限。
     * <p>
     * {@code sys.authority.campus.all} 配置的是用户名列表,命中后把当前会话的
     * campusid 置空;后续所有按 campusid 过滤的 SQL 都会因为参数为空而跳过院区条件。
     * <p>
     * 这里每次读取会话时都按当前配置重新判断,所以不需要重新登录;同时兼容配置值
     * 中带空格、换行、中文逗号/分号的情况。
     */
    private void applyCampusAuthority(LoginUser loginUser) {
        if (loginUser == null || loginUser.getUser() == null) {
            return;
        }
        try {
            String allCampus = configService.selectConfigByKey("sys.authority.campus.all");
            if (StringUtils.isEmpty(allCampus)) {
                return;
            }
            String userName = loginUser.getUser().getUserName();
            if (StringUtils.isEmpty(userName)) {
                return;
            }
            String[] users = allCampus.split("[,,;;\\s]+");
            for (String item : users) {
                if (userName.equals(item.trim())) {
                    loginUser.getUser().setCampusid(null);
                    return;
                }
            }
        } catch (Exception e) {
            // 配置服务异常不能导致 token 解析失败,否则会把用户直接踢下线。
            log.warn("【全院区权限】校验 sys.authority.campus.all 失败,userId={}", loginUser.getUserId(), e);
        }
    }
    /**
     * 设置用户身份信息
     */
    public void setLoginUser(LoginUser loginUser) {
ruoyi-inhospvisit/src/main/resources/mapper/inhospvisit/PatMedInhospMapper.xml
@@ -432,6 +432,41 @@
      主查询已去掉 GROUP BY:pat_med_inhosp.inhospid 唯一、pat_archive.id 是主键,
      a.id = b.patid 一对一,因此 join 结果行数就是业务记录数。
      这里直接对相同的 FROM/WHERE 做 COUNT,不再包一层分组子查询。
      ⚠️⚠️ 【本接口真正的热点是这条 count,不是上面的列表 SQL】
      2026-11-13 实测(smartor_sltd:pat_med_inhosp 50,175 行、pat_archive 249,045 行、
      MySQL 5.7.34、innodb_buffer_pool_size=128MB):
        列表  ORDER BY b.endtime DESC LIMIT 10  →   25 ~ 35 ms
        count 本条语句                            → 10467 ms(冷)/ 350 ms(热)
      用户看到的「全时间段 20s / 3 个月 8s」就是它:一次翻页 = count + 列表。
      原因有三条,改 SQL 解决不了,必须在库上做:
        1) b 侧必须有 (del_flag, orgid, inhospstate, endtime) 复合索引,
           否则 MySQL 要按 orgid 回表逐行过滤 del_flag / inhospstate;
           有了它,count(1) 是 Using index(不回表),列表也能按索引顺序提前 LIMIT。
        2) MySQL 5.7 对「不在索引里的等值条件」按 10% 估(EXPLAIN 的 filtered=10.0),
           会把「没有 inhospstate 的索引」算得更便宜,从而选错。
           sltd 上实测:强行走 idx_pmi_del_org_state_end = 349ms,
           默认执行计划 = 2085ms。所以光建索引不够,还要清掉竞争索引或强制索引。
        3) pat_archive 是 60+ 列的大表,count 每命中一行都要按主键回表一次
           (sltd 是 38,881 次随机读)。缓存命中 0.35s、冷缓存 10s,
           差距全在磁盘随机读 → 见第 5 步 innodb_buffer_pool_size。
      配套脚本:sql/optimize_pat_med_inhosp_list_count_20261113.sql
      ⚠️ 【不要给 pat_med_inhosp 加 idx_pmi_patid (patid)】
         实测加上后优化器会改成「先扫 pat_archive 再回查 pat_med_inhosp」,
         列表查询 25 ms → 481 ms(慢 20 倍),ORDER BY 也用不上索引。
         本查询的驱动表必须固定是 b。
      ⚠️ 【不要为了快把 a 的 join 去掉】
         a.id = b.patid 不是恒等的:pat_med_inhosp.patid 可能为空或在 pat_archive 里
         找不到。2026-11-13 实测 smartor_shiyi 有 5,713 行 patid 为空、
         smartor_lishui 有 9,270 行孤儿行 —— 去掉 join 会把这些行算进 total,
         只会在「孤儿行恰好为 0」的库上看起来正确。
    -->
    <select id="selectPatMedInhospList_COUNT" parameterType="com.ruoyi.inhospvisit.domain.PatMedInhosp"
            resultType="java.lang.Long">
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
@@ -1175,9 +1175,24 @@
            serviceSubtaskPreachform.setSubid(serviceSubtask.getId());
            serviceSubtaskPreachform.setOrgid(serviceSubtask.getOrgid());
            List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
            if (CollectionUtils.isEmpty(serviceSubtaskPreachforms)) {
                // current_preachform 有值但发送方式明细一条都没有,属于不可修复的脏数据。
                log.error("【compensateTask】子任务发送方式明细为空,subid:{},currentPreachform:{}",
                        serviceSubtask.getId(), serviceSubtask.getCurrentPreachform());
                closeSubtaskForMissingPreachform(serviceSubtask);
                continue;
            }
            //获取当前执行方式的序号
            Optional<Long> currentSort = serviceSubtaskPreachforms.stream().filter(item -> serviceSubtask.getCurrentPreachform().equals(item.getPreachform())).map(ServiceSubtaskPreachform::getSort).findFirst();
            Optional<Long> id = serviceSubtaskPreachforms.stream().filter(item -> serviceSubtask.getCurrentPreachform().equals(item.getPreachform())).map(ServiceSubtaskPreachform::getId).findFirst();
            Optional<Long> currentSort = serviceSubtaskPreachforms.stream()
                    .filter(Objects::nonNull)
                    .filter(item -> serviceSubtask.getCurrentPreachform().equals(item.getPreachform()))
                    .map(ServiceSubtaskPreachform::getSort)
                    .findFirst();
            Optional<Long> id = serviceSubtaskPreachforms.stream()
                    .filter(Objects::nonNull)
                    .filter(item -> serviceSubtask.getCurrentPreachform().equals(item.getPreachform()))
                    .map(ServiceSubtaskPreachform::getId)
                    .findFirst();
            if (currentSort.isPresent()) {
                // 方案B:只处理当前子任务——公众号已领取(service_subtask.sendstate=1 + current_preachform=5)
                // 且未填写时,按默认选项完成,避免进入下一种发送方式或最后一步超时。
@@ -1199,7 +1214,9 @@
                }
                //1先检查一下,是不是有执行状态是完成的(怕之前已经有完的了,没有将servuce_subtask的状态改成功,这里再检查一下)
                boolean finishState = serviceSubtaskPreachforms.stream().allMatch(item -> item.getSendstate().equals("9"));
                boolean finishState = serviceSubtaskPreachforms.stream()
                        .filter(Objects::nonNull)
                        .allMatch(item -> "9".equals(item.getSendstate()));
                if (finishState) {
                    serviceSubtask.setSendstate(6L);
                    serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
@@ -1215,15 +1232,20 @@
                    serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                    //修改发送方式的状态为失败
                    serviceSubtaskPreachform.setSendstate("5");
                    serviceSubtaskPreachform.setId(id.get());
                    serviceSubtaskPreachformMapper.updateServiceSubtaskPreachform(serviceSubtaskPreachform);
                    if (id.isPresent()) {
                        serviceSubtaskPreachform.setSendstate("5");
                        serviceSubtaskPreachform.setId(id.get());
                        serviceSubtaskPreachformMapper.updateServiceSubtaskPreachform(serviceSubtaskPreachform);
                    }
                    continue;
                }
                //3.不是最后一个,获取到下一个执行方式(因为都是在今天执行,那就直接发出去就完了)
                sfHandlle(serviceSubtask);
            } else {
                log.error("【compensateTask】current_preachform 在发送方式明细中不存在,subid:{},currentPreachform:{}",
                        serviceSubtask.getId(), serviceSubtask.getCurrentPreachform());
                closeSubtaskForMissingPreachform(serviceSubtask);
            }
        }
    }
@@ -1277,11 +1299,31 @@
    }
    /**
     * 随访-补偿处理
     * 随访-补偿处理。
     * <p>
     * 单条子任务异常只记录日志,不影响同一批次其他子任务。
     *
     * @param serviceSubtask
     */
    private void sfHandlle(ServiceSubtask serviceSubtask) {
        try {
            doSfHandlle(serviceSubtask);
        } catch (Exception e) {
            log.error("【sfHandlle】处理子任务异常,subid={},taskid={},orgid={}",
                    serviceSubtask == null ? null : serviceSubtask.getId(),
                    serviceSubtask == null ? null : serviceSubtask.getTaskid(),
                    serviceSubtask == null ? null : serviceSubtask.getOrgid(), e);
        }
    }
    /**
     * 随访-补偿处理实际逻辑。
     */
    private void doSfHandlle(ServiceSubtask serviceSubtask) {
        if (serviceSubtask == null) {
            log.error("【sfHandlle】子任务为空,跳过处理");
            return;
        }
        //这里需要根据身份证去重(给微信小程序的固定任务用的)
        List<ServiceSubtask> serviceSubtaskListFilter = new ArrayList<>();
        ServiceTask ivrTask1 = serviceTaskMapper.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
@@ -1291,15 +1333,31 @@
        //获取发送方式
        String sendPreachform = getSendPreachform(serviceSubtaskPreachform, serviceSubtask);
        log.info("sendPreachform的发送方式是啥:{}", sendPreachform);
        if (sendPreachform.equals("-1") || serviceSubtask.getSendstate() == 6 || serviceSubtask.getSendstate() == 5 || serviceSubtask.getSendstate() == 4) {
            //说明已经全部遍历完了,当前的执行方式序号是最后一个了   或者  该患者已经随访结束(成功或失败了或不执行)
            log.error("说明已经全部遍历完了,当前的执行方式序号是最后一个了   或者  该患者已经随访结束(成功或失败了或不执行)");
        Long sendstate = serviceSubtask.getSendstate();
        if (Long.valueOf(6L).equals(sendstate)
                || Long.valueOf(5L).equals(sendstate)
                || Long.valueOf(4L).equals(sendstate)) {
            //该患者已经随访结束(成功、失败或不执行)
            log.error("该患者已经随访结束(成功、失败或不执行),subid={},sendstate={}", serviceSubtask.getId(), sendstate);
            return;
        }
        if (StringUtils.isEmpty(sendPreachform)) {
            // 老数据可能存在 service_subtask_preachform 缺失、service_task.preachform_desc 为空、
            // current_preachform 与明细不一致等情况。此时不能继续,否则 sendPreachform.equals(...) 会 NPE。
            closeSubtaskForMissingPreachform(serviceSubtask);
            return;
        }
        if ("-1".equals(sendPreachform)) {
            //说明已经全部遍历完了,当前的执行方式序号是最后一个了
            log.error("说明已经全部遍历完了,当前的执行方式序号是最后一个了,subid={}", serviceSubtask.getId());
            return;
        }
        //先判断患者是不是还要发送
        PatArchive patArchive = patArchiveService.selectPatArchiveByPatid(serviceSubtask.getPatid());
        log.info("patArchive的值为:{}", patArchive);
        if (!Objects.isNull(patArchive) && patArchive.getNotrequiredFlag() == "1") {
        if (!Objects.isNull(patArchive) && "1".equals(patArchive.getNotrequiredFlag())) {
            //不需要发送
            serviceSubtask.setResult("患者不需要随访");
            serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + "," + "患者不需要随访," + patArchive.getNotrequiredreason() : "患者不需要随访," + patArchive.getNotrequiredreason());
@@ -1310,7 +1368,14 @@
            return;
        }
        String descByCode = ServiceFromEnum.getDescByCode(Integer.valueOf(sendPreachform));
        int sendCode = NumberUtils.toInt(sendPreachform, -1);
        String descByCode = ServiceFromEnum.getDescByCode(sendCode);
        if (StringUtils.isEmpty(descByCode)) {
            log.error("【sfHandlle】发送方式编码非法,subid={},taskid={},sendPreachform={}",
                    serviceSubtask.getId(), serviceSubtask.getTaskid(), sendPreachform);
            closeSubtaskForMissingPreachform(serviceSubtask);
            return;
        }
        // ⚠️ 2025 修复(宣教开关失效):type == 3 是**宣教/通知**(纯发送、无作答 ——
        // 见 setSuccessPreachForm 里「宣教或通知…直接改成 9」、以及本方法后面
@@ -1417,7 +1482,7 @@
                        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"))
                        if ("1".equals(serviceSubtask.getType()))
                            url = StringUtils.isNotEmpty(req_path) ? localIP + ":" + req_path + "/sf?p=" + format : localIP + "/sf?p=" + format;
                        sendMagParam.setUrl(url);
@@ -1597,6 +1662,46 @@
    }
    /**
     * 老数据兜底:子任务没有可用发送方式时,标记为超时并写清原因。
     * <p>
     * 这类数据通常来自 {@code service_subtask_preachform} 缺失、{@code service_task.preachform_desc}
     * 为空/非法,或 {@code current_preachform} 与明细不一致。标记为终态后不会再被
     * {@code longTaskSend}/{@code compensateTask} 反复捞取,也不会因一条脏数据中断整批任务。
     */
    private void closeSubtaskForMissingPreachform(ServiceSubtask serviceSubtask) {
        if (serviceSubtask == null || serviceSubtask.getId() == null) {
            log.error("【sfHandlle】子任务为空或缺少ID,无法标记无发送方式");
            return;
        }
        try {
            Long currentSendstate = serviceSubtask.getSendstate();
            if (Long.valueOf(4L).equals(currentSendstate)
                    || Long.valueOf(5L).equals(currentSendstate)
                    || Long.valueOf(6L).equals(currentSendstate)
                    || Long.valueOf(7L).equals(currentSendstate)) {
                log.warn("【sfHandlle】子任务已是终态,不再重复标记。subid={},sendstate={}",
                        serviceSubtask.getId(), currentSendstate);
                return;
            }
            String reason = "未查询到有效发送方式,任务自动终止(数据异常,请检查service_subtask_preachform与service_task.preachform_desc)";
            // 只更新必要字段,避免把查询时的整行快照覆盖回数据库。
            ServiceSubtask update = new ServiceSubtask();
            update.setId(serviceSubtask.getId());
            update.setSendstate(7L);
            update.setUpdateTime(new Date());
            update.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark())
                    ? serviceSubtask.getRemark() + "," + reason : reason);
            serviceSubtaskMapper.updateServiceSubtask(update);
            log.error("【sfHandlle】子任务无有效发送方式,已标记为超时。subid={},taskid={},orgid={},currentPreachform={}",
                    serviceSubtask.getId(), serviceSubtask.getTaskid(), serviceSubtask.getOrgid(),
                    serviceSubtask.getCurrentPreachform());
        } catch (Exception e) {
            log.error("【sfHandlle】标记无发送方式子任务失败,subid={},taskid={}",
                    serviceSubtask.getId(), serviceSubtask.getTaskid(), e);
        }
    }
    /**
     * 按 {@code active}(站点)路由到对应的短信发送方。
     * <p>
     * 各站点差异见 {@code ruoyi-message} 模块各实现类;本方法只做「入参组装 + 路由 + 日志」。
@@ -1694,7 +1799,13 @@
        // 情况1:currentPreachform为空,取sort为1的preachform
        if (StringUtils.isEmpty(serviceSubtask.getCurrentPreachform())) {
            String firstPreachform = serviceSubtaskPreachforms.stream().filter(item -> item.getSort() == 1L).map(ServiceSubtaskPreachform::getPreachform).findFirst().orElse(null);
            String firstPreachform = serviceSubtaskPreachforms.stream()
                    .filter(Objects::nonNull)
                    .filter(item -> Long.valueOf(1L).equals(item.getSort()))
                    .map(ServiceSubtaskPreachform::getPreachform)
                    .filter(item -> StringUtils.isNotEmpty(item))
                    .findFirst()
                    .orElse(null);
            //更新一下当前发送时间
            if (StringUtils.isNotEmpty(firstPreachform)) {
@@ -1706,11 +1817,17 @@
        }
        // 情况2:currentPreachform不为空,找下一个sort
        Optional<Long> currentSort = serviceSubtaskPreachforms.stream().filter(item -> serviceSubtask.getCurrentPreachform().equals(item.getPreachform())).map(ServiceSubtaskPreachform::getSort).findFirst();
        Optional<Long> currentSort = serviceSubtaskPreachforms.stream()
                .filter(Objects::nonNull)
                .filter(item -> serviceSubtask.getCurrentPreachform().equals(item.getPreachform()))
                .map(ServiceSubtaskPreachform::getSort)
                .findFirst();
        if (currentSort.isPresent()) {
            // 检查是否所有sendstate不等于9
            boolean allSendStateNot1AndNot9 = serviceSubtaskPreachforms.stream().allMatch(item -> !item.getSendstate().equals("9"));
            boolean allSendStateNot1AndNot9 = serviceSubtaskPreachforms.stream()
                    .filter(Objects::nonNull)
                    .allMatch(item -> !"9".equals(item.getSendstate()));
            if (!allSendStateNot1AndNot9) {
                //说明,在某种方式发送失败后,又成功了(例如短信或公众号,在规定时间外患者做完了,这个时候,状态改成了9)
                serviceSubtask.setSendstate(6L);
@@ -1740,7 +1857,13 @@
            }
            // 找到下一个sort对应的preachform
            String nextPreachform = serviceSubtaskPreachforms.stream().filter(item -> nextSort.equals(item.getSort())).map(ServiceSubtaskPreachform::getPreachform).findFirst().orElse("-1");
            String nextPreachform = serviceSubtaskPreachforms.stream()
                    .filter(Objects::nonNull)
                    .filter(item -> nextSort.equals(item.getSort()))
                    .map(ServiceSubtaskPreachform::getPreachform)
                    .filter(item -> StringUtils.isNotEmpty(item))
                    .findFirst()
                    .orElse("-1");
            //更新一下当前发送时间
            if (StringUtils.isNotEmpty(nextPreachform)) {
@@ -1750,36 +1873,62 @@
            }
            return nextPreachform;
        }
        log.warn("【getSendPreachform】未找到可用的发送方式,subid={},taskid={},currentPreachform={}",
                serviceSubtask.getId(), serviceSubtask.getTaskid(), serviceSubtask.getCurrentPreachform());
        return null;
    }
    private List<ServiceSubtaskPreachform> addServiceSubtaskPreachform(ServiceSubtask serviceSubtask, String sendstate) {
        //将preachformDesc的json对象,放到list集合中
        ServiceTask serviceTask = serviceTaskMapper.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
        if (serviceTask == null) {
            log.error("ryTask--发送方式初始化失败,任务不存在,taskid={},subid={}",
                    serviceSubtask.getTaskid(), serviceSubtask.getId());
            return Collections.emptyList();
        }
        String preachformDesc = serviceTask.getPreachformDesc();
        List<ServiceSubtaskPreachform> sspl = null;
        ObjectMapper objectMapper = new ObjectMapper();
        if (org.apache.commons.lang3.StringUtils.isNotEmpty(preachformDesc)) {
            try {
                sspl = objectMapper.readValue(preachformDesc, new TypeReference<List<ServiceSubtaskPreachform>>() {
                });
            } catch (JsonProcessingException e) {
                log.error("ryTask--preachform转List<ServiceSubtaskPreachform>报错了:{}", e.getMessage());
            }
            for (ServiceSubtaskPreachform serviceSubtaskPreachform : sspl) {
                serviceSubtaskPreachform.setTaskid(serviceSubtask.getTaskid());
                serviceSubtaskPreachform.setSubid(serviceSubtask.getId());
                //这个sendstate需要在数据库设置一个默认值为“1”
                serviceSubtaskPreachform.setSendstate("1");
                if (StringUtils.isNotEmpty(sendstate)) serviceSubtaskPreachform.setSendstate(sendstate);
                serviceSubtaskPreachform.setOrgid(serviceSubtask.getOrgid());
                serviceSubtaskPreachformMapper.insertServiceSubtaskPreachform(serviceSubtaskPreachform);
            }
        if (StringUtils.isEmpty(preachformDesc)) {
            log.error("ryTask--发送方式初始化失败,preachform_desc为空,taskid={},subid={}",
                    serviceSubtask.getTaskid(), serviceSubtask.getId());
            return Collections.emptyList();
        }
        return sspl;
        List<ServiceSubtaskPreachform> sspl;
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            sspl = objectMapper.readValue(preachformDesc, new TypeReference<List<ServiceSubtaskPreachform>>() {
            });
        } catch (JsonProcessingException e) {
            log.error("ryTask--preachform转List<ServiceSubtaskPreachform>报错了,taskid={},subid={},preachformDesc={}",
                    serviceSubtask.getTaskid(), serviceSubtask.getId(), preachformDesc, e);
            return Collections.emptyList();
        }
        if (CollectionUtils.isEmpty(sspl)) {
            log.error("ryTask--preachformDesc解析后为空,taskid={},subid={},preachformDesc={}",
                    serviceSubtask.getTaskid(), serviceSubtask.getId(), preachformDesc);
            return Collections.emptyList();
        }
        List<ServiceSubtaskPreachform> inserted = new ArrayList<>();
        for (ServiceSubtaskPreachform serviceSubtaskPreachform : sspl) {
            if (serviceSubtaskPreachform == null) {
                continue;
            }
            if (StringUtils.isEmpty(serviceSubtaskPreachform.getPreachform())) {
                log.warn("ryTask--preachformDesc中存在空的preachform,跳过。taskid={},subid={}",
                        serviceSubtask.getTaskid(), serviceSubtask.getId());
                continue;
            }
            serviceSubtaskPreachform.setTaskid(serviceSubtask.getTaskid());
            serviceSubtaskPreachform.setSubid(serviceSubtask.getId());
            //这个sendstate需要在数据库设置一个默认值为“1”
            serviceSubtaskPreachform.setSendstate(StringUtils.isNotEmpty(sendstate) ? sendstate : "1");
            serviceSubtaskPreachform.setOrgid(serviceSubtask.getOrgid());
            serviceSubtaskPreachformMapper.insertServiceSubtaskPreachform(serviceSubtaskPreachform);
            inserted.add(serviceSubtaskPreachform);
        }
        return inserted;
    }
    private Integer setFail(List<ServiceSubtaskPreachform> serviceSubtaskPreachforms, ServiceSubtask serviceSubtask) {
@@ -1872,7 +2021,7 @@
    public Boolean setSuccessPreachForm(ServiceSubtask serviceSubtask, String preachform, String remark, String failSendstate) {
        log.info("setSuccessPreachForm的入参为:serviceSubtask = {},  preachform = {},  remark = {}, failSendstate = {}", serviceSubtask, preachform, remark, failSendstate);
        if (preachform.equals("-1")) {
        if ("-1".equals(preachform)) {
            //说明都不需要执行了
            ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform();
            serviceSubtaskPreachform.setSubid(serviceSubtask.getId());
@@ -1894,7 +2043,7 @@
        //1:未发送状态
        serviceSubtaskPreachform.setSendstate("1");
        //电话的未发送状态,需要查2,因为,数据可能卡在队列里了; (没进队列,电话状态还是1)
        if (preachform.equals("3") && StringUtils.isNotEmpty(failSendstate) && Long.valueOf(failSendstate) > 2)
        if ("3".equals(preachform) && NumberUtils.toInt(failSendstate, 0) > 2)
            serviceSubtaskPreachform.setSendstate("2");
//        //人工的未发送状态,需要查2
//        if (preachform.equals("1") && StringUtils.isNotEmpty(failSendstate) && Long.valueOf(failSendstate) > 2)
@@ -1904,16 +2053,18 @@
        if (CollectionUtils.isNotEmpty(serviceSubtaskPreachforms)) {
            ServiceSubtaskPreachform serviceSubtaskPreachform1 = serviceSubtaskPreachforms.get(0);
            serviceSubtaskPreachform1.setSendstate(failSendstate);
            if (serviceSubtask.getType().equals("3") || serviceSubtask.getType().equals("4")) {
            if ("3".equals(serviceSubtask.getType()) || "4".equals(serviceSubtask.getType())) {
                //如果是宣教或通知,并且发送成功,直接将发送状态改成9
                if (failSendstate.equals("2")) serviceSubtaskPreachform1.setSendstate("9");
                if ("2".equals(failSendstate)) serviceSubtaskPreachform1.setSendstate("9");
            }
            serviceSubtaskPreachform1.setRemark(remark);
            serviceSubtaskPreachformMapper.updateServiceSubtaskPreachform(serviceSubtaskPreachform1);
            log.info("---------serviceSubtaskPreachform1的入参是 :  {}", serviceSubtaskPreachform1);
            //如果当前的preachform已经是最后一个了,那直接将serviceSubtask的sendstate状态改成7就行了(全超时了)
            if (serviceSubtaskPreachform1.getSort() == sspCount.size() && isSubtaskDeadline(serviceSubtask, serviceSubtaskPreachforms)) {
                if (failSendstate.equals("4") || failSendstate.equals("5")) {
            if (serviceSubtaskPreachform1.getSort() != null
                    && serviceSubtaskPreachform1.getSort() == sspCount.size()
                    && isSubtaskDeadline(serviceSubtask, serviceSubtaskPreachforms)) {
                if ("4".equals(failSendstate) || "5".equals(failSendstate)) {
                    serviceSubtask.setCurrentPreachform(preachform);
                    serviceSubtask.setSendstate(7L);
                    log.error("setSuccessPreachForm方法,当前的preachform已经是最后一个了,全部执行失败");
@@ -1933,9 +2084,9 @@
        serviceSubtask.setCurrentPreachform(preachform);
        serviceSubtask.setSendstate(3L);
        if (serviceSubtask.getType().equals("3") || serviceSubtask.getType().equals("4")) {
        if ("3".equals(serviceSubtask.getType()) || "4".equals(serviceSubtask.getType())) {
            //如果是宣教或通知,并且发送成功,则直接将serviceSubtask状态改成6L(这个患者是没有题做的,不会再触发接口进行状态修改)
            if (failSendstate.equals("2")) {
            if ("2".equals(failSendstate)) {
                serviceSubtask.setSendstate(6L);
            }
        }