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 4 import cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.TaskService;
5 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
7 8 import io.swagger.annotations.Api;
8 9 import io.swagger.annotations.ApiOperation;
9 10 import org.springframework.web.bind.annotation.*;
... ... @@ -11,6 +12,7 @@ import org.springframework.web.bind.annotation.*;
11 12 import javax.annotation.Resource;
12 13 import javax.servlet.http.HttpServletResponse;
13 14 import javax.validation.Valid;
  15 +import java.io.IOException;
14 16 import java.util.List;
15 17  
16 18 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
... ... @@ -63,8 +65,9 @@ public class TaskController {
63 65 * @param processInstanceId
64 66 */
65 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 16 ErrorCode DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1003001005, "部门的部门经理不存在");
17 17 ErrorCode HR_POST_NOT_EXISTS = new ErrorCode(1003001006, "HR岗位未设置");
18 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 54 /**
55 55 * 返回高亮的流转进程
56 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 1 package cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.impl;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.io.IoUtil;
4 5 import cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo.*;
5 6 import cn.iocoder.yudao.adminserver.modules.bpm.convert.workflow.TaskConvert;
6 7 import cn.iocoder.yudao.adminserver.modules.bpm.service.workflow.TaskService;
  8 +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
7 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  10 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 11 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
9 12 import lombok.extern.slf4j.Slf4j;
10 13 import org.activiti.api.runtime.shared.query.Page;
... ... @@ -13,6 +16,7 @@ import org.activiti.api.task.model.Task;
13 16 import org.activiti.api.task.model.builders.ClaimTaskPayloadBuilder;
14 17 import org.activiti.api.task.model.builders.TaskPayloadBuilder;
15 18 import org.activiti.api.task.runtime.TaskRuntime;
  19 +import org.activiti.bpmn.constants.BpmnXMLConstants;
16 20 import org.activiti.bpmn.model.BpmnModel;
17 21 import org.activiti.bpmn.model.FlowNode;
18 22 import org.activiti.bpmn.model.SequenceFlow;
... ... @@ -28,17 +32,19 @@ import org.activiti.image.ProcessDiagramGenerator;
28 32 import org.apache.commons.lang3.exception.ExceptionUtils;
29 33 import org.springframework.stereotype.Service;
30 34 import org.springframework.transaction.annotation.Transactional;
31   -import org.springframework.util.CollectionUtils;
32 35  
33 36  
34 37 import javax.annotation.Resource;
35 38 import javax.servlet.http.HttpServletResponse;
  39 +import java.io.ByteArrayOutputStream;
36 40 import java.io.IOException;
37 41 import java.io.InputStream;
38 42 import java.net.URLEncoder;
39 43 import java.util.*;
40 44 import java.util.stream.Collectors;
41 45  
  46 +import static cn.iocoder.yudao.adminserver.modules.bpm.enums.oa.OAErrorCodeConstants.*;
  47 +
42 48 @Slf4j
43 49 @Service
44 50 public class TaskServiceImpl implements TaskService {
... ... @@ -171,69 +177,60 @@ public class TaskServiceImpl implements TaskService {
171 177  
172 178  
173 179 @Override
174   - public void getHighlightImg(String processInstanceId, HttpServletResponse response) {
  180 + public FileResp getHighlightImg(String processInstanceId) {
175 181 // 查询历史
176 182 //TODO 云扬四海 貌似流程结束后,点击审批进度会报错
177 183 // TODO @Li:一些 historyService 的查询,貌似比较通用,是不是抽一些小方法出来
178 184 HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
179   - // 如果有结束时间 TODO @Li:如果查询不到,是不是抛出一个业务异常比较好哈?
  185 + // 如果不存在实例。 说明数据异常
180 186 if (hpi == null) {
181   - return;
  187 + throw ServiceExceptionUtil.exception(PROCESS_INSTANCE_NOT_EXISTS);
182 188 }
183 189 // 没有结束时间。说明流程在执行过程中
184 190 // TODO @Li:一些 runtimeService 的查询,貌似比较通用,是不是抽一些小方法出来
185 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 195 List<HistoricActivityInstance> finishedInstances = historyService.createHistoricActivityInstanceQuery()
193 196 .processInstanceId(processInstanceId).finished().list();
194 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 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 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 216 } catch (IOException e) {
212 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 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 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 234 // 高亮流程已发生流转的线id集合
238 235 List<String> highLightedFlowIds = new ArrayList<>();
239 236 // 全部活动节点
... ... @@ -249,57 +246,43 @@ public class TaskServiceImpl implements TaskService {
249 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 251 // 遍历已完成的活动实例,从每个实例的outgoingFlows中找到已执行的
257 252 for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) {
258 253 // 获得当前活动对应的节点信息及outgoingFlows信息
259   - currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
  254 + FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
260 255 List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();
261 256  
262 257 // 遍历outgoingFlows并找到已流转的 满足如下条件认为已已流转:
263 258 // 1.当前节点是并行网关或兼容网关,则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
264 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 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 265 if (historicActivityNodes.contains(targetFlowNode)) {
271 266 highLightedFlowIds.add(targetFlowNode.getId());
272 267 }
273 268 }
274 269 } else {
275   - // TODO @Li:如果是为了获取到时间更早的一个,是不是遍历的过程中,就可以解决
276   - List<Map<String, Object>> tempMapList = new ArrayList<>();
  270 + long earliestStamp = 0L;
  271 + String highLightedFlowId = null;
  272 + // 循环流出的流
277 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 }
... ...