zxl
12 小时以前 5f14844e81dade9e55725642f42c76568c5ec908
business/src/main/java/com/ycl/task/FlowableTask.java
@@ -26,6 +26,7 @@
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -53,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)
@@ -81,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)) {
@@ -88,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<>();
@@ -96,65 +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;
                if (redTime != null && redTime != 0 && durationTime >= redTime) {
                Long overtimeDurationSec = null; // 超时时长(秒,原始值)
                Double overtimeDurationHour = 0.0; // 超时时长(小时,转换后)
                if (redTime != null && redTime != 0 && finalDurationTime >= redTime) {
                    status = RED; // 如果超过红色时间阈值,则表明该任务超时
                    overtimeStatus = OVERTIME;
                    overtimeDurationSec = finalDurationTime - redTime;
                    overtimeDurationHour = Math.round((overtimeDurationSec / 3600.0) * 10) / 10.0;
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    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 赋码计算结束");
    }
    //减去节点挂起时长
@@ -218,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);
        }
    }
}