zxl
10 小时以前 5f14844e81dade9e55725642f42c76568c5ec908
修改超时时间显示
2个文件已修改
198 ■■■■■ 已修改文件
business/src/main/java/com/ycl/service/impl/IndexHomeServiceImpl.java 57 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/task/FlowableTask.java 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/service/impl/IndexHomeServiceImpl.java
@@ -507,17 +507,11 @@
        List<Task> allRunningTasks  = taskService.createTaskQuery().active().list();
        //这里 获得 当前用户 所属的 流程实例ids集合中 对应的正在运行的任务。
        List<Task> targetRunningTasks = allRunningTasks.stream()
                // 关键匹配:任务的流程定义ID 存在于 projectProcessList 的流程定义ID列表中
                // 关键匹配:任务的流程实例ID 存在于 targetProcessInsIds 列表中
                .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分组
        if (!CollectionUtils.isEmpty(targetRunningTasks)){
@@ -529,6 +523,7 @@
                    .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方面下面获取
@@ -554,6 +549,25 @@
                                }
                        ));
            }
            //为targetRunningTasks进行一次排序 按存在超时时间 由搞到低排序
            Map<String, ProcessCoding> finalProcessCodingMap = processCodingMap;
            targetRunningTasks.sort((t1, t2) -> {
                ProcessCoding pc1 = finalProcessCodingMap.get(t1.getId());
                ProcessCoding pc2 = finalProcessCodingMap.get(t2.getId());
                String status1 = (pc1 != null && pc1.getOvertimeStatus() != null) ? pc1.getOvertimeStatus() : ProcessOverTimeConstants.NORMAL;
                String status2 = (pc2 != null && pc2.getOvertimeStatus() != null) ? pc2.getOvertimeStatus() : ProcessOverTimeConstants.NORMAL;
                // 定义权重:overtime=3, willOvertime=2, normal=1
                int weight1 = ProcessOverTimeConstants.OVERTIME.equals(status1) ? 3 :
                        (ProcessOverTimeConstants.WILLOVERTIME.equals(status1) ? 2 : 1);
                int weight2 = ProcessOverTimeConstants.OVERTIME.equals(status2) ? 3 :
                        (ProcessOverTimeConstants.WILLOVERTIME.equals(status2) ? 2 : 1);
                return Integer.compare(weight2, weight1); // 由高到低排序
            });
            for (Task task : targetRunningTasks) {
                TaskInfoVo taskVo = new TaskInfoVo();
@@ -637,16 +651,33 @@
                }
                //超时时间和次数在 processCodingList中  这里需要更具taskId来获得对应信息
                ProcessCoding processCoding = processCodingMap.get(task.getId());
                //设置超时信息
                if (processCoding != null) {
                    log.error("当前处理processCoding的id:{}",processCoding.getId());
                    String overTimeTotalStr = processCoding.getOverTimeTotal();
                    long overTimeTotal = 0L;
                    log.error("当前处理超时时间:{}",overTimeTotalStr);
                    if (StringUtils.isNotBlank(overTimeTotalStr)) {
                        try {
                            overTimeTotal = Long.parseLong(overTimeTotalStr.trim());
                        } catch (NumberFormatException e) {
                        try {
                            // 第一步:先去除字符串两端空白
                            String trimStr = overTimeTotalStr.trim();
                            // 第二步:先转成Double处理小数,再转Long(可选择四舍五入或直接取整)
                            double doubleValue = Double.parseDouble(trimStr);
                            // 方式1:直接取整数部分(4778.3 → 4778)
                            overTimeTotal = (long) doubleValue;
                            // 方式2:四舍五入(4778.3 → 4778,4778.6 → 4779)
                            // overTimeTotal = Math.round(doubleValue);
                            log.error("打印超时总时长不为null的id:{},对应值{}(原始值:{})",
                                    processCoding.getId(), overTimeTotal, trimStr);
                        } catch (NumberFormatException e) {
                            // 关键:打印异常详情和原始值,方便排查
                            log.error("转换超时时间失败!id:{},原始值:{}",
                                    processCoding.getId(), overTimeTotalStr, e);
                            // 可选:给默认值,避免后续使用overTimeTotal时出现空指针
                            overTimeTotal = 0L; // 或根据业务设置默认值,比如60000L(1分钟)
                        }
                    }
                    String overTimeDesc = convertHoursToDayHourStr(overTimeTotal);
@@ -674,8 +705,6 @@
                }
                list.add(taskVo);
            }
        }
        return Result.ok().data(list);
business/src/main/java/com/ycl/task/FlowableTask.java
@@ -54,22 +54,33 @@
     * 两个逻辑 改项目码、改节点颜色
     */
    public void expireTask() {
        log.info("开始赋码");
        log.info("开始定时任务:expireTask 赋码计算");
        //当前正在运行的所有任务节点
        List<Task> taskList = taskService.createTaskQuery().active().list();
        if (CollectionUtils.isEmpty(taskList)) return;
        log.info("查询到当前活跃任务数量: {}", taskList.size());
        if (CollectionUtils.isEmpty(taskList)) {
            log.info("活跃任务列表为空,结束任务");
            return;
        }
        //排除掉节点挂起的任务
        List<String> allHangupTask = processLogMapper.getAllHangup();
        taskList = taskList.stream()
                .filter(task -> !allHangupTask.contains(task.getId()))
                .collect(Collectors.toList());
        //TODO:筛选出流程实例id,用作项目挂起
        Set<String> proInsIds = taskList.stream()
                .map(TaskInfo::getProcessInstanceId)
                .collect(Collectors.toSet());
        //TODO:查询项目挂起日志
        log.info("查询到挂起任务 ID 数量: {}", allHangupTask.size());
        List<String> taskIds = taskList.stream().map(TaskInfo::getId).collect(Collectors.toList());
        List<Task> filteredTaskList = taskList.stream()
                .filter(task -> {
                    boolean isHangup = allHangupTask.contains(task.getId());
                    if (isHangup) {
                        log.info("任务 [{}] 因处于挂起状态被跳过", task.getId());
                    }
                    return !isHangup;
                })
                .collect(Collectors.toList());
        log.info("排除挂起任务后,待处理任务数量: {}", filteredTaskList.size());
        List<String> taskIds = filteredTaskList
        .stream().map(TaskInfo::getId).collect(Collectors.toList());
        //查询节点挂起日志
        Map<String, List<ProcessLog>> hangupLogMap = new LambdaQueryChainWrapper<>(processLogMapper)
                .in(ProcessLog::getEventType, HANGUP, CANCEL_HANGUP)
@@ -82,6 +93,9 @@
        List<ProcessCoding> processCodingList = new LambdaQueryChainWrapper<>(processCodingMapper)
                .in(ProcessCoding::getTaskId, taskIds)
                .list();
        log.info("开始处理赋码计算逻辑。当前待处理活跃任务数: {}, 已配置监控的任务数: {}", taskIds.size(), processCodingList.size());
        Map<String, ProcessCoding> taskMap = new HashMap<>();
        Map<String, Date> startTaskMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(processCodingList)) {
@@ -89,7 +103,9 @@
            taskMap = processCodingList.stream().collect(Collectors.toMap(ProcessCoding::getTaskId, Function.identity()));
            //拿到开始计时的节点集合 key:taskId value:开始时间
            startTaskMap = getStartTaskList(processCodingList);
            log.info("获取到计时起点记录数量: {}", startTaskMap.size());
        }
        //提前准备接收数据的map key:流程实例id value:需要改变的颜色
        Map<String, List<String>> map = new HashMap<>();
        List<ProcessCoding> list = new ArrayList<>();
@@ -97,85 +113,85 @@
        map.put(RED, new ArrayList<>());
        map.put(YELLOW, new ArrayList<>());
        Date now = new Date();
        //遍历所有代办的节点
        for (Task task : taskList) {
        for (Task task : filteredTaskList) {
            String taskId = task.getId();
            String procInsId = task.getProcessInstanceId();
            ProcessCoding processCoding = taskMap.get(taskId);
            if (processCoding == null) {
                //不需要监控的任务节点项目码直接改为绿色
                map.get(GREEN).add(task.getProcessInstanceId());
                log.info("任务 ID: {}, 流程实例: {}, 名称: {} 未在监控配置中,项目码默认设为 GREEN", taskId, procInsId, task.getName());
                map.get(GREEN).add(procInsId);
                continue;
            }
            //判断赋码统一用秒作为单位,且只需用红码时间判断超时,通过超时去改变项目的赋码状态,节点本身无赋码状态
            Date startTime = startTaskMap.get(processCoding.getStartTaskId());
            if (startTime == null) {
                log.warn("任务 ID: {}, 流程实例: {} 找不到计时起点时间 (StartTaskId: {}), 将默认归为 GREEN 以防止任务丢失", taskId, procInsId, processCoding.getStartTaskId());
                map.get(GREEN).add(procInsId);
                continue;
            }
            try {
                Long redTime = getTime(processCoding.getRedTime());
//                Long yellowTime = getTime(processCoding.getYellowTime());
//                Long overtime = getTime(processCoding.getOvertime());
                if (startTime == null) continue;
                //节点处理时间,需排除节假日
                long durationTime = DateUtils.getWorkingSed(startTime, now);
                //TODO:减去流程挂起时长
                //减去节点挂起时长
                durationTime = subNodeHangupTime(hangupLogMap, task, durationTime);
                long finalDurationTime = subNodeHangupTime(hangupLogMap, task, durationTime);
                String status = GREEN; // 默认状态为绿色
                String overtimeStatus = NORMAL;
                Long overtimeDurationSec = null; // 超时时长(秒,原始值)
                Double overtimeDurationHour = null; // 超时时长(小时,转换后)
                if (redTime != null && redTime != 0 && durationTime >= redTime) {
                Double overtimeDurationHour = 0.0; // 超时时长(小时,转换后)
                if (redTime != null && redTime != 0 && finalDurationTime >= redTime) {
                    status = RED; // 如果超过红色时间阈值,则表明该任务超时
                    overtimeStatus = OVERTIME;
                    overtimeDurationSec = durationTime - redTime;
                    // 2. 转换为小时(保留1位小数,避免小数过多)
                    overtimeDurationSec = finalDurationTime - redTime;
                    overtimeDurationHour = Math.round((overtimeDurationSec / 3600.0) * 10) / 10.0;
                    // 时间格式化(Java标准API,无依赖)
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String formattedStartTime = sdf.format(startTime);
                    String formattedNow = sdf.format(now);
                    // 转换红码阈值为小时(可选,日志更易读)
                    Double redTimeHour = Math.round((redTime / 3600.0) * 10) / 10.0;
                    Double durationTimeHour = Math.round((durationTime / 3600.0) * 10) / 10.0;
                    // 打印超时时长(小时单位)
                    log.info("任务超时预警:任务ID[{}],流程实例ID[{}],计时起点[{}],当前时间[{}],红码阈值[{}小时],实际有效耗时[{}小时],超时时长[{}小时]",
                            taskId, task.getProcessInstanceId(),
                            formattedStartTime, formattedNow,
                            redTimeHour, durationTimeHour, overtimeDurationHour);
                    log.info("任务超时预警:任务ID[{}], 流程实例[{}], 起点[{}], 当前[{}], 红码阈值[{}小时], 实际耗时[{}小时], 超时[{}小时]",
                            taskId, procInsId,
                            sdf.format(startTime), sdf.format(now),
                            Math.round((redTime / 3600.0) * 10) / 10.0,
                            Math.round((finalDurationTime / 3600.0) * 10) / 10.0,
                            overtimeDurationHour);
                } else {
                    log.info("任务未超时:任务ID[{}], 流程实例[{}], 实际耗时[{}小时], 红码阈值[{}小时]",
                            taskId, procInsId,
                            Math.round((finalDurationTime / 3600.0) * 10) / 10.0,
                            redTime != null ? Math.round((redTime / 3600.0) * 10) / 10.0 : 0);
                }
//                else if (yellowTime != null && yellowTime != 0 && durationTime >= yellowTime) {
//                    status = YELLOW; // 否则,如果超过黄色时间阈值,则状态为黄色
//                }
//                //处理办理期限
//                String overtimeStatus = NORMAL;
//                if (overtime != null && overtime != 0 && durationTime >= overtime) {
//                    overtimeStatus = OVERTIME; // 如果超过办理期限
//                } else if (overtime != null && overtime != 0 && durationTime >= (overtime - 12 * 60 * 60)) {
//                    overtimeStatus = WILLOVERTIME; // 如果临期(固定超时前12小时为临期)
//                }
//                else if (overtime != null && overtime != 0 && durationDay >= (overtime - 60)) {
//                    overtimeStatus = WILLOVERTIME; // 如果临期(固定超时前12小时为临期)
//                }
                map.get(status).add(task.getProcessInstanceId());
                map.get(status).add(procInsId);
                processCoding.setOverTimeTotal(String.valueOf(overtimeDurationHour));
                processCoding.setStatus(status);
                processCoding.setOvertimeStatus(overtimeStatus);
                processCoding.setStartTaskTime(task.getCreateTime());
                list.add(processCoding);
            } catch (Exception e) {
                log.error(e.getMessage(), "赋码时间格式有误");
                log.error("任务 [{}] 赋码计算发生异常: {}", taskId, e.getMessage(), e);
            }
        }
        //更新项目码
        map.forEach((key, value) -> updateProjectCoding(value, key));
        map.forEach((key, value) -> {
            if (!value.isEmpty()) {
                log.info("准备更新项目码为 {}: 流程实例列表 {}", key, value);
                updateProjectCoding(value, key);
            }
        });
        //更新节点状态 自定义的mybatis方法
        if (!CollectionUtils.isEmpty(list)) processCodingMapper.updateBatch(list);
        if (!CollectionUtils.isEmpty(list)) {
            log.info("批量更新节点状态数量: {}", list.size());
            processCodingMapper.updateBatch(list);
        }
        log.info("结束赋码");
        log.info("定时任务:expireTask 赋码计算结束");
    }
    //减去节点挂起时长
@@ -239,19 +255,28 @@
            return;
        }
        List<String> projectIds = new LambdaQueryChainWrapper<>(projectProcessMapper)
        List<ProjectProcess> projectProcesses = new LambdaQueryChainWrapper<>(projectProcessMapper)
                .in(ProjectProcess::getProcessInsId, processInstanceIds)
                .eq(ProjectProcess::getProjectType, ProjectProcessTypeEnum.PROJECT)
                .list()
                .stream()
                .map(ProjectProcess::getProjectId)
                .list();
        log.info("赋码类型 [{}]: 查询到 {} 个对应的项目流程关系", coding, projectProcesses.size());
        List<String> projectIds = projectProcesses.stream()
                .map(p -> {
                    log.info("流程实例 [{}] 对应项目 ID [{}]", p.getProcessInsId(), p.getProjectId());
                    return p.getProjectId();
                })
                .collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(projectIds)) {
            log.info("执行数据库更新: 将项目 {} 的编码更新为 {}", projectIds, coding);
            new LambdaUpdateChainWrapper<>(projectInfoMapper)
                    .in(ProjectInfo::getId, projectIds)
                    .set(ProjectInfo::getCoding, coding)
                    .update();
        } else {
            log.warn("未找到对应的项目 ID,无法执行编码更新。流程实例列表: {}", processInstanceIds);
        }
    }
}