ソースを参照

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

jason 3 年 前
コミット
c1884c3196

+ 18 - 7
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;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
 import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
@@ -13,10 +11,7 @@ import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.engine.TaskService;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import javax.validation.Valid;
@@ -50,4 +45,20 @@ public class BpmTaskController {
             @RequestParam("processInstanceId") String processInstanceId) {
         return success(taskService.getTaskListByProcessInstanceId(processInstanceId));
     }
+
+    @PutMapping("/approve")
+    @ApiOperation("通过任务")
+    @PreAuthorize("@ss.hasPermission('bpm:task:update')")
+    public CommonResult<Boolean> approveTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) {
+        taskService.approveTask(getLoginUserId(), reqVO);
+        return success(true);
+    }
+
+    @PutMapping("/reject")
+    @ApiOperation("不通过任务")
+    @PreAuthorize("@ss.hasPermission('bpm:task:update')")
+    public CommonResult<Boolean> rejectTask(@Valid @RequestBody BpmTaskRejectReqVO reqVO) {
+        taskService.rejectTask(getLoginUserId(), reqVO);
+        return success(true);
+    }
 }

+ 21 - 4
yudao-module-bpm/yudao-module-bpm-impl-flowable/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java

@@ -1,12 +1,14 @@
 package cn.iocoder.yudao.module.bpm.convert.task;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageItemRespVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
 import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEvent;
 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
+import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import org.flowable.engine.history.HistoricProcessInstance;
@@ -87,13 +89,28 @@ public interface BpmProcessInstanceConvert {
         return event;
     }
 
+    default BpmProcessInstanceResultEvent convert(Object source, ProcessInstance instance, Integer result) {
+        BpmProcessInstanceResultEvent event = new BpmProcessInstanceResultEvent(source);
+        event.setId(instance.getId());
+        event.setProcessDefinitionKey(instance.getProcessDefinitionKey());
+        event.setBusinessKey(instance.getBusinessKey());
+        event.setResult(result);
+        return event;
+    }
+
     default BpmMessageSendWhenProcessInstanceApproveReqDTO convert2ApprovedReq(ProcessInstance instance){
-        Long startUserId = instance.getStartUserId() == null ? null : Long.valueOf(instance.getStartUserId());
-        BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO = new BpmMessageSendWhenProcessInstanceApproveReqDTO()
-                .setStartUserId(startUserId)
+        return  new BpmMessageSendWhenProcessInstanceApproveReqDTO()
+                .setStartUserId(NumberUtils.parseLong(instance.getStartUserId()))
                 .setProcessInstanceId(instance.getId())
                 .setProcessInstanceName(instance.getName());
-        return reqDTO;
+    }
+
+    default BpmMessageSendWhenProcessInstanceRejectReqDTO convert2RejectReq(ProcessInstance instance, String comment) {
+        return new BpmMessageSendWhenProcessInstanceRejectReqDTO()
+            .setProcessInstanceName(instance.getName())
+            .setProcessInstanceId(instance.getId())
+            .setComment(comment)
+            .setStartUserId(NumberUtils.parseLong(instance.getStartUserId()));
     }
 
 }

+ 1 - 2
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 {
     BpmTaskTodoPageItemRespVO.ProcessInstance convert(HistoricProcessInstance processInstance, AdminUserRespDTO startUser);
 
     default BpmTaskExtDO convert2TaskExt(Task task){
-        Long assigneeUserId = task.getAssignee() == null ? null : Long.valueOf(task.getAssignee());
         BpmTaskExtDO taskExtDO = new BpmTaskExtDO()
                 .setTaskId(task.getId())
-                .setAssigneeUserId(assigneeUserId)
+                .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee()))
                 .setName(task.getName())
                 .setProcessDefinitionId(task.getProcessDefinitionId())
                 .setProcessInstanceId(task.getProcessInstanceId());

+ 1 - 1
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 {
 
     @Override
     protected void taskCompleted(FlowableEngineEntityEvent event) {
-        super.taskCompleted(event);
+        taskService.updateTaskExtComplete((Task)event.getEntity());
     }
 }

+ 8 - 0
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 {
      */
     void updateProcessInstanceExtComplete(ProcessInstance instance);
 
+    /**
+     * 更新 ProcessInstance 拓展记录为不通过
+     *
+     * @param id 流程编号
+     * @param comment 理由。例如说,审批不通过时,需要传递该值
+     */
+    void updateProcessInstanceExtReject(String id, String comment);
+
 
 }

+ 30 - 1
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;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
@@ -29,6 +30,7 @@ import org.flowable.engine.runtime.ProcessInstance;
 import org.flowable.task.api.Task;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
@@ -155,7 +157,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
 
         // 通过删除流程实例,实现流程实例的取消,
         // 删除流程实例,正则执行任务ACT_RU_TASK. 任务会被删除。通过历史表查询
-        runtimeService.deleteProcessInstance(cancelReqVO.getId(),
+        deleteProcessInstance(cancelReqVO.getId(),
                 BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason()));
     }
 
@@ -232,6 +234,33 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
                 BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
     }
 
+    @Transactional(rollbackFor = Exception.class)
+    public void updateProcessInstanceExtReject(String id, String comment) {
+        // 需要主动查询,因为 instance 只有 id 属性
+        ProcessInstance processInstance = getProcessInstance(id);
+        // 删除流程实例,以实现驳回任务时,取消整个审批流程
+        deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(comment)));
+
+        // 更新 status + result
+        // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法,
+        // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的
+        BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id)
+                .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
+                .setResult(BpmProcessInstanceResultEnum.REJECT.getResult());
+        processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
+
+        // 发送流程被不通过的消息
+        messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, comment));
+
+        // 发送流程实例的状态事件
+        processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
+                BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
+    }
+
+    private void deleteProcessInstance(String id, String reason) {
+        runtimeService.deleteProcessInstance(id, reason);
+    }
+
     private String createProcessInstance0(Long userId, ProcessDefinition definition,
                                           Map<String, Object> variables, String businessKey) {
         // 校验流程定义

+ 18 - 3
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;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
 import org.flowable.task.api.Task;
 
 
+import javax.validation.Valid;
 import java.util.List;
 import java.util.Map;
 
@@ -54,6 +53,22 @@ public interface BpmTaskService {
      */
     List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId);
 
+    /**
+     * 通过任务
+     *
+     * @param userId 用户编号
+     * @param reqVO 通过请求
+     */
+    void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO);
+
+    /**
+     * 不通过任务
+     *
+     * @param userId 用户编号
+     * @param reqVO 不通过请求
+     */
+    void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO);
+
     /**
      * 创建 Task 拓展记录
      *

+ 61 - 6
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;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO;
-import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
 import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper;
@@ -27,16 +25,19 @@ import org.flowable.task.api.history.HistoricTaskInstance;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
+import javax.validation.Valid;
 import java.util.*;
 
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
 
 /**
  * 流程任务实例 Service 实现类
  *
- * @author jason
  * @author 芋道源码
+ * @author jason
  */
 @Slf4j
 @Service
@@ -123,6 +124,41 @@ public class BpmTaskServiceImpl implements BpmTaskService{
         return BpmTaskConvert.INSTANCE.convertList3(tasks, bpmTaskExtDOMap, processInstance, userMap, deptMap);
     }
 
+    @Override
+    public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) {
+        // 校验任务存在
+        Task task = checkTask(userId, reqVO.getId());
+        // 校验流程实例存在
+        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
+        if (instance == null) {
+            throw exception(PROCESS_INSTANCE_NOT_EXISTS);
+        }
+
+        // 完成任务,审批通过
+        taskService.complete(task.getId(), instance.getProcessVariables());
+        // 更新任务拓展表为通过
+        taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId())
+                .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()).setComment(reqVO.getComment()));
+    }
+
+    @Override
+    public void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO) {
+        Task task = checkTask(userId, reqVO.getId());
+        // 校验流程实例存在
+        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
+        if (instance == null) {
+            throw exception(PROCESS_INSTANCE_NOT_EXISTS);
+        }
+
+        // 更新流程实例为不通过
+        processInstanceService.updateProcessInstanceExtReject(instance.getProcessInstanceId(), reqVO.getComment());
+
+        // 更新任务拓展表为不通过
+        taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId())
+                .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()).setComment(reqVO.getComment()));
+    }
+
+
     @Override
     public void createTaskExt(Task task) {
         BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert2TaskExt(task)
@@ -133,8 +169,27 @@ public class BpmTaskServiceImpl implements BpmTaskService{
     @Override
     public void updateTaskExtComplete(Task task) {
         BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert2TaskExt(task)
-                .setEndTime(new Date()) // 此时不能使用 task 的 completeData,因为还是空的。
-                .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult());
+                .setEndTime(new Date());
         taskExtMapper.updateByTaskId(taskExtDO);
     }
+
+    /**
+     * 校验任务是否存在, 并且是否是分配给自己的任务
+     * @param userId 用户 id
+     * @param taskId task id
+     */
+    private Task checkTask(Long userId, String taskId) {
+        Task task = getTask(taskId);
+        if (task == null) {
+            throw exception(TASK_COMPLETE_FAIL_NOT_EXISTS);
+        }
+        if (!Objects.equals(userId, NumberUtils.parseLong(task.getAssignee()))) {
+            throw exception(TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF);
+        }
+        return task;
+    }
+
+    private Task getTask(String id) {
+        return taskService.createTaskQuery().taskId(id).singleResult();
+    }
 }