package com.ycl.task; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; import com.ycl.common.enums.business.ProcessLogEventTypeEnum; import com.ycl.common.enums.business.ProjectProcessTypeEnum; import com.ycl.common.utils.DateUtils; 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.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.ProcessCodingService; import com.ycl.service.ProcessLogService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.flowable.task.api.Task; import org.flowable.task.api.TaskInfo; import org.flowable.task.api.history.HistoricTaskInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import static com.ycl.common.constant.ProcessOverTimeConstants.*; import static com.ycl.common.enums.business.ProcessLogEventTypeEnum.CANCEL_HANGUP; import static com.ycl.common.enums.business.ProcessLogEventTypeEnum.HANGUP; @Slf4j @Component("flowableTask") public class FlowableTask extends FlowServiceFactory { @Autowired private ProjectProcessMapper projectProcessMapper; @Autowired private ProjectInfoMapper projectInfoMapper; @Autowired private ProcessCodingMapper processCodingMapper; @Autowired private ProcessLogService processLogService; @Autowired private ProcessLogMapper processLogMapper; /** * 赋码任务 * 两个逻辑 改项目码、改节点颜色 */ public void expireTask() { log.info("开始赋码"); //当前正在运行的所有任务节点 List taskList = taskService.createTaskQuery().active().list(); if (CollectionUtils.isEmpty(taskList)) return; //排除掉节点挂起的任务 List allHangupTask = processLogMapper.getAllHangup(); taskList = taskList.stream() .filter(task -> !allHangupTask.contains(task.getId())) .collect(Collectors.toList()); //TODO:筛选出流程实例id,用作项目挂起 Set proInsIds = taskList.stream() .map(TaskInfo::getProcessInstanceId) .collect(Collectors.toSet()); //TODO:查询项目挂起日志 List taskIds = taskList.stream().map(TaskInfo::getId).collect(Collectors.toList()); //查询节点挂起日志 Map> hangupLogMap = new LambdaQueryChainWrapper<>(processLogMapper) .in(ProcessLog::getEventType, HANGUP, CANCEL_HANGUP) .in(ProcessLog::getProcessInsId, taskIds) .list() .stream() .collect(Collectors.groupingBy(ProcessLog::getTaskId)); //需要监控的赋码任务 List processCodingList = new LambdaQueryChainWrapper<>(processCodingMapper) .in(ProcessCoding::getTaskId, taskIds) .list(); Map taskMap = new HashMap<>(); Map startTaskMap = new HashMap<>(); if (!CollectionUtils.isEmpty(processCodingList)) { //key为taskId value为本体对象 taskMap = processCodingList.stream().collect(Collectors.toMap(ProcessCoding::getTaskId, Function.identity())); //拿到开始计时的节点集合 key:taskId value:开始时间 startTaskMap = getStartTaskList(processCodingList); } //提前准备接收数据的map key:流程实例id value:需要改变的颜色 Map> map = new HashMap<>(); List list = new ArrayList<>(); map.put(GREEN, new ArrayList<>()); map.put(RED, new ArrayList<>()); map.put(YELLOW, new ArrayList<>()); Date now = new Date(); //遍历所有代办的节点 for (Task task : taskList) { String taskId = task.getId(); ProcessCoding processCoding = taskMap.get(taskId); if (processCoding == null) { //不需要监控的任务节点项目码直接改为绿色 map.get(GREEN).add(task.getProcessInstanceId()); continue; } //判断赋码统一用秒作为单位,且只需用红码时间判断超时,通过超时去改变项目的赋码状态,节点本身无赋码状态 Date startTime = startTaskMap.get(processCoding.getStartTaskId()); 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); String status = GREEN; // 默认状态为绿色 String overtimeStatus = NORMAL; if (redTime != null && redTime != 0 && durationTime >= redTime) { status = RED; // 如果超过红色时间阈值,则表明该任务超时 overtimeStatus = OVERTIME; } // 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()); processCoding.setStatus(status); processCoding.setOvertimeStatus(overtimeStatus); processCoding.setStartTaskTime(task.getCreateTime()); list.add(processCoding); } catch (Exception e) { log.error(e.getMessage(), "赋码时间格式有误"); } } //更新项目码 map.forEach((key, value) -> updateProjectCoding(value, key)); //更新节点状态 自定义的mybatis方法 if (!CollectionUtils.isEmpty(list)) processCodingMapper.updateBatch(list); log.info("结束赋码"); } //减去节点挂起时长 private long subNodeHangupTime(Map> hangupLogMap, Task task, long durationTime) { List processLogs = hangupLogMap.get(task.getId()); if (!CollectionUtils.isEmpty(processLogs)) { long hangupTime = 0; //分组分为挂起和取消挂起 Map> logEventTypeMap = processLogs.stream() .sorted(Comparator.comparing(ProcessLog::getGmtCreate)) .collect(Collectors.groupingBy(ProcessLog::getEventType)); List cancelHangup = logEventTypeMap.get(CANCEL_HANGUP); for (int i = 0; i < cancelHangup.size(); i++) { ProcessLog processLog = cancelHangup.get(i); hangupTime += processLog.getGmtCreate().getTime() - logEventTypeMap.get(HANGUP).get(i).getGmtCreate().getTime(); } durationTime = durationTime - hangupTime; } return durationTime; } 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; } private Map getStartTaskList(List processCodingList) { //查出任务计时起始节点集合 List startTaskIds = processCodingList.stream().map(ProcessCoding::getStartTaskId).collect(Collectors.toList()); //查出起始计时节点数据 Map startDateMap = new HashMap<>(); List hisStartTasks = historyService.createHistoricTaskInstanceQuery().taskIds(startTaskIds).list(); if (!CollectionUtils.isEmpty(hisStartTasks)) { hisStartTasks.forEach(hisTask -> { startDateMap.put(hisTask.getId(), hisTask.getStartTime()); }); } return startDateMap; } /** * 赋码 * * @param processInstanceIds 流程实例ID列表 * @param coding 赋码值 */ private void updateProjectCoding(List processInstanceIds, String coding) { if (CollectionUtils.isEmpty(processInstanceIds)) { return; } List projectIds = new LambdaQueryChainWrapper<>(projectProcessMapper) .in(ProjectProcess::getProcessInsId, processInstanceIds) .eq(ProjectProcess::getProjectType, ProjectProcessTypeEnum.PROJECT) .list() .stream() .map(ProjectProcess::getProjectId) .collect(Collectors.toList()); if (!CollectionUtils.isEmpty(projectIds)) { new LambdaUpdateChainWrapper<>(projectInfoMapper) .in(ProjectInfo::getId, projectIds) .set(ProjectInfo::getCoding, coding) .update(); } } }