package com.ycl.service.impl; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.ycl.common.base.Result; import com.ycl.common.constant.ProcessOverTimeConstants; import com.ycl.common.core.domain.BaseEntity; import com.ycl.common.core.domain.entity.SysDept; import com.ycl.common.core.domain.entity.SysRole; import com.ycl.common.core.domain.entity.SysUser; import com.ycl.common.enums.business.*; import com.ycl.common.utils.SecurityUtils; import com.ycl.domain.entity.ProcessCoding; import com.ycl.domain.entity.ProcessLog; import com.ycl.domain.entity.ProjectInfo; import com.ycl.domain.entity.ProjectProcess; import com.ycl.domain.form.ProjectProgressStatisticsForm; import com.ycl.domain.query.WaitTodoQuery; import com.ycl.domain.vo.*; import com.ycl.factory.FlowServiceFactory; import com.ycl.mapper.ProcessCodingMapper; import com.ycl.mapper.ProcessLogMapper; import com.ycl.mapper.ProjectInfoMapper; import com.ycl.service.IndexHomeService; import com.ycl.service.ProjectProcessService; import com.ycl.service.common.TaskCommonService; import com.ycl.system.mapper.SysDeptMapper; import com.ycl.system.service.ISysDeptService; import com.ycl.system.service.ISysRoleService; import com.ycl.system.service.ISysUserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.UserTask; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.identitylink.api.IdentityLink; import org.flowable.identitylink.api.IdentityLinkInfo; import org.flowable.task.api.Task; import org.flowable.task.api.TaskQuery; import org.flowable.task.api.history.HistoricTaskInstance; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.text.DecimalFormat; import java.time.LocalDate; import java.time.ZoneId; import java.time.temporal.TemporalAdjusters; import java.util.*; import java.util.stream.Collectors; /** * nongtou-project-java * 首页实现类 * * @author : zxl * @date : 2025-11-26 16:51 **/ @Service @RequiredArgsConstructor @Slf4j public class IndexHomeServiceImpl extends FlowServiceFactory implements IndexHomeService { private final SysDeptMapper sysDeptMapper; private final ProjectInfoMapper projectInfoMapper; private final ProjectProcessService projectProcessService; private final ProcessLogMapper processLogMapper; private final ProcessCodingMapper processCodingMapper; private final ISysUserService sysUserService; private final ISysDeptService sysDeptService; private final ISysRoleService sysRoleService; private final TaskCommonService taskCommonService; @Override public Result projectCodingStatusCount() { //权限控制 List loginUserOwnProjectInfo = getLoginUserOwnProjectInfo(); Map map = new HashMap<>(); map.put(ProcessOverTimeConstants.GREEN,0); map.put(ProcessOverTimeConstants.YELLOW,0); map.put(ProcessOverTimeConstants.RED,0); map.put("total",0); if (CollectionUtils.isEmpty(loginUserOwnProjectInfo)) { //返回默认值 return Result.ok().data(map); } Map> collect = loginUserOwnProjectInfo.stream() .filter(project -> project.getCoding() != null) // 过滤coding为null的情况 .collect(Collectors.groupingBy(ProjectInfo::getCoding)); if (collect.containsKey(ProcessOverTimeConstants.GREEN)) { map.put(ProcessOverTimeConstants.GREEN, collect.get(ProcessOverTimeConstants.GREEN).size()); } if (collect.containsKey(ProcessOverTimeConstants.YELLOW)) { map.put(ProcessOverTimeConstants.YELLOW, collect.get(ProcessOverTimeConstants.YELLOW).size()); } if (collect.containsKey(ProcessOverTimeConstants.RED)) { map.put(ProcessOverTimeConstants.RED, collect.get(ProcessOverTimeConstants.RED).size()); } map.put("total", loginUserOwnProjectInfo.size()); return Result.ok().data(map); } @Override public Result projectStageCount() { List projectVOS = projectInfoMapper.homeCount(new BaseEntity()); int reserve = 0; int previous = 0; int implement = 0; int finish = 0; for (ProjectVO projectVO : projectVOS) { if (ProjectCategoryEnum.RESERVE.getDesc().equals(ProjectCategoryEnum.getPhaseByProjectStatus(projectVO.getProjectPhase()))) { reserve+=1; } else if (ProjectCategoryEnum.PREVIOUS.getDesc().equals(ProjectCategoryEnum.getPhaseByProjectStatus(projectVO.getProjectPhase()))) { previous+=1; } else if (ProjectCategoryEnum.IMPLEMENT.getDesc().equals(ProjectCategoryEnum.getPhaseByProjectStatus(projectVO.getProjectPhase()))) { implement+=1; } else if (ProjectCategoryEnum.FINISH.getDesc().equals(ProjectCategoryEnum.getPhaseByProjectStatus(projectVO.getProjectPhase()))) { finish+=1; } } Map map = new HashMap<>(); Integer[] yData = new Integer[]{ projectVOS.size(), reserve, previous, implement, finish }; String[] xData = new String[]{ "在库", "储备","前期","实施","竣工" }; map.put("xData",xData); map.put("yData",yData); return Result.ok().data(map); } @Override public List getLoginUserOwnProjectInfo(){ //权限控制 Long userId = SecurityUtils.getUserId(); List list; if (SecurityUtils.isAdmin(userId)){ //查询全部 list = new LambdaQueryChainWrapper<>(projectInfoMapper) .eq(ProjectInfo::getDeleted, Boolean.FALSE) .eq(ProjectInfo::getUsedStatus, 2)//审核通过 .orderBy(true, true, ProjectInfo::getId) .list(); }else{ String ancestors = sysDeptMapper.selectAncestors(userId); String[] ancestorArr = ancestors.split(","); List ancestorList = Arrays.stream(ancestorArr).collect(Collectors.toList()); ancestorList.add(SecurityUtils.getDeptId() + ""); //获得本单位以及其子单位deptId; list = new LambdaQueryChainWrapper<>(projectInfoMapper) .eq(ProjectInfo::getDeleted, Boolean.FALSE) .eq(ProjectInfo::getUsedStatus, 2) //审核通过 .in(ProjectInfo::getProjectOwnerUnit, ancestorList) .orderBy(true, true, ProjectInfo::getId) .list(); } return list; } @Override public Result projectTaskStatus(ProjectProgressStatisticsForm form) { //获得是项目 List loginUserOwnProjectInfo = getLoginUserOwnProjectInfo(); if (CollectionUtils.isEmpty(loginUserOwnProjectInfo)) { return Result.ok().data(loginUserOwnProjectInfo); } Map projectMap = loginUserOwnProjectInfo.stream() .collect(Collectors.toMap( projectInfo -> String.valueOf(projectInfo.getId()), // key: 项目ID ProjectInfo::getProjectName, // value: 项目对象本身 (existing, replacement) -> existing // 处理重复ID的冲突策略(保留原有值) )); //获得项目ids List ids = loginUserOwnProjectInfo.stream().map(ProjectInfo::getId).collect(Collectors.toList()); //更具项目ids获得所有对应流程信息 ProjectProcess 流程定义ID processDefId;流程实例id processInsId; List projectProcessList = new LambdaQueryChainWrapper<>(projectProcessService.getBaseMapper()) .eq(ProjectProcess::getDeleted, Boolean.FALSE) .in(ProjectProcess::getProjectId, ids).list(); Date statisticsTime = form.getStartTime(); if (statisticsTime == null) { statisticsTime = new Date(); // 如果未传入时间,默认使用当前时间 } LocalDate statisticsLocalDate = statisticsTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); LocalDate firstDayOfMonth = statisticsLocalDate.with(TemporalAdjusters.firstDayOfMonth()); LocalDate lastDayOfMonth = statisticsLocalDate.with(TemporalAdjusters.lastDayOfMonth()); Date monthStart = Date.from(firstDayOfMonth.atStartOfDay(ZoneId.systemDefault()).toInstant()); Date monthEnd = Date.from(lastDayOfMonth.atTime(23, 59, 59).atZone(ZoneId.systemDefault()).toInstant()); // 存储每个流程实例的统计结果 List> processStatisticsList = new ArrayList<>(); // 获得流程日志表中 任务信息 按流程实列id分组 Map> groupMap = new LambdaQueryChainWrapper<>(processLogMapper) .eq(ProcessLog::getDeleted, Boolean.FALSE) .in(ProcessLog::getProjectId, ids).list() .stream().collect(Collectors.groupingBy(ProcessLog::getProcessInsId)); for (ProjectProcess projectProcess : projectProcessList) { String processInstanceId = projectProcess.getProcessInsId(); if (processInstanceId == null) { continue; } // 单个流程的统计结果 Map processResult = new HashMap<>(); processResult.put("id",projectProcess.getProjectId()); processResult.put("projectName", projectMap.get(projectProcess.getProjectId())); int completedCount = 0; // 已完成任务数 int inProgressCount = 0; // 进行中任务数 int notStartedCount = 0; // 未开始任务数 try { // 1. 获取该流程实例的所有已完成任务 List completedTasks = historyService.createHistoricTaskInstanceQuery() .processInstanceId(processInstanceId) .finished() .list(); // 去重历史信息 排除相同的任务key保留最新的任务 completedTasks = projectProcessService.distinctHisTask(completedTasks); // 需要排除已完成任务中被驳回的任务数 List taskLogs = groupMap.get(projectProcess.getProcessInsId()); if ("144".equals(projectProcess.getProjectId())) { for (ProcessLog processLog : taskLogs) { System.out.println("打印信息"); System.out.println(processLog.toString()); } } if (!CollectionUtils.isEmpty(taskLogs)) { //排除掉所有被驳回的任务 // 获取所有被驳回任务的ID列表 List rejectedTaskIds = taskLogs.stream() .filter(log -> { // 判断是否为驳回类型的日志(根据你的枚举值调整) return ProcessLogEventTypeEnum.REJECT.equals(log.getEventType()); }) .map(ProcessLog::getTaskId) // 假设日志中有taskId字段关联任务 .filter(Objects::nonNull) // 过滤掉null的taskId .collect(Collectors.toList()); // 排除被驳回的任务,生成新的已完成任务集合 // 将过滤后的集合赋值给原变量 completedTasks = completedTasks.stream() .filter(task -> !rejectedTaskIds.contains(task.getId())) //保留不包含驳回的taskId .collect(Collectors.toList()); } // 本月最后一天完成的 属于已完成 after 之后 before 之前 for (HistoricTaskInstance task : completedTasks) { Date completionTime = task.getEndTime(); if (completionTime != null && completionTime.before(monthEnd)) { completedCount++; } } // 获得上一步未覆盖到查询条件的元素 完成时间是在本月之后 List excessCompletedTasks = completedTasks.stream().filter( task -> { Date endTime = task.getEndTime(); return endTime != null && endTime.after(monthEnd); }).collect(Collectors.toList()); // 循环完成时间是在本月之后的 for (HistoricTaskInstance task : excessCompletedTasks) { Date createTime = task.getCreateTime(); Date endTime = task.getEndTime(); // 本月创建,完成在本月之后 属于进行中 if (createTime != null && endTime != null) { if (createTime.before(monthEnd) && createTime.after(monthStart) && endTime.after(monthEnd)) { inProgressCount++; } } } // 2. 获取该流程实例的所有进行中任务 List activeTasks = taskService.createTaskQuery() .processInstanceId(processInstanceId) .active() .list(); for (Task task : activeTasks) { Date createTime = task.getCreateTime(); // 进行中的任务 判断创建时间是否在下个月之前 如果是本月则加1 if (createTime != null && createTime.before(monthEnd)) { inProgressCount++; } } // 5. 统计未开始任务(获取流程定义的总任务数 - 已统计的任务数) ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() .processInstanceId(processInstanceId) .singleResult(); if (processInstance != null) { BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); Process mainProcess = bpmnModel.getMainProcess(); List userTasks = mainProcess.findFlowElementsOfType(UserTask.class); int totalUserTaskCount = userTasks.size(); // 未开始任务数 = 总任务数 - 已完成 - 进行中 notStartedCount = totalUserTaskCount - completedCount - inProgressCount; // 确保未开始任务数不为负数 if (notStartedCount < 0) { notStartedCount = 0; } } } catch (Exception e) { // 异常处理 notStartedCount = 0; } // 设置当前流程的统计结果 int totalTasks = completedCount + inProgressCount + notStartedCount; double completedPercent = totalTasks == 0 ? 0 : (completedCount * 100.0 / totalTasks); double inProgressPercent = totalTasks == 0 ? 0 : (inProgressCount * 100.0 / totalTasks); double notStartedPercent = totalTasks == 0 ? 0 : (notStartedCount * 100.0 / totalTasks); //四舍五入 completedPercent = Math.round(completedPercent * 10) / 10.0; inProgressPercent = Math.round(inProgressPercent * 10) / 10.0; notStartedPercent = Math.round(notStartedPercent * 10) / 10.0; //保留一位 DecimalFormat df = new DecimalFormat("#.#"); // 格式化结果(可选:保留为字符串或转换为Double) String completedFormatted = df.format(completedPercent); String inProgressFormatted = df.format(inProgressPercent); String notStartedFormatted = df.format(notStartedPercent); processResult.put("completed", completedFormatted); processResult.put("co", completedCount); processResult.put("co2", inProgressCount); processResult.put("co3", notStartedCount); processResult.put("inProgress", inProgressFormatted); processResult.put("notStarted", notStartedFormatted); processResult.put("totalTasks",totalTasks) ; // 添加到结果列表 processStatisticsList.add(processResult); } return Result.ok().data(processStatisticsList); } @Override public Result projectFundingStatus( ) { List projectInfoAndFunding = projectInfoMapper.getProjectInfoAndFunding(new BaseEntity()); //排除空数据 projectInfoAndFunding = projectInfoAndFunding.stream().filter(Objects::nonNull).collect(Collectors.toList()); return Result.ok().data(projectInfoAndFunding); } @Override public int countWaitTask() { Long userId = SecurityUtils.getUserId(); List loginUserOwnProjectInfo = getLoginUserOwnProjectInfo(); if (CollectionUtils.isEmpty(loginUserOwnProjectInfo)) { return 0; } List ids = loginUserOwnProjectInfo.stream() .filter(Objects::nonNull) .map(ProjectInfo::getId) .filter(Objects::nonNull) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(ids)) { return 0; } List projectProcessList = new LambdaQueryChainWrapper<>(projectProcessService.getBaseMapper()) .eq(ProjectProcess::getDeleted, Boolean.FALSE) .in(ProjectProcess::getProjectId, ids) .list(); if (CollectionUtils.isEmpty(projectProcessList)) { return 0; } List targetProcessInsIds = projectProcessList.stream() .filter(Objects::nonNull) // 过滤null的ProjectProcess对象 .map(ProjectProcess::getProcessInsId) .filter(defId -> defId != null && !defId.trim().isEmpty()) // 先判null再trim,修复风险3 .distinct() .collect(Collectors.toList()); if (CollectionUtils.isEmpty(targetProcessInsIds)) { return 0; } List allRunningTasks = taskService.createTaskQuery().active().list(); if (CollectionUtils.isEmpty(allRunningTasks)) { return 0; } List targetRunningTasks = allRunningTasks.stream() .filter(Objects::nonNull) .filter(task -> { // 先判null,再匹配,修复风险1 String processInsId = task.getProcessInstanceId(); return processInsId != null && targetProcessInsIds.contains(processInsId); }) .filter(task -> { String assignee = task.getAssignee(); return assignee == null || userId.toString().equals(assignee);//排除领取人 }) .collect(Collectors.toList()); return targetRunningTasks.size(); } @Override public Result projectAdvanceCheckPoint() { List loginUserOwnProjectInfo = getLoginUserOwnProjectInfo(); if (CollectionUtils.isEmpty(loginUserOwnProjectInfo)) { return Result.ok().data(loginUserOwnProjectInfo); } //获得项目ids List ids = loginUserOwnProjectInfo.stream().map(ProjectInfo::getId).collect(Collectors.toList()); List projectProcessList = new LambdaQueryChainWrapper<>(projectProcessService.getBaseMapper()) .eq(ProjectProcess::getDeleted, Boolean.FALSE) .in(ProjectProcess::getProjectId, ids).list(); Long userId = SecurityUtils.getUserId(); //流程实例id集合 List targetProcessInsIds = projectProcessList.stream() .map(ProjectProcess::getProcessInsId) .distinct() .filter(defId -> defId != null && !defId.trim().isEmpty()) .collect(Collectors.toList()); //组装项目流程信息 项目id为key Map map = new HashMap<>(); for (ProjectInfo projectInfo : loginUserOwnProjectInfo){ CheckPointVO checkPointVO = new CheckPointVO(); checkPointVO.setId(projectInfo.getId()); checkPointVO.setProjectName(projectInfo.getProjectName()); map.put(String.valueOf(projectInfo.getId()),checkPointVO); } //组装流程信息 for (ProjectProcess projectProcess : projectProcessList) { CheckPointVO checkPointVO = map.get(projectProcess.getProjectId()); checkPointVO.setProcessDefinitionId(projectProcess.getProcessDefId()); checkPointVO.setProcessInstanceId(projectProcess.getProcessInsId()); //获得流程部署id ProcessInstance process = runtimeService.createProcessInstanceQuery() .processInstanceId(projectProcess.getProcessInsId()).singleResult(); if (Objects.nonNull(process)) { checkPointVO.setDeployId(process.getDeploymentId()); // 运行中流程直接取部署ID checkPointVO.setProcessName(process.getProcessDefinitionName()); } else { // 2. 运行中流程不存在(已结束),查历史流程实例 HistoricProcessInstance hisProcess = historyService.createHistoricProcessInstanceQuery() .processInstanceId(projectProcess.getProcessInsId()).singleResult(); // 历史流程取部署ID checkPointVO.setDeployId(hisProcess.getDeploymentId()); checkPointVO.setProcessName(hisProcess.getProcessDefinitionName()); } map.put(projectProcess.getProjectId(),checkPointVO); } // 转换为list后按 流程实例id分组 List checkPointList = map.values().stream() .collect(Collectors.toList()); // 获得组装好的 流程map 键为流程实例id, Map processInfoMap = checkPointList.stream() .filter(Objects::nonNull) // 避免null元素导致异常(可选,按需保留) .filter(vo -> vo.getProcessInstanceId() != null) // 过滤流程定义ID为null的无效数据 .collect(Collectors.toMap( CheckPointVO::getProcessInstanceId, // key:流程定义ID(替换为你的实际方法名) vo -> vo, // value:原始CheckPointVO对象 (oldVal, newVal) -> newVal // 冲突策略:存在重复ID时保留后者(按需调整) )); List allRunningTasks = taskService.createTaskQuery().active().list(); List targetRunningTasks = allRunningTasks.stream() // 关键匹配:任务的流程定义ID 存在于 projectProcessList 的流程定义ID列表中 .filter(task -> targetProcessInsIds.contains(task.getProcessInstanceId())) .filter(task -> { String assignee = task.getAssignee(); log.info("assignee:{}",assignee); return assignee == null || userId.toString().equals(assignee); }) .collect(Collectors.toList()); List list = new ArrayList<>(); // 按流程实例id分组 if (!CollectionUtils.isEmpty(targetRunningTasks)){ //查询到所有 List taskIds = targetRunningTasks.stream().map(Task::getId).collect(Collectors.toList()); // 任务运行时不在 list中,可能任务没有设置超时时间 List processCodingList = new LambdaQueryChainWrapper<>(processCodingMapper) .eq(ProcessCoding::getDeleted, Boolean.FALSE) .in(ProcessCoding::getTaskId, taskIds) .list(); log.info("processCodingList.size():{}", processCodingList.size()); // 看日志是否为0 Map processCodingMap = new HashMap<>(); //按taskId转换为map方面下面获取 if (!CollectionUtils.isEmpty(processCodingList)){ processCodingMap = processCodingList.stream() .filter(Objects::nonNull) // 避免null元素导致NPE(可选保留) .filter(vo -> vo.getTaskId() != null) // 过滤taskId为null的无效数据 .collect(Collectors.toMap( ProcessCoding::getTaskId, // key:仍为taskId vo -> vo, // value:原始ProcessCoding对象 // 冲突策略:比较创建时间,保留更新的那个 (oldVal, newVal) -> { // 获取两者创建时间(根据实际方法名调整,如getGmtCreate() Date oldCreateTime = oldVal.getGmtCreate(); Date newCreateTime = newVal.getGmtCreate(); // 处理创建时间为null的情况(优先级:有时间>无时间,都无则保留后者) if (oldCreateTime == null) return newVal; if (newCreateTime == null) return oldVal; // 比较时间戳,返回更大的(更新的)对象 return newCreateTime.getTime() > oldCreateTime.getTime() ? newVal : oldVal; } )); } for (Task task : targetRunningTasks) { TaskInfoVo taskVo = new TaskInfoVo(); taskVo.setId(task.getId()); taskVo.setTaskName(task.getName()); taskVo.setStartTime(task.getCreateTime()); taskVo.setEndTime(task.getDueDate()); // taskVo.setTaskType(); List handlerIds = new ArrayList<>(2); List handlerNames = new ArrayList<>(2); List handlerUnitIds = new ArrayList<>(2); List handlerUnitNames = new ArrayList<>(2); List promoterNames = new ArrayList<>(2); List promoterUnitNames = new ArrayList<>(2); CustomerTaskVO customerTaskVO = new CustomerTaskVO(); customerTaskVO.setHandlerId(handlerIds); customerTaskVO.setHandlerName(handlerNames); customerTaskVO.setHandlerUnitId(handlerUnitIds); customerTaskVO.setHandlerUnitName(handlerUnitNames); customerTaskVO.setPromoterName(promoterNames); customerTaskVO.setPromoterUnitName(promoterUnitNames); // 流程处理人信息 List identityLinksForTask = taskService.getIdentityLinksForTask(task.getId()); for (IdentityLinkInfo identityLink : identityLinksForTask) { // 绑定的是用户,查出用户姓名、部门 if (StringUtils.isNotBlank(identityLink.getUserId())) { // 处理变量表达式,运行中的任务无需再处理表达式了,flowable已经自动根据变量设置了 customerTaskVO.setHandlerType(HandlerTypeEnum.USER); SysUser sysUser = sysUserService.selectUserById(Long.parseLong(identityLink.getUserId())); if (Objects.nonNull(sysUser)) { customerTaskVO.getHandlerId().add(sysUser.getUserId()); customerTaskVO.getHandlerName().add(this.getUserShowName(sysUser)); if (Objects.nonNull(sysUser.getDept())) { customerTaskVO.getHandlerUnitId().add(sysUser.getDept().getDeptId()); customerTaskVO.getHandlerUnitName().add(sysUser.getDept().getDeptName()); customerTaskVO.getPromoterName().add(this.getUserShowName(sysUser)); String[] str = sysUser.getDept().getAncestors().split(","); if (str.length >= 4){ customerTaskVO.getPromoterUnitName().add(sysUser.getDept().getParentName() +"-"+sysUser.getDept().getDeptName()); }else { customerTaskVO.getPromoterUnitName().add(sysUser.getDept().getDeptName()); } } } // 绑定的是角色或者部门 } else if (StringUtils.isNotBlank(identityLink.getGroupId())) { if (identityLink.getGroupId().startsWith("dept")) { // 部门的id是加了前缀的如:dept:1 customerTaskVO.setHandlerType(HandlerTypeEnum.DEPT); String[] split = identityLink.getGroupId().split(":"); if (split.length > 1) { // 部门 SysDept dept = sysDeptService.selectDeptById(Long.parseLong(split[1])); if (Objects.nonNull(dept)) { customerTaskVO.getHandlerUnitId().add(dept.getDeptId()); customerTaskVO.getHandlerUnitName().add(dept.getDeptName()); customerTaskVO.getPromoterName().add(this.getDeptLeaderShowName(dept)); customerTaskVO.getPromoterUnitName().add(this.setDeptNameWithParentName(dept)); } } } else { customerTaskVO.setHandlerType(HandlerTypeEnum.ROLE); SysRole role = sysRoleService.selectRoleById(Long.parseLong(identityLink.getGroupId())); if (Objects.nonNull(role)) { customerTaskVO.getHandlerUnitId().add(Long.parseLong(identityLink.getGroupId())); customerTaskVO.getHandlerUnitName().add(role.getRoleName()); } } } this.distinctVo(customerTaskVO); } taskVo.setCustomerTaskInfo(customerTaskVO); //组装任务信息 CheckPointVO checkPointVO = processInfoMap.get(task.getProcessInstanceId()); if (checkPointVO != null) { taskVo.setCheckPointInfo(checkPointVO); } //超时时间和次数在 processCodingList中 这里需要更具taskId来获得对应信息 ProcessCoding processCoding = processCodingMap.get(task.getId()); //设置超时信息 if (processCoding != null) { String overTimeTotalStr = processCoding.getOverTimeTotal(); long overTimeTotal = 0L; if (StringUtils.isNotBlank(overTimeTotalStr)) { try { overTimeTotal = Long.parseLong(overTimeTotalStr.trim()); } catch (NumberFormatException e) { } } String overTimeDesc = convertHoursToDayHourStr(overTimeTotal); taskVo.setTotalOverTime(overTimeDesc); // 2. 提取红码阈值 如: 0-22 天-小时 并转换为小时 String redTimeStr = processCoding.getRedTime(); Long redTimeSec = getTime(redTimeStr); Double redTimeHour = null; if (redTimeSec != null && redTimeSec > 0) { redTimeHour = redTimeSec / 3600.0; } long overTimeCount = 0L; // 判断超时次数 if (overTimeTotal > 0 && redTimeHour != null && redTimeHour > 0) { overTimeCount = (long) (overTimeTotal / redTimeHour); } taskVo.setOverTimeCount(overTimeCount); }else { //为null 说明未配置超时时间 默认未超时 taskVo.setOverTimeCount(0L); taskVo.setTotalOverTime("0小时"); } list.add(taskVo); } } return Result.ok().data(list); } public HashMap buildEmptyResultMap(){ HashMap map = new HashMap<>(); map.put("data", new ArrayList<>()); map.put("total", 0L); return map; } @Override public Result getWaitTaskList(WaitTodoQuery baseQuery) { int pageNum = (int) baseQuery.getCurrentPage(); int pageSize = (int) baseQuery.getPageSize(); Long userId = SecurityUtils.getUserId(); // 当前用户ID boolean isAdmin = SecurityUtils.getLoginUser().getUser().isAdmin(); // 是否管理员 List userGroups = taskCommonService.getCurrentUserGroups(); // 当前用户所属组 List targetProjectList = new ArrayList<>(); String queryProjectId = baseQuery.getProjectId(); if ("all".equals(queryProjectId) || StringUtils.isBlank(queryProjectId)){ // 查询全部 targetProjectList = getLoginUserOwnProjectInfo(); }else{ try { Long projectId = Long.parseLong(queryProjectId); // ① 查询该项目基础信息(替换为实际查询方法) ProjectInfo projectInfo = projectInfoMapper.selectById(projectId); if (projectInfo == null) { // 项目不存在,返回空数据 return Result.ok().data(buildEmptyResultMap()); } targetProjectList.add(projectInfo); } catch (NumberFormatException e) { // 项目ID格式错误,返回空数据 return Result.ok().data(buildEmptyResultMap()); } } List projectIds = targetProjectList.stream().map(ProjectInfo::getId).collect(Collectors.toList()); List projectProcessList = new LambdaQueryChainWrapper<>(projectProcessService.getBaseMapper()) .eq(ProjectProcess::getDeleted, Boolean.FALSE) .in(ProjectProcess::getProjectId, projectIds).list(); // 4. 提取有效的流程实例ID(去重+非空过滤) List targetProcessInsIds = projectProcessList.stream() .map(ProjectProcess::getProcessInsId) .distinct() .filter(defId -> defId != null && !defId.trim().isEmpty()) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(targetProcessInsIds)) { return Result.ok().data(new HashMap() {{ put("data", new ArrayList<>()); put("total", 0L); }}); } // 5. 构建processInfoMap(核心:项目ID→CheckPointVO,再关联流程信息) Map map = new HashMap<>(); // 先填充项目基础信息 for (ProjectInfo projectInfo : targetProjectList) { CheckPointVO checkPointVO = new CheckPointVO(); checkPointVO.setId(projectInfo.getId()); checkPointVO.setProjectName(projectInfo.getProjectName()); map.put(String.valueOf(projectInfo.getId()), checkPointVO); } // 再填充流程信息(部署ID、流程名称等) for (ProjectProcess projectProcess : projectProcessList) { CheckPointVO checkPointVO = map.get(projectProcess.getProjectId().toString()); // 修复:projectId是Long,需转字符串 if (checkPointVO == null) { continue; // 无匹配项目,跳过 } checkPointVO.setProcessDefinitionId(projectProcess.getProcessDefId()); checkPointVO.setProcessInstanceId(projectProcess.getProcessInsId()); // 获得流程部署id和流程名称(兼容运行中/已结束流程) ProcessInstance process = runtimeService.createProcessInstanceQuery() .processInstanceId(projectProcess.getProcessInsId()).singleResult(); if (Objects.nonNull(process)) { checkPointVO.setDeployId(process.getDeploymentId()); checkPointVO.setProcessName(process.getProcessDefinitionName()); } else { HistoricProcessInstance hisProcess = historyService.createHistoricProcessInstanceQuery() .processInstanceId(projectProcess.getProcessInsId()).singleResult(); if (Objects.nonNull(hisProcess)) { // 避免历史流程为null的NPE checkPointVO.setDeployId(hisProcess.getDeploymentId()); checkPointVO.setProcessName(hisProcess.getProcessDefinitionName()); } } map.put(projectProcess.getProjectId().toString(), checkPointVO); // 修复:projectId转字符串 } Map processInfoMap = map.values().stream() .filter(Objects::nonNull) .filter(vo -> vo.getProcessInstanceId() != null) .collect(Collectors.toMap( CheckPointVO::getProcessInstanceId, vo -> vo, (oldVal, newVal) -> newVal // 重复流程实例ID保留后者 )); TaskQuery query = getTaskService().createTaskQuery() .active() // 未完成、未挂起的活跃任务 .processInstanceIdIn(targetProcessInsIds); // 仅查项目关联的流程实例任务 // 权限过滤:非管理员仅查自己有权限的任务(已领取+候选) if (!isAdmin) { query.or() .taskAssignee(userId+"") // 自己已领取的任务 .taskCandidateUser(userId+"") // 自己是候选用户的任务 .taskCandidateGroupIn(userGroups) // 自己在候选组的任务 .endOr(); } query.orderByTaskCreateTime().desc(); List allTaskList = query.list(); // 排除领取人问题 allTaskList = allTaskList.stream() .filter(Objects::nonNull) // 空任务防护 .filter(task -> { String assignee = task.getAssignee(); return assignee == null || userId.toString().equals(assignee); }) .collect(Collectors.toList()); // 任务去重 Set taskIdSet = new HashSet<>(); List distinctAllTaskList = new ArrayList<>(); for (Task task : allTaskList) { if (!taskIdSet.contains(task.getId())) { taskIdSet.add(task.getId()); distinctAllTaskList.add(task); } } // ========== 修复点1:提前查询任务状态,过滤已完成任务(分页前执行) ========== // 查询任务状态 final HashMap taskInfoMap = new HashMap<>(); List allTaskIds = distinctAllTaskList.stream().map(Task::getId).collect(Collectors.toList()); if (!CollectionUtils.isEmpty(allTaskIds)) { // 2. 不再重新赋值,而是先查询出结果,再放入已初始化的map中 List processLogs = new LambdaQueryChainWrapper<>(processLogMapper) .eq(ProcessLog::getDeleted, Boolean.FALSE) .in(ProcessLog::getTaskId, allTaskIds) .list(); // 3. 填充数据到taskInfoMap,引用未改变 Map tempMap = processLogs.stream() .filter(processLog -> processLog.getTaskId() != null && processLog.getGmtCreate() != null) .collect(Collectors.toMap( ProcessLog::getTaskId, processLog -> processLog, (existing, replacement) -> replacement.getGmtCreate().after(existing.getGmtCreate()) ? replacement : existing, HashMap::new )); taskInfoMap.putAll(tempMap); } // 过滤已完成任务,得到有效任务列表(分页的依据) List validTaskList = distinctAllTaskList.stream() .filter(task -> { ProcessLog processLog = taskInfoMap.get(task.getId()); // 未查询到日志 → 待完成;日志状态不是FINISHED → 有效 return processLog == null || !ProcessLogEventTypeEnum.FINISHED.getDesc().equals(processLog.getEventType().getDesc()); }) .collect(Collectors.toList()); // 有效数据总条数 long validTotal = validTaskList.size(); System.out.println("有效任务总数(排除已完成):" + validTotal); // ========== 修复点2:分页合法性校验 ========== // 计算最大有效页码 int maxPageNum = (int) (validTotal % pageSize == 0 ? validTotal / pageSize : (validTotal / pageSize) + 1); // 若总条数为0,直接返回空数据 if (validTotal == 0) { return Result.ok().data(new HashMap() {{ put("data", new ArrayList<>()); put("total", 0L); }}); } // 若请求页码超过最大页码,默认返回最后一页 if (pageNum > maxPageNum) { pageNum = maxPageNum; } // ========== 修复点3:优化内存分页 ========== int startIndex = (pageNum - 1) * pageSize; int endIndex = Math.min(startIndex + pageSize, validTaskList.size()); // 转为新列表,避免subList视图的风险 List pageTaskList = new ArrayList<>(validTaskList.subList(startIndex, endIndex)); // 组装返回VO List taskInfoVoList = new ArrayList<>(); for (Task task : pageTaskList) { TaskInfoVo taskVo = new TaskInfoVo(); taskVo.setId(task.getId()); taskVo.setTaskName(task.getName()); taskVo.setStartTime(task.getCreateTime()); taskVo.setEndTime(task.getDueDate()); // 填充任务状态字段 taskVo.setTaskType("待完成"); ProcessLog processLog = taskInfoMap.get(task.getId()); if (Objects.nonNull(processLog)) { taskVo.setTaskType(processLog.getEventType().getDesc()); } CheckPointVO checkPointVO = processInfoMap.get(task.getProcessInstanceId()); if (checkPointVO != null) { taskVo.setCheckPointInfo(checkPointVO); } taskInfoVoList.add(taskVo); } return Result.ok().data(taskInfoVoList).total(validTotal); } private String convertHoursToDayHourStr(long totalHours) { if (totalHours < 0) { return "0小时"; // 防御性处理负数场景 } long days = totalHours / 24; // 计算天数 long hours = totalHours % 24; // 计算剩余小时数 StringBuilder sb = new StringBuilder(); if (days > 0) { sb.append(days).append("天"); } // 小时数无论是否为0,都展示(也可根据业务调整:如0小时时不展示) if (hours > 0 || days == 0) { sb.append(hours).append("小时"); } return sb.toString(); } // 对部门负责人集合信息进行去重 private void distinctVo(CustomerTaskVO vo) { vo.setHandlerId(vo.getHandlerId().stream().distinct().collect(Collectors.toList())); vo.setHandlerName(vo.getHandlerName().stream().distinct().collect(Collectors.toList())); vo.setHandlerUnitId(vo.getHandlerUnitId().stream().distinct().collect(Collectors.toList())); vo.setHandlerUnitName(vo.getHandlerUnitName().stream().distinct().collect(Collectors.toList())); } //拼接用户昵称以及电话 private String getUserShowName(SysUser user) { return user.getNickName() + (StringUtils.isNotBlank(user.getPhonenumber()) ? "(" + user.getPhonenumber() + ")" : ""); } //拼接部门负责人以及电话 private String getDeptLeaderShowName(SysDept dept) { return dept.getLeader() + (StringUtils.isNotBlank(dept.getPhone()) ? "(" + dept.getPhone() + ")" : ""); } //拼接上级部门以及本部门名称 private String setDeptNameWithParentName(SysDept dept) { String[] str = dept.getAncestors().split(","); if (str.length >= 4){ return dept.getParentName() + " / " + dept.getDeptName(); }else { return dept.getDeptName(); } } private Long getTime(String timeStr) { Long time = null; if (StringUtils.isNotBlank(timeStr)) { String[] timeArr = timeStr.split("-"); // 解析天数和小时数 int days = Integer.parseInt(timeArr[0]); int hours = 0; if (timeArr.length > 1) { hours = Integer.parseInt(timeArr[1]); } time = (days * 24L + hours) * 3600L; // //分-秒 // time= (days * 60L) + hours; } return time; } @Override public Result getProjectList(){ List loginUserOwnProjectInfo = getLoginUserOwnProjectInfo(); return Result.ok().data(loginUserOwnProjectInfo); } @Override public Result getProjectSelectList() { List loginUserOwnProjectInfo = getLoginUserOwnProjectInfo(); if (CollectionUtils.isEmpty(loginUserOwnProjectInfo)) { return Result.ok().data(loginUserOwnProjectInfo); } Map projectMap = loginUserOwnProjectInfo.stream() .collect(Collectors.toMap( projectInfo -> String.valueOf(projectInfo.getId()), // key: 项目ID ProjectInfo::getProjectName, // value: 项目对象本身 (existing, replacement) -> existing, // 处理重复ID的冲突策略(保留原有值) LinkedHashMap::new )); return Result.ok().data(projectMap); } }