fix: 代码 review 修改

This commit is contained in:
yunlong.li 2021-11-08 11:19:10 +08:00
parent fef0562523
commit 0ff7f2ef74
5 changed files with 76 additions and 68 deletions

View File

@ -4,6 +4,7 @@ import cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo.*;
import cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.TaskService; import cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.TaskService;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -11,6 +12,7 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid; import javax.validation.Valid;
import java.io.IOException;
import java.util.List; import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@ -63,8 +65,9 @@ public class TaskController {
* @param processInstanceId * @param processInstanceId
*/ */
@GetMapping("/process/highlight-img/{id}") @GetMapping("/process/highlight-img/{id}")
public void getHighlightImg(@PathVariable("id") String processInstanceId, HttpServletResponse response) { public void getHighlightImg(@PathVariable("id") String processInstanceId, HttpServletResponse response) throws IOException {
taskService.getHighlightImg(processInstanceId, response); FileResp fileResp = taskService.getHighlightImg(processInstanceId);
ServletUtils.writeAttachment(response, fileResp.getFileName(), fileResp.getFileByte());
} }

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo;
import lombok.Data;
/**
* 文件输出类
* @author yunlongn
*/
@Data
public class FileResp {
/**
* 文件名字
*/
private String fileName;
/**
* 文件输出流
*/
private byte[] fileByte;
}

View File

@ -16,4 +16,7 @@ public interface OAErrorCodeConstants {
ErrorCode DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1003001005, "部门的部门经理不存在"); ErrorCode DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1003001005, "部门的部门经理不存在");
ErrorCode HR_POST_NOT_EXISTS = new ErrorCode(1003001006, "HR岗位未设置"); ErrorCode HR_POST_NOT_EXISTS = new ErrorCode(1003001006, "HR岗位未设置");
ErrorCode DAY_LEAVE_ERROR = new ErrorCode(1003001007, "请假天数必须大于0"); ErrorCode DAY_LEAVE_ERROR = new ErrorCode(1003001007, "请假天数必须大于0");
ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1003001008, "流程实例不存在");
ErrorCode HIGHLIGHT_IMG_ERROR = new ErrorCode(1003001009, "获取高亮流程图异常");
} }

View File

@ -54,7 +54,6 @@ public interface TaskService {
/** /**
* 返回高亮的流转进程 * 返回高亮的流转进程
* @param processInstanceId 实例Id * @param processInstanceId 实例Id
* @param response 响应
*/ */
void getHighlightImg(String processInstanceId, HttpServletResponse response); FileResp getHighlightImg(String processInstanceId);
} }

View File

@ -1,10 +1,13 @@
package cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.impl; package cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.impl;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo.*; import cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo.*;
import cn.iocoder.yudao.adminserver.modules.bpm.convert.workflow.TaskConvert; import cn.iocoder.yudao.adminserver.modules.bpm.convert.workflow.TaskConvert;
import cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.TaskService; import cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.TaskService;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.activiti.api.runtime.shared.query.Page; import org.activiti.api.runtime.shared.query.Page;
@ -13,6 +16,7 @@ import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.builders.ClaimTaskPayloadBuilder; import org.activiti.api.task.model.builders.ClaimTaskPayloadBuilder;
import org.activiti.api.task.model.builders.TaskPayloadBuilder; import org.activiti.api.task.model.builders.TaskPayloadBuilder;
import org.activiti.api.task.runtime.TaskRuntime; import org.activiti.api.task.runtime.TaskRuntime;
import org.activiti.bpmn.constants.BpmnXMLConstants;
import org.activiti.bpmn.model.BpmnModel; import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode; import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow; import org.activiti.bpmn.model.SequenceFlow;
@ -28,17 +32,19 @@ import org.activiti.image.ProcessDiagramGenerator;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static cn.iocoder.yudao.adminserver.modules.bpm.enums.oa.OAErrorCodeConstants.*;
@Slf4j @Slf4j
@Service @Service
public class TaskServiceImpl implements TaskService { public class TaskServiceImpl implements TaskService {
@ -171,69 +177,60 @@ public class TaskServiceImpl implements TaskService {
@Override @Override
public void getHighlightImg(String processInstanceId, HttpServletResponse response) { public FileResp getHighlightImg(String processInstanceId) {
// 查询历史 // 查询历史
//TODO 云扬四海 貌似流程结束后点击审批进度会报错 //TODO 云扬四海 貌似流程结束后点击审批进度会报错
// TODO @Li一些 historyService 的查询貌似比较通用是不是抽一些小方法出来 // TODO @Li一些 historyService 的查询貌似比较通用是不是抽一些小方法出来
HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
// 如果有结束时间 TODO @Li如果查询不到是不是抛出一个业务异常比较好哈 // 如果不存在实例 说明数据异常
if (hpi == null) { if (hpi == null) {
return; throw ServiceExceptionUtil.exception(PROCESS_INSTANCE_NOT_EXISTS);
} }
// 没有结束时间说明流程在执行过程中 // 没有结束时间说明流程在执行过程中
// TODO @Li一些 runtimeService 的查询貌似比较通用是不是抽一些小方法出来 // TODO @Li一些 runtimeService 的查询貌似比较通用是不是抽一些小方法出来
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId()); // TODO @Li这块和下面的逻辑比较相关可以在后面一点查询
List<String> highLightedActivities = new ArrayList<>();
List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId) List<String> highLightedActivities = new ArrayList<>();
.orderByHistoricActivityInstanceId().asc().list(); // TODO @Li这块和下面的逻辑比较相关可以在后面一点查询
// 获取所有活动节点 // 获取所有活动节点
List<HistoricActivityInstance> finishedInstances = historyService.createHistoricActivityInstanceQuery() List<HistoricActivityInstance> finishedInstances = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(processInstanceId).finished().list(); .processInstanceId(processInstanceId).finished().list();
// TODO @LihighLightedActivities 结果可以使用 CollUtils.buildList() 方法即使不用也应该用 stream简洁很重要 // TODO @LihighLightedActivities 结果可以使用 CollUtils.buildList() 方法即使不用也应该用 stream简洁很重要
for (HistoricActivityInstance hai : finishedInstances) { finishedInstances.stream()
highLightedActivities.add(hai.getActivityId()); .map(HistoricActivityInstance::getActivityId)
} .forEach(highLightedActivities::add);
// 已完成的节点+当前节点 // 已完成的节点+当前节点
highLightedActivities.addAll(runtimeService.getActiveActivityIds(processInstanceId)); highLightedActivities.addAll(runtimeService.getActiveActivityIds(processInstanceId));
BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
// 经过的流 // 经过的流
List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, historicActivityInstances); List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, processInstanceId);
//设置"宋体" //设置"宋体"
// TODO @LiService 返回 bytes最终 Controller 去写
try (InputStream inputStream = processDiagramGenerator.generateDiagram(bpmnModel, highLightedActivities, highLightedFlowIds, try (InputStream inputStream = processDiagramGenerator.generateDiagram(bpmnModel, highLightedActivities, highLightedFlowIds,
"宋体", "宋体", "宋体")){ "宋体", "宋体", "宋体")){
String picName = hpi.getProcessDefinitionName()+".svg"; FileResp fileResp = new FileResp();
// 输出到浏览器 String picName = hpi.getProcessDefinitionName() + ".svg";
responseImage(response, inputStream, picName); fileResp.setFileName(picName);
fileResp.setFileByte(IoUtil.readBytes(inputStream));
return fileResp;
} catch (IOException e) { } catch (IOException e) {
log.error(ExceptionUtils.getStackTrace(e)); log.error(ExceptionUtils.getStackTrace(e));
throw ServiceExceptionUtil.exception(HIGHLIGHT_IMG_ERROR);
} }
} }
// TODO @Li参考 ServletUtils 方法如果没有满足的可以在写一个
private void responseImage(HttpServletResponse response, InputStream inputStream, String picName) throws IOException {
response.setContentType("application/octet-stream;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(picName, "UTF-8"));
byte[] b = new byte[1024];
int len = -1;
while ((len = inputStream.read(b, 0, 1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
response.flushBuffer();
}
// TODO @Li这个方法的可读性还有一定的优化空间可以思考下哈 // TODO @Li这个方法的可读性还有一定的优化空间可以思考下哈
/** /**
* 获取已经流转的线 https://blog.csdn.net/qiuxinfa123/article/details/119579863 * 获取指定 processInstanceId 已经高亮的Flows
* @see * 获取已经流转的线 参考 https://blog.csdn.net/qiuxinfa123/article/details/119579863
* @param bpmnModel model * @param bpmnModel model
* @param historicActivityInstances 高亮线条 * @param processInstanceId 流程实例Id
* @return * @return 获取已经流转的列表
*/ */
private List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) { private List<String> getHighLightedFlows(BpmnModel bpmnModel, String processInstanceId) {
// 获取所有的线条
List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId)
.orderByHistoricActivityInstanceId().asc().list();
// 高亮流程已发生流转的线id集合 // 高亮流程已发生流转的线id集合
List<String> highLightedFlowIds = new ArrayList<>(); List<String> highLightedFlowIds = new ArrayList<>();
// 全部活动节点 // 全部活动节点
@ -249,57 +246,43 @@ public class TaskServiceImpl implements TaskService {
finishedActivityInstances.add(historicActivityInstance); finishedActivityInstances.add(historicActivityInstance);
} }
} }
// 提取活动id 是唯一的塞入Map
// TODO @Li这两个变量直接放到循环里这种优化一般不需要做的对性能影响超级小 Map<String, HistoricActivityInstance> historicActivityInstanceMap = CollectionUtils.convertMap(historicActivityInstances, HistoricActivityInstance::getActivityId);
FlowNode currentFlowNode;
FlowNode targetFlowNode;
// 遍历已完成的活动实例从每个实例的outgoingFlows中找到已执行的 // 遍历已完成的活动实例从每个实例的outgoingFlows中找到已执行的
for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) { for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) {
// 获得当前活动对应的节点信息及outgoingFlows信息 // 获得当前活动对应的节点信息及outgoingFlows信息
currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true); FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows(); List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();
// 遍历outgoingFlows并找到已流转的 满足如下条件认为已已流转 // 遍历outgoingFlows并找到已流转的 满足如下条件认为已已流转
// 1.当前节点是并行网关或兼容网关则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转 // 1.当前节点是并行网关或兼容网关则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
// 2.当前节点是以上两种类型之外的通过outgoingFlows查找到的时间最早的流转节点视为有效流转 // 2.当前节点是以上两种类型之外的通过outgoingFlows查找到的时间最早的流转节点视为有效流转
// TODO @LiparallelGateway "inclusiveGateway"有对应的枚举么如果木有可以自己枚举哈 if (BpmnXMLConstants.ELEMENT_GATEWAY_PARALLEL.equals(currentActivityInstance.getActivityType())
if ("parallelGateway".equals(currentActivityInstance.getActivityType()) || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) { || BpmnXMLConstants.ELEMENT_GATEWAY_INCLUSIVE.equals(currentActivityInstance.getActivityType())) {
// 遍历历史活动节点找到匹配流程目标节点的 // 遍历历史活动节点找到匹配流程目标节点的
for (SequenceFlow sequenceFlow : sequenceFlows) { for (SequenceFlow sequenceFlow : sequenceFlows) {
targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true); FlowNode targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
if (historicActivityNodes.contains(targetFlowNode)) { if (historicActivityNodes.contains(targetFlowNode)) {
highLightedFlowIds.add(targetFlowNode.getId()); highLightedFlowIds.add(targetFlowNode.getId());
} }
} }
} else { } else {
// TODO @Li如果是为了获取到时间更早的一个是不是遍历的过程中就可以解决 long earliestStamp = 0L;
List<Map<String, Object>> tempMapList = new ArrayList<>(); String highLightedFlowId = null;
// 循环流出的流
for (SequenceFlow sequenceFlow : sequenceFlows) { for (SequenceFlow sequenceFlow : sequenceFlows) {
for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) { HistoricActivityInstance historicActivityInstance = historicActivityInstanceMap.get(sequenceFlow.getTargetRef());
if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) { if (historicActivityInstance == null) {
Map<String, Object> map = new HashMap<>(); continue;
map.put("highLightedFlowId", sequenceFlow.getId());
map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime());
tempMapList.add(map);
}
} }
} final long startTime = historicActivityInstance.getStartTime().getTime();
if (!CollectionUtils.isEmpty(tempMapList)) {
// 遍历匹配的集合取得开始时间最早的一个 // 遍历匹配的集合取得开始时间最早的一个
long earliestStamp = 0L; if (earliestStamp == 0 || earliestStamp >= startTime) {
String highLightedFlowId = null; highLightedFlowId = sequenceFlow.getId();
for (Map<String, Object> map : tempMapList) { earliestStamp = startTime;
// TODO @Li可以使用 MapUtil get
long highLightedFlowStartTime = Long.valueOf(map.get("highLightedFlowStartTime").toString());
if (earliestStamp == 0 || earliestStamp >= highLightedFlowStartTime) {
highLightedFlowId = map.get("highLightedFlowId").toString();
earliestStamp = highLightedFlowStartTime;
}
} }
highLightedFlowIds.add(highLightedFlowId);
} }
highLightedFlowIds.add(highLightedFlowId);
} }
} }