陈昶聿
8 小时以前 0d10bc99eec75672b049339399e4f762fbd9746e
Merge remote-tracking branch 'origin/master'
已修改45个文件
已添加7个文件
2327 ■■■■ 文件已修改
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServicePhoneController.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskAnswerController.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskController.java 131 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskDetailController.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskDetailTraceController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/SmsTempletController.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java 337 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-xh.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SmsTempletMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/DTO/QuestionResultDTO.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/IvrLibaTemplateScript.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/IvrTaskTemplateScript.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/IvrTaskTemplateScriptVO.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PatMedInhosp.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PhoneCallBackSHVO.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PhoneCallResultYQVO.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/ServiceOutPath.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/ServiceSubtask.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/ServiceSubtaskDetailRatioExport.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/ServiceSubtaskDetailTrace.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/ServiceSubtaskExport.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/SvyLibTemplateScript.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/SvyLibTemplateScriptVO.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/SvyTaskTemplateScript.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/SvyTaskTemplateScriptVO.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/VO/DetailTraceDealVO.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/VO/QuestionResultVO.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/VO/ServiceSubtaskVO.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/entity/ServiceSubtaskEntity.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/mapper/ServiceSubtaskMapper.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/mapper/SvyLibTemplateScriptMapper.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IServicePhoneService.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IServiceSubtaskService.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/PatArchiveImportServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/ServicPhoneServiceImpl.java 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskAnswerServiceImpl.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java 419 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/IvrLibaTemplateScriptMapper.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/IvrTaskTemplateScriptMapper.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/PatMedInhospMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/ServiceOutPathMapper.xml 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/ServiceSubtaskDetailTraceMapper.xml 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/SvyLibTemplateScriptMapper.xml 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/SvyTaskTemplateScriptMapper.xml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServicePhoneController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
package com.ruoyi.web.controller.smartor;
import com.ruoyi.common.core.controller.BaseController;
import com.smartor.domain.OutboundConfigAddReqVO;
import com.smartor.domain.OutboundInstanceResVO;
import com.smartor.domain.PhoneCallBackSHVO;
import com.smartor.domain.PhoneCallReqYQVO;
import com.smartor.domain.PhoneCallResultYQVO;
import com.smartor.service.IServicePhoneService;
import com.smartor.service.IServiceSubtaskService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
 * ä¸‰æ±‡ç”µè¯æµç¨‹æœåŠ¡ Controller
 *
 * @author ls
 * @date 2026-08-26
 */
@Slf4j
@Api(description = "三汇电话流程服务")
@RestController
@RequestMapping("/smartor/shphone")
public class ServicePhoneController extends BaseController {
    @Autowired
    private IServiceSubtaskService serviceSubtaskService;
    @Autowired
    private IServicePhoneService iServicePhoneService;
    @ApiOperation("根据电话号码,获取首个问题")
    @PostMapping("/getFirstQues")
    public Map<String, Object> getFirstQues(@RequestBody PhoneCallReqYQVO phoneCallReqYQVO) {
        log.info("根据电话号码,获取首个问题?{}", phoneCallReqYQVO);
        PhoneCallBackSHVO phoneCallBackSHVO = serviceSubtaskService.getFirstQues(phoneCallReqYQVO);
        return success(phoneCallBackSHVO);
    }
    @ApiOperation("三汇 ASR ç»“果处理")
    @PostMapping("/processResust")
    public Map<String, Object> processResust(@RequestBody PhoneCallResultYQVO resultYQVO) {
        log.info("三汇 ASR ç»“果处理 (三汇)?{}", resultYQVO);
        PhoneCallBackSHVO phoneCallBackSHVO = serviceSubtaskService.processResust(resultYQVO);
        return success(phoneCallBackSHVO);
    }
    @ApiOperation("新增外呼实例配置")
    @PostMapping("/addOutboundInstance")
    public Map<String, Object> addOutboundInstance(@RequestBody OutboundConfigAddReqVO request) {
        log.info("新增外呼实例配置:{}", request);
        OutboundInstanceResVO outboundInstanceResVO = iServicePhoneService.addOutboundInstance(request);
        return success(outboundInstanceResVO);
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskAnswerController.java
@@ -1,6 +1,7 @@
package com.ruoyi.web.controller.smartor;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
@@ -114,15 +115,23 @@
        return toAjax(serviceSubtaskAnswerService.deleteServiceSubtaskAnswerByIds(ids));
    }
    @ApiOperation("患者问题结果记录(外链)")
    /**
     * æ‚£è€…问题结果记录 (外链) - å¼‚步处理
     */
    @ApiOperation("患者问题结果记录 (外链)")
    @PostMapping("/saveQuestionAnswer")
    public AjaxResult saveQuestionAnswer(@RequestBody ServiceSubTaskAnswerReq serviceSubTaskAnswerReq) {
        log.info("【saveQuestionCache-----saveQuestionAnswer】的入参为prarm1:{},prarm2:{},type:{},isabnormal:{}", serviceSubTaskAnswerReq.getParam1(), serviceSubTaskAnswerReq.getParam2(), serviceSubTaskAnswerReq.getType(), serviceSubTaskAnswerReq.getIsabnormal());
        return toAjax(serviceSubtaskAnswerService.saveQuestionAnswer(serviceSubTaskAnswerReq, 0L));
        log.info("【saveQuestionAnswer】的入参为 param1:{},param2:{},type:{},isabnormal:{}",
                serviceSubTaskAnswerReq.getParam1(), serviceSubTaskAnswerReq.getParam2(),
                serviceSubTaskAnswerReq.getType(), serviceSubTaskAnswerReq.getIsabnormal());
        // å¼‚步处理保存逻辑,立即返回成功消息
        serviceSubtaskAnswerService.saveQuestionAnswer(serviceSubTaskAnswerReq, 0L);
        return success("提交成功,正在处理中...");
    }
    @ApiOperation("患者问题结果记录缓存(小程序外链)")
    @RepeatSubmit
    @PostMapping("/saveQuestionCacheXCH")
    public AjaxResult saveQuestionCacheXCH(@RequestBody ServiceSubTaskCacheReq serviceSubTaskCacheReq) {
        log.info("【saveQuestionCache-----saveQuestionCacheXCH】的入参为:{}", serviceSubTaskCacheReq);
@@ -130,6 +139,7 @@
    }
    @ApiOperation("患者问题结果保存")
    @RepeatSubmit
    @PostMapping("/savequestiondetail")
    public AjaxResult saveQuestionDetail(@RequestBody ServiceSubTaskCacheReq serviceSubTaskCacheReq) {
        log.info("【saveQuestionDetail-----saveQuestionCache】的prarm1:{},prarm2:{},type:{},prarm6:{}", serviceSubTaskCacheReq.getParam1(), serviceSubTaskCacheReq.getParam2(), serviceSubTaskCacheReq.getType(), serviceSubTaskCacheReq.getParam6());
@@ -137,11 +147,16 @@
    }
    @ApiOperation("患者问题结果记录(小程充外链)")
    /**
     * æ‚£è€…问题结果记录 (小程序外链) - å¼‚步处理
     */
    @ApiOperation("患者问题结果记录 (小程充外链)")
    @PostMapping("/saveQuestionAnswerXCH")
    public AjaxResult saveQuestionAnswerXCH(@RequestBody ServiceSubTaskAnswerReq serviceSubTaskAnswerReq) {
        log.info("【saveQuestionCache-----saveQuestionAnswerXCH】的入参为:{}", serviceSubTaskAnswerReq);
        return toAjax(serviceSubtaskAnswerService.saveQuestionAnswer(serviceSubTaskAnswerReq, 1L));
        log.info("【saveQuestionAnswerXCH】的入参为:{}", serviceSubTaskAnswerReq);
        // å¼‚步处理保存逻辑,立即返回成功消息
        serviceSubtaskAnswerService.saveQuestionAnswer(serviceSubTaskAnswerReq, 1L);
        return success("提交成功,正在处理中...");
    }
    @ApiOperation("获取患者问题结果记录缓存(小程序外链)")
@@ -179,6 +194,7 @@
    @ApiOperation("患者满意度结果记录(外链)")
    @RepeatSubmit
    @PostMapping("/saveMYDQuestionAnswer")
    public AjaxResult saveMYDQuestionAnswer(@RequestBody ServiceSubTaskMYDAnswerReq serviceSubTaskAnswerReq) {
        log.info("【saveQuestionCache-----saveMYDQuestionAnswer】的入参为:{}", serviceSubTaskAnswerReq);
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskController.java
@@ -24,10 +24,8 @@
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserDeptService;
import com.smartor.domain.*;
import com.smartor.domain.VO.HeLibraryCountVO;
import com.smartor.domain.VO.ServiceSubtaskCotinueCountVO;
import com.smartor.domain.VO.ServiceSubtaskVO;
import com.smartor.domain.VO.TransferDeptVO;
import com.smartor.domain.DTO.QuestionResultDTO;
import com.smartor.domain.VO.*;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.service.IServiceSubtaskRecordService;
import com.smartor.service.IServiceSubtaskService;
@@ -191,59 +189,6 @@
    }
    /**
     * å¸¦Redis缓存的统计查询,供patItem和patItemCount共用,条件不变时直接命中缓存
     */
    private Map<String, Object> getPatItemCountWithCache(ServiceSubtaskEntity serviceSubtaskEntity) {
        Map<String, Object> map = new HashMap<>();
        LoginUser loginUser = getLoginUser();
        Long userId = null;
        if (ObjectUtils.isNotEmpty(loginUser) && ObjectUtils.isNotEmpty(loginUser.getUser())) {
            userId = loginUser.getUser().getUserId();
        }
        if (serviceSubtaskEntity.getSendstateView() != null) {
            //            1:待随访(1 è¢«é¢†å–、2 å¾…发送、3 å·²å‘送、5 å‘送失败、7、超时);2:已完成( 6 å·²å®Œæˆï¼‰ã€3:无需随访(4 ä¸æ‰§è¡Œï¼‰
            if (serviceSubtaskEntity.getSendstateView() == 1)
                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(1L, 2L, 3L, 5L, 7L)));
            if (serviceSubtaskEntity.getSendstateView() == 2)
                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(6L)));
            if (serviceSubtaskEntity.getSendstateView() == 3)
                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(4L)));
        }
        if (ObjectUtils.isNotEmpty(userId)) {
            Map<String, Object> redisMap = redisCache.getCacheObject(userId + "patItemCount");
            Boolean redisFlag = false;
            if (MapUtils.isNotEmpty(redisMap)) {
                ServiceSubtaskEntity oldCondition = (ServiceSubtaskEntity) redisMap.get("searchCondition");
                if (ObjectUtils.isNotEmpty(oldCondition) && isSameCondition(oldCondition, serviceSubtaskEntity)) {
                    redisFlag = true;
                    map = redisMap;
                    redisCache.setCacheObject(userId + "patItemCount", map, 60, TimeUnit.MINUTES);
                }
            }
            if (!redisFlag) {
                map = serviceSubtaskService.patItemCount(serviceSubtaskEntity);
                map.put("searchCondition", serviceSubtaskEntity);
                redisCache.setCacheObject(userId + "patItemCount", map, 120, TimeUnit.MINUTES);
            }
        } else {
            map = serviceSubtaskService.patItemCount(serviceSubtaskEntity);
        }
        return map;
    }
    /**
     * æ¯”较两个ServiceSubtaskVO的查询条件是否一致
     * ä½¿ç”¨åå°„比较所有字段,排除分页参数和无关字段
     */
    private boolean isSameCondition(ServiceSubtaskEntity oldCondition, ServiceSubtaskEntity newCondition) {
        // ä½¿ç”¨ReflectUtils的通用方法比较,排除分页参数等无关字段
        return ReflectUtils.equalsAllFields(oldCondition, newCondition,
                // æŽ’除的字段:分页参数、序列化ID、时间戳等
                "pageNum", "pageSize", "serialVersionUID", "createBy", "createTime", "updateBy", "updateTime", "searchValue", "params");
    }
    /**
     * å¯¼å‡ºå•一任务列表
     */
    //@PreAuthorize("@ss.hasPermi('system:taskcall:export')")
@@ -275,11 +220,11 @@
                    serviceSubtaskExport.setEndDay(endDay);
                }
            }
            //获取serviceSubtaskList中的所有taskid
            List<Long> taskIds = serviceSubtaskList.stream().map(ServiceSubtaskRes::getTaskid).filter(ObjectUtils::isNotEmpty).distinct().collect(Collectors.toList());
            serviceSubtaskDetailRatioExports = serviceSubtaskService.statQuestionOption(taskIds, serviceSubtaskVO.getStartOutHospTime(), serviceSubtaskVO.getEndOutHospTime());
            QuestionResultVO questionResultVO = new QuestionResultVO();
            questionResultVO.setServiceType(serviceSubtaskVO.getServiceType());
            questionResultVO.setEndOutHospTime(serviceSubtaskVO.getEndOutHospTime());
            questionResultVO.setStartOutHospTime(serviceSubtaskVO.getStartOutHospTime());
            serviceSubtaskDetailRatioExports = serviceSubtaskService.getDetailRatio(questionResultVO, serviceSubtaskList);
        }
@@ -472,6 +417,8 @@
        if (ObjectUtils.isNotEmpty(user)) serviceSubtask.setOrgid(user.getOrgid());
        if (ObjectUtils.isNotEmpty(user)) serviceSubtask.setCampusid(user.getCampusid());
        serviceSubtask.setCampusid(user.getCampusid());
        serviceSubtask.setCreateBy(user.getNickName());
        serviceSubtask.setUpdateBy(user.getNickName());
        return success(serviceSubtaskService.insertServiceSubtask(serviceSubtask));
    }
@@ -990,11 +937,12 @@
        return AjaxResult.success("【collectHISDaysBefore】同步任务已启动,天数为" + day + "天,请稍后查看结果");
    }
    @PostMapping("/syncMedOperForShiyi")
    public AjaxResult syncMedOperForShiyi(@RequestParam("startTime") String startTime, @RequestParam("endTime") String endTime) {
        // ç«‹å³è¿”回,后台异步执行
        collectHISService.syncOper(startTime, endTime);
        CompletableFuture.runAsync(() -> {
            collectHISService.syncOper(startTime, endTime);
        });
        return AjaxResult.success("同步任务已启动,请稍后查看结果");
    }
@@ -1004,4 +952,59 @@
        ryTask.dealSubtaskDeadline(orgid, day);
        return AjaxResult.success("【dealSubtaskDeadline】同步任务已启动,天数为" + day + "天,请稍后查看结果");
    }
    /**
     * å¸¦Redis缓存的统计查询,供patItem和patItemCount共用,条件不变时直接命中缓存
     */
    private Map<String, Object> getPatItemCountWithCache(ServiceSubtaskEntity serviceSubtaskEntity) {
        Map<String, Object> map = new HashMap<>();
        LoginUser loginUser = getLoginUser();
        Long userId = null;
        if (ObjectUtils.isNotEmpty(loginUser) && ObjectUtils.isNotEmpty(loginUser.getUser())) {
            userId = loginUser.getUser().getUserId();
        }
        if (serviceSubtaskEntity.getSendstateView() != null) {
            //            1:待随访(1 è¢«é¢†å–、2 å¾…发送、3 å·²å‘送、5 å‘送失败、7、超时);2:已完成( 6 å·²å®Œæˆï¼‰ã€3:无需随访(4 ä¸æ‰§è¡Œï¼‰
            if (serviceSubtaskEntity.getSendstateView() == 1)
                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(1L, 2L, 3L, 5L, 7L)));
            if (serviceSubtaskEntity.getSendstateView() == 2)
                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(6L)));
            if (serviceSubtaskEntity.getSendstateView() == 3)
                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(4L)));
        }
        if (ObjectUtils.isNotEmpty(userId)) {
            Map<String, Object> redisMap = redisCache.getCacheObject(userId + "patItemCount");
            Boolean redisFlag = false;
            if (MapUtils.isNotEmpty(redisMap)) {
                ServiceSubtaskEntity oldCondition = (ServiceSubtaskEntity) redisMap.get("searchCondition");
                if (ObjectUtils.isNotEmpty(oldCondition) && isSameCondition(oldCondition, serviceSubtaskEntity)) {
                    redisFlag = true;
                    map = redisMap;
                    redisCache.setCacheObject(userId + "patItemCount", map, 60, TimeUnit.MINUTES);
                }
            }
            if (!redisFlag) {
                map = serviceSubtaskService.patItemCount(serviceSubtaskEntity);
                map.put("searchCondition", serviceSubtaskEntity);
                redisCache.setCacheObject(userId + "patItemCount", map, 120, TimeUnit.MINUTES);
            }
        } else {
            map = serviceSubtaskService.patItemCount(serviceSubtaskEntity);
        }
        return map;
    }
    /**
     * æ¯”较两个ServiceSubtaskVO的查询条件是否一致
     * ä½¿ç”¨åå°„比较所有字段,排除分页参数和无关字段
     */
    private boolean isSameCondition(ServiceSubtaskEntity oldCondition, ServiceSubtaskEntity newCondition) {
        // ä½¿ç”¨ReflectUtils的通用方法比较,排除分页参数等无关字段
        return ReflectUtils.equalsAllFields(oldCondition, newCondition,
                // æŽ’除的字段:分页参数、序列化ID、时间戳等
                "pageNum", "pageSize", "serialVersionUID", "createBy", "createTime", "updateBy", "updateTime", "searchValue", "params");
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskDetailController.java
@@ -6,10 +6,14 @@
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.DtoConversionUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysConfigService;
import com.smartor.domain.*;
import com.smartor.domain.DTO.QuestionResultDTO;
import com.smartor.domain.DTO.ServiceSubtaskDetailDTO;
import com.smartor.domain.VO.QuestionResultVO;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.service.IServiceSubtaskDetailService;
import com.smartor.service.IServiceSubtaskService;
import io.swagger.annotations.Api;
@@ -19,6 +23,7 @@
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
import static com.ruoyi.framework.datasource.DynamicDataSourceContextHolder.log;
@@ -192,4 +197,41 @@
        map = ServiceSubtaskDetailService.getServiceSubtaskDetails(serviceSubtaskDetailVO);
        return map;
    }
    /**
     * æŸ¥è¯¢æ‚£è€…随访信息
     */
    @ApiOperation("省立同德满意度数据统计")
    @PostMapping("/sltaMydTotalByScore")
    public Map<String, Object> sltaMydTotal(@RequestBody QuestionResultVO questionResultVO) {
        List<ServiceSubtaskRes> serviceSubtaskResList = null;
        ServiceSubtaskEntity serviceSubtaskEntity = new ServiceSubtaskEntity();
        serviceSubtaskEntity.setServiceType(questionResultVO.getServiceType());
        serviceSubtaskEntity.setEndOutHospTime(questionResultVO.getEndOutHospTime());
        serviceSubtaskEntity.setStartOutHospTime(questionResultVO.getStartOutHospTime());
        serviceSubtaskEntity.setDeptOrDistrict(questionResultVO.getDeptOrDistrict());
        serviceSubtaskEntity.setLeavehospitaldistrictcodes(questionResultVO.getLeavehospitaldistrictcodes());
        if (serviceSubtaskEntity != null) {
            List<ServiceSubtask> serviceSubtasks = serviceSubtaskService.getServiceSubtasks(serviceSubtaskEntity);
            serviceSubtaskResList = DtoConversionUtils.sourceToTarget(serviceSubtasks, ServiceSubtaskRes.class);
        }
        List<ServiceSubtaskDetailRatioExport> detailList = serviceSubtaskService.getDetailRatio(questionResultVO, serviceSubtaskResList);
        // æŒ‰ questiontext åˆ†ç»„后转换为 QuestionResultVO
        List<QuestionResultDTO> result = new ArrayList<>();
        if (!detailList.isEmpty()) {
            Map<String, List<ServiceSubtaskDetailRatioExport>> groupedMap = detailList.stream()
                    .collect(Collectors.groupingBy(ServiceSubtaskDetailRatioExport::getQuestiontext));
            groupedMap.forEach((questionText, exportList) -> {
                QuestionResultDTO questionResultDTO = new QuestionResultDTO();
                questionResultDTO.setQeustionText(questionText);
                questionResultDTO.setSubtaskDetailRatioExportList(exportList);
                result.add(questionResultDTO);
            });
        }
        return success(result);
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskDetailTraceController.java
@@ -4,11 +4,9 @@
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.smartor.domain.DTO.DetailTraceDealDTO;
import com.smartor.domain.ServiceSubtaskDetailTrace;
import com.smartor.domain.VO.DetailTraceDealVO;
import com.smartor.domain.VO.SatisfactionExceptConfigQuestionReqVO;
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/SmsTempletController.java
@@ -6,6 +6,7 @@
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@@ -41,16 +42,18 @@
    //@PreAuthorize("@ss.hasPermi('smartor:smstemplet:list')")
    @PostMapping("/list")
    public Map<String, Object> list(@RequestBody SmsTemplet smsTemplet) {
        smsTemplet.setPageNum(PageUtils.getOffset(smsTemplet.getPageNum(), smsTemplet.getPageSize()));
        if (StringUtils.isEmpty(smsTemplet.getUserName()) && StringUtils.isEmpty(smsTemplet.getWardCode()) && StringUtils.isEmpty(smsTemplet.getDeptCode())) {
            SysUser user = getLoginUser().getUser();
            smsTemplet.setUserName(user.getUserName());
            smsTemplet.setNickName(user.getNickName());
        }
        List<SmsTemplet> list = smsTempletService.selectSmsTempletList(smsTemplet);
        //这个不需要分页,一个科室也不会有多少条短信模板
        smsTemplet.setPageNum(null);
        smsTemplet.setPageSize(null);
        List<SmsTemplet> list = smsTempletService.selectSmsTempletList(smsTemplet);
        return getDataTable3(ObjectUtil.isNotEmpty(list) ? list.size() : 0, list);
        List<SmsTemplet> total = smsTempletService.selectSmsTempletList(smsTemplet);
        return getDataTable3(ObjectUtil.isNotEmpty(total) ? total.size() : 0, list);
    }
    /**
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
@@ -79,7 +79,7 @@
            //通用密码:Hrs#当前年月日*
            token = loginService.getTokenWN(loginBody.getUsername());
        } else {
            token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), loginBody.getUuid(), loginBody.getOrgid(), loginBody.getCampusid());
            token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), loginBody.getUuid(), loginBody.getOrgid(), loginBody.getCampusid(), loginBody.getUserType());
        }
        ajax.put(Constants.TOKEN, token);
        return ajax;
@@ -123,12 +123,16 @@
     */
//    @IpWhitelist
    @GetMapping("/SSOLogin")
    public RedirectView SSOLogin(@RequestParam(required = false) String campusid, @RequestParam(required = false) String deptId, String orgid, String userName, String token) {
    public RedirectView SSOLogin(@RequestParam(required = false) String campusid, @RequestParam(required = false) String deptId, @RequestParam(required = false) String systype, String orgid, String userName, String token) {
      log.info("SSOLogin的入参,campusid:{},  deptId:{},  systype:{},  orgid:{},  userName:{},  token:{}",campusid,deptId,systype,orgid,userName,token);
        if (StringUtils.isEmpty(userName) && StringUtils.isEmpty(token) && StringUtils.isEmpty(orgid)) {
            throw new BaseException("参数不能为空");
        }
        String path = null;
        if (StringUtils.isEmpty(systype)) path = sysConfigService.selectConfigByKey("sys.qddz");
        else path = sysConfigService.selectConfigByKey("sys.qddz.yd");
        log.info("qddz的查询值为,path:{}",path);
        String path = sysConfigService.selectConfigByKey("sys.qddz");
        if (StringUtils.isEmpty(path)) {
            throw new BaseException("请配置前端地址");
        }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java
@@ -1,183 +1,154 @@
package com.ruoyi.web.controller.tool;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
/**
 * swagger ç”¨æˆ·æµ‹è¯•方法
 *
 * @author ruoyi
 */
@Api("用户信息管理")
@RestController
@RequestMapping("/test/user")
public class TestController extends BaseController
{
    private final static Map<Integer, UserEntity> users = new LinkedHashMap<Integer, UserEntity>();
    {
        users.put(1, new UserEntity(1, "admin", "admin123", "15888888888"));
        users.put(2, new UserEntity(2, "ry", "admin123", "15666666666"));
    }
    @ApiOperation("获取用户列表")
    @GetMapping("/list")
    public R<List<UserEntity>> userList()
    {
        List<UserEntity> userList = new ArrayList<UserEntity>(users.values());
        return R.ok(userList);
    }
    @ApiOperation("获取用户详细")
    @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
    @GetMapping("/{userId}")
    public R<UserEntity> getUser(@PathVariable Integer userId)
    {
        if (!users.isEmpty() && users.containsKey(userId))
        {
            return R.ok(users.get(userId));
        }
        else
        {
            return R.fail("用户不存在");
        }
    }
    @ApiOperation("新增用户")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class),
        @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class),
        @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class),
        @ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class)
    })
    @PostMapping("/save")
    public R<String> save(UserEntity user)
    {
        if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
        {
            return R.fail("用户ID不能为空");
        }
        users.put(user.getUserId(), user);
        return R.ok();
    }
    @ApiOperation("更新用户")
    @PutMapping("/update")
    public R<String> update(@RequestBody UserEntity user)
    {
        if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
        {
            return R.fail("用户ID不能为空");
        }
        if (users.isEmpty() || !users.containsKey(user.getUserId()))
        {
            return R.fail("用户不存在");
        }
        users.remove(user.getUserId());
        users.put(user.getUserId(), user);
        return R.ok();
    }
    @ApiOperation("删除用户信息")
    @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
    @DeleteMapping("/{userId}")
    public R<String> delete(@PathVariable Integer userId)
    {
        if (!users.isEmpty() && users.containsKey(userId))
        {
            users.remove(userId);
            return R.ok();
        }
        else
        {
            return R.fail("用户不存在");
        }
    }
}
@ApiModel(value = "UserEntity", description = "用户实体")
class UserEntity
{
    @ApiModelProperty("用户ID")
    private Integer userId;
    @ApiModelProperty("用户名称")
    private String username;
    @ApiModelProperty("用户密码")
    private String password;
    @ApiModelProperty("用户手机")
    private String mobile;
    public UserEntity()
    {
    }
    public UserEntity(Integer userId, String username, String password, String mobile)
    {
        this.userId = userId;
        this.username = username;
        this.password = password;
        this.mobile = mobile;
    }
    public Integer getUserId()
    {
        return userId;
    }
    public void setUserId(Integer userId)
    {
        this.userId = userId;
    }
    public String getUsername()
    {
        return username;
    }
    public void setUsername(String username)
    {
        this.username = username;
    }
    public String getPassword()
    {
        return password;
    }
    public void setPassword(String password)
    {
        this.password = password;
    }
    public String getMobile()
    {
        return mobile;
    }
    public void setMobile(String mobile)
    {
        this.mobile = mobile;
    }
}
//package com.ruoyi.web.controller.tool;
//
//import java.util.ArrayList;
//import java.util.LinkedHashMap;
//import java.util.List;
//import java.util.Map;
//
//import org.springframework.web.bind.annotation.DeleteMapping;
//import org.springframework.web.bind.annotation.GetMapping;
//import org.springframework.web.bind.annotation.PathVariable;
//import org.springframework.web.bind.annotation.PostMapping;
//import org.springframework.web.bind.annotation.PutMapping;
//import org.springframework.web.bind.annotation.RequestBody;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RestController;
//import com.ruoyi.common.core.controller.BaseController;
//import com.ruoyi.common.core.domain.R;
//import com.ruoyi.common.utils.StringUtils;
//import io.swagger.annotations.Api;
//import io.swagger.annotations.ApiImplicitParam;
//import io.swagger.annotations.ApiImplicitParams;
//import io.swagger.annotations.ApiModel;
//import io.swagger.annotations.ApiModelProperty;
//import io.swagger.annotations.ApiOperation;
//
///**
// * swagger ç”¨æˆ·æµ‹è¯•方法
// *
// * @author ruoyi
// */
//@Api("用户信息管理")
//@RestController
//@RequestMapping("/test/user")
//public class TestController extends BaseController {
//    private final static Map<Integer, UserEntity> users = new LinkedHashMap<Integer, UserEntity>();
//
//    {
//        users.put(1, new UserEntity(1, "admin", "admin123", "15888888888"));
//        users.put(2, new UserEntity(2, "ry", "admin123", "15666666666"));
//    }
//
//    @ApiOperation("获取用户列表")
//    @GetMapping("/list")
//    public R<List<UserEntity>> userList() {
//        List<UserEntity> userList = new ArrayList<UserEntity>(users.values());
//        return R.ok(userList);
//    }
//
//    @ApiOperation("获取用户详细")
//    @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
//    @GetMapping("/{userId}")
//    public R<UserEntity> getUser(@PathVariable Integer userId) {
//        if (!users.isEmpty() && users.containsKey(userId)) {
//            return R.ok(users.get(userId));
//        } else {
//            return R.fail("用户不存在");
//        }
//    }
//
//    @ApiOperation("新增用户")
//    @ApiImplicitParams({@ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class), @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class), @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class), @ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class)})
//    @PostMapping("/save")
//    public R<String> save(UserEntity user) {
//        if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) {
//            return R.fail("用户ID不能为空");
//        }
//        users.put(user.getUserId(), user);
//        return R.ok();
//    }
//
//    @ApiOperation("更新用户")
//    @PutMapping("/update")
//    public R<String> update(@RequestBody UserEntity user) {
//        if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) {
//            return R.fail("用户ID不能为空");
//        }
//        if (users.isEmpty() || !users.containsKey(user.getUserId())) {
//            return R.fail("用户不存在");
//        }
//        users.remove(user.getUserId());
//        users.put(user.getUserId(), user);
//        return R.ok();
//    }
//
//    @ApiOperation("删除用户信息")
//    @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
//    @GetMapping("/{userId}")
//    public R<String> delete(@PathVariable Integer userId) {
//        if (!users.isEmpty() && users.containsKey(userId)) {
//            users.remove(userId);
//            return R.ok();
//        } else {
//            return R.fail("用户不存在");
//        }
//    }
//}
//
//@ApiModel(value = "UserEntity", description = "用户实体")
//class UserEntity {
//    @ApiModelProperty("用户ID")
//    private Integer userId;
//
//    @ApiModelProperty("用户名称")
//    private String username;
//
//    @ApiModelProperty("用户密码")
//    private String password;
//
//    @ApiModelProperty("用户手机")
//    private String mobile;
//
//    public UserEntity() {
//
//    }
//
//    public UserEntity(Integer userId, String username, String password, String mobile) {
//        this.userId = userId;
//        this.username = username;
//        this.password = password;
//        this.mobile = mobile;
//    }
//
//    public Integer getUserId() {
//        return userId;
//    }
//
//    public void setUserId(Integer userId) {
//        this.userId = userId;
//    }
//
//    public String getUsername() {
//        return username;
//    }
//
//    public void setUsername(String username) {
//        this.username = username;
//    }
//
//    public String getPassword() {
//        return password;
//    }
//
//    public void setPassword(String password) {
//        this.password = password;
//    }
//
//    public String getMobile() {
//        return mobile;
//    }
//
//    public void setMobile(String mobile) {
//        this.mobile = mobile;
//    }
//}
ruoyi-admin/src/main/resources/application-xh.yml
@@ -192,7 +192,7 @@
#本 åœ° çŸ­ä¿¡è¯·æ±‚地址(这个服务是我们自己写的)
#xhsmsPath: http://192.168.2.13:8092/sendSms
#新华短信请求地址(这个服务是我们自己写的)
xhsmsPath: http://192.16.4.220:8092/sendSms
xhsmsPath: http://192.16.4.220:8092/sms
#帐号
xhsmsAccount: 911124
#接口密码
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java
@@ -44,4 +44,9 @@
     */
    private String deptId;
    /**
     * ç”¨æˆ·ç±»åž‹
     */
    private String userType;
}
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -108,7 +108,7 @@
                // è¿‡æ»¤è¯·æ±‚
                .authorizeRequests()
                // å¯¹äºŽç™»å½•login æ³¨å†Œregister éªŒè¯ç captchaImage å…è®¸åŒ¿åè®¿é—®
                .antMatchers("/login","/test/**", "/register", "/captchaImage", "/qrcode/generateStaticHtml", "/qrcode/getQRcode", "/qrcode/getFormDate", "/chat", "/system/file/admin/uploadFile", "/smartor/dingtalk/sendNotification", "/patient/read/patientInfo", "/socket", "/API_ESB_Service", "/API_ESB_Service/Run", "/magic/web/**", "/smartor/serviceSubtask/phoneCallBack", "/smartor/serviceSubtask/taskPull", "/smartor/serviceSubtask/phoneCallBackYQ", "/smartor/robot/callstatus", "/smartor/robot/aidialog", "/smartor/robot/cdrinfo", "/getToken", "/smartor/subtaskAnswer/getQuestionCache", "/smartor/subtaskAnswer/saveQuestionCache", "/smartor/servicetask/getScriptInfoByCondition", "/smartor/subtaskAnswer/saveQuestionAnswer", "/smartor/import/download", "/smartor/serviceSubtask/recordAccept", "/smartor/outPath/getInfoByParam", "/smartor/serviceExternal/addDeptInfo", "/smartor/serviceExternal/**", "/sso/**","/smartor/sltdHealthcareRecord/**","/smartor/servicetask/getScriptByCondition","/smartor/subtaskAnswer/saveMYDQuestionAnswer","smartor/serviceSubtask/goSfRelay","/smartor/serviceSubtask/scanGenerateSubtask").permitAll()
                .antMatchers("/login","/test/**", "/register", "/captchaImage", "/qrcode/generateStaticHtml", "/qrcode/getQRcode", "/qrcode/getFormDate", "/chat", "/system/file/admin/uploadFile", "/smartor/dingtalk/sendNotification", "/patient/read/patientInfo", "/socket", "/API_ESB_Service", "/API_ESB_Service/Run", "/magic/web/**", "/smartor/serviceSubtask/phoneCallBack", "/smartor/serviceSubtask/taskPull", "/smartor/serviceSubtask/phoneCallBackYQ", "/smartor/robot/callstatus", "/smartor/robot/aidialog", "/smartor/robot/cdrinfo", "/getToken", "/smartor/subtaskAnswer/getQuestionCache", "/smartor/subtaskAnswer/saveQuestionCache", "/smartor/servicetask/getScriptInfoByCondition", "/smartor/subtaskAnswer/saveQuestionAnswer", "/smartor/import/download", "/smartor/serviceSubtask/recordAccept", "/smartor/outPath/getInfoByParam", "/smartor/serviceExternal/addDeptInfo", "/smartor/serviceExternal/**", "/sso/**","/smartor/sltdHealthcareRecord/**","/smartor/servicetask/getScriptByCondition","/smartor/subtaskAnswer/saveMYDQuestionAnswer","smartor/serviceSubtask/goSfRelay","/smartor/serviceSubtask/scanGenerateSubtask","/smartor/shphone/getFirstQues","/smartor/shphone/processResust","/smartor/shphone/addOutboundInstance").permitAll()
                // é™æ€èµ„源,可匿名访问
//                .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**","/getDept/*","/SSOLogin/**").permitAll().antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
                .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**","/getDept/*","/SSOLogin/**","/system/dict/data/typeout/*").permitAll().antMatchers( "/druid/**").permitAll()
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java
@@ -1,6 +1,7 @@
package com.ruoyi.framework.config;
import com.ruoyi.common.utils.Threads;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -15,6 +16,7 @@
 *
 * @author ruoyi
 **/
@Slf4j
@Configuration
public class ThreadPoolConfig
{
@@ -34,12 +36,22 @@
    public ThreadPoolTaskExecutor threadPoolTaskExecutor()
    {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setMaxPoolSize(maxPoolSize);
        // æ ¸å¿ƒçº¿ç¨‹æ± å¤§å°
        executor.setCorePoolSize(corePoolSize);
        // æœ€å¤§å¯åˆ›å»ºçš„线程数
        executor.setMaxPoolSize(maxPoolSize);
        // é˜Ÿåˆ—最大长度
        executor.setQueueCapacity(queueCapacity);
        // çº¿ç¨‹æ± ç»´æŠ¤çº¿ç¨‹æ‰€å…è®¸çš„空闲时间
        executor.setKeepAliveSeconds(keepAliveSeconds);
        // çº¿ç¨‹æ± å¯¹æ‹’绝任务(无线程可用)的处理策略
        // çº¿ç¨‹åç§°å‰ç¼€
        executor.setThreadNamePrefix("async-task-executor-");
        // è®¾ç½®çº¿ç¨‹å·¥åŽ‚ (用于给线程命名,便于排查问题)
        executor.setThreadFactory(new BasicThreadFactory.Builder().namingPattern("async-task-%d").daemon(true).build());
        // çº¿ç¨‹æ± å¯¹æ‹’绝任务 (无线程可用) çš„处理策略 - è°ƒç”¨è€…运行策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // åˆå§‹åŒ–
        executor.initialize();
        return executor;
    }
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java
@@ -81,7 +81,7 @@
     * @param uuid     å”¯ä¸€æ ‡è¯†
     * @return ç»“æžœ
     */
    public String login(String username, String password, String code, String uuid, String orgid, String campusid) {
    public String login(String username, String password, String code, String uuid, String orgid, String campusid, String userType) {
        boolean captchaEnabled = configService.selectCaptchaEnabled();
        // éªŒè¯ç å¼€å…³
        if (captchaEnabled) {
@@ -91,7 +91,7 @@
        Authentication authentication = null;
        try {
            if (StringUtils.isEmpty(campusid)) campusid = "1";
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username + "&" + orgid + "&" + campusid, password);
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username + "&" + orgid + "&" + campusid + "&" + userType, password);
            AuthenticationContextHolder.setContext(authenticationToken);
            // è¯¥æ–¹æ³•会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
@@ -395,7 +395,7 @@
                    iIcd10Service.queryAddIcd10();
                }
                // nhfy/sltd回溯30天分段采集判断(每段最多7天)
                // nhfy/sltd回溯30天分段采集判断(每段最多4天)
                boolean isBackfill = (active.trim().equals("nhfy") || active.trim().equals("sltd")) && StringUtils.isNotBlank(todayZeroStr);
                if (isBackfill) {
                    DateTimeFormatter dateFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@@ -403,7 +403,7 @@
                    LocalDate segEndDate = todayZero.toLocalDate();
                    int segCount = 0;
                    while (!segStart.isAfter(segEndDate)) {
                        LocalDate segEnd = segStart.plusDays(6);
                        LocalDate segEnd = segStart.plusDays(3);
                        if (segEnd.isAfter(segEndDate)) {
                            segEnd = segEndDate;
                        }
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -4,6 +4,7 @@
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.*;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DtoConversionUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
@@ -133,6 +134,7 @@
        SysUserDept sysUserDept = new SysUserDept();
        sysUserDept.setUserId(sysUser.getUserId());
        sysUserDept.setDeptType(sysUser.getSearchscope());
        sysUserDept.setOrgid(sysUser.getOrgid());
        List<SysUserDept> sysUserDeptKSs = null;
        List<SysUserDept> sysUserDeptBQs = null;
        if (StringUtils.isNotEmpty(sysUser.getSearchscope()) && sysUser.getSearchscope().equals("0")) {
@@ -181,14 +183,16 @@
        String[] split = userName.split("&");
        SysUser sysUser = userMapper.selectUserByUserNameByCondition(split[0], split[1], null);
        if (Objects.isNull(sysUser)) return sysUser;
        //这里只卡用户类型为“患者”的
        if (sysUser.getUserType().equals("患者") && StringUtils.isNotEmpty(split[3]) && !split[3].equals("null") && !sysUser.getUserType().equals(split[3].trim()))
            throw new BaseException("当前用户类型与登录用户类型不一致");
        boolean isAdmin = StringUtils.isNotEmpty(split[0]) && "admin".equals(split[0]);
        IUserExtInfoService userExtInfoService = SpringUtils.getBean(IUserExtInfoService.class);
        Executor executor = SpringUtils.getBean("threadPoolTaskExecutor");
        // === å¹¶è¡ŒæŸ¥è¯¢: ç”¨æˆ·æœºæž„信息 + éƒ¨é—¨åˆ—表 ===
        CompletableFuture<Map<String, String>> orgInfoFuture = CompletableFuture.supplyAsync(() ->
                userExtInfoService.getUserOrgInfo(sysUser.getUserId().toString(), split[1], split[2]), executor);
        CompletableFuture<Map<String, String>> orgInfoFuture = CompletableFuture.supplyAsync(() -> userExtInfoService.getUserOrgInfo(sysUser.getUserId().toString(), split[1], split[2]), executor);
        CompletableFuture<List<SysDept>> deptsFuture;
        if (isAdmin) {
@@ -340,10 +344,7 @@
        //获取当前登陆人的机构和院�?
        IUserExtInfoService userExtInfoService2 = SpringUtils.getBean(IUserExtInfoService.class);
        Map<String, String> userOrgInfo2 = userExtInfoService2.getUserOrgInfo(
                sysUser.getUserId().toString(),
                split[1],
                split[3]);
        Map<String, String> userOrgInfo2 = userExtInfoService2.getUserOrgInfo(sysUser.getUserId().toString(), split[1], split[3]);
        if (!userOrgInfo2.isEmpty()) {
            sysUser.setOrgid(userOrgInfo2.get("orgid"));
            sysUser.setCampusid(userOrgInfo2.get("campusid"));
ruoyi-system/src/main/resources/mapper/system/SmsTempletMapper.xml
@@ -67,6 +67,7 @@
        <if test="wardCode != null  and wardCode != ''">and ward_code = #{wardCode}</if>
        <if test="userName != null  and userName != ''">and user_name = #{userName}</if>
        <if test="nickName != null  and nickName != ''">and nick_name = #{nickName}</if>
        <if test="pageSize != null  and pageNum != null">limit ${pageSize} OFFSET ${pageNum}</if>
    </select>
    <select id="selectSmsTempletByTempletid" parameterType="Long" resultMap="SmsTempletResult">
smartor/src/main/java/com/smartor/domain/DTO/QuestionResultDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package com.smartor.domain.DTO;
import com.ruoyi.common.core.domain.BaseEntity;
import com.smartor.domain.ServiceSubtaskDetailRatioExport;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
 * é—®å·ç»“æžœ
 *
 * @author ruoyi
 * @date 2024-02-02
 */
@ApiModel(value = "QuestionResultDTO", description = "问卷结果")
@Data
public class QuestionResultDTO extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * é—®å·é¢˜ç›®
     */
    @ApiModelProperty(value = "问卷题目")
    private String qeustionText;
    /**
     * é—®å·ç»“果集合
     */
    @ApiModelProperty(value = "问卷结果集合")
    private List<ServiceSubtaskDetailRatioExport> subtaskDetailRatioExportList;
}
smartor/src/main/java/com/smartor/domain/IvrLibaTemplateScript.java
@@ -372,6 +372,20 @@
    @ApiModelProperty(value = "报备科室责任人编码(如果多个科室用逗号分割)")
    private String reportDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人编码 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人编码(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人编码(如果多个科室用逗号分割)")
    private String circulationDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人名称(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人名称(如果多个科室用逗号分割)")
    private String circulationDeptPersonName;
//    /**
//     * é—®é¢˜ç»“果(给“值类型”是文本或者数值用的)
//     */
smartor/src/main/java/com/smartor/domain/IvrTaskTemplateScript.java
@@ -359,6 +359,23 @@
    @ApiModelProperty(value = "报备科室责任人编码(如果多个科室用逗号分割)")
    private String reportDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人编码 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人编码(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人编码(如果多个科室用逗号分割)")
    private String circulationDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人名称(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人名称(如果多个科室用逗号分割)")
    private String circulationDeptPersonName;
    /**
     * é—®é¢˜é€‰é¡¹
     */
smartor/src/main/java/com/smartor/domain/IvrTaskTemplateScriptVO.java
@@ -392,6 +392,20 @@
    private String reportDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人编码 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人编码(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人编码(如果多个科室用逗号分割)")
    private String circulationDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人名称(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人名称(如果多个科室用逗号分割)")
    private String circulationDeptPersonName;
    /**
     * è¯æœ¯é€‰é¡¹
     */
    @ApiModelProperty(value = "话术选项")
smartor/src/main/java/com/smartor/domain/PatMedInhosp.java
@@ -331,7 +331,7 @@
     */
    @ApiModelProperty(value = "入院病区长期任务,是否处理:0 æœªå¤„理   1 å¤„理成功    2 å¤„理失败 ")
    private String inWardcheckFlag;
    /**
     * è¯å“å®£æ•™é•¿æœŸä»»åŠ¡ï¼Œæ˜¯å¦å¤„ç†ï¼š0 æœªå¤„理   1 å¤„理
     */
@@ -396,7 +396,7 @@
    /**
     * å‡ºå…¥é™¢æ ‡è¯†
     */
    @ApiModelProperty("出入院标识:0入院  1出院 2 è½¬ç§‘  3 é¢„入院")
    @ApiModelProperty("出入院标识:0入院  1出院 2 è½¬ç§‘  3 é¢„入院  4.满意度")
    private Integer cry;
    /**
smartor/src/main/java/com/smartor/domain/PhoneCallBackSHVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
package com.smartor.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * ç”µè¯é—®é¢˜å“åº”(三汇)
 *
 * @author ls
 * 26
 */
@Data
public class PhoneCallBackSHVO {
    /**
     * å­ä»»åŠ¡ID
     */
    @ApiModelProperty(value = "子任务ID")
    private Long subId;
    /**
     * é—®é¢˜ID
     */
    @ApiModelProperty(value = "问题ID")
    private Long scriptId;
    /**
     * ä»»åŠ¡æ¨¡æ¿ID
     */
    @ApiModelProperty(value = "任务模板ID")
    private Long taskTemplateid;
    /**
     * å¯ä»¥å¯¹å½“前通道的音量进行适当调整,取值范围【-4,4】
     */
    @ApiModelProperty(value = "序号")
    private Integer sort;
    @ApiModelProperty(value = "问题内容")
    private String scriptContent;
    @ApiModelProperty(value = "选项")
    private String option;
    @ApiModelProperty(value = "流程状态:0结束   1进行中")
    private String status = "1";
}
smartor/src/main/java/com/smartor/domain/PhoneCallResultYQVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,74 @@
package com.smartor.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * ç”µè¯ASR回调请求(雨绮)
 *
 * @author ls
 * @date 2024-08-8
 */
@Data
public class PhoneCallResultYQVO {
    @ApiModelProperty(value = "任务编号(这里存在的就是subtaskID)")
    private String taskid;
    /**
     * å½“前呼叫的目标号码
     */
    @ApiModelProperty(value = "当前呼叫的目标号码")
    private String phone;
    /**
     * å½“前回调的请求类型,包括:AsrCallback: ASR文本回调    DtmfCallback:收到按键回调       SilentCallback:静默回调      PlayEvent:音乐播放开始、结束事件回调
     */
    @ApiModelProperty(value = "当前回调的请求类型,包括:AsrCallback: ASR文本回调    DtmfCallback:收到按键回调       SilentCallback:静默回调      PlayEvent:音乐播放开始、结束事件回调")
    private String operate;
    /**
     * asr回答的内容
     */
    @ApiModelProperty(value = "asr回答的内容")
    private String asrResult;
    /**
     * å­ä»»åŠ¡ID
     */
    @ApiModelProperty(value = "子任务ID")
    private Long subId;
    /**
     * é—®é¢˜ID
     */
    @ApiModelProperty(value = "问题ID")
    private Long scriptId;
    /**
     * ä»»åŠ¡æ¨¡æ¿ID
     */
    @ApiModelProperty(value = "任务模板ID")
    private Long taskTemplateid;
    /**
     * å¯ä»¥å¯¹å½“前通道的音量进行适当调整,取值范围【-4,4】
     */
    @ApiModelProperty(value = "序号")
    private Integer sort;
    @ApiModelProperty(value = "通过库匹配的结果")
    private String scriptContent;
    @ApiModelProperty(value = "机构ID")
    private String orgid;
    @ApiModelProperty(value = "uuid")
    private String uuid;
    @ApiModelProperty(value = "guid")
    private String guid;
}
smartor/src/main/java/com/smartor/domain/ServiceOutPath.java
@@ -85,5 +85,21 @@
    @ApiModelProperty(value = "病人信息")
    private PatArchive patArchive;
    /**
     * æœªåŠ å¯†çš„ä»»åŠ¡ID
     */
    @ApiModelProperty("未加密的任务ID")
    @Excel(name = "未加密的任务ID")
    private Long taskId;
    /**
     * æœªåŠ å¯†çš„å­ä»»åŠ¡ID
     */
    @ApiModelProperty("未加密的子任务ID")
    @Excel(name = "未加密的子任务ID")
    private Long subId;
}
smartor/src/main/java/com/smartor/domain/ServiceSubtask.java
@@ -1,15 +1,13 @@
package com.smartor.domain;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import java.util.Date;
import java.util.List;
/**
 * å•一任务(随访)对象 ivr_tasksingle
@@ -595,7 +593,7 @@
    /**
     * æ‚£è€…来源
     */
    @ApiModelProperty(value = "患者来源:0本地患者表    1外部患者表")
    @ApiModelProperty(value = "任务病人的来源:0:医院患者;1:外部患者  2:导入患者")
    private String patfrom;
    /**
smartor/src/main/java/com/smartor/domain/ServiceSubtaskDetailRatioExport.java
@@ -35,6 +35,15 @@
    @Excel(name = "比例")
    private String ratio;
    @Excel(name = "分数")
    private String score;
    @ApiModelProperty(value = "问题类型(1.单选 2多选 3问答 4填空  5其它)")
    private String valueType;
    @Excel(name = "选项")
    private String targetvalue;
    @Excel(name = "任务名称")
    private String taskname;
smartor/src/main/java/com/smartor/domain/ServiceSubtaskDetailTrace.java
@@ -370,13 +370,68 @@
    private String dutyDeptPersonCode;
    @Excel(name = "报备科室责任人名称(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "报备科室名称(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "报备科室责任人名称(如果多个科室用逗号分割)")
    private String reportDeptPersonName;
    @Excel(name = "报备科室编码(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "报备科室责任人编码(如果多个科室用逗号分割)")
    private String reportDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人编码 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人编码(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人编码(如果多个科室用逗号分割)")
    private String circulationDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人名称(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人名称(如果多个科室用逗号分割)")
    private String circulationDeptPersonName;
    /**
     * æµè½¬ç§‘室责任人编码 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室编码(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室编码(如果多个科室用逗号分割)")
    private String circulationDeptCode;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室名称(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室名称(如果多个科室用逗号分割)")
    private String circulationDeptName;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转说明")
    @ApiModelProperty(value = "流转说明")
    private String circulationExplain;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转时间")
    @ApiModelProperty(value = "流转时间")
    private Date circulationTime;
    /**
     * æµè½¬çŠ¶æ€ï¼š0不流转(默认) 1流转
     */
    @Excel(name = "流转状态:0不流转(默认) 1流转")
    @ApiModelProperty(value = "流转状态:0不流转(默认) 1流转")
    private String circulationStatus;
    @Excel(name = "流转人姓名")
    @ApiModelProperty(value = "流转人姓名")
    private String circulationPersonName;
    @Excel(name = "流转人编码")
    @ApiModelProperty(value = "流转人编码")
    private String circulationPersonCode;
}
smartor/src/main/java/com/smartor/domain/ServiceSubtaskExport.java
@@ -19,6 +19,14 @@
@Data
public class ServiceSubtaskExport extends BaseEntity {
    private static final long serialVersionUID = 1L;
//
//    /**
//     * ç§‘室名称
//     */
//    @Excel(name = "主键")
//    @ApiModelProperty(value = "主键")
//    private Long id;
    /**
     * ç§‘室名称
smartor/src/main/java/com/smartor/domain/SvyLibTemplateScript.java
@@ -315,6 +315,20 @@
    private String reportDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人编码 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人编码(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人编码(如果多个科室用逗号分割)")
    private String circulationDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人名称(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人名称(如果多个科室用逗号分割)")
    private String circulationDeptPersonName;
    /**
     * é—®åˆ¸é¢˜ç›®åˆ†ç±»ID集合
     */
    @ApiModelProperty("满意度意见异常(只针对文本)")
smartor/src/main/java/com/smartor/domain/SvyLibTemplateScriptVO.java
@@ -331,6 +331,22 @@
    @ApiModelProperty(value = "报备科室责任人编码(如果多个科室用逗号分割)")
    private String reportDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人编码 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人编码(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人编码(如果多个科室用逗号分割)")
    private String circulationDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人名称(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人名称(如果多个科室用逗号分割)")
    private String circulationDeptPersonName;
    @Excel(name = "分类ID集合")
    @ApiModelProperty(value = "分类ID集合")
    private List<Long> categoryids;
smartor/src/main/java/com/smartor/domain/SvyTaskTemplateScript.java
@@ -279,6 +279,19 @@
    private String reportDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人编码 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人编码(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人编码(如果多个科室用逗号分割)")
    private String circulationDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人名称(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人名称(如果多个科室用逗号分割)")
    private String circulationDeptPersonName;
    /**
     * é—®åˆ¸é¢˜ç›®åˆ†ç±»ID集合
     */
    @ApiModelProperty("满意度意见异常(只针对文本)")
smartor/src/main/java/com/smartor/domain/SvyTaskTemplateScriptVO.java
@@ -359,11 +359,25 @@
    @ApiModelProperty(value = "报备科室名称(如果多个科室用逗号分割)")
    private String reportDeptPersonName;
    @Excel(name = "报备科室编码(如果多个科室用逗号分割)")
    @Excel(name = "报备科室责任人编码(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "报备科室责任人编码(如果多个科室用逗号分割)")
    private String reportDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人编码 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人编码(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人编码(如果多个科室用逗号分割)")
    private String circulationDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人名称(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人名称(如果多个科室用逗号分割)")
    private String circulationDeptPersonName;
    /**
     * é—®åˆ¸é¢˜ç›®åˆ†ç±»ID集合
     */
    @ApiModelProperty("满意度意见异常(只针对文本)")
smartor/src/main/java/com/smartor/domain/VO/DetailTraceDealVO.java
@@ -99,5 +99,61 @@
    @Excel(name = "报备科室编码(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "报备科室责任人编码(如果多个科室用逗号分割)")
    private String reportDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人编码 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人编码(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人编码(如果多个科室用逗号分割)")
    private String circulationDeptPersonCode;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室责任人名称(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室责任人名称(如果多个科室用逗号分割)")
    private String circulationDeptPersonName;
    /**
     * æµè½¬ç§‘室责任人编码 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室编码(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室编码(如果多个科室用逗号分割)")
    private String circulationDeptCode;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转科室名称(如果多个科室用逗号分割)")
    @ApiModelProperty(value = "流转科室名称(如果多个科室用逗号分割)")
    private String circulationDeptName;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转说明")
    @ApiModelProperty(value = "流转说明")
    private String circulationExplain;
    /**
     * æµè½¬ç§‘室责任人名称 (如果多个科室用逗号分割)
     */
    @Excel(name = "流转时间")
    @ApiModelProperty(value = "流转时间")
    private Date circulationTime;
    /**
     * æµè½¬çŠ¶æ€ï¼š0不流转(默认) 1流转
     */
    @Excel(name = "流转状态:0不流转(默认) 1流转")
    @ApiModelProperty(value = "流转状态:0不流转(默认) 1流转")
    private Integer circulationStatus;
    @Excel(name = "流转人姓名")
    @ApiModelProperty(value = "流转人姓名")
    private String circulationPersonName;
    @Excel(name = "流转人编码")
    @ApiModelProperty(value = "流转人编码")
    private String circulationPersonCode;
}
smartor/src/main/java/com/smartor/domain/VO/QuestionResultVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,96 @@
package com.smartor.domain.VO;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import java.util.List;
/**
 * é—®é¢˜ç»“æžœDTO
 *
 * @author ruoyi
 * @date 2024-02-02
 */
@ApiModel(value = "QuestionResultVO", description = "问题结果VO")
@Data
public class QuestionResultVO extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * ç§‘室与病区的查询是or还是and:   1:and(默认)(离院病区与离院科室组合)     2:or(离院病区与离院科室组合)    3:and(离院病区与随访科室组合)   4:or(离院病区与随访科室组合)
     */
    @ApiModelProperty(value = "科室与病区的查询是or还是and:   1:and(默认)(离院病区与离院科室组合)     2:or(离院病区与离院科室组合)    3:and(离院病区与随访科室组合)   4:or(离院病区与随访科室组合)")
    private String deptOrDistrict = "1";
    /**
     *
     */
    @ApiModelProperty(value = "主键id")
    private Long subId;
    /**
     * å‡ºé™¢ç—…区编号
     */
    @ApiModelProperty(value = "出院病区集合(必填)")
    private List<String> leavehospitaldistrictcodes;
    /**
     * ä¸»é”®id集合
     */
    @ApiModelProperty(value = "主键id集合")
    private List<Long> subIdList;
    /**
     * å¼€å§‹å‡ºé™¢æ—¥æœŸ
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @ApiModelProperty(value = "开始出院日期")
    private Date startOutHospTime;
    /**
     * ç»“束出院日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @ApiModelProperty(value = "结束出院日期")
    private Date endOutHospTime;
    /**
     * é—®é¢˜å†…容
     */
    @ApiModelProperty(value = "问题内容")
    private String questionContent;
    /**
     * åˆ†æ•°æŸ¥è¯¢å¼€å§‹
     */
    @ApiModelProperty(value = "分数查询开始")
    private Double startScore;
    /**
     * åˆ†æ•°æŸ¥è¯¢ç»“束
     */
    @ApiModelProperty(value = "分数查询结束")
    private Double endScore;
    /**
     * æœåŠ¡ç±»åž‹(1、监测评估;2、出院随访;3、门诊随访;4、宣教关怀;5、复诊通知;6、满意度调查;7、患者报告; 8、其他通知)
     */
    @Excel(name = "服务类型(1、监测评估;2、出院随访;3、门诊随访;4、宣教关怀;5、复诊管理;6、满意度调查;7、患者报告; 8、其他通知 9体检随访 10.医技随访  11,影像专科随访  12、心电专科随访, 13专科随访)")
    @ApiModelProperty(value = "服务类型(1、监测评估;2、出院随访;3、门诊随访;4、宣教关怀;5、复诊管理;6、满意度调查;7、患者报告; 8、其他通知 9体检随访 10.医技随访  11,影像专科随访  12、心电专科随访, 13专科随访)")
    private String serviceType;
    @ApiModelProperty(value = "问题类型(1.单选 2多选 3问答 4填空  5其它)")
    private String valueType;
}
smartor/src/main/java/com/smartor/domain/VO/ServiceSubtaskVO.java
@@ -94,8 +94,8 @@
    /**
     * æœåŠ¡ç±»åž‹(1、监测评估;2、出院随访;3、门诊随访;4、宣教关怀;5、复诊通知;6、满意度调查;7、患者报告; 8、其他通知)
     */
    @Excel(name = "服务类型(1、监测评估;2、出院随访;3、门诊随访;4、宣教关怀;5、复诊管理;6、满意度调查;7、患者报告; 8、其他通知 9体检随访 10.医技随访  11,影像专科随访  12、心电专科随访, 13专科随访)")
    @ApiModelProperty(value = "服务类型(1、监测评估;2、出院随访;3、门诊随访;4、宣教关怀;5、复诊管理;6、满意度调查;7、患者报告; 8、其他通知 9体检随访 10.医技随访  11,影像专科随访  12、心电专科随访, 13专科随访)")
    @Excel(name = "服务类型(服务类型(1、监测评估;2、出院随访;3、门诊随访;4、宣教关怀(出院)  5、复诊管理;6、住院满意度调查;7、患者报告; 8、其他通知 9体检随访 10.医技随访  11,影像专科随访  12、心电专科随访, 13专科随访   14、门诊满意度调查  15投诉建议 16课题随访  17、宣教关怀(入院)  18、入院随访)")
    @ApiModelProperty(value = "服务类型(服务类型(1、监测评估;2、出院随访;3、门诊随访;4、宣教关怀(出院)  5、复诊管理;6、住院满意度调查;7、患者报告; 8、其他通知 9体检随访 10.医技随访  11,影像专科随访  12、心电专科随访, 13专科随访   14、门诊满意度调查  15投诉建议 16课题随访  17、宣教关怀(入院)  18、入院随访  19手术随访  20 æ‚£è€…异常报告表)")
    private String serviceType;
    /**
@@ -292,6 +292,20 @@
    @ApiModelProperty(value = "完成时间")
    private Date finishtime;
    /**
     * å®Œæˆæ—¶é—´
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @ApiModelProperty(value = "完成时间(开始查询)")
    private Date finishtimeStart;
    /**
     * å®Œæˆæ—¶é—´(结束查询)
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @ApiModelProperty(value = "完成时间(结束查询)")
    private Date finishtimeEnd;
    /**
     * å°±è¯Šè®°å½•编号
smartor/src/main/java/com/smartor/domain/entity/ServiceSubtaskEntity.java
@@ -307,6 +307,19 @@
    @ApiModelProperty(value = "完成时间")
    private Date finishtime;
    /**
     * å®Œæˆæ—¶é—´
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @ApiModelProperty(value = "完成时间(开始查询)")
    private Date finishtimeStart;
    /**
     * å®Œæˆæ—¶é—´(结束查询)
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @ApiModelProperty(value = "完成时间(结束查询)")
    private Date finishtimeEnd;
    /**
     * å°±è¯Šè®°å½•编号
smartor/src/main/java/com/smartor/mapper/ServiceSubtaskMapper.java
@@ -78,14 +78,12 @@
     * @param scheduleId è®¡åˆ’主键
     * @param loopCount  è½®æ¬¡ï¼ˆ0=首轮)
     */
    public int countActiveByScheduleIdAndLoop(@Param("scheduleId") Long scheduleId,
                                              @Param("loopCount") Long loopCount);
    public int countActiveByScheduleIdAndLoop(@Param("scheduleId") Long scheduleId, @Param("loopCount") Long loopCount);
    /**
     * ä»»åŠ¡ç»„ï¼ˆappltype=5):拉取某一轮次的全部 subtask(用于克隆到下一轮)。
     */
    public List<ServiceSubtask> selectByScheduleIdAndLoop(@Param("scheduleId") Long scheduleId,
                                                          @Param("loopCount") Long loopCount);
    public List<ServiceSubtask> selectByScheduleIdAndLoop(@Param("scheduleId") Long scheduleId, @Param("loopCount") Long loopCount);
    /**
     * éšè®¿åŠæ—¶çŽ‡ï¼šåº”éšè®¿æ—¶é—´<实际随访时间
@@ -259,9 +257,21 @@
    /**
     * ç»Ÿè®¡é—®é¢˜çš„选项的百分比
     *
     * @param taskIds
     * @param taskIds          ä»»åŠ¡ ID åˆ—表 (用于服务类型 2)
     * @param startOutHospTime
     * @param endOutHospTime
     * @return
     */
    List<ServiceSubtaskDetailRatioExport> statQuestionOption(@Param("taskIds") List<Long> taskIds, @Param("startOutHospTime") Date startOutHospTime, @Param("endOutHospTime") Date endOutHospTime);
    List<ServiceSubtaskDetailRatioExport> statQuestionOption(@Param("taskIds") List<Long> taskIds, @Param("startOutHospTime") Date startOutHospTime, @Param("endOutHospTime") Date endOutHospTime, @Param("scoreStart") Double scoreStart, @Param("scoreEnd") Double scoreEnd, @Param("questiontext") String questiontext, @Param("valueType") String valueType);
    /**
     * æ ¹æ®å­ä»»åŠ¡ ID ç»Ÿè®¡é—®é¢˜çš„选项的百分比(用于满意度统计 serviceType=6)
     *
     * @param subtaskIds       å­ä»»åŠ¡ ID åˆ—表 (只能单个 taskid ä¸‹çš„æ‰€æœ‰å­ä»»åŠ¡ id)
     * @param startOutHospTime
     * @param endOutHospTime
     * @return
     */
    List<ServiceSubtaskDetailRatioExport> statQuestionOptionBySubtaskIds(@Param("subtaskIds") List<Long> subtaskIds, @Param("startOutHospTime") Date startOutHospTime, @Param("endOutHospTime") Date endOutHospTime, @Param("scoreStart") Double scoreStart, @Param("scoreEnd") Double scoreEnd, @Param("questiontext") String questiontext, @Param("valueType") String valueType);
}
smartor/src/main/java/com/smartor/mapper/SvyLibTemplateScriptMapper.java
@@ -1,6 +1,5 @@
package com.smartor.mapper;
import com.smartor.domain.IvrLibaTemplateScript;
import com.smartor.domain.SvyLibTemplateScript;
import com.smartor.domain.SvyLibTemplateScriptVO;
import org.apache.ibatis.annotations.Mapper;
smartor/src/main/java/com/smartor/service/IServicePhoneService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package com.smartor.service;
import com.smartor.domain.*;
import com.smartor.domain.VO.HeLibraryCountVO;
import com.smartor.domain.VO.ServiceSubtaskCotinueCountVO;
import com.smartor.domain.VO.TransferDeptVO;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
 * ä¸‰æ±‡ç”µè¯æœåŠ¡
 *
 * @author ruoyi
 * @date 2026-08-27
 */
public interface IServicePhoneService {
    OutboundInstanceResVO addOutboundInstance(OutboundConfigAddReqVO request);
}
smartor/src/main/java/com/smartor/service/IServiceSubtaskService.java
@@ -2,8 +2,10 @@
import com.ruoyi.common.core.domain.AjaxResult;
import com.smartor.domain.*;
import com.smartor.domain.DTO.QuestionResultDTO;
import com.smartor.domain.ServiceSubtask;
import com.smartor.domain.VO.HeLibraryCountVO;
import com.smartor.domain.VO.QuestionResultVO;
import com.smartor.domain.VO.ServiceSubtaskCotinueCountVO;
import com.smartor.domain.VO.TransferDeptVO;
import com.smartor.domain.entity.ServiceSubtaskEntity;
@@ -53,8 +55,11 @@
     */
    public List<ServiceSubtaskRes> patItem(ServiceSubtaskEntity serviceSubtaskEntity);
    public List<ServiceSubtask> getServiceSubtasks(ServiceSubtaskEntity serviceSubtaskEntity);
    /**
     * è½¬ç§»ç§‘室病区
     *
     * @param transferDeptVO
     * @return
     */
@@ -129,6 +134,20 @@
     */
    public PhoneCallBackYQVO phoneCallBackYQ(PhoneCallReqYQVO phoneCallReqYQVO);
    public PhoneCallBackSHVO getFirstQues(PhoneCallReqYQVO phoneCallReqYQVO);
    public PhoneCallBackSHVO processResust(PhoneCallResultYQVO phoneCallReqYQVO);
    /**
     * é—®é¢˜ç»“果占比
     *
     * @param QuestionResultVO
     * @param serviceSubtaskRes
     * @return
     */
    public List<ServiceSubtaskDetailRatioExport> getDetailRatio(QuestionResultVO questionResultVO, List<ServiceSubtaskRes> serviceSubtaskRes);
    /**
     * é›¨ç»®ä»»åŠ¡æ‹‰å–
     *
@@ -202,12 +221,20 @@
    Map<String, Object> smsSubTask(Long subid);
    /**
     * ç»Ÿè®¡é—®é¢˜çš„选项的百分比
     * ç»Ÿè®¡é—®é¢˜çš„选项的百分比 (用于服务类型 2)
     *
     * @param taskIds
     * @return
     */
    public List<ServiceSubtaskDetailRatioExport> statQuestionOption(List<Long> taskIds, Date startOutHospTime, Date endOutHospTime);
    public List<ServiceSubtaskDetailRatioExport> statQuestionOption(List<Long> taskIds, Date startOutHospTime, Date endOutHospTime, String scriptContent, Double scoreStart, Double scoreEnd, String valueType);
    /**
     * æ ¹æ®å­ä»»åŠ¡ ID ç»Ÿè®¡é—®é¢˜çš„选项的百分比(用于满意度统计 serviceType=6)
     *
     * @param subtaskIds å­ä»»åŠ¡ ID åˆ—表
     * @return
     */
    public List<ServiceSubtaskDetailRatioExport> statQuestionOptionBySubtaskIds(List<Long> subtaskIds, Date startOutHospTime, Date endOutHospTime, String scriptContent, Double scoreStart, Double scoreEnd, String valueType);
    public String scanGenerateSubtask(ServiceTask serviceTaskVo);
smartor/src/main/java/com/smartor/service/impl/PatArchiveImportServiceImpl.java
@@ -199,8 +199,11 @@
                        int start = patArchiveImport.getIdcardno().length() - 6;
                        String lastSixDigits = patArchiveImport.getIdcardno().substring(start, start + 6);
                        sysUser.setUserName(lastSixDigits);
                        //密码:xh@123456
                        sysUser.setPassword("$2a$10$zja67LEWTESHutljd8bqEeRy.VgWb./8iJPpFR6VjTUa7ZYNfRRrO");
                        //原密码:xh@123456
                        //sysUser.setPassword("$2a$10$zja67LEWTESHutljd8bqEeRy.VgWb./8iJPpFR6VjTUa7ZYNfRRrO");
                        //新密码:123456
                        sysUser.setPassword("$2a$10$1CRhRCwPEqhEI4bkXYFOLu.ga0PLfDhI3pnvVQ1GX0sbHY3N/UJky");
                        sysUser.setPhonenumber(patArchiveImport.getTelcode());
                        sysUser.setUserType("患者");
                        sysUser.setDeptId(395L);
smartor/src/main/java/com/smartor/service/impl/ServicPhoneServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,200 @@
package com.smartor.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.service.IConfigService;
import com.ruoyi.common.dx.MessageSend;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.service.ISysConfigService;
import com.smartor.common.FtpService;
import com.smartor.common.MtSubmitSmUtil;
import com.smartor.common.ScheduleSubtaskBuilder;
import com.smartor.config.PhoneUtils;
import com.smartor.domain.DTO.ServiceSubtaskDetailDTO;
import com.smartor.domain.*;
import com.smartor.domain.VO.HeLibraryCountVO;
import com.smartor.domain.VO.ServiceSubtaskCotinueCountVO;
import com.smartor.domain.VO.TransferDeptVO;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.mapper.*;
import com.smartor.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
 * å•一任务(随访)Service业务层处理
 *
 * @author ruoyi
 * @date 2024-02-02
 */
@Slf4j
@Service
public class ServicPhoneServiceImpl implements IServicePhoneService {
    @Value("${shurl}")
    private String shurl;
    @Value("${shversion}")
    private String shversion;
    /**
     * æ–°å¢žå¤–呼实例配置
     *
     * @param request å¤–呼实例配置请求对象
     * @return å¤–呼实例配置结果
     * @throws BaseException æŽ¥å£è°ƒç”¨å¤±è´¥æˆ–数据解析异常时抛出
     */
    @Override
    public OutboundInstanceResVO addOutboundInstance(OutboundConfigAddReqVO request) {
        // 1. å‚数校验
        validateRequest(request);
        // 2. å‘送 HTTP è¯·æ±‚
        String responseJson = sendHttpPostRequest(shurl + shversion + "/config/addOutboundInstance", request);
        // 3. è§£æžå“åº”并返回结果
        return parseResponse(responseJson);
    }
    /**
     * å‚数校验
     */
    private void validateRequest(OutboundConfigAddReqVO request) {
        if (request == null) {
            throw new BaseException("请求参数不能为空");
        }
        // å¿…传参数校验
        assertNotBlank(request.getName(), "项目名称不能为空");
        assertNotBlank(request.getAgent(), "坐席号不能为空");
        assertNotBlank(request.getTenantId(), "资源组号不能为空");
        assertNotBlank(request.getCallType(), "呼叫类型不能为空");
        assertNotBlank(request.getActionID(), "actionID ä¸èƒ½ä¸ºç©º");
        // æ¡ä»¶å¿…填校验:isVgc=0 æ—¶ï¼ŒqueueId å’Œ ratio ä¸ºå¿…å¡«
        if ("0".equals(request.getIsVgc())) {
            assertNotBlank(request.getQueueId(), "isVgc=0 æ—¶ï¼Œæ‰§è¡Œåå¸­ç»„ ID ä¸èƒ½ä¸ºç©º");
            assertNotBlank(request.getRatio(), "isVgc=0 æ—¶ï¼Œå¤–呼速率不能为空");
        }
        // æ¡ä»¶å¿…填校验:isVgc=1 æ—¶ï¼ŒivrId ä¸ºå¿…å¡«
        else if ("1".equals(request.getIsVgc())) {
            assertNotBlank(request.getIvrId(), "isVgc=1 æ—¶ï¼ŒIVR æµç¨‹ ID ä¸èƒ½ä¸ºç©º");
        }
    }
    /**
     * å‘送 POST HTTP è¯·æ±‚
     *
     * @param url     API åœ°å€
     * @param request è¯·æ±‚对象
     * @return æœåŠ¡å™¨å“åº” JSON å­—符串
     */
    private String sendHttpPostRequest(String url, Object request) {
        log.info("开始调用外呼实例配置接口,URL: {}", url);
        Map<String, String> headers = buildHeaders();
        String requestBody = new Gson().toJson(request);
        String response = HttpUtils.sendPostByHeader(url, requestBody, headers);
        if (StringUtils.isEmpty(response)) {
            log.error("外呼实例配置接口调用失败,未返回有效数据,URL: {}", url);
            throw new BaseException("接口调用失败,未返回有效数据");
        }
        log.info("外呼实例配置接口调用成功,响应:{}", response);
        return response;
    }
    /**
     * æž„建 HTTP è¯·æ±‚头
     */
    private Map<String, String> buildHeaders() {
        Map<String, String> headers = new HashMap<>(1);
        headers.put("Content-Type", "application/json; charset=UTF-8");
        return headers;
    }
    /**
     * è§£æž HTTP å“åº”并转换为 OutboundInstanceResVO
     *
     * @param responseJson å“åº” JSON å­—符串
     * @return å¤–呼实例配置响应对象
     * @throws BaseException å½“响应码不为 200 æˆ–响应数据解析失败时抛出
     */
    private OutboundInstanceResVO parseResponse(String responseJson) {
        try {
            JSONObject response = JSON.parseObject(responseJson);
            // æ£€æŸ¥å“åº”码
            Integer code = response.getInteger("code");
            if (!"200".equals(code.toString())) {
                String message = response.getString("message");
                log.error("外呼实例配置接口返回失败,code: {}, message: {}", code, message);
                throw new BaseException("新增外呼实例配置失败:" + message);
            }
            // è§£æž data å­—段
            JSONObject data = response.getJSONObject("data");
            if (data == null || data.isEmpty()) {
                throw new BaseException("接口返回数据为空");
            }
            return data.toJavaObject(OutboundInstanceResVO.class);
        } catch (BaseException e) {
            throw e;
        } catch (Exception e) {
            log.error("解析外呼实例配置响应异常,response: {}", responseJson, e);
            throw new BaseException("解析响应数据异常:" + e.getMessage());
        }
    }
    /**
     * æ–­è¨€å­—符串非空工具方法
     *
     * @param value   å¾…检查的值
     * @param message æ–­è¨€å¤±è´¥æ—¶çš„错误消息
     * @throws BaseException å½“值为空时抛出
     */
    private void assertNotBlank(String value, String message) {
        if (StringUtils.isBlank(value)) {
            throw new BaseException(message);
        }
    }
}
smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskAnswerServiceImpl.java
@@ -21,6 +21,7 @@
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
@@ -163,8 +164,19 @@
        return serviceSubtaskAnswerMapper.deleteServiceSubtaskAnswerById(id);
    }
    /**
     * ä¿å­˜é—®å·ç­”案 (异步处理,快速返回)
     *
     * @param serviceSubTaskAnswerReq ç­”案请求
     * @param flag æ ‡å¿—位
     * @return ç»“æžœ
     */
    @Override
    @Async("threadPoolTaskExecutor")
    public Integer saveQuestionAnswer(ServiceSubTaskAnswerReq serviceSubTaskAnswerReq, Long flag) {
        String threadName = Thread.currentThread().getName();
        log.info("[{}] å¼€å§‹å¼‚步处理保存问卷答案", threadName);
        try {
        //参数解密
        Long tid = null;
        Long pid = null;
@@ -365,16 +377,6 @@
            log.error("将该key和value从redis中,serviceSubtask的值为--------:{}", serviceSubtask);
            if (score != null && !score.isEmpty()) serviceSubtask.setScore(score);
            serviceSubtaskMapper.updateServiceSubtaskByCondition(serviceSubtask);
            //修改住院满意度状态(避免重复查询)
            if (serviceSubtask.getServiceType() != null && "6".equals(serviceSubtask.getServiceType())) {
                Long inhospid = serviceSubtask.getInhospid();
                if (inhospid != null) {
                    PatMedInhosp patMedInhosp = new PatMedInhosp();
                    patMedInhosp.setInhospMydFlag("1");
                    patMedInhosp.setInhospid(inhospid);
                    patMedInhospMapper.updatePatMedInhosp(patMedInhosp);
                }
            }
            //将当前发送状态改成 9 å®Œæˆï¼ˆç¼“å­˜ preachform æŸ¥è¯¢ï¼‰
            String currentPreachform = serviceSubtask.getCurrentPreachform();
@@ -401,9 +403,13 @@
            }
        }
        log.info("[{}] å®Œæˆå¼‚步处理保存问卷答案", Thread.currentThread().getName());
        return 1;
    } catch (Exception e) {
        log.error("[{}] å¼‚步处理保存问卷答案发生异常 : {},请求的对象为:{}", Thread.currentThread().getName(), e.getMessage(), e,serviceSubTaskAnswerReq);
        throw new RuntimeException("保存问卷答案失败", e);
    }
}
    @Override
    public Integer saveQuestionDetail(ServiceSubTaskCacheReq serviceSubTaskCacheReq, Long flag) {
        Long tid = Long.valueOf(serviceSubTaskCacheReq.getParam1());
@@ -1107,6 +1113,8 @@
            subtaskDetailTrace.setDutyDeptPersonName(svyTaskTemplateScriptVO.getDutyDeptPersonName());
            subtaskDetailTrace.setReportDeptPersonCode(svyTaskTemplateScriptVO.getReportDeptPersonCode());
            subtaskDetailTrace.setReportDeptPersonName(svyTaskTemplateScriptVO.getReportDeptPersonName());
            subtaskDetailTrace.setCirculationDeptPersonCode(svyTaskTemplateScriptVO.getCirculationDeptPersonCode());
            subtaskDetailTrace.setCirculationDeptPersonName(svyTaskTemplateScriptVO.getCirculationDeptPersonName());
            Long existTraceId = existingTraceIdMap.get(subtaskDetailTrace.getScriptid());
            if (existTraceId != null) {
smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java
@@ -8,6 +8,7 @@
import com.google.gson.Gson;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.service.IConfigService;
import com.ruoyi.common.dx.MessageSend;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.*;
@@ -21,9 +22,11 @@
import com.smartor.common.MtSubmitSmUtil;
import com.smartor.common.ScheduleSubtaskBuilder;
import com.smartor.config.PhoneUtils;
import com.smartor.domain.DTO.QuestionResultDTO;
import com.smartor.domain.DTO.ServiceSubtaskDetailDTO;
import com.smartor.domain.*;
import com.smartor.domain.VO.HeLibraryCountVO;
import com.smartor.domain.VO.QuestionResultVO;
import com.smartor.domain.VO.ServiceSubtaskCotinueCountVO;
import com.smartor.domain.VO.TransferDeptVO;
import com.smartor.domain.entity.ServiceSubtaskEntity;
@@ -33,7 +36,9 @@
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.junit.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
@@ -208,6 +213,8 @@
    private PatMedOperationMapper patMedOperationMapper;
    @Autowired
    private PatMedOperationItemMapper patMedOperationItemMapper;
    @Autowired
    private ServiceOutPathMapper serviceOutPathMapper;
    /**
     * æŸ¥è¯¢å•一任务(随访)
@@ -410,26 +417,7 @@
    @Override
    public List<ServiceSubtaskRes> patItem(ServiceSubtaskEntity serviceSubtaskEntity) {
        if (serviceSubtaskEntity.getSendstateView() != null) {
            //            1:待随访(1 è¢«é¢†å–、2 å¾…发送、3 å·²å‘送、5 å‘送失败、7、超时);2:已完成( 6 å·²å®Œæˆï¼‰ã€3:无需随访(4 ä¸æ‰§è¡Œï¼‰
            if (serviceSubtaskEntity.getSendstateView() == 1)
                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(1L, 2L, 3L, 5L, 7L)));
            if (serviceSubtaskEntity.getSendstateView() == 2)
                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(6L)));
            if (serviceSubtaskEntity.getSendstateView() == 3)
                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(4L)));
        }
        //患者获取随访任务-需要查询同个患者(身份证)所有的任务 æ™¯å®
        if (StringUtils.isNotEmpty(serviceSubtaskEntity.getOrgid()) && serviceSubtaskEntity.getOrgid().equals("47255004333112711A1001")) {
            if (ObjectUtils.isNotEmpty(serviceSubtaskEntity.getPatid())) {
                PatArchive patArchive = patArchiveService.selectPatArchiveByPatid(serviceSubtaskEntity.getPatid());
                if (ObjectUtils.isNotEmpty(patArchive) && StringUtils.isNotEmpty(patArchive.getIdcardno())) {
                    serviceSubtaskEntity.setPatid(null);
                    serviceSubtaskEntity.setSfzh(patArchive.getIdcardno());
                }
            }
        }
        List<ServiceSubtask> selectServiceSubtaskList = this.selectServiceSubtaskList(serviceSubtaskEntity);
        List<ServiceSubtask> selectServiceSubtaskList = getServiceSubtasks(serviceSubtaskEntity);
        List<ServiceSubtaskRes> serviceSubtaskResList = new ArrayList<>();
        for (ServiceSubtask serviceSubtask : selectServiceSubtaskList) {
@@ -465,6 +453,30 @@
            serviceSubtaskResList.add(serviceSubtaskRes);
        }
        return serviceSubtaskResList;
    }
    public List<ServiceSubtask> getServiceSubtasks(ServiceSubtaskEntity serviceSubtaskEntity) {
        if (serviceSubtaskEntity.getSendstateView() != null) {
            //            1:待随访(1 è¢«é¢†å–、2 å¾…发送、3 å·²å‘送、5 å‘送失败、7、超时);2:已完成( 6 å·²å®Œæˆï¼‰ã€3:无需随访(4 ä¸æ‰§è¡Œï¼‰
            if (serviceSubtaskEntity.getSendstateView() == 1)
                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(1L, 2L, 3L, 5L, 7L)));
            if (serviceSubtaskEntity.getSendstateView() == 2)
                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(6L)));
            if (serviceSubtaskEntity.getSendstateView() == 3)
                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(4L)));
        }
        //患者获取随访任务-需要查询同个患者(身份证)所有的任务 æ™¯å®
        if (StringUtils.isNotEmpty(serviceSubtaskEntity.getOrgid()) && serviceSubtaskEntity.getOrgid().equals("47255004333112711A1001")) {
            if (ObjectUtils.isNotEmpty(serviceSubtaskEntity.getPatid())) {
                PatArchive patArchive = patArchiveService.selectPatArchiveByPatid(serviceSubtaskEntity.getPatid());
                if (ObjectUtils.isNotEmpty(patArchive) && StringUtils.isNotEmpty(patArchive.getIdcardno())) {
                    serviceSubtaskEntity.setPatid(null);
                    serviceSubtaskEntity.setSfzh(patArchive.getIdcardno());
                }
            }
        }
        List<ServiceSubtask> selectServiceSubtaskList = this.selectServiceSubtaskList(serviceSubtaskEntity);
        return selectServiceSubtaskList;
    }
    @Override
@@ -632,9 +644,66 @@
     */
    @Override
    public Map<String, Object> insertServiceSubtask(ServiceSubtask serviceSubtask) {
        serviceSubtask.setCreateTime(DateUtils.getNowDate());
        serviceSubtaskMapper.insertServiceSubtask(serviceSubtask);
        List<ServiceSubtask> serviceSubtasks = null;
        //先查询一下该患者是否已经创建根据serviceType,sendstate,inhospid,patid,orgid
        //这样判断主要是为了想让新华医院的外部导入患者创建任务,也能用这个接口
        if (StringUtils.isEmpty(serviceSubtask.getPatfrom()) || !serviceSubtask.getPatfrom().equals("2")) {
            ServiceSubtaskEntity serviceSubtaskVO = new ServiceSubtaskEntity();
            serviceSubtaskVO.setServiceType(serviceSubtask.getServiceType());
            serviceSubtaskVO.setInhospid(serviceSubtask.getInhospid());
            serviceSubtaskVO.setSendstates(new ArrayList<>(Arrays.asList(1L, 2L, 3L, 5L, 7L)));
            serviceSubtaskVO.setPatid(serviceSubtask.getPatid());
            serviceSubtaskVO.setOrgid(serviceSubtask.getOrgid());
            serviceSubtaskVO.setCampusid(serviceSubtask.getCampusid());
            serviceSubtasks = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskVO);
        }
        RSAPublicKeyExample rsaPublicKeyExample = new RSAPublicKeyExample();
        String format = null;
        if (CollectionUtils.isNotEmpty(serviceSubtasks)) {
            ServiceSubtask serviceSubtask1 = serviceSubtasks.get(0);
            ServiceOutPath serviceOutPath = new ServiceOutPath();
            serviceOutPath.setSubId(serviceSubtask1.getId());
            List<ServiceOutPath> serviceOutPaths = serviceOutPathMapper.selectServiceOutPathList(serviceOutPath);
            if (CollectionUtils.isNotEmpty(serviceOutPaths))
                format = String.format("%03X", serviceOutPaths.get(0).getId());
            else format = getOutPath(serviceSubtask1, rsaPublicKeyExample);
            serviceSubtask = serviceSubtask1;
        } else {
            //如果不存在,则新增
            ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
            if (ObjectUtils.isNotEmpty(serviceTask)) {
                serviceSubtask.setDeptcode(serviceTask.getDeptcode());
                serviceSubtask.setTaskDesc(serviceTask.getTaskDesc());
                serviceSubtask.setTaskName(serviceTask.getTaskName());
                serviceSubtask.setDeptname(serviceTask.getDeptname());
                serviceSubtask.setTemplatename(serviceTask.getTemplatename());
            }
            serviceSubtask.setCreateTime(DateUtils.getNowDate());
            serviceSubtaskMapper.insertServiceSubtask(serviceSubtask);
            //修改住院满意度状态(避免重复查询)
            if (serviceSubtask.getServiceType() != null && "6".equals(serviceSubtask.getServiceType())) {
                Long inhospid = serviceSubtask.getInhospid();
                if (inhospid != null) {
                    PatMedInhosp patMedInhosp = new PatMedInhosp();
                    patMedInhosp.setInhospMydFlag("1");
                    patMedInhosp.setInhospid(inhospid);
                    patMedInhospMapper.updatePatMedInhosp(patMedInhosp);
                }
            }
            format = getOutPath(serviceSubtask, rsaPublicKeyExample);
        }
        Map<String, Object> result = new HashMap<>();
        if (StringUtils.isNotEmpty(format))
            result.put("url", StringUtils.isNotEmpty(req_path) ? localIP + ":" + req_path + "/wt?p=" + format : localIP + "/wt?p=" + format);
        else result.put("url", null);
        result.put("subId", serviceSubtask.getId());
        return result;
    }
    private String getOutPath(ServiceSubtask serviceSubtask, RSAPublicKeyExample rsaPublicKeyExample) {
        String format;
        String taskId = rsaPublicKeyExample.encryptedData(serviceSubtask.getTaskid().toString(), pub_key);
        String patid = rsaPublicKeyExample.encryptedData(serviceSubtask.getPatid().toString(), pub_key);
        String subId = rsaPublicKeyExample.encryptedData(serviceSubtask.getId().toString(), pub_key);
@@ -644,17 +713,15 @@
        serviceOutPath.setCreateTime(new Date());
        serviceOutPath.setParam6(subId);
        serviceOutPath.setOrgid(serviceSubtask.getOrgid());
        serviceOutPath.setTaskId(serviceSubtask.getTaskid());
        serviceOutPath.setSubId(serviceSubtask.getId());
        iServiceOutPathService.insertServiceOutPath(serviceOutPath);
        //转成16进制
        String format = String.format("%03X", serviceOutPath.getId());
        format = String.format("%03X", serviceOutPath.getId());
        serviceOutPath.setRadix(format);
        serviceOutPath.setUpdateTime(new Date());
        iServiceOutPathService.updateServiceOutPath(serviceOutPath);
        Map<String, Object> result = new HashMap<>();
        result.put("url", StringUtils.isNotEmpty(req_path) ? localIP + ":" + req_path + "/wt?p=" + format : localIP + "/wt?p=" + format);
        result.put("subId", serviceSubtask.getId());
        return result;
        return format;
    }
    /**
@@ -1079,8 +1146,7 @@
                }
            }
        }
        else if (serviceTaskVO.getIsoperation() != null && serviceTaskVO.getIsoperation() == 2) {
        } else if (serviceTaskVO.getIsoperation() != null && serviceTaskVO.getIsoperation() == 2) {
            //任务修改
            if (ObjectUtils.isNotEmpty(serviceTaskVO.getSendTimeslot()))
                serviceTask.setSendTimeSlot(JSON.toJSONString(serviceTaskVO.getSendTimeslot()));
@@ -1129,15 +1195,6 @@
                                patMedOuthosp.setServerState("0");
                                patMedOuthospService.updatePatMedOuthosp(patMedOuthosp);
                            }
                        }
                    } else if (serviceSubtask.getServiceType().equals("19")) {
                        ServiceTask st = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                        //如果任务是长期任务,需要将该患者的手术详情数据
                        if (st != null && st.getLongTask() == 1 && ObjectUtils.isNotEmpty(serviceSubtask.getOperationItemId())) {
                            PatMedOperationItem patMedOperationItem = new PatMedOperationItem();
                            patMedOperationItem.setOpercheckFlag("0");
                            patMedOperationItem.setId(serviceSubtask.getOperationItemId());
                            patMedOperationItemMapper.updatePatMedOperationItem(patMedOperationItem);
                        }
                    }
                }
@@ -4955,13 +5012,107 @@
    }
    @Override
    public List<ServiceSubtaskDetailRatioExport> statQuestionOption(List<Long> taskIds, Date startOutHospTime, Date endOutHospTime) {
    public List<ServiceSubtaskDetailRatioExport> statQuestionOption(List<Long> taskIds, Date startOutHospTime, Date endOutHospTime, String scriptContent, Double scoreStart, Double scoreEnd,String valueType) {
        //先清理一下重复数据
        List<Long> repeatIds = serviceSubtaskDetailMapper.selectRepeatDetailIds();
        if (!CollectionUtils.isEmpty(repeatIds)) {
            serviceSubtaskDetailMapper.deleteRepeatDetailByIds(repeatIds);
        }
        return serviceSubtaskMapper.statQuestionOption(taskIds, startOutHospTime, endOutHospTime);
        List<ServiceSubtaskDetailRatioExport> serviceSubtaskDetailRatioExports = serviceSubtaskMapper.statQuestionOption(taskIds, startOutHospTime, endOutHospTime, scoreStart, scoreEnd, scriptContent, valueType);
        // å¤„理 targetvalue:按 & åˆ†å‰²æˆå¤šæ¡è®°å½•
        List<ServiceSubtaskDetailRatioExport> result = new ArrayList<>();
        for (ServiceSubtaskDetailRatioExport export : serviceSubtaskDetailRatioExports) {
            if (StringUtils.isNotEmpty(export.getTargetvalue()) && export.getTargetvalue().contains("&")) {
                String[] values = export.getTargetvalue().split("&");
                // å¦‚果有多条目标值,拆分成多行
                for (String value : values) {
                    //先去 result ä¸­æŸ¥ä¸€ä¸‹ï¼Œæœ‰æ²¡æœ‰è¿™ä¸ª questiontext å’Œå¯¹åº”çš„ targetvalue
                    ServiceSubtaskDetailRatioExport existingExport = result.stream()
                            .filter(e -> e.getQuestiontext().equals(export.getQuestiontext())
                                    && value.trim().equals(e.getTargetvalue()))
                            .findFirst()
                            .orElse(null);
                    if (existingExport != null && existingExport.getRatio().equals("0") && existingExport.getCount().equals("0")) {
                        //如果存在,则将 result é‡Œçš„这个问题所对应的 Targetvalue=value è¿™æ¡æ•°æ®æ›´æ–°æˆå½“前这条数据
                        if(value.equals(export.getOptionresult())) {
                            existingExport.setRatio(export.getRatio());
                            existingExport.setScore(export.getScore());
                            existingExport.setCount(export.getCount());
                            existingExport.setOptionresult(export.getOptionresult());
                        }
                    } else if (existingExport != null && !existingExport.getRatio().equals("0")) {
                        continue;
                    } else {
                        ServiceSubtaskDetailRatioExport newExport = DtoConversionUtils.sourceToTarget(export, ServiceSubtaskDetailRatioExport.class);
                        newExport.setTargetvalue(value.trim()); // targetvalue æ˜¾ç¤ºå•个选项
                        if (!value.equals(export.getOptionresult())) {
                            newExport.setOptionresult(null);
                            newExport.setCount("0");
                            newExport.setRatio("0");
                        }
                        result.add(newExport);
                    }
                }
            } else {
                // targetvalue ä¸åŒ…含 &,直接添加到结果
                result.add(export);
            }
        }
        return result;
    }
    @Override
    public List<ServiceSubtaskDetailRatioExport> statQuestionOptionBySubtaskIds(List<Long> subtaskIds, Date startOutHospTime, Date endOutHospTime, String scriptContent, Double scoreStart, Double scoreEnd,String valueType) {
        //先清理一下重复数据
        List<Long> repeatIds = serviceSubtaskDetailMapper.selectRepeatDetailIds();
        if (!CollectionUtils.isEmpty(repeatIds)) {
            serviceSubtaskDetailMapper.deleteRepeatDetailByIds(repeatIds);
        }
        List<ServiceSubtaskDetailRatioExport> serviceSubtaskDetailRatioExports = serviceSubtaskMapper.statQuestionOptionBySubtaskIds(subtaskIds, startOutHospTime, endOutHospTime, scoreStart, scoreEnd, scriptContent,valueType);
        // å¤„理 targetvalue:按 & åˆ†å‰²æˆå¤šæ¡è®°å½•
        List<ServiceSubtaskDetailRatioExport> result = new ArrayList<>();
        for (ServiceSubtaskDetailRatioExport export : serviceSubtaskDetailRatioExports) {
            if (StringUtils.isNotEmpty(export.getTargetvalue()) && export.getTargetvalue().contains("&")) {
                String[] values = export.getTargetvalue().split("&");
                // å¦‚果有多条目标值,拆分成多行
                for (String value : values) {
                    //先去 result ä¸­æŸ¥ä¸€ä¸‹ï¼Œæœ‰æ²¡æœ‰è¿™ä¸ª questiontext å’Œå¯¹åº”çš„ targetvalue
                    ServiceSubtaskDetailRatioExport existingExport = result.stream()
                            .filter(e -> e.getQuestiontext().equals(export.getQuestiontext())
                                    && value.trim().equals(e.getTargetvalue()))
                            .findFirst()
                            .orElse(null);
                    if (existingExport != null && existingExport.getRatio().equals("0") && existingExport.getCount().equals("0")) {
                        //如果存在,则将 result é‡Œçš„这个问题所对应的 Targetvalue=value è¿™æ¡æ•°æ®æ›´æ–°æˆå½“前这条数据
                        if(value.equals(export.getOptionresult())) {
                            existingExport.setRatio(export.getRatio());
                            existingExport.setScore(export.getScore());
                            existingExport.setCount(export.getCount());
                            existingExport.setOptionresult(export.getOptionresult());
                        }
                    } else if (existingExport != null && !existingExport.getRatio().equals("0")) {
                        continue;
                    } else {
                        ServiceSubtaskDetailRatioExport newExport = DtoConversionUtils.sourceToTarget(export, ServiceSubtaskDetailRatioExport.class);
                        newExport.setTargetvalue(value.trim()); // targetvalue æ˜¾ç¤ºå•个选项
                        if (!value.equals(export.getOptionresult())) {
                            newExport.setOptionresult(null);
                            newExport.setCount("0");
                            newExport.setRatio("0");
                        }
                        result.add(newExport);
                    }
                }
            } else {
                // targetvalue ä¸åŒ…含 &,直接添加到结果
                result.add(export);
            }
        }
        return result;
    }
    /**
@@ -5266,6 +5417,163 @@
        }
    }
    public PhoneCallBackSHVO getFirstQues(PhoneCallReqYQVO phoneCallReqYQVO) {
        //用于返回
        PhoneCallBackSHVO phoneCallBackSHVO = new PhoneCallBackSHVO();
        //根据手机号获取用户的的子任务信息
        ServiceSubtaskEntity serviceSubtaskVO = new ServiceSubtaskEntity();
        serviceSubtaskVO.setPhone(phoneCallReqYQVO.getPhone());
        serviceSubtaskVO.setType("1");
        serviceSubtaskVO.setSendstate(2L);
        List<ServiceSubtask> serviceSubtasks = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskVO);
        if (CollectionUtils.isNotEmpty(serviceSubtasks)) {
            ServiceSubtask serviceSubtask = serviceSubtasks.get(0);
            IvrTaskTemplate ivrTaskTemplate = ivrTaskTemplateService.selectIvrTaskTemplateByID(serviceSubtask.getTemplateid());
            //通过任务模板中的"第一次问题编号"获取第一个问题;
            IvrTaskTemplateScript ivrTaskTemplateScript = null;
            IvrTaskTemplateScript ivrTaskTemplateScript1 = new IvrTaskTemplateScript();
            ivrTaskTemplateScript1.setTemplateID(ivrTaskTemplate.getId());
            List<IvrTaskTemplateScript> ivrTaskTemplateScripts = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptList(ivrTaskTemplateScript1);
            for (IvrTaskTemplateScript ivrTaskTemplateScript2 : ivrTaskTemplateScripts) {
                if (ivrTaskTemplate.getFirstQuestionNum() == Long.valueOf(ivrTaskTemplateScript2.getSort())) {
                    ivrTaskTemplateScript = ivrTaskTemplateScript2;
                    break;
                }
            }
            //获取开场白
            ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
            phoneCallBackSHVO.setSubId(serviceSubtask.getId());
            phoneCallBackSHVO.setScriptId(ivrTaskTemplateScript.getId());
            phoneCallBackSHVO.setSort(ivrTaskTemplateScript.getSort());
            phoneCallBackSHVO.setTaskTemplateid(ivrTaskTemplateScript.getTemplateID());
            phoneCallBackSHVO.setScriptContent(serviceTask.getKcb() + "," + ivrTaskTemplateScript.getScriptContent());
        }
        return phoneCallBackSHVO;
    }
    /**
     * ä¸‰æ±‡ASR结果处理
     *
     * @param phoneCallResultYQVO
     * @return
     */
    public PhoneCallBackSHVO processResust(PhoneCallResultYQVO phoneCallResultYQVO) {
        PhoneCallBackSHVO phoneCallBackSHVO = new PhoneCallBackSHVO();
        //通过scriptid,获取所有的结果
        String url = configService.selectConfigByKey("sys.voice.match.ai.url", phoneCallResultYQVO.getOrgid());
        ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(phoneCallResultYQVO.getSubId());
        IvrTaskTemplate ivrTaskTemplate = ivrTaskTemplateService.selectIvrTaskTemplateByID(serviceSubtask.getTemplateid());
        IvrTaskTemplateScriptVO ivrTaskTemplateScriptVO = iIvrTaskTemplateScriptService.getTaskTempScriptInfoByid(Long.valueOf(phoneCallResultYQVO.getScriptId()));
        Integer i = matchOptionIndex(url, phoneCallResultYQVO.getScriptContent(), phoneCallResultYQVO.getAsrResult(), ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList(), phoneCallResultYQVO.getUuid());
        if (i >= 0) {
            String optiondesc = ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(i).getOptiondesc();
            //将患者的回答写进service_subtask_detail中
            ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
            List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
            ivrTaskTemplateScriptVO.setQuestionResult(optiondesc);
            PhoneCallReqYQVO phoneCallReqYQVO = new PhoneCallReqYQVO();
            phoneCallReqYQVO.setTaskid("" + serviceSubtask.getId());
            phoneCallReqYQVO.setPhone(phoneCallResultYQVO.getPhone());
            phoneCallReqYQVO.setAsrtext(phoneCallResultYQVO.getAsrResult());
            ServiceSubtaskDetail serviceSubtaskDetail = getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate);
            //修改一下语音路径(改成前端可以访问的,存到数据库中)
            if (StringUtils.isNotEmpty(serviceSubtaskDetail.getQuestionvoice())) {
                String questionvoice = serviceSubtaskDetail.getQuestionvoice();
                String[] split = questionvoice.split("\\\\");
                if (split.length > 0) {
                    String lastPart = split[split.length - 1];
                    serviceSubtaskDetail.setQuestionvoice(voicePathPrefix + lastPart);
                }
            }
            serviceSubtaskDetailList.add(serviceSubtaskDetail);
            serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
            serviceSubTaskDetailReq.setGuid(phoneCallResultYQVO.getGuid());
            serviceSubTaskDetailReq.setOrgid(phoneCallResultYQVO.getOrgid());
            saveQuestionAnswerPhone(serviceSubTaskDetailReq);
            IvrTaskTemplateScript ivrTaskTemplateScript = new IvrTaskTemplateScript();
            ivrTaskTemplateScript.setTemplateID(ivrTaskTemplateScriptVO.getTemplateID());
            List<IvrTaskTemplateScript> ivrTaskTemplateScripts = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptList(ivrTaskTemplateScript);
            //获取下一题
            for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) {
                if (ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getNextScriptno() == 0) {
                    ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                    phoneCallBackSHVO.setScriptContent(serviceTask.getJsy());
                    phoneCallBackSHVO.setTaskTemplateid(script.getTemplateID());
                    phoneCallBackSHVO.setSort(script.getSort());
                    phoneCallBackSHVO.setScriptId(script.getId());
                    phoneCallBackSHVO.setSubId(serviceSubtask.getId());
                    phoneCallBackSHVO.setStatus("0");
                } else if (script.getSort() == ivrTaskTemplateScriptVO.getNextScriptno().intValue()) {
                    phoneCallBackSHVO.setScriptContent(script.getScriptContent());
                    phoneCallBackSHVO.setTaskTemplateid(script.getTemplateID());
                    phoneCallBackSHVO.setSort(script.getSort());
                    phoneCallBackSHVO.setScriptId(script.getId());
                    phoneCallBackSHVO.setSubId(serviceSubtask.getId());
                    break;
                }
            }
            //把缓存清除
            redisCache.deleteObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId());
        } else {
            //再将重复询问
            //记录一下这个问题被问题的次数
            Object cacheObject = redisCache.getCacheObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId());
            if (cacheObject == null || (Integer) cacheObject < ivrTaskTemplate.getMateNum()) {
                // èŽ·å–é€‰é¡¹é›†åˆä¸­çš„ optiondesc ç”¨äºŽå¼•导患者
                List<IvrTaskTemplateTargetoption> optionList = ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList();
                String optionDescStr = "";
                if (optionList != null && !optionList.isEmpty()) {
                    optionDescStr = optionList.stream().map(IvrTaskTemplateTargetoption::getTargetvalue).filter(StringUtils::isNotEmpty).collect(Collectors.joining("或"));
                }
                String scriptContent = "请问" + phoneCallResultYQVO.getScriptContent();
                if (StringUtils.isNotEmpty(optionDescStr)) {
                    scriptContent += ",你可以回答:" + optionDescStr;
                }
                phoneCallBackSHVO.setScriptContent(scriptContent);
                phoneCallBackSHVO.setTaskTemplateid(phoneCallResultYQVO.getTaskTemplateid());
                phoneCallBackSHVO.setSort(phoneCallResultYQVO.getSort());
                phoneCallBackSHVO.setScriptId(phoneCallResultYQVO.getScriptId());
                phoneCallBackSHVO.setSubId(phoneCallResultYQVO.getSubId());
                if (cacheObject == null)
                    redisCache.setCacheObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId(), 1, 120, TimeUnit.MINUTES);
                else
                    redisCache.setCacheObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId(), (Integer) cacheObject + 1, 120, TimeUnit.MINUTES);
            } else if ((Integer) cacheObject >= ivrTaskTemplate.getMateNum()) {
                //如果已经问了规定次数,就不问了,继续问下一题(这个要商量一下,是继续问下一题,还是停了,是不是要做个开关)
                IvrTaskTemplateScript ivrTaskTemplateScript = new IvrTaskTemplateScript();
                ivrTaskTemplateScript.setTemplateID(ivrTaskTemplateScriptVO.getTemplateID());
                List<IvrTaskTemplateScript> ivrTaskTemplateScripts = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptList(ivrTaskTemplateScript);
                //获取下一题
                for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) {
                    if (script.getSort() == ivrTaskTemplateScriptVO.getNextScriptno().intValue()) {
                        phoneCallBackSHVO.setScriptContent(script.getScriptContent());
                        phoneCallBackSHVO.setTaskTemplateid(script.getTemplateID());
                        phoneCallBackSHVO.setSort(script.getSort());
                        phoneCallBackSHVO.setScriptId(script.getId());
                        phoneCallBackSHVO.setSubId(serviceSubtask.getId());
                        break;
                    }
                }
                //把缓存清除
                redisCache.deleteObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId());
            }
        }
        return phoneCallBackSHVO;
    }
    public PhoneCallBackYQVO AiMatch(String url, PhoneCallReqYQVO phoneCallReqYQVO, PhoneCallBackYQVO phoneCallBackYQVO, IvrTaskTemplateScriptVO ivrTaskTemplateScriptVO, ServiceSubtask serviceSubtask, IvrTaskTemplate ivrTaskTemplate, List<IvrTaskTemplateScript> ivrTaskTemplateScripts, String scriptId, Integer flag, String uuid) {
        PhoneCallBackYQVO back = null;
        List<IvrTaskTemplateTargetoption> options = ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList();
@@ -5492,15 +5800,36 @@
        return back;
    }
    /**
     * é—®é¢˜ç»“果占比
     *
     * @param serviceSubtaskEntity
     * @param serviceSubtaskRes
     * @return
     */
    public List<ServiceSubtaskDetailRatioExport> getDetailRatio(QuestionResultVO questionResultVO, List<ServiceSubtaskRes> serviceSubtaskRes) {
        List<ServiceSubtaskDetailRatioExport> serviceSubtaskDetailRatioExports = null;
        if (StringUtils.isNotEmpty(questionResultVO.getServiceType()) && questionResultVO.getServiceType().equals("2")) {
            //获取serviceSubtaskList中的所有taskid
            List<Long> taskIds = serviceSubtaskRes.stream().map(ServiceSubtaskRes::getTaskid).filter(ObjectUtils::isNotEmpty).distinct().collect(Collectors.toList());
            serviceSubtaskDetailRatioExports = statQuestionOption(taskIds, questionResultVO.getStartOutHospTime(), questionResultVO.getEndOutHospTime(), questionResultVO.getQuestionContent(), questionResultVO.getStartScore(), questionResultVO.getEndScore(),questionResultVO.getValueType());
        } else if (StringUtils.isNotEmpty(questionResultVO.getServiceType()) && questionResultVO.getServiceType().equals("6")) {
            //满意度统计 ï¼ˆé€šè¿‡ subtask çš„ id åŽ»ç»Ÿè®¡ï¼‰
            List<Long> subtaskIds = serviceSubtaskRes.stream().map(ServiceSubtaskRes::getId).collect(Collectors.toList());
            serviceSubtaskDetailRatioExports = statQuestionOptionBySubtaskIds(subtaskIds, questionResultVO.getStartOutHospTime(), questionResultVO.getEndOutHospTime(), questionResultVO.getQuestionContent(), questionResultVO.getStartScore(), questionResultVO.getEndScore(),questionResultVO.getValueType());
        }
        return serviceSubtaskDetailRatioExports;
    }
    //获取任务组信息
    private void findServiceTaskScheduleVO(ServiceTaskVO serviceTaskVO){
        if(StringUtils.isNotEmpty(serviceTaskVO.getAppltype()) && serviceTaskVO.getAppltype().equals("5")){
    private void findServiceTaskScheduleVO(ServiceTaskVO serviceTaskVO) {
        if (StringUtils.isNotEmpty(serviceTaskVO.getAppltype()) && serviceTaskVO.getAppltype().equals("5")) {
            //任务组信息
            ServiceTaskSchedule schedule = serviceTaskScheduleService.selectByTaskid(serviceTaskVO.getTaskid());
            if (ObjectUtils.isNotEmpty(schedule)) {
                ServiceTaskScheduleVO serviceTaskScheduleVO = DtoConversionUtils.sourceToTarget(schedule, ServiceTaskScheduleVO.class);
                List<ServiceTaskScheduleDetail> scheduleDetailList =
                        serviceTaskScheduleService.selectDetailByScheduleid(schedule.getId());
                List<ServiceTaskScheduleDetail> scheduleDetailList = serviceTaskScheduleService.selectDetailByScheduleid(schedule.getId());
                serviceTaskScheduleVO.setDetailList(scheduleDetailList);
                serviceTaskVO.setServiceTaskScheduleVO(serviceTaskScheduleVO);
            }
smartor/src/main/resources/mapper/smartor/IvrLibaTemplateScriptMapper.xml
@@ -59,6 +59,8 @@
        <result property="dutyDeptPersonCode" column="duty_dept_person_code"/>
        <result property="reportDeptPersonCode" column="report_dept_person_name"/>
        <result property="reportDeptPersonName" column="report_dept_person_code"/>
        <result property="circulationDeptPersonCode" column="circulation_dept_person_code"/>
        <result property="circulationDeptPersonName" column="circulation_dept_person_name"/>
    </resultMap>
    <sql id="selectIvrLibaTemplateScriptVo">
@@ -69,6 +71,8 @@
               duty_dept_person_code,
               report_dept_person_name,
               report_dept_person_code,
               circulation_dept_person_code,
               circulation_dept_person_name,
               dimension,
               sort,
               script_assortname,
@@ -193,6 +197,12 @@
        <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">and report_dept_person_code like concat('%', #{reportDeptPersonCode},
            '%')
        </if>
        <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">and circulation_dept_person_code like concat('%', #{circulationDeptPersonCode},
            '%')
        </if>
        <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">and circulation_dept_person_name like concat('%', #{circulationDeptPersonName},
            '%')
        </if>
    </select>
@@ -283,6 +293,8 @@
            <if test="dutyDeptPersonCode != null  and dutyDeptPersonCode != ''">duty_dept_person_code,</if>
            <if test="reportDeptPersonName != null  and reportDeptPersonName != ''">report_dept_person_name,</if>
            <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">report_dept_person_code,</if>
            <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">circulation_dept_person_code,</if>
            <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">circulation_dept_person_name,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="scriptno != null">#{scriptno},</if>
@@ -337,6 +349,8 @@
            <if test="dutyDeptPersonCode != null  and dutyDeptPersonCode != ''">#{dutyDeptPersonCode},</if>
            <if test="reportDeptPersonName != null  and reportDeptPersonName != ''">#{reportDeptPersonName},</if>
            <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">#{reportDeptPersonCode},</if>
            <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">#{circulationDeptPersonCode},</if>
            <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">#{circulationDeptPersonName},</if>
        </trim>
    </insert>
@@ -374,6 +388,8 @@
            <if test="dutyDeptPersonCode != null  and dutyDeptPersonCode != ''">duty_dept_person_code = #{dutyDeptPersonCode},</if>
            <if test="reportDeptPersonName != null  and reportDeptPersonName != ''">report_dept_person_name = #{reportDeptPersonName},</if>
            <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">report_dept_person_code = #{reportDeptPersonCode},</if>
            <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">circulation_dept_person_name = #{circulationDeptPersonName},</if>
            <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">circulation_dept_person_code = #{circulationDeptPersonCode},</if>
            <if test="targetid != null">targetid = #{targetid},</if>
            <if test="scriptTopic != null">script_topic = #{scriptTopic},</if>
            <if test="nextScriptno != null">next_scriptno = #{nextScriptno},</if>
smartor/src/main/resources/mapper/smartor/IvrTaskTemplateScriptMapper.xml
@@ -59,6 +59,8 @@
        <result property="dutyDeptPersonCode" column="duty_dept_person_code"/>
        <result property="reportDeptPersonCode" column="report_dept_person_name"/>
        <result property="reportDeptPersonName" column="report_dept_person_code"/>
        <result property="circulationDeptPersonCode" column="circulation_dept_person_code"/>
        <result property="circulationDeptPersonName" column="circulation_dept_person_name"/>
    </resultMap>
    <sql id="selectIvrTaskTemplateScriptVo">
@@ -75,6 +77,8 @@
               duty_dept_person_code,
               report_dept_person_name,
               report_dept_person_code,
               circulation_dept_person_code,
               circulation_dept_person_name,
               sort,
               ivrtext,
               script_type,
@@ -178,6 +182,14 @@
            #{reportDeptPersonCode},
            '%')
        </if>
        <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">and circulation_dept_person_code like concat('%',
            #{circulationDeptPersonCode},
            '%')
        </if>
        <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">and circulation_dept_person_name like concat('%',
            #{circulationDeptPersonName},
            '%')
        </if>
    </select>
    <select id="selectIvrTaskTemplateScriptByID" parameterType="Long" resultMap="IvrTaskTemplateScriptResult">
@@ -241,6 +253,8 @@
            <if test="dutyDeptPersonCode != null  and dutyDeptPersonCode != ''">duty_dept_person_code,</if>
            <if test="reportDeptPersonName != null  and reportDeptPersonName != ''">report_dept_person_name,</if>
            <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">report_dept_person_code,</if>
            <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">circulation_dept_person_code,</if>
            <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">circulation_dept_person_name,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="taskid != null">#{taskid},</if>
@@ -295,6 +309,8 @@
            <if test="dutyDeptPersonCode != null  and dutyDeptPersonCode != ''">#{dutyDeptPersonCode},</if>
            <if test="reportDeptPersonName != null  and reportDeptPersonName != ''">#{reportDeptPersonName},</if>
            <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">#{reportDeptPersonCode},</if>
            <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">#{circulationDeptPersonCode},</if>
            <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">#{circulationDeptPersonName},</if>
        </trim>
    </insert>
@@ -355,6 +371,8 @@
            <if test="dutyDeptPersonCode != null  and dutyDeptPersonCode != ''">duty_dept_person_code = #{dutyDeptPersonCode},</if>
            <if test="reportDeptPersonName != null  and reportDeptPersonName != ''">report_dept_person_name = #{reportDeptPersonName},</if>
            <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">report_dept_person_code = #{reportDeptPersonCode},</if>
            <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">circulation_dept_person_name = #{circulationDeptPersonName},</if>
            <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">circulation_dept_person_code = #{circulationDeptPersonCode},</if>
        </trim>
        where id = #{id}
    </update>
smartor/src/main/resources/mapper/smartor/PatMedInhospMapper.xml
@@ -370,7 +370,7 @@
            order by b.endtime desc
        </if>
        <if test="cry != null and cry==0">
            order by b.starttime desc
            order by b.starttime desc , b.bed_no asc
        </if>
    </select>
smartor/src/main/resources/mapper/smartor/ServiceOutPathMapper.xml
@@ -22,6 +22,9 @@
        <result property="wardName" column="ward_name"/>
        <result property="deptCode" column="dept_code"/>
        <result property="deptName" column="dept_name"/>
        <result property="taskId" column="task_id"/>
        <result property="subId" column="sub_id"/>
    </resultMap>
    <sql id="selectServiceOutPathVo">
@@ -31,6 +34,8 @@
               param3,
               param5,
               param6,
               task_id,
               sub_id,
               create_time,
               update_time,
               radix,
@@ -87,6 +92,12 @@
        <if test="deptName != null  and deptName != ''">
            and dept_name=#{deptName}
        </if>
        <if test="subId != null">
            and sub_id=#{subId}
        </if>
        <if test="taskId != null">
            and task_id=#{taskId}
        </if>
    </select>
    <select id="selectServiceOutPathById" parameterType="Long"
@@ -125,7 +136,12 @@
            </if>
            <if test="deptName != null">dept_name,
            </if>
            <if test="subId != null">
                sub_id,
            </if>
            <if test="taskId != null ">
                task_id,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="param1 != null">#{param1},
@@ -153,6 +169,12 @@
            <if test="deptCode != null">#{deptCode},
            </if>
            <if test="deptName != null">#{deptName},
            </if>
            <if test="subId != null ">
                #{subId},
            </if>
            <if test="taskId != null ">
                #{taskId},
            </if>
        </trim>
    </insert>
@@ -202,6 +224,12 @@
            <if test="deptName != null">dept_name =
                #{deptName},
            </if>
            <if test="subId != null">
                sub_id=#{subId},
            </if>
            <if test="taskId != null">
                task_id=#{taskId},
            </if>
        </trim>
        where id = #{id}
    </update>
smartor/src/main/resources/mapper/smartor/ServiceSubtaskDetailTraceMapper.xml
@@ -59,14 +59,33 @@
        <result property="dutyDeptPersonCode" column="duty_dept_person_code"/>
        <result property="reportDeptPersonCode" column="report_dept_person_name"/>
        <result property="reportDeptPersonName" column="report_dept_person_code"/>
        <result property="circulationDeptPersonCode" column="circulation_dept_person_code"/>
        <result property="circulationDeptPersonName" column="circulation_dept_person_name"/>
        <result property="circulationDeptCode" column="circulation_dept_code"/>
        <result property="circulationDeptName" column="circulation_dept_name"/>
        <result property="circulationExplain" column="circulation_explain"/>
        <result property="circulationTime" column="circulation_time"/>
        <result property="circulationStatus" column="circulation_status"/>
        <result property="circulationPersonName" column="circulation_person_name"/>
        <result property="circulationPersonCode" column="circulation_person_code"/>
    </resultMap>
    <sql id="selectServiceSubtaskDetailTtraceVo">
        select id,
               circulation_status,
               circulation_person_name,
               circulation_person_code,
               duty_dept_person_name,
               duty_dept_person_code,
               report_dept_person_name,
               report_dept_person_code,
               circulation_dept_person_code,
               circulation_dept_person_name,
               circulation_dept_code,
               circulation_dept_name,
               circulation_explain,
               circulation_time,
               detail_id,
               is_myd_exception,
               dimension,
@@ -277,6 +296,33 @@
        <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">
            and report_dept_person_code like concat('%', #{reportDeptPersonCode}, '%')
        </if>
        <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">
            and circulation_dept_person_code like concat('%', #{circulationDeptPersonCode}, '%')
        </if>
        <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">
            and circulation_dept_person_name like concat('%', #{circulationDeptPersonName}, '%')
        </if>
        <if test="circulationDeptCode != null  and circulationDeptCode != ''">
            and circulation_dept_code like concat('%', #{circulationDeptCode}, '%')
        </if>
        <if test="circulationDeptName != null  and circulationDeptName != ''">
            and circulation_dept_name like concat('%', #{circulationDeptName}, '%')
        </if>
        <if test="circulationExplain != null  and circulationExplain != ''">
            and circulation_explain like concat('%', #{circulationExplain}, '%')
        </if>
        <if test="circulationTime != null ">
            and circulation_time = #{circulationTime}
        </if>
        <if test="circulationStatus != null ">
            and circulation_status = #{circulationStatus}
        </if>
        <if test="circulationPersonName != null  and circulationPersonName != ''">
            and circulation_person_name like concat('%', #{circulationPersonName}, '%')
        </if>
        <if test="circulationPersonCode != null  and circulationPersonCode != ''">
            and circulation_person_code =#{circulationPersonCode}
        </if>
        <if test="pageSize != null  and pageNum != null">limit ${pageSize} OFFSET ${pageNum}</if>
    </select>
@@ -325,6 +371,33 @@
        </if>
        <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">
            and report_dept_person_code like concat('%', #{reportDeptPersonCode}, '%')
        </if>
        <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">
            and circulation_dept_person_code like concat('%', #{circulationDeptPersonCode}, '%')
        </if>
        <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">
            and circulation_dept_person_name like concat('%', #{circulationDeptPersonName}, '%')
        </if>
        <if test="circulationDeptCode != null  and circulationDeptCode != ''">
            and circulation_dept_code like concat('%', #{circulationDeptCode}, '%')
        </if>
        <if test="circulationDeptName != null  and circulationDeptName != ''">
            and circulation_dept_name like concat('%', #{circulationDeptName}, '%')
        </if>
        <if test="circulationExplain != null  and circulationExplain != ''">
            and circulation_explain like concat('%', #{circulationExplain}, '%')
        </if>
        <if test="circulationTime != null ">
            and circulation_time = #{circulationTime}
        </if>
        <if test="circulationStatus != null ">
            and circulation_status = #{circulationStatus}
        </if>
        <if test="circulationPersonName != null  and circulationPersonName != ''">
            and circulation_person_name like concat('%', #{circulationPersonName}, '%')
        </if>
        <if test="circulationPersonCode != null  and circulationPersonCode != ''">
            and circulation_person_code =#{circulationPersonCode}
        </if>
    </select>
@@ -447,6 +520,31 @@
            </if>
            <if test="reportDeptPersonCode != null">report_dept_person_code,
            </if>
            <if test="circulationDeptPersonCode != null">circulation_dept_person_code,
            </if>
            <if test="circulationDeptPersonName != null">circulation_dept_person_name,
            </if>
            <if test="circulationDeptCode != null ">
                circulation_dept_code,
            </if>
            <if test="circulationDeptName != null">
                circulation_dept_name,
            </if>
            <if test="circulationExplain != null ">
                circulation_explain,
            </if>
            <if test="circulationTime != null ">
                circulation_time,
            </if>
            <if test="circulationStatus != null ">
                circulation_status,
            </if>
            <if test="circulationPersonName != null  and circulationPersonName != ''">
                circulation_person_name,
            </if>
            <if test="circulationPersonCode != null  and circulationPersonCode != ''">
                circulation_person_code,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="detailId != null">#{detailId},
@@ -556,6 +654,31 @@
            <if test="reportDeptPersonName != null">#{reportDeptPersonName},
            </if>
            <if test="reportDeptPersonCode != null">#{reportDeptPersonCode},
            </if>
            <if test="circulationDeptPersonCode != null">#{circulationDeptPersonCode},
            </if>
            <if test="circulationDeptPersonName != null">#{circulationDeptPersonName},
            </if>
            <if test="circulationDeptCode != null">
                #{circulationDeptCode},
            </if>
            <if test="circulationDeptName != null">
                #{circulationDeptName},
            </if>
            <if test="circulationExplain != null">
                #{circulationExplain},
            </if>
            <if test="circulationTime != null ">
                #{circulationTime},
            </if>
            <if test="circulationStatus != null ">
               #{circulationStatus},
            </if>
            <if test="circulationPersonName != null  and circulationPersonName != ''">
               #{circulationPersonName},
            </if>
            <if test="circulationPersonCode != null  and circulationPersonCode != ''">
              #{circulationPersonCode},
            </if>
        </trim>
    </insert>
@@ -725,6 +848,33 @@
            <if test="reportDeptPersonCode != null">report_dept_person_code =
                #{reportDeptPersonCode},
            </if>
            <if test="circulationDeptPersonCode != null">circulation_dept_person_code =
                #{circulationDeptPersonCode},
            </if>
            <if test="circulationDeptPersonName != null">circulation_dept_person_name =
                #{circulationDeptPersonName},
            </if>
            <if test="circulationDeptCode != null">circulation_dept_code =
                #{circulationDeptCode},
            </if>
            <if test="circulationDeptName != null">circulation_dept_name =
                #{circulationDeptName},
            </if>
            <if test="circulationExplain != null">circulation_explain =
                #{circulationExplain},
            </if>
            <if test="circulationTime != null">circulation_time =
                #{circulationTime},
            </if>
            <if test="circulationStatus != null ">
                circulation_status = #{circulationStatus},
            </if>
            <if test="circulationPersonName != null  and circulationPersonName != ''">
                circulation_person_name =#{circulationPersonName},
            </if>
            <if test="circulationPersonCode != null  and circulationPersonCode != ''">
                circulation_person_code =#{circulationPersonCode},
            </if>
        </trim>
        where id = #{id}
    </update>
smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml
@@ -380,6 +380,12 @@
        <if test="endSendDateTime != null">
            AND date_format(visit_time,'%y%m%d') &lt;= date_format(#{endSendDateTime},'%y%m%d')
        </if>
        <if test="finishtimeStart != null">
            AND date_format(finishtime,'%y%m%d') &gt;= date_format(#{finishtimeStart},'%y%m%d')
        </if>
        <if test="finishtimeEnd != null">
            AND date_format(finishtime,'%y%m%d') &lt;= date_format(#{finishtimeEnd},'%y%m%d')
        </if>
        <if test="longSendTime != null">
            AND date_format(long_send_time,'%y%m%d') &lt;= date_format(#{longSendTime},'%y%m%d')
        </if>
@@ -402,7 +408,7 @@
        </if>
        <if test="deptOrDistrict==2">
            AND (1=1
            <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
            <if test="leavehospitaldistrictcodes != null">
                AND leavehospitaldistrictcode IN
                <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="("
                         separator=","
@@ -410,7 +416,7 @@
                    #{leavehospitaldistrictcode}
                </foreach>
            </if>
            <if test=" leaveldeptcodes != null and leaveldeptcodes.size()>0">
            <if test=" leaveldeptcodes != null">
                OR deptcode IN
                <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
                         close=")">
@@ -3050,6 +3056,9 @@
        t.taskid AS taskid,
        t.task_name AS taskName,
        d.asrtext AS optionresult,
        d.score AS score,
        d.targetvalue AS targetvalue,
        d.value_type AS valueType,
        COUNT(*) AS count,
        CONCAT(ROUND(COUNT(*) * 100.0 / t2.total_count, 2), '%') AS ratio
        FROM
@@ -3065,22 +3074,110 @@
        service_task t
        INNER JOIN service_subtask s ON s.taskid = t.taskid
        INNER JOIN service_subtask_detail d ON d.sub_id = s.id
        WHERE t.taskid IN
        <foreach collection="taskIds" item="taskId" open="(" separator="," close=")">
            #{taskId}
        </foreach>
        WHERE 1=1
        <if test="taskIds != null and taskIds.size() > 0">
            AND t.taskid IN
            <foreach collection="taskIds" item="taskId" open="(" separator="," close=")">
                #{taskId}
            </foreach>
        </if>
        <if test="valueType != null">AND d.value_type = #{valueType}</if>
        <if test="scoreStart != null">AND d.score &gt;= #{scoreStart}</if>
        <if test="scoreEnd != null">AND d.score &lt;= #{scoreEnd}</if>
        <if test="questiontext != null  and questiontext != ''">and d.questiontext like concat('%', #{questiontext},
            '%')
        </if>
        and s.sendstate=6
        and d.del_flag=0
        GROUP BY
        t.taskid,
        d.questiontext
        ) t2 ON t2.taskid = t.taskid AND t2.questiontext = d.questiontext
        WHERE t.taskid IN
        <foreach collection="taskIds" item="taskId" open="(" separator="," close=")">
            #{taskId}
        </foreach>
        WHERE 1=1
        <if test="taskIds != null and taskIds.size() > 0">
            AND t.taskid IN
            <foreach collection="taskIds" item="taskId" open="(" separator="," close=")">
                #{taskId}
            </foreach>
        </if>
        <if test="valueType != null">AND d.value_type = #{valueType}</if>
        <if test="scoreStart != null">AND d.score &gt;= #{scoreStart}</if>
        <if test="scoreEnd != null">AND d.score &lt;= #{scoreEnd}</if>
        <if test="questiontext != null  and questiontext != ''">and d.questiontext like concat('%', #{questiontext},
            '%')
        </if>
        AND s.sendstate = 6
        and d.del_flag=0
        <if test="startOutHospTime != null">
            AND date_format(s.endtime,'%y%m%d') &gt;= date_format(#{startOutHospTime},'%y%m%d')
        </if>
        <if test="endOutHospTime != null">
            AND date_format(s.endtime,'%y%m%d') &lt;= date_format(#{endOutHospTime},'%y%m%d')
        </if>
        GROUP BY
        t.taskid,
        d.questiontext,
        d.score,
        d.targetvalue,
        d.asrtext,
        d.value_type
        ORDER BY
        d.questiontext,
        t.taskid
    </select>
    <!-- æ–°æ–¹æ³•:根据子任务 ID ç»Ÿè®¡é—®é¢˜é€‰é¡¹ç™¾åˆ†æ¯” (用于满意度统计 serviceType=6) -->
    <select id="statQuestionOptionBySubtaskIds" resultType="com.smartor.domain.ServiceSubtaskDetailRatioExport">
        SELECT
        d.questiontext AS questiontext,
        t.taskid AS taskid,
        t.task_name AS taskName,
        d.asrtext AS optionresult,
        d.score AS score,
        d.targetvalue AS targetvalue,
        d.value_type AS valueType,
        COUNT(*) AS count,
        CONCAT(ROUND(COUNT(*) * 100.0 / t2.total_count, 2), '%') AS ratio
        FROM
        service_task t
        INNER JOIN service_subtask s ON s.taskid = t.taskid
        INNER JOIN service_subtask_detail d ON d.sub_id = s.id
        INNER JOIN (
        SELECT
            d.questiontext,
            COUNT(*) AS total_count
        FROM
            service_task t
            INNER JOIN service_subtask s ON s.taskid = t.taskid
            INNER JOIN service_subtask_detail d ON d.sub_id = s.id
            WHERE 1=1
            <if test="subtaskIds != null and subtaskIds.size() > 0">
            AND s.id IN
            <foreach collection="subtaskIds" item="subtaskId" open="(" separator="," close=")">
                #{subtaskId}
            </foreach>
            </if>
        <if test="valueType != null">AND d.value_type &gt;= #{valueType}</if>
        <if test="scoreStart != null">AND d.score = #{scoreStart}</if>
        <if test="scoreEnd != null">AND d.score &lt;= #{scoreEnd}</if>
        <if test="questiontext != null  and questiontext != ''">and d.questiontext like concat('%', #{questiontext}, '%')</if>
            AND s.sendstate = 6
            AND d.del_flag=0
            GROUP BY d.questiontext
        ) t2 ON t2.questiontext = d.questiontext
        WHERE 1=1
        <if test="subtaskIds != null and subtaskIds.size() > 0">
        AND s.id IN
        <foreach collection="subtaskIds" item="subtaskId" open="(" separator="," close=")">
            #{subtaskId}
        </foreach>
        </if>
        AND s.sendstate = 6
        AND d.del_flag=0
        <if test="valueType != null">AND d.value_type &gt;= #{valueType}</if>
        <if test="scoreStart != null">AND d.score &gt;= #{scoreStart}</if>
        <if test="scoreEnd != null">AND d.score &lt;= #{scoreEnd}</if>
        <if test="questiontext != null  and questiontext != ''">and d.questiontext like concat('%', #{questiontext}, '%')</if>
      <if test="startOutHospTime != null">
        AND date_format(s.endtime,'%y%m%d') &gt;= date_format(#{startOutHospTime},'%y%m%d')
    </if>
@@ -3090,6 +3187,8 @@
        GROUP BY
        t.taskid,
        d.questiontext,
        d.score,
        d.targetvalue,
        d.asrtext
        ORDER BY
        d.questiontext,
smartor/src/main/resources/mapper/smartor/SvyLibTemplateScriptMapper.xml
@@ -56,6 +56,8 @@
        <result property="reportDeptPersonName" column="report_dept_person_code"/>
        <result property="isMydException" column="is_myd_exception"/>
        <result property="dimension" column="dimension"/>
        <result property="circulationDeptPersonCode" column="circulation_dept_person_code"/>
        <result property="circulationDeptPersonName" column="circulation_dept_person_name"/>
    </resultMap>
@@ -108,6 +110,7 @@
               script_picture,
               script_topic,
               isavailable, language, otherdata, value_type, reply, scoretype
                , circulation_dept_person_code, circulation_dept_person_name
        from svy_lib_template_script
    </sql>
@@ -192,6 +195,14 @@
            #{reportDeptPersonCode},
            '%')
        </if>
        <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">and circulation_dept_person_code like concat('%',
            #{circulationDeptPersonCode},
            '%')
        </if>
        <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">and circulation_dept_person_name like concat('%',
            #{circulationDeptPersonName},
            '%')
        </if>
        <if test="pageSize != null  and pageNum != null">limit ${pageSize} OFFSET ${pageNum}</if>
    </select>
@@ -218,6 +229,14 @@
            </if>
            <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">and report_dept_person_code like concat('%',
                #{reportDeptPersonCode},
                '%')
            </if>
            <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">and circulation_dept_person_code like concat('%',
                #{circulationDeptPersonCode},
                '%')
            </if>
            <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">and circulation_dept_person_name like concat('%',
                #{circulationDeptPersonName},
                '%')
            </if>
            <if test="dimensionList == null ">and dimension IS NOT NULL </if>
@@ -284,6 +303,8 @@
            <if test="dutyDeptPersonCode != null  and dutyDeptPersonCode != ''">duty_dept_person_code,</if>
            <if test="reportDeptPersonName != null  and reportDeptPersonName != ''">report_dept_person_name,</if>
            <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">report_dept_person_code,</if>
            <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">circulation_dept_person_code,</if>
            <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">circulation_dept_person_name,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="nextScriptno != null">#{nextScriptno},</if>
@@ -337,6 +358,8 @@
            <if test="dutyDeptPersonCode != null  and dutyDeptPersonCode != ''">#{dutyDeptPersonCode},</if>
            <if test="reportDeptPersonName != null  and reportDeptPersonName != ''">#{reportDeptPersonName},</if>
            <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">#{reportDeptPersonCode},</if>
            <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">#{circulationDeptPersonCode},</if>
            <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">#{circulationDeptPersonName},</if>
        </trim>
    </insert>
@@ -396,6 +419,8 @@
            <if test="dutyDeptPersonCode != null  and dutyDeptPersonCode != ''">duty_dept_person_code = #{dutyDeptPersonCode},</if>
            <if test="reportDeptPersonName != null  and reportDeptPersonName != ''">report_dept_person_name = #{reportDeptPersonName},</if>
            <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">report_dept_person_code = #{reportDeptPersonCode},</if>
            <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">circulation_dept_person_name = #{circulationDeptPersonName},</if>
            <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">circulation_dept_person_code = #{circulationDeptPersonCode},</if>
        </trim>
        where id = #{id}
    </update>
smartor/src/main/resources/mapper/smartor/SvyTaskTemplateScriptMapper.xml
@@ -55,6 +55,8 @@
        <result property="reportDeptPersonName" column="report_dept_person_code"/>
        <result property="isMydException" column="is_myd_exception"/>
        <result property="dimension" column="dimension"/>
        <result property="circulationDeptPersonCode" column="circulation_dept_person_code"/>
        <result property="circulationDeptPersonName" column="circulation_dept_person_name"/>
    </resultMap>
    <sql id="selectSvyTaskTemplateScriptVo">
@@ -82,6 +84,7 @@
               scriptid,
               script_topic, language, categoryName, script_content, is_must, del_flag, update_by, update_time, create_by, create_time, isupload, upload_time, orgid, pid, guid, value_type, targetoptions, script_type, targetid, targetvalue, next_scriptno, script_result, otherdata, picture_path
                , duty_dept_person_name, duty_dept_person_code, report_dept_person_name, report_dept_person_code
                 , circulation_dept_person_code, circulation_dept_person_name
        from svy_task_template_script
    </sql>
@@ -152,6 +155,12 @@
            <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">and report_dept_person_code like concat('%', #{reportDeptPersonCode},
                '%')
            </if>
            <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">and circulation_dept_person_code like concat('%', #{circulationDeptPersonCode},
                '%')
            </if>
            <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">and circulation_dept_person_name like concat('%', #{circulationDeptPersonName},
                '%')
            </if>
    </select>
    <select id="selectSvyTaskTemplateScriptByID" parameterType="Long" resultMap="SvyTaskTemplateScriptResult">
@@ -212,6 +221,8 @@
            <if test="dutyDeptPersonCode != null  and dutyDeptPersonCode != ''">duty_dept_person_code,</if>
            <if test="reportDeptPersonName != null  and reportDeptPersonName != ''">report_dept_person_name,</if>
            <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">report_dept_person_code,</if>
            <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">circulation_dept_person_code,</if>
            <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">circulation_dept_person_name,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="taskid != null">#{taskid},</if>
@@ -263,6 +274,8 @@
            <if test="dutyDeptPersonCode != null  and dutyDeptPersonCode != ''">#{dutyDeptPersonCode},</if>
            <if test="reportDeptPersonName != null  and reportDeptPersonName != ''">#{reportDeptPersonName},</if>
            <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">#{reportDeptPersonCode},</if>
            <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">#{circulationDeptPersonCode},</if>
            <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">#{circulationDeptPersonName},</if>
        </trim>
    </insert>
@@ -320,6 +333,8 @@
            <if test="dutyDeptPersonCode != null  and dutyDeptPersonCode != ''">duty_dept_person_code = #{dutyDeptPersonCode},</if>
            <if test="reportDeptPersonName != null  and reportDeptPersonName != ''">report_dept_person_name = #{reportDeptPersonName},</if>
            <if test="reportDeptPersonCode != null  and reportDeptPersonCode != ''">report_dept_person_code = #{reportDeptPersonCode},</if>
            <if test="circulationDeptPersonName != null  and circulationDeptPersonName != ''">circulation_dept_person_name = #{circulationDeptPersonName},</if>
            <if test="circulationDeptPersonCode != null  and circulationDeptPersonCode != ''">circulation_dept_person_code = #{circulationDeptPersonCode},</if>
        </trim>
        where id = #{d}
    </update>