Commit 0ff7f2ef746b4350c6d67c7efcabd90b7693832a
1 parent
fef05625
fix: 代码 review 修改
Showing
5 changed files
with
76 additions
and
68 deletions
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 | } | ... | ... |