package com.smartor.service.impl; 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.ServiceSubtaskDetailTraceVO; import com.smartor.mapper.ServiceSubtaskDetailMapper; import com.smartor.mapper.ServiceSubtaskDetailTraceMapper; import com.smartor.service.*; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * 随访任务异常记录跟踪Service业务层处理 * * @author lihu * @date 2026-03-24 */ @Service public class ServiceSubtaskDetailTraceServiceImpl implements IServiceSubtaskDetailTraceService { @Autowired private ServiceSubtaskDetailTraceMapper serviceSubtaskDetailTraceMapper; @Autowired private ServiceSubtaskDetailMapper serviceSubtaskDetailMapper; @Autowired private ISvyLibTemplateScriptService isvyLibTemplateScriptService; /** * 查询随访任务异常记录跟踪 * * @param id 随访任务异常记录跟踪主键 * @return 随访任务异常记录跟踪 */ @Override public ServiceSubtaskDetailTrace selectServiceSubtaskDetailTtraceById(Long id) { return serviceSubtaskDetailTraceMapper.selectServiceSubtaskDetailTtraceById(id); } /** * 查询随访任务异常记录跟踪列表 * * @param serviceSubtaskDetailTraceVO 随访任务异常记录跟踪 * @return 随访任务异常记录跟踪 */ @Override public List selectServiceSubtaskDetailTtraceList(ServiceSubtaskDetailTraceVO serviceSubtaskDetailTraceVO) { List serviceSubtaskDetailTraces = serviceSubtaskDetailTraceMapper.selectServiceSubtaskDetailTtraceList(serviceSubtaskDetailTraceVO); for (ServiceSubtaskDetailTrace serviceSubtaskDetailTrace : serviceSubtaskDetailTraces) { if (StringUtils.isNotEmpty(serviceSubtaskDetailTrace.getPatdesc())) { serviceSubtaskDetailTrace.setPatdescJson(JSON.parseObject(serviceSubtaskDetailTrace.getPatdesc())); } } return serviceSubtaskDetailTraces; } @Override public Map tracedeallist(DetailTraceDealVO detailTraceDealVO) { // 优化前:① 这条 SQL 没有 limit,每次都把整表 trace(含 patdesc/recordurl 大字段)拉回 Java 再分组; // ② 每个问题都拿 scriptid 去 service_subtask_detail 查一次全表只为取 size(N+1); // ③ controller 把这个方法调了两遍(第二遍为了总数),等于上面两步各跑两次。 // 优化后:固定的 5 条聚合 SQL,DB 只处理当前页的题目。 // 1) 当前页的问题分组聚合(分页下推到 SQL) List> groupRows = serviceSubtaskDetailTraceMapper.selectTraceDealGroups(detailTraceDealVO); // 2) 问题总数(分页 total,全量口径) long questionTotal = serviceSubtaskDetailTraceMapper.countTraceDealQuestions(detailTraceDealVO); // 3) 汇总统计(全量口径,不受分页影响) Map 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 detailTraceDealDTOList = new ArrayList<>(); if (CollectionUtils.isNotEmpty(groupRows)) { // 本页涉及的问题文本,后面 3 条查询都按它过滤 List questiontexts = groupRows.stream() .map(row -> row.get("questiontext")) .filter(Objects::nonNull) .map(String::valueOf) .distinct() .collect(Collectors.toList()); // 4) 本页问题的负责科室(按科室编号去重) Map>> deptMap = new HashMap<>(); // 5) 本页问题的最新处理人/处理时间 Map> latestHandleMap = new HashMap<>(); if (CollectionUtils.isNotEmpty(questiontexts)) { for (Map row : serviceSubtaskDetailTraceMapper.selectTraceDealDepts(detailTraceDealVO, questiontexts)) { String questiontext = row.get("questiontext") == null ? null : String.valueOf(row.get("questiontext")); if (questiontext == null) continue; Map 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 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); } } // 6) 本页问题的有效填写数:一次 group by 查回,替代逐个 scriptid 查询 // 一个问题可能对应多个 scriptid,所以先把 questiontext -> scriptid 列表解开, // 再把该问题所有 scriptid 的填写数相加(与原实现 for 循环累加的口径一致) Map> questionScriptIds = new LinkedHashMap<>(); Set scriptIdSet = new LinkedHashSet<>(); for (Map row : groupRows) { String questiontext = row.get("questiontext") == null ? null : String.valueOf(row.get("questiontext")); if (questiontext == null) continue; List ids = parseScriptIds(row.get("scriptids")); questionScriptIds.put(questiontext, ids); scriptIdSet.addAll(ids); } Map effectiveFillMap = new HashMap<>(); if (CollectionUtils.isNotEmpty(scriptIdSet)) { for (Map row : serviceSubtaskDetailMapper.countDetailByScriptIds(new ArrayList<>(scriptIdSet))) { Long scriptId = toLongObj(row.get("scriptid")); if (scriptId == null) continue; effectiveFillMap.put(scriptId, toLong(row.get("cnt"), 0L)); } } // 7) 组装返回结构(顺序 = SQL 的 order by questiontext,保证分页稳定) for (Map row : groupRows) { String questiontext = row.get("questiontext") == null ? null : String.valueOf(row.get("questiontext")); if (questiontext == null) continue; 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<>())); 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 fillSituation = new HashMap<>(); fillSituation.put("effectiveFillNum", effectiveFillNum); fillSituation.put("exceptionFillNum", exceptionFillNum); dto.setFillSituation(fillSituation); long yesDeal = toLong(row.get("yesDeal"), 0L); long noDeal = toLong(row.get("noDeal"), 0L); Map exceptionQuesNum = new HashMap<>(); exceptionQuesNum.put("yesDeal", yesDeal); exceptionQuesNum.put("noDeal", noDeal); exceptionQuesNum.put("all", exceptionFillNum); dto.setExceptionQuesNum(exceptionQuesNum); Map 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); } } Map 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 parseScriptIds(Object value) { List 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; } /** * 新增随访任务异常记录跟踪 * * @param serviceSubtaskDetailTrace 随访任务异常记录跟踪 * @return 结果 */ @Override public int insertServiceSubtaskDetailTtrace(ServiceSubtaskDetailTrace serviceSubtaskDetailTrace) { serviceSubtaskDetailTrace.setCreateTime(DateUtils.getNowDate()); return serviceSubtaskDetailTraceMapper.insertServiceSubtaskDetailTtrace(serviceSubtaskDetailTrace); } /** * 修改随访任务异常记录跟踪 * * @param serviceSubtaskDetailTrace 随访任务异常记录跟踪 * @return 结果 */ @Override public int updateServiceSubtaskDetailTtrace(ServiceSubtaskDetailTrace serviceSubtaskDetailTrace) { serviceSubtaskDetailTrace.setUpdateTime(DateUtils.getNowDate()); return serviceSubtaskDetailTraceMapper.updateServiceSubtaskDetailTtrace(serviceSubtaskDetailTrace); } /** * 批量删除随访任务异常记录跟踪 * * @param ids 需要删除的随访任务异常记录跟踪主键 * @return 结果 */ @Override public int deleteServiceSubtaskDetailTtraceByIds(Long[] ids) { return serviceSubtaskDetailTraceMapper.deleteServiceSubtaskDetailTtraceByIds(ids); } /** * 删除随访任务异常记录跟踪信息 * * @param id 随访任务异常记录跟踪主键 * @return 结果 */ @Override public int deleteServiceSubtaskDetailTtraceById(Long id) { return serviceSubtaskDetailTraceMapper.deleteServiceSubtaskDetailTtraceById(id); } }