Commit 0ff7f2ef746b4350c6d67c7efcabd90b7693832a

Authored by yunlong.li
1 parent fef05625

fix: 代码 review 修改

yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/workflow/TaskController.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo.*; @@ -4,6 +4,7 @@ import cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo.*;
4 import cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.TaskService; 4 import cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.TaskService;
5 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
7 import io.swagger.annotations.Api; 8 import io.swagger.annotations.Api;
8 import io.swagger.annotations.ApiOperation; 9 import io.swagger.annotations.ApiOperation;
9 import org.springframework.web.bind.annotation.*; 10 import org.springframework.web.bind.annotation.*;
@@ -11,6 +12,7 @@ import org.springframework.web.bind.annotation.*; @@ -11,6 +12,7 @@ import org.springframework.web.bind.annotation.*;
11 import javax.annotation.Resource; 12 import javax.annotation.Resource;
12 import javax.servlet.http.HttpServletResponse; 13 import javax.servlet.http.HttpServletResponse;
13 import javax.validation.Valid; 14 import javax.validation.Valid;
  15 +import java.io.IOException;
14 import java.util.List; 16 import java.util.List;
15 17
16 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 18 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -63,8 +65,9 @@ public class TaskController { @@ -63,8 +65,9 @@ public class TaskController {
63 * @param processInstanceId 65 * @param processInstanceId
64 */ 66 */
65 @GetMapping("/process/highlight-img/{id}") 67 @GetMapping("/process/highlight-img/{id}")
66 - public void getHighlightImg(@PathVariable("id") String processInstanceId, HttpServletResponse response) {  
67 - taskService.getHighlightImg(processInstanceId, response); 68 + public void getHighlightImg(@PathVariable("id") String processInstanceId, HttpServletResponse response) throws IOException {
  69 + FileResp fileResp = taskService.getHighlightImg(processInstanceId);
  70 + ServletUtils.writeAttachment(response, fileResp.getFileName(), fileResp.getFileByte());
68 } 71 }
69 72
70 73
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/workflow/vo/FileResp.java 0 → 100644
  1 +package cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * 文件输出类
  7 + * @author yunlongn
  8 + */
  9 +@Data
  10 +public class FileResp {
  11 + /**
  12 + * 文件名字
  13 + */
  14 + private String fileName;
  15 +
  16 + /**
  17 + * 文件输出流
  18 + */
  19 + private byte[] fileByte;
  20 +}
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/oa/OAErrorCodeConstants.java
@@ -16,4 +16,7 @@ public interface OAErrorCodeConstants { @@ -16,4 +16,7 @@ public interface OAErrorCodeConstants {
16 ErrorCode DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1003001005, "部门的部门经理不存在"); 16 ErrorCode DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1003001005, "部门的部门经理不存在");
17 ErrorCode HR_POST_NOT_EXISTS = new ErrorCode(1003001006, "HR岗位未设置"); 17 ErrorCode HR_POST_NOT_EXISTS = new ErrorCode(1003001006, "HR岗位未设置");
18 ErrorCode DAY_LEAVE_ERROR = new ErrorCode(1003001007, "请假天数必须大于0"); 18 ErrorCode DAY_LEAVE_ERROR = new ErrorCode(1003001007, "请假天数必须大于0");
  19 +
  20 + ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1003001008, "流程实例不存在");
  21 + ErrorCode HIGHLIGHT_IMG_ERROR = new ErrorCode(1003001009, "获取高亮流程图异常");
19 } 22 }
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/workflow/TaskService.java
@@ -54,7 +54,6 @@ public interface TaskService { @@ -54,7 +54,6 @@ public interface TaskService {
54 /** 54 /**
55 * 返回高亮的流转进程 55 * 返回高亮的流转进程
56 * @param processInstanceId 实例Id 56 * @param processInstanceId 实例Id
57 - * @param response 响应  
58 */ 57 */
59 - void getHighlightImg(String processInstanceId, HttpServletResponse response); 58 + FileResp getHighlightImg(String processInstanceId);
60 } 59 }
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/workflow/impl/TaskServiceImpl.java
1 package cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.impl; 1 package cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.impl;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.io.IoUtil;
4 import cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo.*; 5 import cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo.*;
5 import cn.iocoder.yudao.adminserver.modules.bpm.convert.workflow.TaskConvert; 6 import cn.iocoder.yudao.adminserver.modules.bpm.convert.workflow.TaskConvert;
6 import cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.TaskService; 7 import cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.TaskService;
  8 +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  10 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; 11 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
9 import lombok.extern.slf4j.Slf4j; 12 import lombok.extern.slf4j.Slf4j;
10 import org.activiti.api.runtime.shared.query.Page; 13 import org.activiti.api.runtime.shared.query.Page;
@@ -13,6 +16,7 @@ import org.activiti.api.task.model.Task; @@ -13,6 +16,7 @@ import org.activiti.api.task.model.Task;
13 import org.activiti.api.task.model.builders.ClaimTaskPayloadBuilder; 16 import org.activiti.api.task.model.builders.ClaimTaskPayloadBuilder;
14 import org.activiti.api.task.model.builders.TaskPayloadBuilder; 17 import org.activiti.api.task.model.builders.TaskPayloadBuilder;
15 import org.activiti.api.task.runtime.TaskRuntime; 18 import org.activiti.api.task.runtime.TaskRuntime;
  19 +import org.activiti.bpmn.constants.BpmnXMLConstants;
16 import org.activiti.bpmn.model.BpmnModel; 20 import org.activiti.bpmn.model.BpmnModel;
17 import org.activiti.bpmn.model.FlowNode; 21 import org.activiti.bpmn.model.FlowNode;
18 import org.activiti.bpmn.model.SequenceFlow; 22 import org.activiti.bpmn.model.SequenceFlow;
@@ -28,17 +32,19 @@ import org.activiti.image.ProcessDiagramGenerator; @@ -28,17 +32,19 @@ import org.activiti.image.ProcessDiagramGenerator;
28 import org.apache.commons.lang3.exception.ExceptionUtils; 32 import org.apache.commons.lang3.exception.ExceptionUtils;
29 import org.springframework.stereotype.Service; 33 import org.springframework.stereotype.Service;
30 import org.springframework.transaction.annotation.Transactional; 34 import org.springframework.transaction.annotation.Transactional;
31 -import org.springframework.util.CollectionUtils;  
32 35
33 36
34 import javax.annotation.Resource; 37 import javax.annotation.Resource;
35 import javax.servlet.http.HttpServletResponse; 38 import javax.servlet.http.HttpServletResponse;
  39 +import java.io.ByteArrayOutputStream;
36 import java.io.IOException; 40 import java.io.IOException;
37 import java.io.InputStream; 41 import java.io.InputStream;
38 import java.net.URLEncoder; 42 import java.net.URLEncoder;
39 import java.util.*; 43 import java.util.*;
40 import java.util.stream.Collectors; 44 import java.util.stream.Collectors;
41 45
  46 +import static cn.iocoder.yudao.adminserver.modules.bpm.enums.oa.OAErrorCodeConstants.*;
  47 +
42 @Slf4j 48 @Slf4j
43 @Service 49 @Service
44 public class TaskServiceImpl implements TaskService { 50 public class TaskServiceImpl implements TaskService {
@@ -171,69 +177,60 @@ public class TaskServiceImpl implements TaskService { @@ -171,69 +177,60 @@ public class TaskServiceImpl implements TaskService {
171 177
172 178
173 @Override 179 @Override
174 - public void getHighlightImg(String processInstanceId, HttpServletResponse response) { 180 + public FileResp getHighlightImg(String processInstanceId) {
175 // 查询历史 181 // 查询历史
176 //TODO 云扬四海 貌似流程结束后,点击审批进度会报错 182 //TODO 云扬四海 貌似流程结束后,点击审批进度会报错
177 // TODO @Li:一些 historyService 的查询,貌似比较通用,是不是抽一些小方法出来 183 // TODO @Li:一些 historyService 的查询,貌似比较通用,是不是抽一些小方法出来
178 HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); 184 HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
179 - // 如果有结束时间 TODO @Li:如果查询不到,是不是抛出一个业务异常比较好哈? 185 + // 如果不存在实例。 说明数据异常
180 if (hpi == null) { 186 if (hpi == null) {
181 - return; 187 + throw ServiceExceptionUtil.exception(PROCESS_INSTANCE_NOT_EXISTS);
182 } 188 }
183 // 没有结束时间。说明流程在执行过程中 189 // 没有结束时间。说明流程在执行过程中
184 // TODO @Li:一些 runtimeService 的查询,貌似比较通用,是不是抽一些小方法出来 190 // TODO @Li:一些 runtimeService 的查询,貌似比较通用,是不是抽一些小方法出来
185 ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); 191 ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
186 - BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId()); // TODO @Li:这块和下面的逻辑比较相关,可以在后面一点查询。  
187 - List<String> highLightedActivities = new ArrayList<>();  
188 192
189 - List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId)  
190 - .orderByHistoricActivityInstanceId().asc().list(); // TODO @Li:这块和下面的逻辑比较相关,可以在后面一点查询。 193 + List<String> highLightedActivities = new ArrayList<>();
191 // 获取所有活动节点 194 // 获取所有活动节点
192 List<HistoricActivityInstance> finishedInstances = historyService.createHistoricActivityInstanceQuery() 195 List<HistoricActivityInstance> finishedInstances = historyService.createHistoricActivityInstanceQuery()
193 .processInstanceId(processInstanceId).finished().list(); 196 .processInstanceId(processInstanceId).finished().list();
194 // TODO @Li:highLightedActivities 结果,可以使用 CollUtils.buildList() 方法。即使不用,也应该用 stream。简洁很重要。 197 // TODO @Li:highLightedActivities 结果,可以使用 CollUtils.buildList() 方法。即使不用,也应该用 stream。简洁很重要。
195 - for (HistoricActivityInstance hai : finishedInstances) {  
196 - highLightedActivities.add(hai.getActivityId());  
197 - } 198 + finishedInstances.stream()
  199 + .map(HistoricActivityInstance::getActivityId)
  200 + .forEach(highLightedActivities::add);
198 // 已完成的节点+当前节点 201 // 已完成的节点+当前节点
199 highLightedActivities.addAll(runtimeService.getActiveActivityIds(processInstanceId)); 202 highLightedActivities.addAll(runtimeService.getActiveActivityIds(processInstanceId));
200 203
  204 + BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
201 // 经过的流 205 // 经过的流
202 - List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, historicActivityInstances); 206 + List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, processInstanceId);
203 207
204 //设置"宋体" 208 //设置"宋体"
205 - // TODO @Li:Service 返回 bytes,最终 Controller 去写  
206 try (InputStream inputStream = processDiagramGenerator.generateDiagram(bpmnModel, highLightedActivities, highLightedFlowIds, 209 try (InputStream inputStream = processDiagramGenerator.generateDiagram(bpmnModel, highLightedActivities, highLightedFlowIds,
207 "宋体", "宋体", "宋体")){ 210 "宋体", "宋体", "宋体")){
208 - String picName = hpi.getProcessDefinitionName()+".svg";  
209 - // 输出到浏览器  
210 - responseImage(response, inputStream, picName); 211 + FileResp fileResp = new FileResp();
  212 + String picName = hpi.getProcessDefinitionName() + ".svg";
  213 + fileResp.setFileName(picName);
  214 + fileResp.setFileByte(IoUtil.readBytes(inputStream));
  215 + return fileResp;
211 } catch (IOException e) { 216 } catch (IOException e) {
212 log.error(ExceptionUtils.getStackTrace(e)); 217 log.error(ExceptionUtils.getStackTrace(e));
  218 + throw ServiceExceptionUtil.exception(HIGHLIGHT_IMG_ERROR);
213 } 219 }
214 } 220 }
215 221
216 - // TODO @Li:参考 ServletUtils 方法。如果没有满足的,可以在写一个。  
217 - private void responseImage(HttpServletResponse response, InputStream inputStream, String picName) throws IOException {  
218 - response.setContentType("application/octet-stream;charset=UTF-8");  
219 - response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(picName, "UTF-8"));  
220 - byte[] b = new byte[1024];  
221 - int len = -1;  
222 - while ((len = inputStream.read(b, 0, 1024)) != -1) {  
223 - response.getOutputStream().write(b, 0, len);  
224 - }  
225 - response.flushBuffer();  
226 - }  
227 -  
228 // TODO @Li:这个方法的可读性还有一定的优化空间,可以思考下哈。 222 // TODO @Li:这个方法的可读性还有一定的优化空间,可以思考下哈。
229 /** 223 /**
230 - * 获取已经流转的线 https://blog.csdn.net/qiuxinfa123/article/details/119579863  
231 - * @see 224 + * 获取指定 processInstanceId 已经高亮的Flows
  225 + * 获取已经流转的线 参考: https://blog.csdn.net/qiuxinfa123/article/details/119579863
232 * @param bpmnModel model 226 * @param bpmnModel model
233 - * @param historicActivityInstances 高亮线条  
234 - * @return 227 + * @param processInstanceId 流程实例Id
  228 + * @return 获取已经流转的列表
235 */ 229 */
236 - private List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) { 230 + private List<String> getHighLightedFlows(BpmnModel bpmnModel, String processInstanceId) {
  231 + // 获取所有的线条
  232 + List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId)
  233 + .orderByHistoricActivityInstanceId().asc().list();
237 // 高亮流程已发生流转的线id集合 234 // 高亮流程已发生流转的线id集合
238 List<String> highLightedFlowIds = new ArrayList<>(); 235 List<String> highLightedFlowIds = new ArrayList<>();
239 // 全部活动节点 236 // 全部活动节点
@@ -249,57 +246,43 @@ public class TaskServiceImpl implements TaskService { @@ -249,57 +246,43 @@ public class TaskServiceImpl implements TaskService {
249 finishedActivityInstances.add(historicActivityInstance); 246 finishedActivityInstances.add(historicActivityInstance);
250 } 247 }
251 } 248 }
252 -  
253 - // TODO @Li:这两个变量,直接放到循环里。这种优化一般不需要做的,对性能影响超级小。  
254 - FlowNode currentFlowNode;  
255 - FlowNode targetFlowNode; 249 + // 提取活动id 是唯一的。塞入Map
  250 + Map<String, HistoricActivityInstance> historicActivityInstanceMap = CollectionUtils.convertMap(historicActivityInstances, HistoricActivityInstance::getActivityId);
256 // 遍历已完成的活动实例,从每个实例的outgoingFlows中找到已执行的 251 // 遍历已完成的活动实例,从每个实例的outgoingFlows中找到已执行的
257 for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) { 252 for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) {
258 // 获得当前活动对应的节点信息及outgoingFlows信息 253 // 获得当前活动对应的节点信息及outgoingFlows信息
259 - currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true); 254 + FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
260 List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows(); 255 List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();
261 256
262 // 遍历outgoingFlows并找到已流转的 满足如下条件认为已已流转: 257 // 遍历outgoingFlows并找到已流转的 满足如下条件认为已已流转:
263 // 1.当前节点是并行网关或兼容网关,则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转 258 // 1.当前节点是并行网关或兼容网关,则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
264 // 2.当前节点是以上两种类型之外的,通过outgoingFlows查找到的时间最早的流转节点视为有效流转 259 // 2.当前节点是以上两种类型之外的,通过outgoingFlows查找到的时间最早的流转节点视为有效流转
265 - // TODO @Li:“parallelGateway” 和 "inclusiveGateway",有对应的枚举么?如果木有,可以自己枚举哈  
266 - if ("parallelGateway".equals(currentActivityInstance.getActivityType()) || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) { 260 + if (BpmnXMLConstants.ELEMENT_GATEWAY_PARALLEL.equals(currentActivityInstance.getActivityType())
  261 + || BpmnXMLConstants.ELEMENT_GATEWAY_INCLUSIVE.equals(currentActivityInstance.getActivityType())) {
267 // 遍历历史活动节点,找到匹配流程目标节点的 262 // 遍历历史活动节点,找到匹配流程目标节点的
268 for (SequenceFlow sequenceFlow : sequenceFlows) { 263 for (SequenceFlow sequenceFlow : sequenceFlows) {
269 - targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true); 264 + FlowNode targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
270 if (historicActivityNodes.contains(targetFlowNode)) { 265 if (historicActivityNodes.contains(targetFlowNode)) {
271 highLightedFlowIds.add(targetFlowNode.getId()); 266 highLightedFlowIds.add(targetFlowNode.getId());
272 } 267 }
273 } 268 }
274 } else { 269 } else {
275 - // TODO @Li:如果是为了获取到时间更早的一个,是不是遍历的过程中,就可以解决  
276 - List<Map<String, Object>> tempMapList = new ArrayList<>(); 270 + long earliestStamp = 0L;
  271 + String highLightedFlowId = null;
  272 + // 循环流出的流
277 for (SequenceFlow sequenceFlow : sequenceFlows) { 273 for (SequenceFlow sequenceFlow : sequenceFlows) {
278 - for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {  
279 - if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {  
280 - Map<String, Object> map = new HashMap<>();  
281 - map.put("highLightedFlowId", sequenceFlow.getId());  
282 - map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime());  
283 - tempMapList.add(map);  
284 - } 274 + HistoricActivityInstance historicActivityInstance = historicActivityInstanceMap.get(sequenceFlow.getTargetRef());
  275 + if (historicActivityInstance == null) {
  276 + continue;
285 } 277 }
286 - }  
287 -  
288 - if (!CollectionUtils.isEmpty(tempMapList)) { 278 + final long startTime = historicActivityInstance.getStartTime().getTime();
289 // 遍历匹配的集合,取得开始时间最早的一个 279 // 遍历匹配的集合,取得开始时间最早的一个
290 - long earliestStamp = 0L;  
291 - String highLightedFlowId = null;  
292 - for (Map<String, Object> map : tempMapList) {  
293 - // TODO @Li:可以使用 MapUtil 去 get 值  
294 - long highLightedFlowStartTime = Long.valueOf(map.get("highLightedFlowStartTime").toString());  
295 - if (earliestStamp == 0 || earliestStamp >= highLightedFlowStartTime) {  
296 - highLightedFlowId = map.get("highLightedFlowId").toString();  
297 - earliestStamp = highLightedFlowStartTime;  
298 - } 280 + if (earliestStamp == 0 || earliestStamp >= startTime) {
  281 + highLightedFlowId = sequenceFlow.getId();
  282 + earliestStamp = startTime;
299 } 283 }
300 - highLightedFlowIds.add(highLightedFlowId);  
301 } 284 }
302 - 285 + highLightedFlowIds.add(highLightedFlowId);
303 } 286 }
304 287
305 } 288 }