4b8ef03697b9205203e2f0baec1f629ae408ab66..50879bb28a51ae8c21cd00d273d106431e1d2c8f
4 天以前 陈昶聿
【市一】大模型
50879b 对比 | 目录
4 天以前 陈昶聿
【市一】大模型
05c82d 对比 | 目录
4 天以前 陈昶聿
【市一】微信发送日志补充
3187b8 对比 | 目录
4 天以前 陈昶聿
【市一】人工超时时间延后一天
2b4b19 对比 | 目录
已修改5个文件
已添加1个文件
381 ■■■■■ 文件已修改
ruoyi-admin/src/main/java/com/ruoyi/web/component/RedisMqReceiver.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/sms/smsUtils.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/common/QwenLLMUtil.java 221 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/component/RedisMqReceiver.java
@@ -633,7 +633,7 @@
                    String wxCode = "";
                    if (active.equals("hzszlyy")) {
                        wxCode = smsUtils.sendChat(url, patArchive.getTelcode(), serviceSubtask.getSfzh());
                        log.info(wxCode);
                        log.info("wxCode的值为:{}", wxCode);
                    } else {
                        wxCode = getWXCode(serviceSubtask.getSfzh(), url, serviceSubtask.getTaskName(), serviceSubtask.getTaskName(), patArchive.getTelcode(), patArchive.getName(), patArchive.getPatidHis(), wxqqxx);
                        log.error("wxCode的值为:{}", wxCode);
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskController.java
@@ -28,6 +28,7 @@
import com.ruoyi.quartz.task.RyTask;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserDeptService;
import com.smartor.common.QwenLLMUtil;
import com.smartor.domain.*;
import com.smartor.domain.VO.HeLibraryCountVO;
import com.smartor.domain.VO.ServiceSubtaskCotinueCountVO;
@@ -883,6 +884,16 @@
        ryTask.dealOutHospInfo();
    }
    @PostMapping("/compensateTasktest")
    public void compensateTasktest(@RequestParam("subId")Long subId) {
        ryTask.compensateTaskTest(subId);
    }
    @PostMapping("/longTaskSendtest")
    public void longTaskSendtest(@RequestParam("subId")Long subId) {
        ryTask.longTaskSendTest(subId);
    }
    @PostMapping("/syncMedInhospForShiyi")
    public void syncMedInhospForShiyi(@RequestParam("startTime") String startTime, @RequestParam("endTime") String endTime) {
        collectHISService.syncMedInhosp(startTime, endTime);
@@ -892,4 +903,11 @@
    public void syncMedOperForShiyi(@RequestParam("startTime") String startTime, @RequestParam("endTime") String endTime) {
        collectHISService.syncOper(startTime, endTime);
    }
    @PostMapping("/qwenLLMTest")
    public int qwenLLMTest(@RequestParam("voiceText") String questionText, @RequestParam("voiceText") String voiceText,
                                      @RequestParam("value") String value,@RequestParam("regexText") String regexText) {
        int result = ryTask.qwenLLMTest(questionText, voiceText, value, regexText);
        return result;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/sms/smsUtils.java
@@ -86,8 +86,9 @@
                + "&url=" + url + "&key=ff76f8904f5f32b5ee1739e8ea46e60g";
        String sendMsg = "";
        try {
            log.info("【sendChat】市一微信公众号请求参数:{}", urlTemp);
            sendMsg = HttpUtils.sendPost(urlTemp);
            log.info(sendMsg);
            log.info("【sendChat】市一微信公众号返回值:{}", sendMsg);
        } catch (Exception ex) {
            sendMsg=ex.getMessage();
            log.error(ex.getMessage());
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
@@ -24,6 +24,7 @@
import com.ruoyi.system.service.ISysDeptService;
import com.smartor.common.LSHospTokenUtil;
import com.smartor.common.MtSubmitSmUtil;
import com.smartor.common.QwenLLMUtil;
import com.smartor.domain.*;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.mapper.*;
@@ -34,6 +35,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestParam;
import java.io.IOException;
import java.net.URLEncoder;
@@ -685,6 +687,86 @@
    }
    /**
     * é•¿æœŸä»»åŠ¡å‘é€
     */
    public void longTaskSendTest(Long subId) {
        //获取任务信息
        ServiceTask st = new ServiceTask();
        st.setDelFlag("0");
        st.setLongTask(1);
        List<ServiceTask> serviceTasks = serviceTaskMapper.selectServiceTaskList(st);
        log.info("【longTaskSend】获取到{}个长期任务", serviceTasks.size());
        for (ServiceTask serviceTask : serviceTasks) {
            CommonTaskcallMQ commonTaskcallMQ = new CommonTaskcallMQ();
            commonTaskcallMQ.setTaskid(serviceTask.getTaskid());
            commonTaskcallMQ.setPreachform(serviceTask.getPreachform());
            commonTaskcallMQ.setSendType("2");
            //通过任务ID拿到患者信息,并且随访时间得是今天之前的
            ServiceSubtaskEntity serviceSubtaskVO = new ServiceSubtaskEntity();
            serviceSubtaskVO.setTaskid(commonTaskcallMQ.getTaskid());
            serviceSubtaskVO.setSendstate(2L);
            serviceSubtaskVO.setIsVisitAgain(1);
            serviceSubtaskVO.setSubId(subId);
            serviceSubtaskVO.setVisitTime(new Date());
            List<ServiceSubtask> selectServiceSubtaskList = serviceSubtaskMapper.queryServiceSubtaskList(serviceSubtaskVO);
            for (ServiceSubtask serviceSubtask : selectServiceSubtaskList) {
                sfHandlle(serviceSubtask);
            }
        }
    }
    /**
     * å¤„理补偿任务
     */
    public void compensateTaskTest(Long subId) {
        //获取到sendstate=3 å¹¶ä¸” visit_time为小于等于今天的subtask
        ServiceSubtaskEntity serviceSubtaskVO = new ServiceSubtaskEntity();
        serviceSubtaskVO.setSendstate(3L);
        serviceSubtaskVO.setSubId(subId);
        serviceSubtaskVO.setVisitTime(new Date());
        List<ServiceSubtask> serviceSubtaskList = serviceSubtaskMapper.getCompensateServiceSubtaskList(serviceSubtaskVO);
        for (ServiceSubtask serviceSubtask : serviceSubtaskList) {
            //根据当前的执行方式,获取下一种执行方式
            ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform();
            serviceSubtaskPreachform.setTaskid(serviceSubtask.getTaskid());
            serviceSubtaskPreachform.setSubid(serviceSubtask.getId());
            serviceSubtaskPreachform.setOrgid(serviceSubtask.getOrgid());
            List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
            //获取当前执行方式的序号
            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();
            if (currentSort.isPresent()) {
                //1先检查一下,是不是有执行状态是完成的(怕之前已经有完的了,没有将servuce_subtask的状态改成功,这里再检查一下)
                boolean finishState = serviceSubtaskPreachforms.stream().allMatch(item -> item.getSendstate().equals("9"));
                if (finishState) {
                    serviceSubtask.setSendstate(6L);
                    serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                    continue;
                }
                //2判断一下,当前的sort是不是等于需要执行的总个数,如果等于的话,说明是最后一个,直接将servuce_subtask的状态改成5,执行失败就行了
                Long cs = currentSort.get();
                if (cs.equals(serviceSubtaskPreachforms.size())) {
                    serviceSubtask.setSendstate(7L);
                    serviceSubtask.setRemark("处理补偿任务,当前处理最后补偿,全部执行失败(超时)");
                    serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                    //修改发送方式的状态为失败
                    serviceSubtaskPreachform.setSendstate("5");
                    serviceSubtaskPreachform.setId(id.get());
                    serviceSubtaskPreachformMapper.updateServiceSubtaskPreachform(serviceSubtaskPreachform);
                    continue;
                }
                //3.不是最后一个,获取到下一个执行方式(因为都是在今天执行,那就直接发出去就完了)
                sfHandlle(serviceSubtask);
            }
        }
    }
    /**
     * è®¾ç½®å¤±è´¥ä»»åŠ¡é»˜è®¤å€¼,并将失败任务重新置为成功
     * <p>
     * *@param failDay   (失败天数:距离当前日期失败天数)
@@ -971,6 +1053,7 @@
                    //任务发送记录
                    ServiceSubtaskRecord serviceSubtaskRecord = new ServiceSubtaskRecord();
                    serviceSubtaskRecord.setTaskid(serviceSubtask.getTaskid().toString());
                    serviceSubtaskRecord.setSubtaskId(serviceSubtask.getId());
                    serviceSubtaskRecord.setUuid(UUID.randomUUID().toString());
                    serviceSubtaskRecord.setTasktype(serviceSubtask.getType());
                    serviceSubtaskRecord.setPreachform("4");
@@ -1028,8 +1111,6 @@
                    if (active.equals("hzszlyy")) {
                        //处理中文乱码问题
                        wxCode = smsUtils.sendChat(url, patArchive.getTelcode(), serviceSubtask.getSfzh());
                        log.info(wxCode);
                    } else {
                        wxCode = getWXCode(serviceSubtask.getSfzh(), url, serviceSubtask.getTaskName(), serviceSubtask.getTaskDesc(), patArchive.getTelcode(), serviceSubtask.getSendname(), patArchive.getPatidHis(), wxqqxx);
                    }
@@ -1346,9 +1427,7 @@
                    //serviceSubtask.setRemark("setSuccessPreachForm方法,当前的preachform已经是最后一个了,全部执行失败");
                    serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                    return true;
                }
            }
        } else {
            log.error("【定时任务中该患者没有查询到属于他的发送方式,subid:{},prechform:{},orgid:{}】", serviceSubtask.getId(), preachform, serviceSubtask.getOrgid());
@@ -1413,4 +1492,8 @@
        return serviceTaskMap;
    }
    public int qwenLLMTest(String questionText, String voiceText,String value,String regexText) {
        int result = QwenLLMUtil.matchRegex(questionText, voiceText, value, regexText);
        return result;
    }
}
smartor/src/main/java/com/smartor/common/QwenLLMUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,221 @@
package com.smartor.common;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.utils.http.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * é€šä¹‰åƒé—®ï¼ˆQwen)大模型工具类。
 * <p>
 * åŸºäºŽé˜¿é‡Œäº‘百炼 DashScope çš„ OpenAI å…¼å®¹æŽ¥å£ï¼ˆ{@code /compatible-mode/v1/chat/completions}),
 * ä¸»è¦ç”¨äºŽè¯­ä¹‰åŒ¹é…ï¼šæŠŠè¯­éŸ³è¯†åˆ«å¾—到的自由文本,归一化到一组预设选项中最接近的一个。
 * <p>
 * é…ç½®é¡¹ï¼ˆapplication.yml):
 * <pre>
 * qwen:
 *   api-key: sk-xxxxxxxx          # ç™¾ç‚¼ API Key,必填
 *   model: qwen-plus             # æ¨¡åž‹åç§°ï¼Œé»˜è®¤ qwen-plus
 *   url: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
 * </pre>
 */
@Slf4j
@Component
public class QwenLLMUtil {
    /** ç™¾ç‚¼ API Key */
    @Value("${qwen.api-key:}")
    private static String apiKey = "sk-712da9346f0940ff909b40dce17579b1";
    /** æ¨¡åž‹åç§° */
    @Value("${qwen.model:qwen-plus}")
    private static String model = "qwen-plus";
    /** æŽ¥å£åœ°å€ï¼ˆOpenAI å…¼å®¹æ¨¡å¼ï¼‰ */
    @Value("${qwen.url:https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions}")
    private static String url = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions";
    /**
     * åˆ¤æ–­è¯­éŸ³æ–‡æœ¬æœ€æŽ¥è¿‘哪个选项。
     *
     * @param voiceText è¯­éŸ³è¯†åˆ«å¾—到的文本
     * @param options   å€™é€‰é€‰é¡¹åˆ—表
     * @return å‘½ä¸­çš„选项原文;无法匹配任一选项时返回 {@code null}
     */
    public String matchOption(String voiceText, List<String> options) {
        int index = matchOptionIndex(voiceText, options);
        return index < 0 ? null : options.get(index);
    }
    /**
     * åˆ¤æ–­è¯­éŸ³æ–‡æœ¬æœ€æŽ¥è¿‘哪个选项,返回选项在列表中的下标。
     *
     * @param voiceText è¯­éŸ³è¯†åˆ«å¾—到的文本
     * @param options   å€™é€‰é€‰é¡¹åˆ—表
     * @return å‘½ä¸­é€‰é¡¹çš„下标(从 0 å¼€å§‹ï¼‰ï¼›æ— æ³•匹配任一选项时返回 {@code -1}
     */
    public int matchOptionIndex(String voiceText, List<String> options) {
        if (StringUtils.isBlank(voiceText) || options == null || options.isEmpty()) {
            return -1;
        }
        // åªæœ‰ä¸€ä¸ªé€‰é¡¹æ—¶æ— éœ€è°ƒç”¨æ¨¡åž‹
        if (options.size() == 1) {
            return 0;
        }
        StringBuilder optionText = new StringBuilder();
        for (int i = 0; i < options.size(); i++) {
            optionText.append(i + 1).append(". ").append(options.get(i)).append('\n');
        }
        String systemPrompt = "你是一个语义匹配助手。用户会给出一段语音识别文本和若干个带编号的选项,"
                + "请判断这段文本在语义上最接近哪一个选项。只允许从给定选项中选择,"
                + "不要做任何解释。直接输出最匹配选项的编号数字;若没有任何选项与文本相关,则输出 0。";
        String userPrompt = "语音文本:" + voiceText + "\n\n选项:\n" + optionText
                + "\n请只输出一个数字(最匹配选项的编号,没有匹配则输出 0)。";
        String content = chat(systemPrompt, userPrompt);
        if (StringUtils.isBlank(content)) {
            return -1;
        }
        Integer number = extractFirstNumber(content);
        if (number == null || number <= 0 || number > options.size()) {
            log.warn("Qwen é€‰é¡¹åŒ¹é…æœªå‘½ä¸­ï¼ŒvoiceText={}, options={}, modelReturn={}", voiceText, options, content);
            return -1;
        }
        return number - 1;
    }
    /**
     * åˆ¤æ–­è¯­éŸ³æ–‡æœ¬æ˜¯å¦ç¬¦åˆè¿™ä¸ªæ„æ€
     * @param questionText
     * @param voiceText è¯­éŸ³è¯†åˆ«å¾—到的文本
     * @return å‘½ä¸­çš„选项原文;无法匹配任一选项时返回 {@code null}
     */
    public static int matchRegex(String questionText, String voiceText, String value, String regexText) {
        if (StringUtils.isBlank(voiceText) || regexText == null || regexText.isEmpty()) {
            return -1;
        }
        String systemPrompt = "你是一个专业的语音识别文本语义匹配助手。你的任务是判断用户的语音文本是否在语义上符合给定的<正则匹配规则>或<对应指标值>。"
                + "由于正则表达式无法覆盖所有自然语言的同义表达,你需要作为语义兜底机制,判断语音文本是否表达了与正则规则或指标值相同或相近的核心意图。"
                + "【核心规则】"
                + "1. å¦‚果语音文本在字面上匹配了正则,或者在语义上表达了正则/指标值的意思,请输出:1。"
                + "2. å¦‚果语音文本与正则/指标值的意思完全无关、意思相反或无法推断出相关意图,请输出:0。"
                + "3. ç»å¯¹ç¦æ­¢è¾“出任何解释、标点符号、换行符或其他无关字符。你的最终回复只能是一个数字(1 æˆ– 0)。"
                ;
        String userPrompt = "请根据以下信息进行语义匹配判断:\n" +
                "- é—®é¢˜æ–‡æœ¬ï¼š" + questionText + "\n\n"
                + "- è¯­éŸ³è¯†åˆ«æ–‡æœ¬ï¼š" + voiceText + "\n\n"
                + "- æ­£åˆ™åŒ¹é…æ–‡æœ¬ï¼š\n" + regexText + "\n\n"
                + "- å¯¹åº”指标值:\n" + value
                + "\n请判断这段语音文本是否接近正则匹配规则或者对应指标值的意思。若有相关意思、能匹配得上,直接输出 1;若与文本意思完全不相关,则输出 0。";
        String content = chat(systemPrompt, userPrompt);
        if (StringUtils.isBlank(content)) {
            return -1;
        }
        Integer number = extractFirstNumber(content);
        if (number == null || number <= 0) {
            log.warn("Qwen é€‰é¡¹åŒ¹é…æœªå‘½ä¸­ï¼ŒvoiceText={}, regexText={}, modelReturn={}", voiceText, regexText, content);
            return -1;
        }
        return number - 1;
    }
    /**
     * é€šç”¨å¯¹è¯è°ƒç”¨ï¼Œè¿”回模型回复的文本内容。
     *
     * @param systemPrompt ç³»ç»Ÿæç¤ºè¯ï¼Œå¯ä¸ºç©º
     * @param userPrompt   ç”¨æˆ·æç¤ºè¯
     * @return æ¨¡åž‹å›žå¤æ­£æ–‡ï¼›è°ƒç”¨å¤±è´¥è¿”回 {@code null}
     */
    public static String chat(String systemPrompt, String userPrompt) {
        if (StringUtils.isBlank(apiKey)) {
            throw new IllegalStateException("通义千问 API Key æœªé…ç½®ï¼ˆqwen.api-key)");
        }
        if (StringUtils.isBlank(userPrompt)) {
            throw new IllegalArgumentException("userPrompt ä¸èƒ½ä¸ºç©º");
        }
        JSONArray messages = new JSONArray();
        if (StringUtils.isNotBlank(systemPrompt)) {
            messages.add(message("system", systemPrompt));
        }
        messages.add(message("user", userPrompt));
        JSONObject body = new JSONObject();
        body.put("model", model);
        body.put("messages", messages);
        // åŒ¹é…åœºæ™¯éœ€è¦ç¨³å®šç»“果,温度调低
        body.put("temperature", 0.01);
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", "Bearer " + apiKey);
        headers.put("Content-Type", "application/json");
        String response = HttpUtils.sendPostByHeader(url, body.toJSONString(), headers);
        if (StringUtils.isBlank(response)) {
            log.error("通义千问返回为空,url={}, body={}", url, body.toJSONString());
            return null;
        }
        try {
            JSONObject json = JSONObject.parseObject(response);
            JSONArray choices = json.getJSONArray("choices");
            if (choices == null || choices.isEmpty()) {
                log.error("通义千问响应无 choices,response={}", response);
                return null;
            }
            JSONObject msg = choices.getJSONObject(0).getJSONObject("message");
            return msg == null ? null : StringUtils.trim(msg.getString("content"));
        } catch (Exception e) {
            log.error("解析通义千问响应失败,response={}", response, e);
            return null;
        }
    }
    private static JSONObject message(String role, String content) {
        JSONObject msg = new JSONObject();
        msg.put("role", role);
        msg.put("content", content);
        return msg;
    }
    /**
     * ä»Žæ¨¡åž‹å›žå¤ä¸­æå–第一个整数。模型偶尔会回复 â€œé€‰é¡¹2” â€œ2。” ä¹‹ç±»ï¼Œåšä¸€æ¬¡å…œåº•解析。
     */
    private static Integer extractFirstNumber(String text) {
        List<Character> digits = new ArrayList<>();
        for (int i = 0; i < text.length(); i++) {
            char c = text.charAt(i);
            if (c >= '0' && c <= '9') {
                digits.add(c);
            } else if (!digits.isEmpty()) {
                break;
            }
        }
        if (digits.isEmpty()) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (char c : digits) {
            sb.append(c);
        }
        try {
            return Integer.parseInt(sb.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }
}
smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml
@@ -271,12 +271,36 @@
        <include refid="selectServiceSubtaskVo"/>
        where 1=1
        and del_flag = 0
        and sendstate != 5
        <if test="orgid != null and orgid != ''">
            and orgid = #{orgid}
        </if>
        <if test="subId != null">
            AND id = #{subId}
        </if>
        <if test="taskid != null ">and taskid = #{taskid}</if>
        <if test="visitTime != null">
            AND date_format(visit_time,'%y%m%d') &lt;= date_format(#{visitTime},'%y%m%d')
        </if>
        <if test="sendstate != null ">and sendstate = #{sendstate}</if>
        <if test="continueFlag != null ">and continue_flag = #{continueFlag}</if>
        <if test="continueTimeNow != null ">and continue_time_now = #{continueTimeNow,jdbcType=TIMESTAMP}</if>
        <if test="continueCount != null ">and continue_count = #{continueCount}</if>
        <if test="continueTimeNext != null ">and continue_time_next = #{continueTimeNext}</if>
        UNION ALL
        <include refid="selectServiceSubtaskVo"/>
        where 1=1
        and del_flag = 0
        and sendstate = 5
        <if test="orgid != null and orgid != ''">
            and orgid = #{orgid}
        </if>
        <if test="subId != null">
            AND id = #{subId}
        </if>
        <if test="taskid != null ">and taskid = #{taskid}</if>
        <if test="visitTime != null">
            AND date_format(visit_time,'%y%m%d') &lt;= date_format(DATE_ADD(#{visitTime}, INTERVAL 1 DAY),'%y%m%d')
        </if>
        <if test="sendstate != null ">and sendstate = #{sendstate}</if>
        <if test="continueFlag != null ">and continue_flag = #{continueFlag}</if>
@@ -545,12 +569,34 @@
            resultMap="ServiceSubtaskResult">
        <include refid="selectServiceSubtaskVo"/>
        where del_flag = 0
        and sendstate != 5
        <if test="orgid != null and orgid != ''">
            and orgid = #{orgid}
        </if>
        <if test="visitTime != null">
            AND date_format(visit_time,'%y%m%d') &lt;= date_format(#{visitTime},'%y%m%d')
        </if>
        <if test="subId != null">
            AND id = #{subId}
        </if>
        <if test="sendstate != null ">and sendstate = #{sendstate}</if>
        <if test="continueFlag != null ">and continue_flag = #{continueFlag}</if>
        <if test="continueTimeNow != null ">and continue_time_now = #{continueTimeNow,jdbcType=TIMESTAMP}</if>
        <if test="continueCount != null ">and continue_count = #{continueCount}</if>
        <if test="continueTimeNext != null ">and continue_time_next = #{continueTimeNext}</if>
        UNION ALL
        <include refid="selectServiceSubtaskVo"/>
        where del_flag = 0
        and sendstate = 5
        <if test="orgid != null and orgid != ''">
            and orgid = #{orgid}
        </if>
        <if test="visitTime != null">
            AND date_format(visit_time,'%y%m%d') &lt;= date_format(DATE_ADD(#{visitTime}, INTERVAL 1 DAY),'%y%m%d')
        </if>
        <if test="subId != null">
            AND id = #{subId}
        </if>
        <if test="sendstate != null ">and sendstate = #{sendstate}</if>
        <if test="continueFlag != null ">and continue_flag = #{continueFlag}</if>
        <if test="continueTimeNow != null ">and continue_time_now = #{continueTimeNow,jdbcType=TIMESTAMP}</if>