| | |
| | | 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.satisfaction.api.SatisfactionFacade; |
| | | import com.ruoyi.message.sms.provider.sltd.ws.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.message.SendResult; |
| | | import com.ruoyi.message.sms.SmsSendRequest; |
| | | import com.ruoyi.message.sms.SmsSenderFactory; |
| | | import com.ruoyi.message.wechat.WechatSendRequest; |
| | | import com.ruoyi.message.wechat.WechatSenderFactory; |
| | | 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.QuestionCountKsOrBqDTO; |
| | | import com.smartor.domain.DTO.ServiceSubtaskDetailDTO; |
| | | import com.smartor.domain.DTO.*; |
| | | import com.smartor.domain.*; |
| | | import com.smartor.domain.VO.*; |
| | | import com.smartor.domain.entity.ServiceSubtaskEntity; |
| | |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.net.URLEncoder; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.security.MessageDigest; |
| | | import java.text.DecimalFormat; |
| | | import java.text.SimpleDateFormat; |
| | | import java.time.*; |
| | |
| | | import java.util.regex.Pattern; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.hutool.core.convert.Convert.toHex; |
| | | |
| | | /** |
| | | * 单一任务(随访)Service业务层处理 |
| | | * |
| | |
| | | @Slf4j |
| | | @Service |
| | | public class ServiceSubtaskServiceImpl implements IServiceSubtaskService { |
| | | |
| | | /** |
| | | * 满意度模块对外门面(常驻 Bean)。 |
| | | * <p> |
| | | * 随访统计里的「满意度指数」({@code getSfStatisticsJoy})只能通过它取数; |
| | | * 满意度模块关闭时门面返回全零 Map,不影响随访统计的其它字段与启动。 |
| | | */ |
| | | @Autowired |
| | | private SatisfactionFacade satisfactionFacade; |
| | | |
| | | @Autowired |
| | | private ServiceSubtaskMapper serviceSubtaskMapper; |
| | | |
| | |
| | | private ISysConfigService configService; |
| | | |
| | | @Autowired |
| | | private MtSubmitSmUtil mtSubmitSmUtil; |
| | | private SysConfigMapper sysConfigMapper; |
| | | |
| | | /** |
| | | * 消息发送方工厂(短信/微信公众号),替代原来直接调用各站点发送代码的写法 |
| | | */ |
| | | @Autowired |
| | | private SmsSenderFactory smsSenderFactory; |
| | | |
| | | @Autowired |
| | | private SysConfigMapper sysConfigMapper; |
| | | private WechatSenderFactory wechatSenderFactory; |
| | | |
| | | @Autowired |
| | | private RedisTemplate redisTemplate; |
| | |
| | | |
| | | @Value("${req_path}") |
| | | private String req_path; |
| | | |
| | | @Value("${xhsmsPath}") |
| | | private String xhsmsPath; |
| | | |
| | | @Value("${phoneUpEveryHour}") |
| | | private Integer phoneUpEveryHour; |
| | |
| | | // return map; |
| | | // } |
| | | |
| | | |
| | | /** |
| | | * 统计任务各种状态的数量(已优化) |
| | | * 优化说明: |
| | |
| | | result.putIfAbsent("wxsf", 0); |
| | | result.putIfAbsent("xsf", 0); |
| | | result.putIfAbsent("dsf", 0); |
| | | result.putIfAbsent("ywc", 0); |
| | | result.putIfAbsent("yc", 0); |
| | | result.putIfAbsent("jg", 0); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 统计任务各种状态的数量(已优化) |
| | | * 优化说明: |
| | | * 1. 使用数据库层聚合计算,替代应用层遍历 |
| | | * 2. 性能提升 10-100 倍(取决于数据量) |
| | | * 3. 降低内存占用和网络传输 |
| | | * |
| | | * @param serviceSubtaskEntity 查询条件 |
| | | * @return 各状态统计结果 |
| | | */ |
| | | @Override |
| | | public Map<String, Object> countByCondition(ServiceSubtaskEntity serviceSubtaskEntity) { |
| | | // 直接调用数据库统计方法,在数据库层完成聚合计算 |
| | | Map<String, Object> result = serviceSubtaskMapper.countByCondition(serviceSubtaskEntity); |
| | | // 处理空异常 |
| | | if (MapUtils.isEmpty(result)) { |
| | | result = new HashMap<>(); |
| | | // 确保所有 key 都有值,避免空指针异常 |
| | | // MyBatis 的 SUM 在没有匹配行时会返回 null |
| | | result.putIfAbsent("wzx", 0); |
| | | result.putIfAbsent("ysf", 0); |
| | | result.putIfAbsent("fssb", 0); |
| | | result.putIfAbsent("yfs", 0); |
| | | result.putIfAbsent("blq", 0); |
| | | result.putIfAbsent("total", 0); |
| | | result.putIfAbsent("wxsf", 0); |
| | | result.putIfAbsent("xsf", 0); |
| | | result.putIfAbsent("dsf", 0); |
| | | result.putIfAbsent("dfs", 0); |
| | | result.putIfAbsent("yfs", 0); |
| | | result.putIfAbsent("ywc", 0); |
| | | result.putIfAbsent("yc", 0); |
| | | result.putIfAbsent("jg", 0); |
| | |
| | | serviceSubtask.setUpdateTime(DateUtils.getNowDate()); |
| | | if (serviceSubtask.getSendstate() != null && serviceSubtask.getSendstate() == 6) |
| | | serviceSubtask.setFinishtime(new Date()); |
| | | |
| | | return serviceSubtaskMapper.updateServiceSubtask(serviceSubtask); |
| | | } |
| | | |
| | |
| | | svyTaskTemplateService.deleteSvyTaskTemplateBySvyid(serviceSubtask.getTemplateid()); |
| | | } else if (serviceSubtask.getType().equals("1")) |
| | | ivrTaskTemplateService.deleteIvrTaskTemplateByID(serviceSubtask.getTemplateid()); |
| | | |
| | | |
| | | String tempName = svyTaskTemplateService.selectSvyTaskTemplateBySvyid(Long.valueOf(taskTempid)).getSvyname(); |
| | | serviceSubtask.setTemplateid(taskTempid.longValue()); |
| | |
| | | //将任务信息放到服务表中 |
| | | ServiceSubtask serviceSubtask = DtoConversionUtils.sourceToTarget(serviceTaskVO, ServiceSubtask.class); |
| | | serviceSubtask.setTaskid(serviceTask.getTaskid().longValue()); |
| | | serviceSubtask.setTemplatename(tempName); |
| | | serviceSubtask.setTemplateid(tempid); |
| | | serviceSubtask.setTemplatename(ObjectUtils.isNotEmpty(tempid) ? tempName : serviceTask.getTemplatename()); |
| | | serviceSubtask.setTemplateid(ObjectUtils.isNotEmpty(tempid) ? tempid : serviceTask.getTemplateid()); |
| | | serviceSubtask.setLibtemplateid(ObjectUtils.isEmpty(serviceTask.getLibtemplateid()) ? null : Long.valueOf(serviceTask.getLibtemplateid())); |
| | | serviceSubtask.setLibtemplatename(serviceTask.getLibtemplatename()); |
| | | //新增 |
| | |
| | | ServiceSubtask serviceSubtask = DtoConversionUtils.sourceToTarget(serviceTaskVO, ServiceSubtask.class); |
| | | serviceSubtask.setLibtemplateid(ObjectUtils.isEmpty(serviceTaskVO.getLibtemplateid()) ? null : Long.valueOf(serviceTaskVO.getLibtemplateid())); |
| | | serviceSubtask.setLibtemplatename(serviceTaskVO.getLibtemplatename()); |
| | | serviceSubtask.setTemplateid(serviceTask.getTemplateid()); |
| | | serviceSubtask.setTemplatename(serviceTask.getTemplatename()); |
| | | serviceSubtask.setSendname(patTaskRelevance.getName()); |
| | | serviceSubtask.setAge(patTaskRelevance.getAge()); |
| | | serviceSubtask.setSfzh(patTaskRelevance.getSfzh()); |
| | |
| | | return phoneCallBackYQVO; |
| | | } |
| | | |
| | | |
| | | private String getObject(ServiceSubtask serviceSubtask, String scriptContent) { |
| | | Map<String, Map<String, String>> param = getParam(serviceSubtask.getTaskid()); |
| | | for (Map<String, String> map : param.values()) { |
| | |
| | | |
| | | return scriptContent; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取任务里的通配符 |
| | |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 雨绮任务拉取 |
| | |
| | | log.info("-----------实际拉取的电话数据量:{}", CollectionUtils.isNotEmpty(pullTaskVOList) ? pullTaskVOList.size() : null); |
| | | return pullTaskVOList; |
| | | } |
| | | |
| | | |
| | | private List<PullTaskVO> getPullTaskList(List<String> subIds, String cacheName) { |
| | | log.error("任务拉取subIds的值是多少:{}", subIds); |
| | |
| | | |
| | | return pullTaskVOList; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public Integer saveQuestionAnswerPhone(ServiceSubTaskDetailReq serviceSubTaskDetailReq) { |
| | |
| | | * @return |
| | | */ |
| | | @Override |
| | | @Cacheable(value = "sfStatistics", key = "T(org.springframework.util.DigestUtils).md5DigestAsHex(#serviceSubtaskCountReq.toString().getBytes())", unless = "#result == null or #result.isEmpty()") |
| | | // @Cacheable(value = "sfStatistics", key = "T(org.springframework.util.DigestUtils).md5DigestAsHex(#serviceSubtaskCountReq.toString().getBytes())", unless = "#result == null or #result.isEmpty()") |
| | | public List<ServiceSubtaskStatistic> getSfStatistics(ServiceSubtaskCountReq serviceSubtaskCountReq) { |
| | | log.info("getSfStatistics的入参为:{}", serviceSubtaskCountReq); |
| | | String drcode = "drcode"; |
| | |
| | | ssRate.setOrgid(serviceSubtaskCountReq.getOrgid()); |
| | | ssRate.setStarttime(serviceSubtaskCountReq.getStartTime()); |
| | | ssRate.setEndtime(serviceSubtaskCountReq.getEndTime()); |
| | | ssRate.setStartOutHospTime(serviceSubtaskCountReq.getHospitalStartTime()); |
| | | ssRate.setEndOutHospTime(serviceSubtaskCountReq.getHospitalEndTime()); |
| | | ssRate.setVisitCount(serviceSubtaskCountReq.getVisitCount()); |
| | | ssRate.setServiceTypeList(serviceSubtaskCountReq.getServiceType()); |
| | | if (serviceSubtaskCountReq.getEndTime() != null && new Date().before(serviceSubtaskCountReq.getEndTime())) { |
| | |
| | | } |
| | | return jsRates; |
| | | } |
| | | |
| | | |
| | | public List<ServiceSubtaskStatistic> getSfStatisticsExport(ServiceSubtaskCountReq serviceSubtaskCountReq) { |
| | | List<ServiceSubtaskStatistic> sfStatistics = getSfStatistics(serviceSubtaskCountReq); |
| | |
| | | if (serviceSubtaskCountReq.getEndTime() != null && new Date().before(serviceSubtaskCountReq.getEndTime())) { |
| | | ss.setEndtime(new Date()); |
| | | } |
| | | ss.setStartOutHospTime(serviceSubtaskCountReq.getHospitalStartTime()); |
| | | ss.setEndOutHospTime(serviceSubtaskCountReq.getHospitalEndTime()); |
| | | Double jsRate = serviceSubtaskMapper.selectTimelyRate(ss); |
| | | statistic.setRate(jsRate); |
| | | if (ObjectUtils.isNotEmpty(statistic)) { |
| | |
| | | if (CollectionUtils.isNotEmpty(serviceSubtaskStatistics)) { |
| | | for (ServiceSubtaskStatistic serviceSubtaskStatistic : serviceSubtaskStatistics) { |
| | | //所有满意度数量 |
| | | //满意度模块化后:满意度指数据此改为调用满意度模块门面; |
| | | //模块关闭时门面返回全零 Map(与被迁移实现的实际输出一致),不影响随访统计其它字段。 |
| | | if (CollectionUtils.isNotEmpty(serviceSubtaskCountReq.getDeptcodes())) { |
| | | Map<String, Object> satisfactionIndex = getSatisfactionIndex("1", serviceSubtaskStatistic.getDeptcode(), orgid, serviceSubtaskCountReq.getIvrCategoryid(), serviceSubtaskCountReq.getSvyCategoryid()); |
| | | Map<String, Object> satisfactionIndex = satisfactionFacade.getSatisfactionIndex("1", serviceSubtaskStatistic.getDeptcode(), orgid, serviceSubtaskCountReq.getIvrCategoryid(), serviceSubtaskCountReq.getSvyCategoryid()); |
| | | serviceSubtaskStatistic.setJoyAllCount(satisfactionIndex.get("mydtmzl") != null ? (Integer) satisfactionIndex.get("mydtmzl") : 0); |
| | | serviceSubtaskStatistic.setJoyCount(satisfactionIndex.get("mydtbl") != null ? (Integer) satisfactionIndex.get("mydtbl") : 0); |
| | | serviceSubtaskStatistic.setJoyTotal((Double) satisfactionIndex.get("wcbl")); |
| | | } else if (CollectionUtils.isNotEmpty(serviceSubtaskCountReq.getLeavehospitaldistrictcodes())) { |
| | | Map<String, Object> satisfactionIndex = getSatisfactionIndex("2", serviceSubtaskStatistic.getLeavehospitaldistrictcode(), orgid, serviceSubtaskCountReq.getIvrCategoryid(), serviceSubtaskCountReq.getSvyCategoryid()); |
| | | Map<String, Object> satisfactionIndex = satisfactionFacade.getSatisfactionIndex("2", serviceSubtaskStatistic.getLeavehospitaldistrictcode(), orgid, serviceSubtaskCountReq.getIvrCategoryid(), serviceSubtaskCountReq.getSvyCategoryid()); |
| | | serviceSubtaskStatistic.setJoyAllCount(satisfactionIndex.get("mydtmzl") != null ? (Integer) satisfactionIndex.get("mydtmzl") : 0); |
| | | serviceSubtaskStatistic.setJoyCount(satisfactionIndex.get("mydtbl") != null ? (Integer) satisfactionIndex.get("mydtbl") : 0); |
| | | serviceSubtaskStatistic.setJoyTotal((Double) satisfactionIndex.get("wcbl")); |
| | |
| | | |
| | | return serviceSubtaskStatistics; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取满意度题目总量 |
| | | * type : 1-科室 2病区 |
| | | * code: 科室或病区code |
| | | * |
| | | * @return |
| | | */ |
| | | private Map<String, Object> getSatisfactionIndex(String type, String code, String orgid, Long ivrCategoryid, Long svyCategoryid) { |
| | | Map<String, Object> map = new HashMap<>(); |
| | | List<ServiceTask> serviceTasks = null; |
| | | if (type.equals("1")) { |
| | | ServiceTask serviceTask = new ServiceTask(); |
| | | serviceTask.setDeptcode(code); |
| | | serviceTask.setOrgid(orgid); |
| | | serviceTasks = serviceTaskService.selectServiceTaskList(serviceTask); |
| | | } else if (type.equals("2")) { |
| | | ServiceTask serviceTask = new ServiceTask(); |
| | | serviceTask.setLeavehospitaldistrictcode(code); |
| | | serviceTask.setOrgid(orgid); |
| | | serviceTasks = serviceTaskService.selectServiceTaskList(serviceTask); |
| | | } |
| | | |
| | | //获取满意度题目总量 |
| | | BigDecimal mydtmzlsum = new BigDecimal(0); |
| | | //获取满意度题目填报量 |
| | | BigDecimal mydtblsum = new BigDecimal(0); |
| | | if (CollectionUtils.isNotEmpty(serviceTasks)) { |
| | | //获取满意度题目总量 |
| | | for (ServiceTask serviceTask : serviceTasks) { |
| | | if (serviceTask.getType().equals("1")) { |
| | | //语音 |
| | | Long templateid = serviceTask.getTemplateid(); |
| | | IvrTaskTemplateScript ivrTaskTemplateScript = new IvrTaskTemplateScript(); |
| | | ivrTaskTemplateScript.setTemplateID(templateid); |
| | | ivrTaskTemplateScript.setOrgid(serviceTask.getOrgid()); |
| | | //先默认为1吧,assortid=1 代表是满意度 |
| | | ivrTaskTemplateScript.setScriptAssortid(ivrCategoryid); |
| | | List<IvrTaskTemplateScript> ivrTaskTemplateScripts = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptList(ivrTaskTemplateScript); |
| | | if (CollectionUtils.isNotEmpty(ivrTaskTemplateScripts)) { |
| | | //需要先计算出发送量,再乘以单个问券满意度题目数量,得到满意度题目总量(包含了未做的) |
| | | ServiceSubtaskEntity serviceSubtaskVO = new ServiceSubtaskEntity(); |
| | | serviceSubtaskVO.setTaskid(serviceTask.getTaskid()); |
| | | List<Long> sendstates = new ArrayList<>(Arrays.asList(3L, 5L, 6L)); |
| | | serviceSubtaskVO.setSendstates(sendstates); |
| | | List<ServiceSubtask> serviceSubtaskList = serviceSubtaskMapper.selectServiceSubtaskBySendstate(serviceSubtaskVO); |
| | | if (CollectionUtils.isNotEmpty(serviceSubtaskList)) { |
| | | int mydtmzlCount = ivrTaskTemplateScripts.size() * serviceSubtaskList.size(); |
| | | mydtmzlsum.add(new BigDecimal(mydtmzlCount)); |
| | | } |
| | | |
| | | } |
| | | } else if (serviceTask.getType().equals("2")) { |
| | | //问卷 |
| | | SvyTaskTemplateScript svyTaskTemplateScript = new SvyTaskTemplateScript(); |
| | | svyTaskTemplateScript.setTemplateID(serviceTask.getTemplateid()); |
| | | svyTaskTemplateScript.setOrgid(serviceTask.getOrgid()); |
| | | svyTaskTemplateScript.setCategoryid(svyCategoryid); |
| | | List<SvyTaskTemplateScript> svyTaskTemplateScriptList = svyTaskTemplateScriptService.selectSvyTaskTemplateScriptList(svyTaskTemplateScript); |
| | | if (CollectionUtils.isNotEmpty(svyTaskTemplateScriptList)) { |
| | | ServiceSubtaskEntity serviceSubtaskEntity = new ServiceSubtaskEntity(); |
| | | serviceSubtaskEntity.setTaskid(serviceTask.getTaskid()); |
| | | List<Long> sendstates = new ArrayList<>(Arrays.asList(3L, 5L, 6L)); |
| | | serviceSubtaskEntity.setSendstates(sendstates); |
| | | List<ServiceSubtask> serviceSubtaskList = serviceSubtaskMapper.selectServiceSubtaskBySendstate(serviceSubtaskEntity); |
| | | if (CollectionUtils.isNotEmpty(serviceSubtaskList)) { |
| | | int mydtmzlCount = svyTaskTemplateScriptList.size() * serviceSubtaskList.size(); |
| | | mydtmzlsum.add(new BigDecimal(mydtmzlCount)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | //获取满意度题目填报量 |
| | | ServiceSubtaskDetail serviceSubtaskDetail = new ServiceSubtaskDetail(); |
| | | serviceSubtaskDetail.setTaskid(serviceTask.getTaskid()); |
| | | if (serviceTask.getType().equals("1")) serviceSubtaskDetail.setCategoryid(ivrCategoryid); |
| | | if (serviceTask.getType().equals("2")) serviceSubtaskDetail.setCategoryid(svyCategoryid); |
| | | serviceSubtaskDetail.setOrgid(serviceTask.getOrgid()); |
| | | List<ServiceSubtaskDetail> serviceSubtaskDetails = serviceSubtaskDetailMapper.selectServiceSubtaskDetailList(serviceSubtaskDetail); |
| | | if (CollectionUtils.isNotEmpty(serviceSubtaskDetails)) |
| | | mydtblsum.add(new BigDecimal(serviceSubtaskDetails.size())); |
| | | } |
| | | |
| | | |
| | | } |
| | | //满意度题目总量 |
| | | map.put("mydtmzl", mydtmzlsum.intValue()); |
| | | map.put("mydtbl", mydtblsum.intValue()); |
| | | map.put("wcbl", 0.0); |
| | | if (mydtmzlsum.intValue() != 0) { |
| | | map.put("wcbl", mydtblsum.divide(mydtmzlsum, 2, RoundingMode.HALF_UP).doubleValue()); |
| | | } |
| | | |
| | | return map; |
| | | } |
| | | |
| | | |
| | | private ServiceSubtaskStatistic getStatistic(List<ServiceSubtask> serviceSubtaskList, String followUpCountStyle, Integer visitCount) { |
| | | ServiceSubtaskStatistic serviceSubtaskStatistic = new ServiceSubtaskStatistic(); |
| | |
| | | return serviceSubtaskStatistic; |
| | | } |
| | | |
| | | |
| | | private ServiceSubtaskStatistic getStatisticJoy(List<ServiceSubtask> serviceSubtaskList, List<ServiceSubtaskCount> joySubCount) { |
| | | ServiceSubtaskStatistic serviceSubtaskStatistic = new ServiceSubtaskStatistic(); |
| | | //格式化为两位小数的百分比 |
| | |
| | | if (subCounts == null) { |
| | | subCounts = new ArrayList<>(); |
| | | } |
| | | |
| | | |
| | | for (ServiceSubtask serviceSubtask : serviceSubtaskList) { |
| | | List<ServiceSubtaskCount> subCountTemp = subCounts.stream().filter(r -> r.getSubTaskId().equals(serviceSubtask.getId())).collect(Collectors.toList()); |
| | |
| | | serviceOutPath.setUpdateTime(new Date()); |
| | | serviceOutPath.setUrl(url); |
| | | iServiceOutPathService.updateServiceOutPath(serviceOutPath); |
| | | Map<String, String> map = new HashMap<>(); |
| | | map.put("phone", serviceSubtask.getPhone()); |
| | | map.put("content", sendMagParam.getContent()); |
| | | HttpUtil.postJsonRequest(xhsmsPath, new Gson().toJson(map)); |
| | | //短信发送已收口到 ruoyi-message 模块;与原实现一致:不判断发送结果 |
| | | smsSenderFactory.send(active, SmsSendRequest.of(serviceSubtask.getPhone(), sendMagParam.getContent(), serviceSubtask.getOrgid())); |
| | | redisCache.setCacheObject(subTaskId + "recordAccept-hungup", "1", 10, TimeUnit.MINUTES); |
| | | if (StringUtils.isNotEmpty(serviceSubtask.getRemark())) |
| | | serviceSubtask.setRemark("电话发送拒接,短信补偿发送成功"); |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取随访统计比例 |
| | | * |
| | |
| | | return result; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * @param serviceSubtask |
| | | * @param preachform |
| | |
| | | serviceSubtaskPreachform1.setSendstate(failSendstate); |
| | | serviceSubtaskPreachform1.setRemark(remark); |
| | | serviceSubtaskPreachformMapper.updateServiceSubtaskPreachform(serviceSubtaskPreachform1); |
| | | |
| | | |
| | | //如果当前的preachform已经是最后一个了,那直接将serviceSubtask的sendstate状态改成5就行了(全失败了) |
| | | if (serviceSubtaskPreachform1.getSort() == spSize.size() && failSendstate.equals("4")) { |
| | |
| | | } |
| | | return visitTime; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 计算总计行 |
| | |
| | | String subId = rsaPublicKeyExample.encryptedData(serviceSubtask.getId().toString(), pub_key); |
| | | String taskId = rsaPublicKeyExample.encryptedData(serviceSubtask.getTaskid().toString(), pub_key); |
| | | |
| | | //丽水中医院发送的是微信公众号 |
| | | if (StringUtils.isNotEmpty(serviceSubtask.getOrgid()) && serviceSubtask.getOrgid().equals("47231022633110211A2101")) { |
| | | resultMap = wechatSubTask(subid); |
| | | return resultMap; |
| | | } |
| | | |
| | | //发送数据的封装 |
| | | ServiceOutPath serviceOutPath = new ServiceOutPath(); |
| | | serviceOutPath.setParam1(taskId); |
| | |
| | | sendMagParam.setContent(smsContemt.replace("${wjurl}", sendMagParam.getUrl())); |
| | | } |
| | | |
| | | //短信发送:各站点对接方式差异较大,已收口到 ruoyi-message 模块 |
| | | if (active.equals("nhfy")) { |
| | | Map<String, Object> smsResult = mtSubmitSmUtil.submitSm(sendMagParam.getPhone(), sendMagParam.getContent()); |
| | | if (smsResult != null && smsResult.get("result") != null && smsResult.get("result").toString().equals("0")) { |
| | | SendResult smsResult = smsSenderFactory.send(active, |
| | | SmsSendRequest.of(sendMagParam.getPhone(), sendMagParam.getContent(), serviceSubtask.getOrgid())); |
| | | if (smsResult.isOk()) { |
| | | resultMap.put("code", "200"); |
| | | resultMap.put("msg", "发送成功"); |
| | | serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + ";短信发送成功(手动提交)" : "短信发送成功(手动提交)"); |
| | |
| | | return resultMap; |
| | | } |
| | | |
| | | @Override |
| | | 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); |
| | | } |
| | | List<ServiceSubtaskDetailRatioExport> serviceSubtaskDetailRatioExports = serviceSubtaskMapper.statQuestionOption(taskIds, startOutHospTime, endOutHospTime, scoreStart, scoreEnd, scriptContent, valueType); |
| | | public Map<String,Object> wechatSubTask(Long subid){ |
| | | log.info("wechatSubTask的入参为:{}", subid); |
| | | Map<String, Object> resultMap = new HashMap<>(); |
| | | resultMap.put("code", "500"); |
| | | resultMap.put("msg", "发送失败"); |
| | | try{ |
| | | //获取随访的基本信息 |
| | | ServiceSubtask serviceSubtask = selectServiceSubtaskById(subid); |
| | | //获取患者信息 |
| | | PatArchive patArchive = patArchiveMapper.selectPatArchiveByPatid(serviceSubtask.getPatid()); |
| | | |
| | | // 处理 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); |
| | | } |
| | | } |
| | | RSAPublicKeyExample rsaPublicKeyExample = new RSAPublicKeyExample(); |
| | | String patid = rsaPublicKeyExample.encryptedData(serviceSubtask.getPatid().toString(), pub_key); |
| | | String subId = rsaPublicKeyExample.encryptedData(serviceSubtask.getId().toString(), pub_key); |
| | | String taskId = rsaPublicKeyExample.encryptedData(serviceSubtask.getTaskid().toString(), pub_key); |
| | | String url = localIP + ":" + req_path + "/outsideChainwt?param1=" + taskId + "¶m2=" + patid + "¶m3=" |
| | | + URLEncoder.encode(serviceSubtask.getTaskName(), StandardCharsets.UTF_8.toString()) + "¶m5=false" |
| | | + "$param6=" + subId; |
| | | |
| | | ServiceOutPath serviceOutPath = new ServiceOutPath(); |
| | | serviceOutPath.setParam1(taskId); |
| | | serviceOutPath.setParam2(patid); |
| | | serviceOutPath.setParam3(serviceSubtask.getTaskName()); |
| | | serviceOutPath.setParam6(subId); |
| | | serviceOutPath.setCreateTime(new Date()); |
| | | serviceOutPath.setOrgid(serviceSubtask.getOrgid()); |
| | | iServiceOutPathService.insertServiceOutPath(serviceOutPath); |
| | | String format = String.format("%03X", serviceOutPath.getId()); |
| | | serviceOutPath.setRadix(format); |
| | | serviceOutPath.setUpdateTime(new Date()); |
| | | serviceOutPath.setUrl(url); |
| | | iServiceOutPathService.updateServiceOutPath(serviceOutPath); |
| | | |
| | | //his患者id校验(与原实现一致:先校验这一项) |
| | | if (StringUtils.isEmpty(serviceSubtask.getPatidHis())) { |
| | | resultMap.put("code", -1); |
| | | resultMap.put("message", "发送异常:" + "【公众号】公众号发送失败,his系统的患者id为空"); |
| | | return resultMap; |
| | | } |
| | | |
| | | //机构配置校验、报文签名与返回判定均已收口到 ruoyi-message 的发送方实现 |
| | | WechatSendRequest wechatRequest = new WechatSendRequest(); |
| | | wechatRequest.setOrgid(serviceSubtask.getOrgid()); |
| | | wechatRequest.setIdCard(serviceSubtask.getSfzh()); |
| | | wechatRequest.setUrl(url); |
| | | wechatRequest.setTitle(serviceSubtask.getTaskName()); |
| | | wechatRequest.setContent(serviceSubtask.getTaskDesc()); |
| | | wechatRequest.setPhone(patArchive.getTelcode()); |
| | | wechatRequest.setPatientName(serviceSubtask.getSendname()); |
| | | wechatRequest.setPatidHis(patArchive.getPatidHis()); |
| | | SendResult wechatResult = wechatSenderFactory.send(active, wechatRequest); |
| | | |
| | | //发送完成后,修改状态 |
| | | serviceSubtask.setSendstate(3L); |
| | | if (wechatResult.isOk()) { |
| | | serviceSubtask.setResult("success"); |
| | | resultMap.put("code", "200"); |
| | | resultMap.put("msg", "发送成功"); |
| | | } else { |
| | | // targetvalue 不包含 &,直接添加到结果 |
| | | result.add(export); |
| | | log.error("【sfHandlle】公众号发送失败,患者信息:{},错误信息:{}", serviceSubtask, wechatResult.getMessage()); |
| | | resultMap.put("code", -1); |
| | | resultMap.put("message", "【公众号】公众号发送失败"); |
| | | return resultMap; |
| | | } |
| | | } catch (Exception e) { |
| | | String guid = UUID.randomUUID().toString(); |
| | | log.error("【sfHandlle】长期任务公众号发送失败,原因:{},GUID:{}", e.getMessage(), guid); |
| | | resultMap.put("code", -1); |
| | | resultMap.put("message", "【公众号】长期任务公众号发送失败,原因:" + e.getMessage()); |
| | | return resultMap; |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /* |
| | | * 根据科室或病区统计问卷选项(科室、题目查询的科室评分表) |
| | | */ |
| | | @Override |
| | | public List<QuestionCountKsOrBqDTO> statQuestionOptionByKsOrBq(QuestionCountKsOrBqVO vo) { |
| | | List<QuestionCountKsOrBqDTO> sourceList = serviceSubtaskMapper.statQuestionOptionByKsOrBq(vo); |
| | | if (CollectionUtils.isEmpty(sourceList)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | boolean byDept = vo.getLeaveldeptcodes() != null; |
| | | boolean byDistrict = !byDept && vo.getLeavehospitaldistrictcodes() != null; |
| | | if (!byDept && !byDistrict) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | Map<String, QuestionCountKsOrBqDTO> map = new LinkedHashMap<>(); |
| | | |
| | | for (QuestionCountKsOrBqDTO dto : sourceList) { |
| | | if (StringUtils.isEmpty(dto.getTargetvalue())) { |
| | | continue; |
| | | } |
| | | |
| | | String groupName = byDept ? dto.getDeptname() : dto.getLeavehospitaldistrictname(); |
| | | if (StringUtils.isEmpty(groupName)) { |
| | | continue; |
| | | } |
| | | |
| | | // 关键:两边都用 trimAll,兼容全角空格 |
| | | String asrtext = trimAll(dto.getAsrtext()); |
| | | String questionKey = dto.getTaskid() + "\u0001" + trimAll(dto.getQeustionText()); |
| | | |
| | | for (String raw : dto.getTargetvalue().split("&")) { |
| | | String option = trimAll(raw); // ← 修复点:用 trimAll 而不是 trim |
| | | if (option.isEmpty()) { |
| | | continue; |
| | | } |
| | | |
| | | String key = buildKey(groupName, option, questionKey); |
| | | QuestionCountKsOrBqDTO item = map.get(key); |
| | | if (item == null) { |
| | | item = DtoConversionUtils.sourceToTarget(dto, QuestionCountKsOrBqDTO.class); |
| | | item.setTargetvalue(option); // 输出的 targetvalue 就是当前选项 |
| | | item.setAsrtext(option); |
| | | item.setAnswerCount("0"); |
| | | map.put(key, item); |
| | | } |
| | | |
| | | if (option.equals(asrtext)) { |
| | | item.setAnswerCount(String.valueOf(parseCount(item.getAnswerCount()) + 1)); |
| | | item.setSingleScore(String.valueOf(new BigDecimal(item.getSingleScore()).add(new BigDecimal(dto.getSingleScore())))); |
| | | } |
| | | } |
| | | } |
| | | ArrayList<QuestionCountKsOrBqDTO> questionCountKsOrBqDTOS = new ArrayList<>(map.values()); |
| | | for (QuestionCountKsOrBqDTO questionCountKsOrBqDTO : questionCountKsOrBqDTOS) { |
| | | String singleScore = questionCountKsOrBqDTO.getSingleScore(); |
| | | String answerCount = questionCountKsOrBqDTO.getAnswerCount(); |
| | | |
| | | BigDecimal avg; |
| | | if (StringUtils.isEmpty(answerCount) |
| | | || BigDecimal.ZERO.compareTo(new BigDecimal(answerCount.trim())) == 0) { |
| | | // 除数为 0,避免 / by zero |
| | | avg = BigDecimal.ZERO; |
| | | } else { |
| | | avg = new BigDecimal(singleScore) |
| | | .divide(new BigDecimal(answerCount.trim()), 2, RoundingMode.HALF_UP); |
| | | } |
| | | questionCountKsOrBqDTO.setAvgScore(avg.toPlainString()); |
| | | |
| | | } |
| | | |
| | | return questionCountKsOrBqDTOS; |
| | | } |
| | | |
| | | |
| | | |
| | | @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 -> { |
| | | if (e == null || export == null) { |
| | | return false; |
| | | } |
| | | String q1 = e.getQuestiontext() != null ? e.getQuestiontext() : ""; |
| | | String q2 = export.getQuestiontext() != null ? export.getQuestiontext() : ""; |
| | | String t1 = value.trim(); |
| | | String t2 = e.getTargetvalue() != null ? e.getTargetvalue() : ""; |
| | | return q1.equals(q2) && t1.equals(t2); |
| | | }).findFirst().orElse(null); |
| | | if (existingExport != null && "0".equals(existingExport.getRatio()) && "0".equals(existingExport.getCount())) { |
| | | //如果存在,则将 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 && !"0".equals(existingExport.getRatio())) { |
| | | 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; |
| | | } |
| | | |
| | | /** |
| | | * 标准化问卷答案文本:去掉 A/B/C/D 单字母前缀和后续空格 |
| | | * 例如 "A 是" -> "是","B 否" -> "否" |
| | | */ |
| | | private String normalizeOptionAnswer(String answer) { |
| | | if (StringUtils.isBlank(answer)) { |
| | | return ""; |
| | | } |
| | | String trimmed = answer.trim(); |
| | | if (trimmed.length() >= 3 && trimmed.charAt(1) == ' ') { |
| | | char firstChar = trimmed.charAt(0); |
| | | if (firstChar >= 'A' && firstChar <= 'Z') { |
| | | return trimmed.substring(2).trim(); |
| | | } |
| | | } |
| | | return trimmed; |
| | | return resultMap; |
| | | } |
| | | |
| | | private Boolean saveServiceSubtaskDetail(ServiceSubtask serviceSubtask) { |
| | |
| | | Date newDate = new Date(); |
| | | serviceSubtask.setLongSendTime(newDate); |
| | | serviceSubtask.setVisitTime(newDate); |
| | | // 院区:/scanGenerateSubtask 是匿名接口(SecurityConfig permitAll),取不到登录用户; |
| | | // 而 service_task.campusid 在本库是占位值 '1',直接沿用会让统计接口(@AddOrgId 按院区过滤)查不到数据。 |
| | | // 这里用任务配置的病区(其次科室)反查 sys_dept.campusid 落库,保证问卷统计能按院区取到。 |
| | | String campusIdByDept = queryCampusIdByDeptCode(serviceSubtask.getLeavehospitaldistrictcode(), serviceTask.getOrgid()); |
| | | if (StringUtils.isEmpty(campusIdByDept)) { |
| | | campusIdByDept = queryCampusIdByDeptCode(serviceSubtask.getDeptcode(), serviceTask.getOrgid()); |
| | | } |
| | | if (StringUtils.isNotEmpty(campusIdByDept)) { |
| | | serviceSubtask.setCampusid(campusIdByDept); |
| | | } |
| | | // 只配了科室(没有病区)的满意度任务:这道答卷在按病区筛选的报表里统计不到,留日志便于运维发现 |
| | | if (StringUtils.isEmpty(serviceSubtask.getLeavehospitaldistrictcode()) |
| | | && ("6".equals(serviceSubtask.getServiceType()) || "14".equals(serviceSubtask.getServiceType()))) { |
| | | log.warn("【扫码生成任务】满意度子任务没有病区,按病区筛选的报表将统计不到该答卷:taskid={}, deptcode={}, campusid={}", |
| | | serviceSubtask.getTaskid(), serviceSubtask.getDeptcode(), serviceSubtask.getCampusid()); |
| | | } |
| | | return serviceSubtask; |
| | | } |
| | | |
| | | /** |
| | | * 按科室/病区编码反查所属院区(查不到返回 null) |
| | | */ |
| | | private String queryCampusIdByDeptCode(String deptCode, String orgid) { |
| | | if (StringUtils.isEmpty(deptCode)) { |
| | | return null; |
| | | } |
| | | try { |
| | | SysDept sysDept = sysDept2Mapper.selectDeptByCode(deptCode, orgid); |
| | | return sysDept == null ? null : sysDept.getCampusid(); |
| | | } catch (Exception e) { |
| | | log.warn("按编码反查院区失败:deptCode={}, orgid={}, err={}", deptCode, orgid, e.getMessage()); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | public PhoneCallBackSHVO getFirstQues(PhoneCallReqYQVO phoneCallReqYQVO) { |
| | | //用于返回 |
| | | PhoneCallBackSHVO phoneCallBackSHVO = new PhoneCallBackSHVO(); |
| | |
| | | } |
| | | return phoneCallBackSHVO; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 三汇ASR结果处理 |
| | |
| | | Object cacheObject = redisCache.getCacheObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId()); |
| | | if (cacheObject == null || (Integer) cacheObject < ivrTaskTemplate.getMateNum()) { |
| | | |
| | | |
| | | // 获取选项集合中的 optiondesc 用于引导患者 |
| | | List<IvrTaskTemplateTargetoption> optionList = ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList(); |
| | | String optionDescStr = ""; |
| | |
| | | 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(); |
| | | String voiceText = phoneCallReqYQVO.getAsrtext(); |
| | | String questionText = StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrtext()) ? ivrTaskTemplateScriptVO.getIvrtext() : ivrTaskTemplateScriptVO.getScriptContent(); |
| | | Integer matched = -1; |
| | | log.info("AI匹配:url = {},questionText = {},voiceText = {},options = {},uuid ={}", |
| | | url,questionText,voiceText,options,uuid); |
| | | matched = matchOptionIndex(url, questionText, voiceText, options, uuid); |
| | | //说明匹配正确了 |
| | | if (matched != null && matched >= 0) { |
| | |
| | | phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask.getJsy()); |
| | | //记录状态 |
| | | setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9"); |
| | | return phoneCallBackYQVO; |
| | | back = phoneCallBackYQVO; |
| | | return back; |
| | | } |
| | | |
| | | flag = 1; |
| | |
| | | redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop"); |
| | | // //获取下一题 |
| | | log.error("获取下一题的信息:{}", ivrTaskTemplateScriptVO); |
| | | if (ivrTaskTemplateScriptVO.getBranchFlag().equals("1") || ivrTaskTemplateScriptVO.getBranchFlag().equals("0") && ivrTaskTemplateScriptVO.getNextScriptno() != null && ivrTaskTemplateScriptVO.getNextScriptno() != 0) { |
| | | //branchFlag为null时按分支0处理,避免NPE |
| | | if ("1".equals(ivrTaskTemplateScriptVO.getBranchFlag()) || "0".equals(ivrTaskTemplateScriptVO.getBranchFlag()) && ivrTaskTemplateScriptVO.getNextScriptno() != null && ivrTaskTemplateScriptVO.getNextScriptno() != 0) { |
| | | Long nextQuestion = null; |
| | | if (ivrTaskTemplateScriptVO.getBranchFlag().equals("1")) { |
| | | if ("1".equals(ivrTaskTemplateScriptVO.getBranchFlag())) { |
| | | nextQuestion = chosenOption.getNextQuestion(); |
| | | //更新分数 |
| | | double score = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE"); |
| | |
| | | redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES); |
| | | phoneCallBackYQVO.setType("text"); |
| | | phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask.getJsy()); |
| | | //将结果写进detail表 |
| | | ServiceSubTaskDetailReq ssdReq = new ServiceSubTaskDetailReq(); |
| | | List<ServiceSubtaskDetail> serviceSubtaskDetailList2 = new ArrayList<>(); |
| | | serviceSubtaskDetailList.add(getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate)); |
| | | serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList2); |
| | | ssdReq.setGuid(phoneCallReqYQVO.getGuid()); |
| | | ssdReq.setOrgid(phoneCallReqYQVO.getOrgid()); |
| | | saveQuestionAnswerPhone(ssdReq); |
| | | return phoneCallBackYQVO; |
| | | //明细在本题匹配成功时已落库(见上方 saveQuestionAnswerPhone),这里不再重复保存 |
| | | back = phoneCallBackYQVO; |
| | | return back; |
| | | } |
| | | |
| | | for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) { |
| | |
| | | redisCache.deleteObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid()); |
| | | } |
| | | } |
| | | //下一题脚本没找到时,兜底重问当前题,避免调用方误判为未处理而退回正则识别 |
| | | if (StringUtils.isEmpty(phoneCallBackYQVO.getValue())) { |
| | | log.error("下一题脚本未找到,重问当前题,nextQuestion:{}", nextQuestion); |
| | | phoneCallBackYQVO.setType("text"); |
| | | phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue()); |
| | | phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + getObject(serviceSubtask, questionText)); |
| | | } |
| | | back = phoneCallBackYQVO; |
| | | return back; |
| | | } else if (ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getNextScriptno() == 0) { |
| | | ServiceTask serviceTask1 = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid()); |
| | | phoneCallBackYQVO.setType("text"); |
| | |
| | | //在redis中保存一下结束语,在调用挂电话的方法时删除 |
| | | ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid()); |
| | | redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES); |
| | | // return phoneCallBackYQVO; |
| | | //结束语已拼进返回值,挂机由平台按返回值执行 |
| | | back = phoneCallBackYQVO; |
| | | return back; |
| | | |
| | | } |
| | | } else { |
| | | log.info("没有匹配上----------------------------"); |
| | | //flag=0,说明没 匹配上,也要把患者说的话记录下来 |
| | | if (matched == ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().size() - 1 && flag == 0) { |
| | | ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq(); |
| | | List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>(); |
| | | 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); |
| | | //AI没有匹配上:由AI分支自己按"没问清就重问"的次数处理,不再退回正则识别 |
| | | log.info("ai没有匹配上----------------------------"); |
| | | //没匹配上也要把患者说的话记录下来(matchedtext置空,与正则路径口径一致) |
| | | ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq(); |
| | | List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>(); |
| | | 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); |
| | | //如果没有 匹配上,这个必须为null |
| | | serviceSubtaskDetailList.get(0).setMatchedtext(""); |
| | | serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList); |
| | | serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid()); |
| | | serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid()); |
| | | saveQuestionAnswerPhone(serviceSubTaskDetailReq); |
| | | |
| | | String mateKey = phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid(); |
| | | Integer count = null; |
| | | Object countObj = redisCache.getCacheObject(mateKey); |
| | | if (ObjectUtils.isNotEmpty(countObj)) count = (Integer) countObj; |
| | | if (count != null && count >= ivrTaskTemplate.getMateNum()) { |
| | | //没匹配次数已经用满,不能再重问了 |
| | | boolean noNextQuestion = ("0".equals(ivrTaskTemplateScriptVO.getBranchFlag()) || "1".equals(ivrTaskTemplateScriptVO.getBranchFlag())) |
| | | && (ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getNextScriptno() == 0); |
| | | if (noNextQuestion) { |
| | | //已经是最后一题,直接挂机 |
| | | ServiceTask serviceTask1 = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid()); |
| | | phoneCallBackYQVO.setType("text"); |
| | | phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue()); |
| | | phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask1.getJsy()); |
| | | //更新一下分数 |
| | | Double score = null; |
| | | Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE"); |
| | | if (ObjectUtils.isNotEmpty(scoreObj)) score = (Double) scoreObj; |
| | | serviceSubtask.setScore(String.valueOf(score)); |
| | | serviceSubtask.setFinishtime(new Date()); |
| | | serviceSubtask.setSendstate(6L); |
| | | serviceSubtask.setRemark("电话拨打已完成"); |
| | | serviceSubtaskMapper.updateServiceSubtask(serviceSubtask); |
| | | //记录状态 |
| | | setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9"); |
| | | //在redis中保存一下结束语,在调用挂电话的方法时删除 |
| | | ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid()); |
| | | redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES); |
| | | //去redis中,把该子任务ID删除 |
| | | Long id = serviceSubtask.getId(); |
| | | Map<String, String> map = delRedisValue(null, id.toString()); |
| | | if (ObjectUtils.isNotEmpty(map)) { |
| | | redisCache.setCacheObject(map.get("cacheName"), map.get("val")); |
| | | redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone()); |
| | | redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE"); |
| | | } |
| | | } else { |
| | | //还有下一题,按nextScriptno继续提问 |
| | | for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) { |
| | | if (script.getSort() == ivrTaskTemplateScriptVO.getNextScriptno().intValue()) { |
| | | phoneCallBackYQVO.setType("text"); |
| | | phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue()); |
| | | String scriptContent = StringUtils.isNotEmpty(script.getIvrtext()) ? script.getIvrtext() : script.getScriptContent(); |
| | | phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + getObject(serviceSubtask, scriptContent)); |
| | | //将该患者的Redis中的题目ID,进行修改 |
| | | redisCache.setCacheObject(phoneCallReqYQVO.getTaskid().trim() + "-" + phoneCallReqYQVO.getPhone().trim(), script.getId().toString(), 120, TimeUnit.MINUTES); |
| | | //更新一下分数 |
| | | Double score = null; |
| | | Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE"); |
| | | if (ObjectUtils.isNotEmpty(scoreObj)) score = (Double) scoreObj; |
| | | score = BigDecimal.valueOf(score).add(new BigDecimal(script.getScore())).doubleValue(); |
| | | redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score, 120, TimeUnit.MINUTES); |
| | | //换题成功,没匹配次数清零 |
| | | redisCache.deleteObject(mateKey); |
| | | } |
| | | } |
| | | } |
| | | serviceSubtaskDetailList.add(serviceSubtaskDetail); |
| | | //如果没有 匹配上,这个必须为null |
| | | serviceSubtaskDetailList.get(0).setMatchedtext(""); |
| | | serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList); |
| | | serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid()); |
| | | serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid()); |
| | | saveQuestionAnswerPhone(serviceSubTaskDetailReq); |
| | | } else { |
| | | //还没问满次数,用noMatchText引导患者重答 |
| | | redisCache.setCacheObject(mateKey, count == null ? 1 : count + 1, 120, TimeUnit.MINUTES); |
| | | phoneCallBackYQVO.setType("text"); |
| | | phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue()); |
| | | phoneCallBackYQVO.setValue(ivrTaskTemplateScriptVO.getNoMatchText() + getObject(serviceSubtask, StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrtext()) ? ivrTaskTemplateScriptVO.getIvrtext() : ivrTaskTemplateScriptVO.getScriptContent())); |
| | | } |
| | | back = phoneCallBackYQVO; |
| | | return back; |
| | | } |
| | | return back; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 问题结果占比 |
| | | * |
| | | * @param questionResultVO |
| | | * @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; |
| | | } |
| | | |
| | | //获取任务组信息 |
| | |
| | | log.info("ai匹配成功,匹配结果是:" + matchedText); |
| | | } |
| | | |
| | | private static String trimAll(String s) { |
| | | if (s == null) { |
| | | return ""; |
| | | } |
| | | int start = 0; |
| | | int end = s.length(); |
| | | while (start < end && isBlankChar(s.charAt(start))) { |
| | | start++; |
| | | } |
| | | while (end > start && isBlankChar(s.charAt(end - 1))) { |
| | | end--; |
| | | } |
| | | return s.substring(start, end); |
| | | } |
| | | |
| | | private static Boolean isBlankChar(char c) { |
| | | return c == '\u3000' // 全角空格 |
| | | || c == ' ' // 半角空格 |
| | | || c == '\t' |
| | | || c == '\n' |
| | | || c == '\r' |
| | | || c == '\f' |
| | | || Character.isWhitespace(c); |
| | | } |
| | | |
| | | private String buildKey(String groupName, String option, String questionKey) { |
| | | return questionKey + KEY_SEP + groupName + KEY_SEP + option; |
| | | } |
| | | |
| | | private static int parseCount(String s) { |
| | | if (StringUtils.isEmpty(s)) { |
| | | return 0; |
| | | } |
| | | try { |
| | | return Integer.parseInt(s.trim()); |
| | | } catch (NumberFormatException e) { |
| | | return 0; |
| | | } |
| | | } |
| | | // --------------------------------------------------------------------- |
| | | // 说明:原私有 getWXCode / encode 已随发送方一起收口到 ruoyi-message 模块 |
| | | // (XinHuaMpApi),此处已删除,避免三份拷贝各写一套签名逻辑。 |
| | | // --------------------------------------------------------------------- |
| | | } |