| | |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.github.pagehelper.util.StringUtil; |
| | | import com.ruoyi.common.utils.DateUtils; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.smartor.domain.*; |
| | | import com.smartor.domain.DTO.DetailTraceDealDTO; |
| | | import com.smartor.domain.VO.DetailTraceDealVO; |
| | | import com.smartor.domain.VO.SatisfactionExceptConfigQuestionReqVO; |
| | | import com.smartor.domain.VO.ServiceSubtaskDetailTraceVO; |
| | | import com.smartor.mapper.ServiceSubtaskDetailMapper; |
| | | import com.smartor.mapper.ServiceSubtaskDetailTraceMapper; |
| | | import com.smartor.mapper.SvyLibScriptCategoryMapper; |
| | | import com.smartor.mapper.SvyLibTemplateScriptMapper; |
| | | import com.smartor.service.*; |
| | | import org.apache.commons.collections4.CollectionUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | |
| | | @Autowired |
| | | private ServiceSubtaskDetailMapper serviceSubtaskDetailMapper; |
| | | |
| | | @Autowired |
| | | private ISvyLibScriptCategoryService iSvyLibScriptCategoryService; |
| | | |
| | | @Autowired |
| | | private SvyLibScriptCategoryMapper svyLibScriptCategoryMapper; |
| | | |
| | | @Autowired |
| | | private ISvyLibTemplateScriptService isvyLibTemplateScriptService; |
| | | |
| | | @Autowired |
| | | private IIvrLibaScriptAssortService iIvrLibaScriptAssortService; |
| | | |
| | | @Autowired |
| | | private IIvrLibaTemplateScriptService iIvrLibaTemplateScriptService; |
| | | |
| | | /** |
| | | * 查询随访任务异常记录跟踪 |
| | |
| | | /** |
| | | * 查询随访任务异常记录跟踪列表 |
| | | * |
| | | * @param serviceSubtaskDetailTrace 随访任务异常记录跟踪 |
| | | * @param serviceSubtaskDetailTraceVO 随访任务异常记录跟踪 |
| | | * @return 随访任务异常记录跟踪 |
| | | */ |
| | | @Override |
| | | public List<ServiceSubtaskDetailTrace> selectServiceSubtaskDetailTtraceList(ServiceSubtaskDetailTrace serviceSubtaskDetailTrace) { |
| | | return serviceSubtaskDetailTraceMapper.selectServiceSubtaskDetailTtraceList(serviceSubtaskDetailTrace); |
| | | public List<ServiceSubtaskDetailTrace> selectServiceSubtaskDetailTtraceList(ServiceSubtaskDetailTraceVO serviceSubtaskDetailTraceVO) { |
| | | List<ServiceSubtaskDetailTrace> serviceSubtaskDetailTraces = serviceSubtaskDetailTraceMapper.selectServiceSubtaskDetailTtraceList(serviceSubtaskDetailTraceVO); |
| | | for (ServiceSubtaskDetailTrace serviceSubtaskDetailTrace : serviceSubtaskDetailTraces) { |
| | | if (StringUtils.isNotEmpty(serviceSubtaskDetailTrace.getPatdesc())) { |
| | | serviceSubtaskDetailTrace.setPatdescJson(JSON.parseObject(serviceSubtaskDetailTrace.getPatdesc())); |
| | | } |
| | | } |
| | | return serviceSubtaskDetailTraces; |
| | | } |
| | | |
| | | @Override |
| | | public List<DetailTraceDealDTO> tracedeallist(DetailTraceDealVO detailTraceDealVO) { |
| | | // 获取所有需要处理的记录 |
| | | List<ServiceSubtaskDetailTrace> tracedeallist = serviceSubtaskDetailTraceMapper.tracedeallist(detailTraceDealVO); |
| | | |
| | | // 按 questiontext 分组 |
| | | Map<String, List<ServiceSubtaskDetailTrace>> groupByQuestion = tracedeallist.stream() |
| | | .filter(t -> t.getQuestiontext() != null) |
| | | .collect(Collectors.groupingBy(ServiceSubtaskDetailTrace::getQuestiontext)); |
| | | public Map<String, Object> tracedeallist(DetailTraceDealVO detailTraceDealVO) { |
| | | // 优化前:① 这条 SQL 没有 limit,每次都把整表 trace(含 patdesc/recordurl 大字段)拉回 Java 再分组; |
| | | // ② 每个问题都拿 scriptid 去 service_subtask_detail 查一次全表只为取 size(N+1); |
| | | // ③ controller 把这个方法调了两遍(第二遍为了总数),等于上面两步各跑两次。 |
| | | // 优化后:固定的 5 条聚合 SQL,DB 只处理当前页的题目。 |
| | | // 1) 当前页的问题分组聚合(分页下推到 SQL) |
| | | List<Map<String, Object>> groupRows = serviceSubtaskDetailTraceMapper.selectTraceDealGroups(detailTraceDealVO); |
| | | // 2) 问题总数(分页 total,全量口径) |
| | | long questionTotal = serviceSubtaskDetailTraceMapper.countTraceDealQuestions(detailTraceDealVO); |
| | | // 3) 汇总统计(全量口径,不受分页影响) |
| | | Map<String, Object> sumRow = serviceSubtaskDetailTraceMapper.sumTraceDeal(detailTraceDealVO); |
| | | long totalException = toLong(sumRow == null ? null : sumRow.get("totalException"), 0L); |
| | | long yesDealException = toLong(sumRow == null ? null : sumRow.get("yesDeal"), 0L); |
| | | long noDealException = totalException - yesDealException; |
| | | |
| | | List<DetailTraceDealDTO> detailTraceDealDTOList = new ArrayList<>(); |
| | | for (Map.Entry<String, List<ServiceSubtaskDetailTrace>> entry : groupByQuestion.entrySet()) { |
| | | List<ServiceSubtaskDetailTrace> group = entry.getValue(); |
| | | DetailTraceDealDTO dto = new DetailTraceDealDTO(); |
| | | |
| | | // 问题内容 |
| | | dto.setQuestiontext(entry.getKey()); |
| | | |
| | | // 负责科室(去重) |
| | | List<Map<String, Object>> responsibleDept = group.stream() |
| | | .filter(t -> t.getTodeptcode() != null) |
| | | .collect(Collectors.collectingAndThen( |
| | | Collectors.toMap( |
| | | ServiceSubtaskDetailTrace::getTodeptcode, |
| | | t -> { |
| | | Map<String, Object> deptMap = new HashMap<>(); |
| | | deptMap.put("deptName", t.getTodeptname()); |
| | | deptMap.put("deptCode", t.getTodeptcode()); |
| | | return deptMap; |
| | | }, |
| | | (existing, replacement) -> existing |
| | | ), |
| | | map -> new ArrayList<>(map.values()) |
| | | )); |
| | | dto.setResponsibleDept(responsibleDept); |
| | | |
| | | // 有效填写数:通过组内的 scriptid 去 service_subtask_detail 表查询 |
| | | List<Long> scriptIds = group.stream() |
| | | .map(ServiceSubtaskDetailTrace::getScriptid) |
| | | if (CollectionUtils.isNotEmpty(groupRows)) { |
| | | // 本页涉及的问题文本,后面 3 条查询都按它过滤 |
| | | List<String> questiontexts = groupRows.stream() |
| | | .map(row -> row.get("questiontext")) |
| | | .filter(Objects::nonNull) |
| | | .map(String::valueOf) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | long effectiveFillNum = 0L; |
| | | if (CollectionUtils.isNotEmpty(scriptIds)) { |
| | | ServiceSubtaskDetail query = new ServiceSubtaskDetail(); |
| | | query.setDelFlag("0"); |
| | | for (Long scriptId : scriptIds) { |
| | | query.setScriptid(scriptId); |
| | | List<ServiceSubtaskDetail> details = serviceSubtaskDetailMapper.selectServiceSubtaskDetailList(query); |
| | | effectiveFillNum += CollectionUtils.isEmpty(details) ? 0 : details.size(); |
| | | |
| | | // 4) 本页问题的负责科室(按科室编号去重) |
| | | Map<String, List<Map<String, Object>>> deptMap = new HashMap<>(); |
| | | // 5) 本页问题的最新处理人/处理时间 |
| | | Map<String, Map<String, Object>> latestHandleMap = new HashMap<>(); |
| | | if (CollectionUtils.isNotEmpty(questiontexts)) { |
| | | for (Map<String, Object> row : serviceSubtaskDetailTraceMapper.selectTraceDealDepts(detailTraceDealVO, questiontexts)) { |
| | | String questiontext = row.get("questiontext") == null ? null : String.valueOf(row.get("questiontext")); |
| | | if (questiontext == null) continue; |
| | | Map<String, Object> deptMapItem = new HashMap<>(); |
| | | deptMapItem.put("deptName", row.get("deptName")); |
| | | deptMapItem.put("deptCode", row.get("deptCode")); |
| | | deptMap.computeIfAbsent(questiontext, key -> new ArrayList<>()).add(deptMapItem); |
| | | } |
| | | |
| | | for (Map<String, Object> row : serviceSubtaskDetailTraceMapper.selectTraceDealLatestHandle(detailTraceDealVO, questiontexts)) { |
| | | String questiontext = row.get("questiontext") == null ? null : String.valueOf(row.get("questiontext")); |
| | | if (questiontext == null) continue; |
| | | // 同一个最大处理时间可能有多条,保留第一条即可(原实现取组内 handle_time 最大的那条) |
| | | latestHandleMap.putIfAbsent(questiontext, row); |
| | | } |
| | | } |
| | | |
| | | // 异常填写数 = 当前分组的 List 数量 |
| | | long exceptionFillNum = group.size(); |
| | | // 6) 本页问题的有效填写数:一次 group by 查回,替代逐个 scriptid 查询 |
| | | // 一个问题可能对应多个 scriptid,所以先把 questiontext -> scriptid 列表解开, |
| | | // 再把该问题所有 scriptid 的填写数相加(与原实现 for 循环累加的口径一致) |
| | | Map<String, List<Long>> questionScriptIds = new LinkedHashMap<>(); |
| | | Set<Long> scriptIdSet = new LinkedHashSet<>(); |
| | | for (Map<String, Object> row : groupRows) { |
| | | String questiontext = row.get("questiontext") == null ? null : String.valueOf(row.get("questiontext")); |
| | | if (questiontext == null) continue; |
| | | List<Long> ids = parseScriptIds(row.get("scriptids")); |
| | | questionScriptIds.put(questiontext, ids); |
| | | scriptIdSet.addAll(ids); |
| | | } |
| | | Map<Long, Long> effectiveFillMap = new HashMap<>(); |
| | | if (CollectionUtils.isNotEmpty(scriptIdSet)) { |
| | | for (Map<String, Object> row : serviceSubtaskDetailMapper.countDetailByScriptIds(new ArrayList<>(scriptIdSet))) { |
| | | Long scriptId = toLongObj(row.get("scriptid")); |
| | | if (scriptId == null) continue; |
| | | effectiveFillMap.put(scriptId, toLong(row.get("cnt"), 0L)); |
| | | } |
| | | } |
| | | |
| | | Map<String, Object> fillSituation = new HashMap<>(); |
| | | fillSituation.put("effectiveFillNum", effectiveFillNum); |
| | | fillSituation.put("exceptionFillNum", exceptionFillNum); |
| | | dto.setFillSituation(fillSituation); |
| | | // 7) 组装返回结构(顺序 = SQL 的 order by questiontext,保证分页稳定) |
| | | for (Map<String, Object> row : groupRows) { |
| | | String questiontext = row.get("questiontext") == null ? null : String.valueOf(row.get("questiontext")); |
| | | if (questiontext == null) continue; |
| | | |
| | | // 异常问题处理数量:从 handleFlag 统计 |
| | | long yesDeal = group.stream().filter(t -> "1".equals(t.getHandleFlag())).count(); |
| | | long noDeal = group.stream().filter(t -> !"1".equals(t.getHandleFlag())).count(); |
| | | Map<String, Object> exceptionQuesNum = new HashMap<>(); |
| | | exceptionQuesNum.put("yesDeal", yesDeal); |
| | | exceptionQuesNum.put("noDeal", noDeal); |
| | | exceptionQuesNum.put("all", exceptionFillNum); |
| | | dto.setExceptionQuesNum(exceptionQuesNum); |
| | | DetailTraceDealDTO dto = new DetailTraceDealDTO(); |
| | | dto.setQuestiontext(questiontext); |
| | | Long scriptId = toLongObj(row.get("scriptid")); |
| | | dto.setScriptid(scriptId); |
| | | dto.setScriptids(parseScriptIds(row.get("scriptids"))); |
| | | Object templateType = row.get("templateType"); |
| | | dto.setTemplateType(templateType == null ? null : Integer.valueOf(String.valueOf(templateType))); |
| | | dto.setResponsibleDept(deptMap.getOrDefault(questiontext, new ArrayList<>())); |
| | | |
| | | // 最新处理人和处理时间(取 handleTime 最新的一条) |
| | | group.stream() |
| | | .filter(t -> t.getHandleTime() != null) |
| | | .max(Comparator.comparing(ServiceSubtaskDetailTrace::getHandleTime)) |
| | | .ifPresent(latest -> { |
| | | dto.setHandleBy(latest.getHandleBy()); |
| | | dto.setHandleTime(latest.getHandleTime()); |
| | | }); |
| | | long exceptionFillNum = toLong(row.get("exceptionFillNum"), 0L); |
| | | // 该问题下所有 scriptid 的有效填写数之和(一个 questiontext 可能对应多个 scriptid) |
| | | long effectiveFillNum = 0L; |
| | | for (Long sid : questionScriptIds.getOrDefault(questiontext, new ArrayList<>())) { |
| | | effectiveFillNum += effectiveFillMap.getOrDefault(sid, 0L); |
| | | } |
| | | Map<String, Object> fillSituation = new HashMap<>(); |
| | | fillSituation.put("effectiveFillNum", effectiveFillNum); |
| | | fillSituation.put("exceptionFillNum", exceptionFillNum); |
| | | dto.setFillSituation(fillSituation); |
| | | |
| | | detailTraceDealDTOList.add(dto); |
| | | long yesDeal = toLong(row.get("yesDeal"), 0L); |
| | | long noDeal = toLong(row.get("noDeal"), 0L); |
| | | Map<String, Object> exceptionQuesNum = new HashMap<>(); |
| | | exceptionQuesNum.put("yesDeal", yesDeal); |
| | | exceptionQuesNum.put("noDeal", noDeal); |
| | | exceptionQuesNum.put("all", exceptionFillNum); |
| | | dto.setExceptionQuesNum(exceptionQuesNum); |
| | | |
| | | Map<String, Object> latestHandle = latestHandleMap.get(questiontext); |
| | | if (latestHandle != null) { |
| | | dto.setHandleBy(latestHandle.get("handleBy") == null ? null : String.valueOf(latestHandle.get("handleBy"))); |
| | | Object handleTime = latestHandle.get("handleTime"); |
| | | dto.setHandleTime(handleTime instanceof Date ? (Date) handleTime : null); |
| | | } |
| | | |
| | | detailTraceDealDTOList.add(dto); |
| | | } |
| | | } |
| | | |
| | | return detailTraceDealDTOList; |
| | | Map<String, Object> result = new HashMap<>(); |
| | | result.put("detailTraceDealDTOList", detailTraceDealDTOList); |
| | | result.put("totalException", totalException); |
| | | result.put("noDealException", noDealException); |
| | | result.put("yesDealException", yesDealException); |
| | | // 分页总数(按问题维度),controller 直接用它,不用再全量查一遍 |
| | | result.put("questionTotal", questionTotal); |
| | | return result; |
| | | } |
| | | |
| | | private static Long toLongObj(Object value) { |
| | | if (value == null) return null; |
| | | if (value instanceof Number) return ((Number) value).longValue(); |
| | | String text = String.valueOf(value).trim(); |
| | | return text.isEmpty() ? null : Long.valueOf(text); |
| | | } |
| | | |
| | | private static long toLong(Object value, long defaultValue) { |
| | | Long result = toLongObj(value); |
| | | return result == null ? defaultValue : result; |
| | | } |
| | | |
| | | /** |
| | | * 解析 group_concat(distinct scriptid) 的结果(如 "101,102")为 scriptid 列表 |
| | | */ |
| | | private static List<Long> parseScriptIds(Object value) { |
| | | List<Long> scriptIds = new ArrayList<>(); |
| | | if (value == null) return scriptIds; |
| | | for (String item : String.valueOf(value).split(",")) { |
| | | Long scriptId = toLongObj(item); |
| | | if (scriptId != null && !scriptIds.contains(scriptId)) { |
| | | scriptIds.add(scriptId); |
| | | } |
| | | } |
| | | return scriptIds; |
| | | } |
| | | |
| | | /** |
| | |
| | | public int deleteServiceSubtaskDetailTtraceById(Long id) { |
| | | return serviceSubtaskDetailTraceMapper.deleteServiceSubtaskDetailTtraceById(id); |
| | | } |
| | | |
| | | @Override |
| | | public Map<String, Object> getSatisfactionExceptConfigQuestion(SatisfactionExceptConfigQuestionReqVO questionReqVO) { |
| | | // 初始化结果集 |
| | | Map<String, Object> result = new HashMap<>(); |
| | | |
| | | if (StringUtils.isEmpty(questionReqVO.getScriptType()) || questionReqVO.getScriptType().equals("1")) { |
| | | List<Map<String, Object>> voicetDataList = new ArrayList<>(); |
| | | //获取满意度的分类 |
| | | IvrLibaScriptAssort ivrLibaScriptAssort = new IvrLibaScriptAssort(); |
| | | ivrLibaScriptAssort.setType("myd"); |
| | | if (!StringUtils.isEmpty(questionReqVO.getType())) ivrLibaScriptAssort.setType(questionReqVO.getType()); |
| | | List<IvrLibaScriptAssortVO> ivrLibaScriptAssortVOS = iIvrLibaScriptAssortService.selectIvrLibaScriptAssortList(ivrLibaScriptAssort); |
| | | List<Long> assortIdList = Optional.ofNullable(ivrLibaScriptAssortVOS).filter(list -> !list.isEmpty()).map(list -> list.stream().map(IvrLibaScriptAssortVO::getId).collect(Collectors.toList())).orElse(Collections.emptyList()); |
| | | |
| | | //用来记录满意度的所有问题 |
| | | List<IvrLibaTemplateScript> ivrLibaTemplateScriptList = new ArrayList<>(); |
| | | if (CollectionUtils.isNotEmpty(assortIdList)) { |
| | | for (Long assortId : assortIdList) { |
| | | IvrLibaTemplateScript ivrLibaTemplateScript = new IvrLibaTemplateScript(); |
| | | ivrLibaTemplateScript.setScriptAssortid(assortId); |
| | | List<IvrLibaTemplateScript> ivrLibaTemplateScripts = iIvrLibaTemplateScriptService.selectIvrLibaTemplateScriptList(ivrLibaTemplateScript); |
| | | ivrLibaTemplateScriptList.addAll(ivrLibaTemplateScripts); |
| | | } |
| | | for (IvrLibaTemplateScript ivrLibaTemplateScript : ivrLibaTemplateScriptList) { |
| | | Map<String, Object> voicetData = new HashMap<>(); |
| | | voicetData.put("questionId", ivrLibaTemplateScript.getId()); |
| | | voicetData.put("libtemplateid", ivrLibaTemplateScript.getTemplateid()); |
| | | voicetData.put("questionContent", ivrLibaTemplateScript.getScriptContent()); |
| | | voicetData.put("questionTopic", ivrLibaTemplateScript.getScriptTopic()); |
| | | voicetData.put("questionType", ivrLibaTemplateScript.getScriptType()); |
| | | voicetData.put("categoryName", ivrLibaTemplateScript.getScriptAssortname()); |
| | | voicetData.put("categoryId", ivrLibaTemplateScript.getScriptAssortid()); |
| | | voicetDataList.add(voicetData); |
| | | } |
| | | } |
| | | //语音数据返回集合 |
| | | result.put("voiceDataList", voicetDataList); |
| | | } |
| | | |
| | | if (StringUtils.isEmpty(questionReqVO.getScriptType()) || questionReqVO.getScriptType().equals("2")) { |
| | | List<Map<String, Object>> questionnaireDataList = new ArrayList<>(); |
| | | //获取满意度的分类 |
| | | SvyLibScriptCategory svyLibScriptCategory = new SvyLibScriptCategory(); |
| | | svyLibScriptCategory.setType("myd"); |
| | | if (!StringUtils.isEmpty(questionReqVO.getType())) svyLibScriptCategory.setType(questionReqVO.getType()); |
| | | List<SvyLibScriptCategory> svyLibScriptCategoryVOS = svyLibScriptCategoryMapper.selectSvyLibScriptCategoryList(svyLibScriptCategory); |
| | | List<Long> assortIdList = Optional.ofNullable(svyLibScriptCategoryVOS).filter(list -> !list.isEmpty()).map(list -> list.stream().map(SvyLibScriptCategory::getId).collect(Collectors.toList())).orElse(Collections.emptyList()); |
| | | |
| | | //用来记录满意度的所有问题 |
| | | List<SvyLibTemplateScript> svyLibTemplateScriptList = new ArrayList<>(); |
| | | if (CollectionUtils.isNotEmpty(assortIdList)) { |
| | | for (Long categoryid : assortIdList) { |
| | | SvyLibTemplateScript svyLibTemplateScript = new SvyLibTemplateScript(); |
| | | svyLibTemplateScript.setCategoryid(categoryid); |
| | | List<SvyLibTemplateScript> svyLibTemplateScripts = isvyLibTemplateScriptService.selectSvyLibTemplateScriptList(svyLibTemplateScript); |
| | | svyLibTemplateScriptList.addAll(svyLibTemplateScripts); |
| | | } |
| | | for (SvyLibTemplateScript svyLibTemplateScript : svyLibTemplateScriptList) { |
| | | Map<String, Object> voicetData = new HashMap<>(); |
| | | voicetData.put("questionId", svyLibTemplateScript.getId()); |
| | | voicetData.put("libtemplateid", svyLibTemplateScript.getSvyid()); |
| | | voicetData.put("questionContent", svyLibTemplateScript.getScriptContent()); |
| | | voicetData.put("questionTopic", svyLibTemplateScript.getScriptTopic()); |
| | | voicetData.put("questionType", svyLibTemplateScript.getScriptType()); |
| | | voicetData.put("categoryName", svyLibTemplateScript.getCategoryName()); |
| | | voicetData.put("categoryId", svyLibTemplateScript.getCategoryid()); |
| | | questionnaireDataList.add(voicetData); |
| | | } |
| | | } |
| | | //问卷数据返回集合 |
| | | result.put("questionnaireDataList", questionnaireDataList); |
| | | } |
| | | return result; |
| | | } |
| | | } |
| | | } |