Commit c1884c31967a616e55235d749638761e612c8a1e

Authored by jason
1 parent d30bf060

工作流 Flowable 通过任务,拒绝任务 实现

yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
... ... @@ -2,9 +2,7 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5   -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
6   -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO;
7   -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageReqVO;
  5 +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
8 6 import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
9 7 import io.swagger.annotations.Api;
10 8 import io.swagger.annotations.ApiImplicitParam;
... ... @@ -13,10 +11,7 @@ import org.flowable.bpmn.model.BpmnModel;
13 11 import org.flowable.engine.TaskService;
14 12 import org.springframework.security.access.prepost.PreAuthorize;
15 13 import org.springframework.validation.annotation.Validated;
16   -import org.springframework.web.bind.annotation.GetMapping;
17   -import org.springframework.web.bind.annotation.RequestMapping;
18   -import org.springframework.web.bind.annotation.RequestParam;
19   -import org.springframework.web.bind.annotation.RestController;
  14 +import org.springframework.web.bind.annotation.*;
20 15  
21 16 import javax.annotation.Resource;
22 17 import javax.validation.Valid;
... ... @@ -50,4 +45,20 @@ public class BpmTaskController {
50 45 @RequestParam("processInstanceId") String processInstanceId) {
51 46 return success(taskService.getTaskListByProcessInstanceId(processInstanceId));
52 47 }
  48 +
  49 + @PutMapping("/approve")
  50 + @ApiOperation("通过任务")
  51 + @PreAuthorize("@ss.hasPermission('bpm:task:update')")
  52 + public CommonResult<Boolean> approveTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) {
  53 + taskService.approveTask(getLoginUserId(), reqVO);
  54 + return success(true);
  55 + }
  56 +
  57 + @PutMapping("/reject")
  58 + @ApiOperation("不通过任务")
  59 + @PreAuthorize("@ss.hasPermission('bpm:task:update')")
  60 + public CommonResult<Boolean> rejectTask(@Valid @RequestBody BpmTaskRejectReqVO reqVO) {
  61 + taskService.rejectTask(getLoginUserId(), reqVO);
  62 + return success(true);
  63 + }
53 64 }
... ...
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
1 1 package cn.iocoder.yudao.module.bpm.convert.task;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
4 5 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageItemRespVO;
5 6 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
6 7 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
7 8 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
8 9 import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEvent;
9 10 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
  11 +import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
10 12 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
11 13 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
12 14 import org.flowable.engine.history.HistoricProcessInstance;
... ... @@ -87,13 +89,28 @@ public interface BpmProcessInstanceConvert {
87 89 return event;
88 90 }
89 91  
  92 + default BpmProcessInstanceResultEvent convert(Object source, ProcessInstance instance, Integer result) {
  93 + BpmProcessInstanceResultEvent event = new BpmProcessInstanceResultEvent(source);
  94 + event.setId(instance.getId());
  95 + event.setProcessDefinitionKey(instance.getProcessDefinitionKey());
  96 + event.setBusinessKey(instance.getBusinessKey());
  97 + event.setResult(result);
  98 + return event;
  99 + }
  100 +
90 101 default BpmMessageSendWhenProcessInstanceApproveReqDTO convert2ApprovedReq(ProcessInstance instance){
91   - Long startUserId = instance.getStartUserId() == null ? null : Long.valueOf(instance.getStartUserId());
92   - BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO = new BpmMessageSendWhenProcessInstanceApproveReqDTO()
93   - .setStartUserId(startUserId)
  102 + return new BpmMessageSendWhenProcessInstanceApproveReqDTO()
  103 + .setStartUserId(NumberUtils.parseLong(instance.getStartUserId()))
94 104 .setProcessInstanceId(instance.getId())
95 105 .setProcessInstanceName(instance.getName());
96   - return reqDTO;
  106 + }
  107 +
  108 + default BpmMessageSendWhenProcessInstanceRejectReqDTO convert2RejectReq(ProcessInstance instance, String comment) {
  109 + return new BpmMessageSendWhenProcessInstanceRejectReqDTO()
  110 + .setProcessInstanceName(instance.getName())
  111 + .setProcessInstanceId(instance.getId())
  112 + .setComment(comment)
  113 + .setStartUserId(NumberUtils.parseLong(instance.getStartUserId()));
97 114 }
98 115  
99 116 }
... ...
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
... ... @@ -102,10 +102,9 @@ public interface BpmTaskConvert {
102 102 BpmTaskTodoPageItemRespVO.ProcessInstance convert(HistoricProcessInstance processInstance, AdminUserRespDTO startUser);
103 103  
104 104 default BpmTaskExtDO convert2TaskExt(Task task){
105   - Long assigneeUserId = task.getAssignee() == null ? null : Long.valueOf(task.getAssignee());
106 105 BpmTaskExtDO taskExtDO = new BpmTaskExtDO()
107 106 .setTaskId(task.getId())
108   - .setAssigneeUserId(assigneeUserId)
  107 + .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee()))
109 108 .setName(task.getName())
110 109 .setProcessDefinitionId(task.getProcessDefinitionId())
111 110 .setProcessInstanceId(task.getProcessInstanceId());
... ...
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java
... ... @@ -42,6 +42,6 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
42 42  
43 43 @Override
44 44 protected void taskCompleted(FlowableEngineEntityEvent event) {
45   - super.taskCompleted(event);
  45 + taskService.updateTaskExtComplete((Task)event.getEntity());
46 46 }
47 47 }
... ...
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
... ... @@ -110,5 +110,13 @@ public interface BpmProcessInstanceService {
110 110 */
111 111 void updateProcessInstanceExtComplete(ProcessInstance instance);
112 112  
  113 + /**
  114 + * 更新 ProcessInstance 拓展记录为不通过
  115 + *
  116 + * @param id 流程编号
  117 + * @param comment 理由。例如说,审批不通过时,需要传递该值
  118 + */
  119 + void updateProcessInstanceExtReject(String id, String comment);
  120 +
113 121  
114 122 }
... ...
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.service.task;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.lang.Assert;
  5 +import cn.hutool.core.util.StrUtil;
5 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
7 8 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
... ... @@ -29,6 +30,7 @@ import org.flowable.engine.runtime.ProcessInstance;
29 30 import org.flowable.task.api.Task;
30 31 import org.springframework.context.annotation.Lazy;
31 32 import org.springframework.stereotype.Service;
  33 +import org.springframework.transaction.annotation.Transactional;
32 34 import org.springframework.validation.annotation.Validated;
33 35  
34 36 import javax.annotation.Resource;
... ... @@ -155,7 +157,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
155 157  
156 158 // 通过删除流程实例,实现流程实例的取消,
157 159 // 删除流程实例,正则执行任务ACT_RU_TASK. 任务会被删除。通过历史表查询
158   - runtimeService.deleteProcessInstance(cancelReqVO.getId(),
  160 + deleteProcessInstance(cancelReqVO.getId(),
159 161 BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason()));
160 162 }
161 163  
... ... @@ -232,6 +234,33 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
232 234 BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
233 235 }
234 236  
  237 + @Transactional(rollbackFor = Exception.class)
  238 + public void updateProcessInstanceExtReject(String id, String comment) {
  239 + // 需要主动查询,因为 instance 只有 id 属性
  240 + ProcessInstance processInstance = getProcessInstance(id);
  241 + // 删除流程实例,以实现驳回任务时,取消整个审批流程
  242 + deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(comment)));
  243 +
  244 + // 更新 status + result
  245 + // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法,
  246 + // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的
  247 + BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id)
  248 + .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
  249 + .setResult(BpmProcessInstanceResultEnum.REJECT.getResult());
  250 + processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
  251 +
  252 + // 发送流程被不通过的消息
  253 + messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, comment));
  254 +
  255 + // 发送流程实例的状态事件
  256 + processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
  257 + BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
  258 + }
  259 +
  260 + private void deleteProcessInstance(String id, String reason) {
  261 + runtimeService.deleteProcessInstance(id, reason);
  262 + }
  263 +
235 264 private String createProcessInstance0(Long userId, ProcessDefinition definition,
236 265 Map<String, Object> variables, String businessKey) {
237 266 // 校验流程定义
... ...
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
... ... @@ -2,12 +2,11 @@ package cn.iocoder.yudao.module.bpm.service.task;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5   -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
6   -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO;
7   -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageReqVO;
  5 +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
8 6 import org.flowable.task.api.Task;
9 7  
10 8  
  9 +import javax.validation.Valid;
11 10 import java.util.List;
12 11 import java.util.Map;
13 12  
... ... @@ -55,6 +54,22 @@ public interface BpmTaskService {
55 54 List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId);
56 55  
57 56 /**
  57 + * 通过任务
  58 + *
  59 + * @param userId 用户编号
  60 + * @param reqVO 通过请求
  61 + */
  62 + void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO);
  63 +
  64 + /**
  65 + * 不通过任务
  66 + *
  67 + * @param userId 用户编号
  68 + * @param reqVO 不通过请求
  69 + */
  70 + void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO);
  71 +
  72 + /**
58 73 * 创建 Task 拓展记录
59 74 *
60 75 * @param task 任务实体
... ...
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
... ... @@ -5,9 +5,7 @@ import cn.hutool.core.util.StrUtil;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
7 7 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
8   -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
9   -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO;
10   -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageReqVO;
  8 +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
11 9 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
12 10 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
13 11 import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper;
... ... @@ -27,16 +25,19 @@ import org.flowable.task.api.history.HistoricTaskInstance;
27 25 import org.springframework.stereotype.Service;
28 26  
29 27 import javax.annotation.Resource;
  28 +import javax.validation.Valid;
30 29 import java.util.*;
31 30  
  31 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
32 32 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
33 33 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  34 +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
34 35  
35 36 /**
36 37 * 流程任务实例 Service 实现类
37 38 *
38   - * @author jason
39 39 * @author 芋道源码
  40 + * @author jason
40 41 */
41 42 @Slf4j
42 43 @Service
... ... @@ -124,6 +125,41 @@ public class BpmTaskServiceImpl implements BpmTaskService{
124 125 }
125 126  
126 127 @Override
  128 + public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) {
  129 + // 校验任务存在
  130 + Task task = checkTask(userId, reqVO.getId());
  131 + // 校验流程实例存在
  132 + ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
  133 + if (instance == null) {
  134 + throw exception(PROCESS_INSTANCE_NOT_EXISTS);
  135 + }
  136 +
  137 + // 完成任务,审批通过
  138 + taskService.complete(task.getId(), instance.getProcessVariables());
  139 + // 更新任务拓展表为通过
  140 + taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId())
  141 + .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()).setComment(reqVO.getComment()));
  142 + }
  143 +
  144 + @Override
  145 + public void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO) {
  146 + Task task = checkTask(userId, reqVO.getId());
  147 + // 校验流程实例存在
  148 + ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
  149 + if (instance == null) {
  150 + throw exception(PROCESS_INSTANCE_NOT_EXISTS);
  151 + }
  152 +
  153 + // 更新流程实例为不通过
  154 + processInstanceService.updateProcessInstanceExtReject(instance.getProcessInstanceId(), reqVO.getComment());
  155 +
  156 + // 更新任务拓展表为不通过
  157 + taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId())
  158 + .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()).setComment(reqVO.getComment()));
  159 + }
  160 +
  161 +
  162 + @Override
127 163 public void createTaskExt(Task task) {
128 164 BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert2TaskExt(task)
129 165 .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
... ... @@ -133,8 +169,27 @@ public class BpmTaskServiceImpl implements BpmTaskService{
133 169 @Override
134 170 public void updateTaskExtComplete(Task task) {
135 171 BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert2TaskExt(task)
136   - .setEndTime(new Date()) // 此时不能使用 task 的 completeData,因为还是空的。
137   - .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult());
  172 + .setEndTime(new Date());
138 173 taskExtMapper.updateByTaskId(taskExtDO);
139 174 }
  175 +
  176 + /**
  177 + * 校验任务是否存在, 并且是否是分配给自己的任务
  178 + * @param userId 用户 id
  179 + * @param taskId task id
  180 + */
  181 + private Task checkTask(Long userId, String taskId) {
  182 + Task task = getTask(taskId);
  183 + if (task == null) {
  184 + throw exception(TASK_COMPLETE_FAIL_NOT_EXISTS);
  185 + }
  186 + if (!Objects.equals(userId, NumberUtils.parseLong(task.getAssignee()))) {
  187 + throw exception(TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF);
  188 + }
  189 + return task;
  190 + }
  191 +
  192 + private Task getTask(String id) {
  193 + return taskService.createTaskQuery().taskId(id).singleResult();
  194 + }
140 195 }
... ...