三期内容优化

This commit is contained in:
2025-03-20 18:19:31 +08:00
parent ed7012e2dd
commit 72036bc7bd
44 changed files with 780 additions and 366 deletions

View File

@@ -0,0 +1,14 @@
package com.ruoyi.workflow.domain;
import lombok.Data;
import java.util.Date;
@Data
public class FlowRecord {
private Date startTime;
private Date endTime;
}

View File

@@ -1,5 +1,6 @@
package com.ruoyi.workflow.service;
import com.ruoyi.workflow.domain.FlowRecord;
import com.ruoyi.workflow.domain.bo.WfTaskBo;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.engine.runtime.ProcessInstance;
@@ -114,4 +115,6 @@ public interface IWfTaskService {
* @param variables 流程参数
*/
void startFirstTask(ProcessInstance processInstance, Map<String, Object> variables);
public List<FlowRecord> getApprovedRecords(Long userId, String category);
}

View File

@@ -17,6 +17,7 @@ import com.ruoyi.flowable.flow.CustomProcessDiagramGenerator;
import com.ruoyi.flowable.flow.FlowableUtils;
import com.ruoyi.flowable.utils.ModelUtils;
import com.ruoyi.flowable.utils.TaskUtils;
import com.ruoyi.workflow.domain.FlowRecord;
import com.ruoyi.workflow.domain.bo.WfTaskBo;
import com.ruoyi.workflow.service.IWfCopyService;
import com.ruoyi.workflow.service.IWfTaskService;
@@ -39,10 +40,12 @@ import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
@@ -119,15 +122,15 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
}
// 获取流程实例
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(taskBo.getProcInsId())
.singleResult();
.processInstanceId(taskBo.getProcInsId())
.singleResult();
if (processInstance == null) {
throw new RuntimeException("流程实例不存在,请确认!");
}
// 获取流程定义信息
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionId(task.getProcessDefinitionId())
.singleResult();
.processDefinitionId(task.getProcessDefinitionId())
.singleResult();
identityService.setAuthenticatedUserId(TaskUtils.getUserId());
// 添加审批意见
@@ -141,9 +144,9 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
List<Execution> executions = runtimeService.createExecutionQuery().parentId(task.getProcessInstanceId()).list();
List<String> executionIds = executions.stream().map(Execution::getId).collect(Collectors.toList());
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId())
.moveExecutionsToSingleActivityId(executionIds, endEvent.getId())
.changeState();
.processInstanceId(task.getProcessInstanceId())
.moveExecutionsToSingleActivityId(executionIds, endEvent.getId())
.changeState();
// 处理抄送用户
if (!copyService.makeCopy(taskBo)) {
throw new RuntimeException("抄送任务失败");
@@ -206,8 +209,8 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
try {
// 1 对 1 或 多 对 1 情况currentIds 当前要跳转的节点列表(1或多)targetKey 跳转到的节点(1)
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId())
.moveActivityIdsToSingleActivityId(currentIds, bo.getTargetKey()).changeState();
.processInstanceId(task.getProcessInstanceId())
.moveActivityIdsToSingleActivityId(currentIds, bo.getTargetKey()).changeState();
} catch (FlowableObjectNotFoundException e) {
throw new RuntimeException("未找到流程实例,流程可能已发生变化");
} catch (FlowableException e) {
@@ -238,16 +241,16 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
// 查询历史节点实例
List<HistoricActivityInstance> activityInstanceList = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(task.getProcessInstanceId())
.activityType(BpmnXMLConstants.ELEMENT_TASK_USER)
.finished()
.orderByHistoricActivityInstanceEndTime().asc()
.list();
.processInstanceId(task.getProcessInstanceId())
.activityType(BpmnXMLConstants.ELEMENT_TASK_USER)
.finished()
.orderByHistoricActivityInstanceEndTime().asc()
.list();
List<String> activityIdList = activityInstanceList.stream()
.map(HistoricActivityInstance::getActivityId)
.filter(activityId -> !StringUtils.equals(activityId, task.getTaskDefinitionKey()))
.distinct()
.collect(Collectors.toList());
.map(HistoricActivityInstance::getActivityId)
.filter(activityId -> !StringUtils.equals(activityId, task.getTaskDefinitionKey()))
.distinct()
.collect(Collectors.toList());
// 获取当前任务节点元素
FlowElement source = ModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
List<FlowElement> elementList = new ArrayList<>();
@@ -313,7 +316,7 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
throw new ServiceException("获取任务失败!");
}
StringBuilder commentBuilder = new StringBuilder(LoginHelper.getNickName())
.append("->");
.append("->");
String nickName = sysUserService.selectNickNameById(Long.parseLong(bo.getUserId()));
if (StringUtils.isNotBlank(nickName)) {
commentBuilder.append(nickName);
@@ -353,7 +356,7 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
throw new ServiceException("获取任务失败!");
}
StringBuilder commentBuilder = new StringBuilder(LoginHelper.getNickName())
.append("->");
.append("->");
String nickName = sysUserService.selectNickNameById(Long.parseLong(bo.getUserId()));
if (StringUtils.isNotBlank(nickName)) {
commentBuilder.append(nickName);
@@ -392,7 +395,7 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
}
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(bo.getProcInsId()).singleResult();
.processInstanceId(bo.getProcInsId()).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
if (Objects.nonNull(bpmnModel)) {
Process process = bpmnModel.getMainProcess();
@@ -404,12 +407,12 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
// 获取当前流程最后一个节点
String endId = endNodes.get(0).getId();
List<Execution> executions = runtimeService.createExecutionQuery()
.parentId(processInstance.getProcessInstanceId()).list();
.parentId(processInstance.getProcessInstanceId()).list();
List<String> executionIds = new ArrayList<>();
executions.forEach(execution -> executionIds.add(execution.getId()));
// 变更流程为已结束状态
runtimeService.createChangeActivityStateBuilder()
.moveExecutionsToSingleActivityId(executionIds, endId).changeState();
.moveExecutionsToSingleActivityId(executionIds, endId).changeState();
}
}
}
@@ -426,17 +429,17 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
String taskId = taskBo.getTaskId();
// 校验流程是否结束
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(procInsId)
.active()
.singleResult();
if(ObjectUtil.isNull(processInstance)) {
.processInstanceId(procInsId)
.active()
.singleResult();
if (ObjectUtil.isNull(processInstance)) {
throw new RuntimeException("流程已结束或已挂起,无法执行撤回操作");
}
// 获取待撤回任务实例
HistoricTaskInstance currTaskIns = historyService.createHistoricTaskInstanceQuery()
.taskId(taskId)
.taskAssignee(TaskUtils.getUserId())
.singleResult();
.taskId(taskId)
.taskAssignee(TaskUtils.getUserId())
.singleResult();
if (ObjectUtil.isNull(currTaskIns)) {
throw new RuntimeException("当前任务不存在,无法执行撤回操作");
}
@@ -449,10 +452,10 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
// 获取当前节点之后已完成的流程历史节点
List<HistoricTaskInstance> finishedTaskInsList = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(procInsId)
.taskCreatedAfter(currTaskIns.getEndTime())
.finished()
.list();
.processInstanceId(procInsId)
.taskCreatedAfter(currTaskIns.getEndTime())
.finished()
.list();
for (HistoricTaskInstance finishedTaskInstance : finishedTaskInsList) {
// 检查已完成流程历史节点是否存在下一级中
if (CollUtil.contains(nextUserTaskKeys, finishedTaskInstance.getTaskDefinitionKey())) {
@@ -474,8 +477,8 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
}
try {
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(procInsId)
.moveExecutionsToSingleActivityId(revokeExecutionIds, currTaskIns.getTaskDefinitionKey()).changeState();
.processInstanceId(procInsId)
.moveExecutionsToSingleActivityId(revokeExecutionIds, currTaskIns.getTaskDefinitionKey()).changeState();
} catch (FlowableObjectNotFoundException e) {
throw new RuntimeException("未找到流程实例,流程可能已发生变化");
} catch (FlowableException e) {
@@ -507,7 +510,7 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
// 获得活动的节点
List<HistoricActivityInstance> highLightedFlowList = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list();
.processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list();
List<String> highLightedFlows = new ArrayList<>();
List<String> highLightedNodes = new ArrayList<>();
@@ -528,7 +531,7 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
//获取自定义图片生成器
ProcessDiagramGenerator diagramGenerator = new CustomProcessDiagramGenerator();
return diagramGenerator.generateDiagram(bpmnModel, "png", highLightedNodes, highLightedFlows, configuration.getActivityFontName(),
configuration.getLabelFontName(), configuration.getAnnotationFontName(), configuration.getClassLoader(), 1.0, true);
configuration.getLabelFontName(), configuration.getAnnotationFontName(), configuration.getClassLoader(), 1.0, true);
}
@@ -541,10 +544,10 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
@Override
public Map<String, Object> getProcessVariables(String taskId) {
HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery()
.includeProcessVariables()
.finished()
.taskId(taskId)
.singleResult();
.includeProcessVariables()
.finished()
.taskId(taskId)
.singleResult();
if (Objects.nonNull(historicTaskInstance)) {
return historicTaskInstance.getProcessVariables();
}
@@ -553,8 +556,9 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
/**
* 启动第一个任务
*
* @param processInstance 流程实例
* @param variables 流程参数
* @param variables 流程参数
*/
@Override
public void startFirstTask(ProcessInstance processInstance, Map<String, Object> variables) {
@@ -574,15 +578,16 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
/**
* 指派下一任务审批人
* @param bpmnModel bpmn模型
*
* @param bpmnModel bpmn模型
* @param processInsId 流程实例id
* @param userIds 用户ids
* @param userIds 用户ids
*/
private void assignNextUsers(BpmnModel bpmnModel, String processInsId, String userIds) {
// 获取所有节点信息
List<Task> list = taskService.createTaskQuery()
.processInstanceId(processInsId)
.list();
.processInstanceId(processInsId)
.list();
if (list.size() == 0) {
return;
}
@@ -620,4 +625,107 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
}
}
}
/**
* 根据 category + userId 查询已审批通过(流程已结束)的记录,提取 startTime, endTime 返回
*
* @param userId 当前用户ID
* @param category 流程分类 (如 "trip" 出差, "absence" 请假)
* @return 已审批通过的记录列表,每个包含 startTime, endTime
*/
@Override
public List<FlowRecord> getApprovedRecords(Long userId, String category) {
List<FlowRecord> result = new ArrayList<>();
// 1) 先查找所有 与 category 匹配的流程定义(可能有多个版本)
List<ProcessDefinition> definitions = repositoryService.createProcessDefinitionQuery()
.processDefinitionCategory(category)
.latestVersion() // 如果只想用最新版本,可加这一行
.list();
if (definitions == null || definitions.isEmpty()) {
return result; // 没找到对应分类的流程定义,直接返回空
}
// 收集所有流程定义ID
List<String> defIds = definitions.stream()
.map(ProcessDefinition::getId)
.collect(Collectors.toList());
// 2) 根据流程定义ID列表 + startedBy(userId) + 已结束流程,查流程实例
for (String defId : defIds) {
List<HistoricProcessInstance> hpiList = historyService.createHistoricProcessInstanceQuery()
.processDefinitionId(defId)
.startedBy(String.valueOf(userId))
.finished() // 已审批完成 / 已结束
.list();
// 3)对每个已结束的流程实例,去找 startTime, endTime 变量
for (HistoricProcessInstance hpi : hpiList) {
String procInstId = hpi.getId();
// 3.1) 如果 startTime, endTime 是存流程变量,直接查 HistoricVariableInstance
List<HistoricVariableInstance> varList = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(procInstId)
.list();
// 根据你的实际变量名提取
Date startDate = null;
Date endDate = null;
for (HistoricVariableInstance var : varList) {
if ("startTime".equals(var.getVariableName())) {
// 若你的流程里存的就是 Date 类型,可以直接转
startDate = (Date) var.getValue();
} else if ("endTime".equals(var.getVariableName())) {
endDate = (Date) var.getValue();
}
}
// 3.2) 或者,如果 startTime, endTime 是 FormProperty则可用 HistoricDetailQuery
// 下面演示如果在表单属性里
// (示例仅供参考,实际要看你在模型里是否使用了 <formProperty/> )
/*
List<HistoricDetail> details = historyService.createHistoricDetailQuery()
.processInstanceId(procInstId)
.formProperties()
.list();
for (HistoricDetail d : details) {
HistoricFormProperty fp = (HistoricFormProperty) d;
if ("startTime".equals(fp.getPropertyId())) {
// 这里可能需要 parse 成 Date
startDate = parseDate(fp.getPropertyValue());
} else if ("endTime".equals(fp.getPropertyId())) {
endDate = parseDate(fp.getPropertyValue());
}
}
*/
// 4) 最后封装到 FlowRecord
if (startDate != null && endDate != null) {
FlowRecord record = new FlowRecord();
record.setStartTime(startDate);
record.setEndTime(endDate);
// 你还可以记录更多字段,如: record.setCategory(category);
result.add(record);
}
}
}
return result;
}
/**
* 如果需要解析字符串 => Date可以用 SimpleDateFormat 或其他方式
*/
private Date parseDate(String dateStr) {
if (dateStr == null) return null;
try {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateStr);
} catch (Exception e) {
// 失败再试别的格式
try {
return new SimpleDateFormat("yyyy-MM-dd").parse(dateStr);
} catch (Exception ignored) {
}
}
return null;
}
}