zxl
2025-12-24 419fab9d84e6b2fbeb6d32b7d7253a6cd195200d
business/src/main/java/com/ycl/service/impl/IndexHomeServiceImpl.java
@@ -1,9 +1,7 @@
package com.ycl.service.impl;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.ycl.common.annotation.DataScope;
import com.ycl.common.base.Result;
import com.ycl.common.constant.ProcessOverTimeConstants;
import com.ycl.common.core.domain.BaseEntity;
@@ -17,14 +15,15 @@
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.mapper.ProjectProcessMapper;
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;
@@ -39,8 +38,8 @@
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.identitylink.api.IdentityLinkInfo;
import org.flowable.identitylink.api.IdentityLinkType;
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;
@@ -78,13 +77,14 @@
    private final ISysDeptService sysDeptService;
    private final ISysRoleService sysRoleService;
    private final TaskCommonService taskCommonService;
    @Override
    public Result projectCodingStatusCount() {
        //权限控制
        List<ProjectInfo> loginUserOwnProjectInfo = getLoginUserOwnProjectInfo();
        Map<String,Integer> map = new HashMap<>();
        map.put(ProcessOverTimeConstants.GREEN,0);
@@ -151,6 +151,7 @@
        return Result.ok().data(map);
    }
    @Override
    public List<ProjectInfo> getLoginUserOwnProjectInfo(){
        //权限控制
        Long userId = SecurityUtils.getUserId();
@@ -389,6 +390,58 @@
        return Result.ok().data(projectInfoAndFunding);
    }
    @Override
    public int countWaitTask() {
        Long userId = SecurityUtils.getUserId();
        List<ProjectInfo> loginUserOwnProjectInfo = getLoginUserOwnProjectInfo();
        if (CollectionUtils.isEmpty(loginUserOwnProjectInfo)) {
            return 0;
        }
        List<Long> ids = loginUserOwnProjectInfo.stream()
                .filter(Objects::nonNull)
                .map(ProjectInfo::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(ids)) {
            return 0;
        }
        List<ProjectProcess> projectProcessList = new LambdaQueryChainWrapper<>(projectProcessService.getBaseMapper())
                .eq(ProjectProcess::getDeleted, Boolean.FALSE)
                .in(ProjectProcess::getProjectId, ids)
                .list();
        if (CollectionUtils.isEmpty(projectProcessList)) {
            return 0;
        }
        List<String> 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<Task> allRunningTasks = taskService.createTaskQuery().active().list();
        if (CollectionUtils.isEmpty(allRunningTasks)) {
            return 0;
        }
        List<Task> 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() {
@@ -401,7 +454,7 @@
        List<ProjectProcess> projectProcessList = new LambdaQueryChainWrapper<>(projectProcessService.getBaseMapper())
                .eq(ProjectProcess::getDeleted, Boolean.FALSE)
                .in(ProjectProcess::getProjectId, ids).list();
        Long userId = SecurityUtils.getUserId();
        //流程实例id集合
        List<String> targetProcessInsIds = projectProcessList.stream()
                .map(ProjectProcess::getProcessInsId)
@@ -454,11 +507,16 @@
        List<Task> allRunningTasks  = taskService.createTaskQuery().active().list();
        List<Task> 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<TaskInfoVo> list = new ArrayList<>();
        // 按流程实例id分组
@@ -471,9 +529,11 @@
                    .eq(ProcessCoding::getDeleted, Boolean.FALSE)
                    .in(ProcessCoding::getTaskId, taskIds)
                    .list();
            log.info("processCodingList.size():{}", processCodingList.size()); // 看日志是否为0
            Map<String, ProcessCoding> processCodingMap = new HashMap<>();
            //按taskId转换为map方面下面获取
            if (!CollectionUtils.isEmpty(processCodingList)){
                Map<String, ProcessCoding> processCodingMap = processCodingList.stream()
                processCodingMap = processCodingList.stream()
                        .filter(Objects::nonNull) // 避免null元素导致NPE(可选保留)
                        .filter(vo -> vo.getTaskId() != null) // 过滤taskId为null的无效数据
                        .collect(Collectors.toMap(
@@ -493,128 +553,126 @@
                                    return newCreateTime.getTime() > oldCreateTime.getTime() ? newVal : oldVal;
                                }
                        ));
            }
            for (Task task : targetRunningTasks) {
                for (Task task : targetRunningTasks) {
                TaskInfoVo taskVo = new TaskInfoVo();
                    TaskInfoVo taskVo = new TaskInfoVo();
                    taskVo.setId(task.getId());
                    taskVo.setTaskName(task.getName());
                    taskVo.setStartTime(task.getCreateTime());
                    taskVo.setEndTime(task.getDueDate());
                taskVo.setId(task.getId());
                taskVo.setTaskName(task.getName());
                taskVo.setStartTime(task.getCreateTime());
                taskVo.setEndTime(task.getDueDate());
//                    taskVo.setTaskType();
                    List<Long> handlerIds = new ArrayList<>(2);
                    List<String> handlerNames = new ArrayList<>(2);
                    List<Long> handlerUnitIds = new ArrayList<>(2);
                    List<String> handlerUnitNames = new ArrayList<>(2);
                    List<String> promoterNames = new ArrayList<>(2);
                    List<String> 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<Long> handlerIds = new ArrayList<>(2);
                List<String> handlerNames = new ArrayList<>(2);
                List<Long> handlerUnitIds = new ArrayList<>(2);
                List<String> handlerUnitNames = new ArrayList<>(2);
                List<String> promoterNames = new ArrayList<>(2);
                List<String> 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<IdentityLink> 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());
                                    }
                // 流程处理人信息
                List<IdentityLink> 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;
                        // 绑定的是角色或者部门
                    } 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());
                            }
                        }
                        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);
                    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);
            }
@@ -622,6 +680,222 @@
        return Result.ok().data(list);
    }
    public HashMap<String,Object> buildEmptyResultMap(){
        HashMap<String,Object> 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<String> userGroups = taskCommonService.getCurrentUserGroups(); // 当前用户所属组
        List<ProjectInfo> 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<Long> projectIds = targetProjectList.stream().map(ProjectInfo::getId).collect(Collectors.toList());
        List<ProjectProcess> projectProcessList = new LambdaQueryChainWrapper<>(projectProcessService.getBaseMapper())
                .eq(ProjectProcess::getDeleted, Boolean.FALSE)
                .in(ProjectProcess::getProjectId, projectIds).list();
        // 4. 提取有效的流程实例ID(去重+非空过滤)
        List<String> 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<String, Object>() {{
                put("data", new ArrayList<>());
                put("total", 0L);
            }});
        }
        // 5. 构建processInfoMap(核心:项目ID→CheckPointVO,再关联流程信息)
        Map<String, CheckPointVO> 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<String, CheckPointVO> 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<Task> 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<String> taskIdSet = new HashSet<>();
        List<Task> distinctAllTaskList = new ArrayList<>();
        for (Task task : allTaskList) {
            if (!taskIdSet.contains(task.getId())) {
                taskIdSet.add(task.getId());
                distinctAllTaskList.add(task);
            }
        }
        // ========== 修复点1:提前查询任务状态,过滤已完成任务(分页前执行) ==========
        // 查询任务状态
        final HashMap<String, ProcessLog> taskInfoMap = new HashMap<>();
        List<String> allTaskIds = distinctAllTaskList.stream().map(Task::getId).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(allTaskIds)) {
            // 2. 不再重新赋值,而是先查询出结果,再放入已初始化的map中
            List<ProcessLog> processLogs = new LambdaQueryChainWrapper<>(processLogMapper)
                    .eq(ProcessLog::getDeleted, Boolean.FALSE)
                    .in(ProcessLog::getTaskId, allTaskIds)
                    .list();
            // 3. 填充数据到taskInfoMap,引用未改变
            Map<String, ProcessLog> 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<Task> 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<String, Object>() {{
                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<Task> pageTaskList = new ArrayList<>(validTaskList.subList(startIndex, endIndex));
        // 组装返回VO
        List<TaskInfoVo> 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小时"; // 防御性处理负数场景
@@ -681,11 +955,18 @@
    }
    @Override
    public Result getProjectList(){
        List<ProjectInfo> loginUserOwnProjectInfo = getLoginUserOwnProjectInfo();
        return Result.ok().data(loginUserOwnProjectInfo);
    }
    @Override
    public Result getProjectSelectList() {
        List<ProjectInfo> loginUserOwnProjectInfo = getLoginUserOwnProjectInfo();
        if (CollectionUtils.isEmpty(loginUserOwnProjectInfo)) {
            return Result.ok().data(loginUserOwnProjectInfo);
        }
        Map<String, String> projectMap = loginUserOwnProjectInfo.stream()
                .collect(Collectors.toMap(
                        projectInfo -> String.valueOf(projectInfo.getId()),          // key: 项目ID
@@ -695,4 +976,6 @@
                ));
        return Result.ok().data(projectMap);
    }
}