package com.ruoyi.task.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 org.apache.commons.collections4.CollectionUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import com.ruoyi.task.mapper.ServiceSubtaskDetailMapper;
|
import com.ruoyi.task.domain.DTO.DetailTraceDealDTO;
|
import com.ruoyi.task.domain.ServiceSubtaskDetailTrace;
|
import com.ruoyi.task.domain.VO.DetailTraceDealVO;
|
import com.ruoyi.task.domain.VO.ServiceSubtaskDetailTraceVO;
|
import com.ruoyi.task.mapper.ServiceSubtaskDetailTraceMapper;
|
import com.ruoyi.task.service.IServiceSubtaskDetailTraceService;
|
|
/**
|
* 随访任务异常记录跟踪Service业务层处理
|
*
|
* @author lihu
|
* @date 2026-03-24
|
*/
|
@Service
|
public class ServiceSubtaskDetailTraceServiceImpl implements IServiceSubtaskDetailTraceService {
|
@Autowired
|
private ServiceSubtaskDetailTraceMapper serviceSubtaskDetailTraceMapper;
|
|
@Autowired
|
private ServiceSubtaskDetailMapper serviceSubtaskDetailMapper;
|
|
// 说明:原实现里还注入了一个 smartor 的 ISvyLibTemplateScriptService,
|
// 但全类从未使用(死注入)。随本类搬入 ruoyi-task 时删除 ——
|
// 保留它会让 ruoyi-task 反向依赖 smartor,形成硬循环。
|
|
/**
|
* 查询随访任务异常记录跟踪
|
*
|
* @param id 随访任务异常记录跟踪主键
|
* @return 随访任务异常记录跟踪
|
*/
|
@Override
|
public ServiceSubtaskDetailTrace selectServiceSubtaskDetailTtraceById(Long id) {
|
return serviceSubtaskDetailTraceMapper.selectServiceSubtaskDetailTtraceById(id);
|
}
|
|
/**
|
* 查询随访任务异常记录跟踪列表
|
*
|
* @param serviceSubtaskDetailTraceVO 随访任务异常记录跟踪
|
* @return 随访任务异常记录跟踪
|
*/
|
@Override
|
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 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<>();
|
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());
|
|
// 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);
|
}
|
}
|
|
// 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));
|
}
|
}
|
|
// 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;
|
|
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<String, Object> 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<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);
|
}
|
}
|
|
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;
|
}
|
|
/**
|
* 新增随访任务异常记录跟踪
|
*
|
* @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);
|
}
|
}
|