From 9860a7d552ab9eaf2bbede297055d69d73e659e0 Mon Sep 17 00:00:00 2001
From: YunaiV <zhijiantianya@gmail.com>
Date: Sun, 9 Jan 2022 18:40:13 +0800
Subject: [PATCH] =?UTF-8?q?1.=20=E5=9F=BA=E4=BA=8E=20BpmTaskEventListener?=
 =?UTF-8?q?=20=E5=AE=9E=E7=8E=B0=20Task=20=E6=8B=93=E5=B1=95=E8=A1=A8?=
 =?UTF-8?q?=E7=9A=84=E5=90=8C=E6=AD=A5=202.=20=E5=9F=BA=E4=BA=8E=20BpmProc?=
 =?UTF-8?q?essInstanceEventListener=20=E5=AE=9E=E7=8E=B0=20ProcessInstance?=
 =?UTF-8?q?=20=E6=8B=93=E5=B1=95=E8=A1=A8=E7=9A=84=E5=90=8C=E6=AD=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../task/BpmProcessInstanceConvert.java       |  9 +++
 .../bpm/convert/task/BpmTaskConvert.java      |  8 ++
 .../bpm/dal/dataobject/task/BpmTaskExtDO.java | 79 +++++++++++++++++++
 .../task/BpmProcessInstanceExtMapper.java     |  5 +-
 .../bpm/dal/mysql/task/BpmTaskExtMapper.java  | 15 ++++
 .../bpm/enums/BpmErrorCodeConstants.java      |  1 +
 .../activiti/BpmActivitiConfiguration.java    | 11 +--
 .../task/BpmProcessInstanceService.java       | 30 +++++++
 .../bpm/service/task/BpmTaskService.java      | 30 +++++++
 .../impl/BpmProcessInstanceServiceImpl.java   | 72 +++++++++++------
 .../service/task/impl/BpmTaskServiceImpl.java | 47 ++++++++++-
 .../BpmProcessInstanceEventListener.java      | 52 +++++++-----
 .../task/listener/BpmTaskEventListener.java   | 64 +++++++++++++++
 13 files changed, 367 insertions(+), 56 deletions(-)
 create mode 100644 yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/task/BpmTaskExtDO.java
 create mode 100644 yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/task/BpmTaskExtMapper.java
 create mode 100644 yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/listener/BpmTaskEventListener.java

diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmProcessInstanceConvert.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmProcessInstanceConvert.java
index e630b9aab..95fd9fc60 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmProcessInstanceConvert.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmProcessInstanceConvert.java
@@ -11,6 +11,7 @@ import org.mapstruct.Mapping;
 import org.mapstruct.Mappings;
 import org.mapstruct.factory.Mappers;
 
+import java.sql.SQLXML;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
@@ -49,4 +50,12 @@ public interface BpmProcessInstanceConvert {
     @Mapping(source = "processInstanceId", target = "id")
     BpmProcessInstancePageItemRespVO convert(BpmProcessInstanceExtDO bean);
 
+    @Mappings({
+            @Mapping(source = "id", target = "processInstanceId"),
+            @Mapping(source = "startDate", target = "createTime"),
+            @Mapping(source = "initiator", target = "startUserId"),
+            @Mapping(source = "status", target = "status", ignore = true)
+    })
+    BpmProcessInstanceExtDO convert(org.activiti.api.process.model.ProcessInstance bean);
+
 }
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmTaskConvert.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmTaskConvert.java
index 96f273bf6..a4556ce24 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmTaskConvert.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/task/BpmTaskConvert.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.adminserver.modules.bpm.convert.task;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.BpmTaskDonePageItemRespVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.BpmTaskTodoPageItemRespVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.TaskStepVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
 import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import org.activiti.engine.history.HistoricActivityInstance;
@@ -88,4 +89,11 @@ public interface BpmTaskConvert {
     })
     BpmTaskDonePageItemRespVO convert(HistoricTaskInstance task, HistoricProcessInstance processInstance, SysUserDO user);
 
+    @Mappings({
+            @Mapping(source = "id", target = "taskId"),
+            @Mapping(source = "assignee", target = "assigneeUserId"),
+            @Mapping(source = "createdDate", target = "createTime")
+    })
+    BpmTaskExtDO convert(org.activiti.api.task.model.Task bean);
+
 }
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/task/BpmTaskExtDO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/task/BpmTaskExtDO.java
new file mode 100644
index 000000000..454b2c803
--- /dev/null
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/dataobject/task/BpmTaskExtDO.java
@@ -0,0 +1,79 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.activiti.engine.history.HistoricTaskInstance;
+import org.activiti.engine.repository.ProcessDefinition;
+import org.activiti.engine.runtime.ProcessInstance;
+import org.activiti.engine.task.Task;
+
+import java.util.Date;
+
+/**
+ * Bpm 流程任务的拓展表
+ * 主要解决 Activiti {@link Task} 和 {@link HistoricTaskInstance} 不支持拓展字段,所以新建拓展表
+ *
+ * @author 芋道源码
+ */
+@TableName(value = "bpm_task_ext", autoResultMap = true)
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+//@Builder
+//@NoArgsConstructor
+//@AllArgsConstructor
+public class BpmTaskExtDO extends BaseDO {
+
+    /**
+     * 任务的审批人
+     *
+     * 冗余 {@link Task#getAssignee()}
+     */
+    private Long assigneeUserId;
+    /**
+     * 任务的名字
+     *
+     * 冗余 {@link Task#getName()} 为了筛选
+     */
+    private String name;
+    /**
+     * 任务的编号
+     *
+     * 关联 {@link Task#getId()}
+     */
+    private String taskId;
+    /**
+     * 任务的结果
+     *
+     * 枚举 {@link BpmProcessInstanceResultEnum}
+     */
+    private Integer result;
+    /**
+     * 审批建议
+     */
+    private String comment;
+    /**
+     * 任务的结束时间
+     *
+     * 冗余 {@link HistoricTaskInstance#getEndTime()}
+     */
+    private Date endTime;
+
+    /**
+     * 流程实例的编号
+     *
+     * 关联 {@link ProcessInstance#getId()}
+     */
+    private String processInstanceId;
+    /**
+     * 流程定义的编号
+     *
+     * 关联 {@link ProcessDefinition#getId()}
+     */
+    private String processDefinitionId;
+
+}
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/task/BpmProcessInstanceExtMapper.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/task/BpmProcessInstanceExtMapper.java
index a0d28616b..344921884 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/task/BpmProcessInstanceExtMapper.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/task/BpmProcessInstanceExtMapper.java
@@ -23,8 +23,9 @@ public interface BpmProcessInstanceExtMapper extends BaseMapperX<BpmProcessInsta
                 .orderByDesc("id"));
     }
 
-    default void updateByProcessInstanceId(String processInstanceId, BpmProcessInstanceExtDO updateObj) {
-        update(updateObj, new QueryWrapper<BpmProcessInstanceExtDO>().eq("process_instance_id", processInstanceId));
+    default void updateByProcessInstanceId(BpmProcessInstanceExtDO updateObj) {
+        update(updateObj, new QueryWrapper<BpmProcessInstanceExtDO>()
+                .eq("process_instance_id", updateObj.getProcessInstanceId()));
     }
 
 }
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/task/BpmTaskExtMapper.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/task/BpmTaskExtMapper.java
new file mode 100644
index 000000000..c7d2fb72e
--- /dev/null
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/task/BpmTaskExtMapper.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.task;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface BpmTaskExtMapper extends BaseMapperX<BpmTaskExtDO> {
+
+    default void updateByTaskId(BpmTaskExtDO entity) {
+        update(entity, new QueryWrapper<BpmTaskExtDO>().eq("task_id", entity.getTaskId()));
+    }
+
+}
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java
index 0aaf030cf..5519347ec 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java
@@ -35,6 +35,7 @@ public interface BpmErrorCodeConstants {
     // ========== 流程实例 1-009-004-000 ==========
     ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1009004000, "流程实例不存在");
     ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1009004001, "流程取消失败,流程不处于运行中");
+    ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1009004002, "流程取消失败,该流程不是你发起的");
 
     // ========== 流程实例 1-009-005-000 ==========
     ErrorCode TASK_COMPLETE_FAIL_NOT_EXISTS = new ErrorCode(1009004000, "审批任务失败,原因:该任务不处于未审批");
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/BpmActivitiConfiguration.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/BpmActivitiConfiguration.java
index e4e158e29..d4e8302c6 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/BpmActivitiConfiguration.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/BpmActivitiConfiguration.java
@@ -1,26 +1,19 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti;
 
-import cn.iocoder.yudao.adminserver.modules.bpm.service.task.listener.BpmProcessInstanceEventListener;
 import org.activiti.spring.SpringProcessEngineConfiguration;
 import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
 import org.springframework.context.annotation.Configuration;
 
-import javax.annotation.Resource;
-import java.util.Arrays;
-
 /**
  * BPM 模块的 Activiti 配置类
  */
 @Configuration
 public class BpmActivitiConfiguration implements ProcessEngineConfigurationConfigurer {
 
-    @Resource
-    private BpmProcessInstanceEventListener processInstanceEventListener;
-
     @Override
     public void configure(SpringProcessEngineConfiguration configuration) {
-        // 注册监听器,例如说 BpmProcessInstanceEventListener
-        configuration.setEventListeners(Arrays.asList(processInstanceEventListener));
+        // 注册监听器,例如说 ActivitiEventListener 的实现类
+//        configuration.setEventListeners(Arrays.asList(processInstanceEventListener));
     }
 
 }
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmProcessInstanceService.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmProcessInstanceService.java
index 255b8f5ae..9c47e24e1 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmProcessInstanceService.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmProcessInstanceService.java
@@ -46,10 +46,12 @@ public interface BpmProcessInstanceService {
      * @param id 流程编号
      * @param reason 删除原因。可选 {@link BpmProcessInstanceDeleteReasonEnum}
      */
+    @Deprecated
     void deleteProcessInstance(String id, String reason);
 
     /**
      * 更新流程实例的结果
+     * 1. 如果更新为已拒绝时,会进行任务的删除
      *
      * @param id 流程编号
      * @param result 结果,{@link BpmProcessInstanceResultEnum}
@@ -118,4 +120,32 @@ public interface BpmProcessInstanceService {
         return CollectionUtils.convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId);
     }
 
+    /**
+     * 创建 ProcessInstance 拓展记录
+     *
+     * @param instance 流程任务
+     */
+    void createProcessInstanceExt(org.activiti.api.process.model.ProcessInstance instance);
+
+    /**
+     * 更新 ProcessInstance 拓展记录
+     *
+     * @param instance 流程任务
+     */
+    void updateProcessInstanceExt(org.activiti.api.process.model.ProcessInstance instance);
+
+    /**
+     * 更新 ProcessInstance 拓展记录为取消
+     *
+     * @param instance 流程任务
+     */
+    void updateProcessInstanceExtCancel(org.activiti.api.process.model.ProcessInstance instance);
+
+    /**
+     * 更新 ProcessInstance 拓展记录为完成
+     *
+     * @param instance 流程任务
+     */
+    void updateProcessInstanceExtComplete(org.activiti.api.process.model.ProcessInstance instance);
+
 }
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmTaskService.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmTaskService.java
index 8ecbf9fe7..ea53a0900 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmTaskService.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmTaskService.java
@@ -105,4 +105,34 @@ public interface BpmTaskService {
      */
     FileResp getHighlightImg(String processInstanceId);
 
+    // ========== Task 拓展表相关 ==========
+
+    /**
+     * 创建 Task 拓展记录
+     *
+     * @param task 任务实体
+     */
+    void createTaskExt(org.activiti.api.task.model.Task task);
+
+    /**
+     * 更新 Task 拓展记录
+     *
+     * @param task 任务实体
+     */
+    void updateTaskExt(org.activiti.api.task.model.Task task);
+
+    /**
+     * 更新 Task 拓展记录为取消
+     *
+     * @param task 任务实体
+     */
+    void updateTaskExtCancel(org.activiti.api.task.model.Task task);
+
+    /**
+     * 更新 Task 拓展记录为完成
+     *
+     * @param task 任务实体
+     */
+    void updateTaskExtComplete(org.activiti.api.task.model.Task task);
+
 }
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/impl/BpmProcessInstanceServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/impl/BpmProcessInstanceServiceImpl.java
index 66770cb07..e41595d9f 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/impl/BpmProcessInstanceServiceImpl.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/impl/BpmProcessInstanceServiceImpl.java
@@ -7,8 +7,11 @@ import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmP
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceMyPageReqVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstancePageItemRespVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.convert.task.BpmProcessInstanceConvert;
+import cn.iocoder.yudao.adminserver.modules.bpm.convert.task.BpmTaskConvert;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.task.BpmProcessInstanceExtMapper;
+import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
 import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
 import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceStatusEnum;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmProcessDefinitionService;
@@ -33,9 +36,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.function.Consumer;
 
 import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
@@ -92,8 +93,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
         ProcessInstance instance = runtimeService.startProcessInstanceById(createReqVO.getProcessDefinitionId(), variables);
         // 设置流程名字
         runtimeService.setProcessInstanceName(instance.getId(), definition.getName());
-        // 记录流程实例的拓展表
-        createProcessInstanceExt(instance, definition);
+        // 更新流程实例拓展表的 category TODO 芋艿:暂时没好的办法,task 的 category 不正确。另外,definition 返回的 category 也不太正确,后续在解决;
+        processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO()
+                .setProcessInstanceId(instance.getId()).setCategory(definition.getCategory()));
 
         // TODO 芋艿:临时使用, 保证分配
         List<Task> tasks = taskService.getTasksByProcessInstanceId(instance.getId());
@@ -114,19 +116,6 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
         return instance.getId();
     }
 
-    /**
-     * 创建流程实例的拓展
-     *
-     * @param definition 流程定义
-     * @param instance 流程实例
-     */
-    private void createProcessInstanceExt(ProcessInstance instance, ProcessDefinition definition) {
-        BpmProcessInstanceExtDO instanceExt = BpmProcessInstanceConvert.INSTANCE.convert(instance, definition);
-        instanceExt.setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus());
-        instanceExt.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
-        processInstanceExtMapper.insert(instanceExt);
-    }
-
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void cancelProcessInstance(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) {
@@ -135,12 +124,13 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
         if (instance == null) {
             throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
         }
-        // TODO 芋艿:只能自己取消流程
+        // 只能取消自己的
+        if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
+            throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
+        }
 
         // 通过删除流程实例,实现流程实例的取消
         runtimeService.deleteProcessInstance(cancelReqVO.getId(), cancelReqVO.getReason());
-        // 更新流程实例的拓展表为取消状态
-        updateProcessInstanceResult(cancelReqVO.getId(), BpmProcessInstanceResultEnum.CANCEL.getResult());
     }
 
     @Override
@@ -149,8 +139,14 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void updateProcessInstanceResult(String id, Integer result) {
-        processInstanceExtMapper.updateByProcessInstanceId(id, new BpmProcessInstanceExtDO()
+        // 删除流程实例,以实现驳回任务时,取消整个审批流程
+        if (Objects.equals(result, BpmProcessInstanceResultEnum.REJECT.getResult())) {
+            deleteProcessInstance(id, BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.getReason());
+        }
+        // 更新 status + result
+        processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(id)
                 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()).setResult(result));
     }
 
@@ -196,4 +192,36 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
         return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
     }
 
+    @Override
+    public void createProcessInstanceExt(org.activiti.api.process.model.ProcessInstance instance) {
+        BpmProcessInstanceExtDO instanceExtDO = BpmProcessInstanceConvert.INSTANCE.convert(instance)
+                .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus())
+                .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
+        processInstanceExtMapper.insert(instanceExtDO);
+    }
+
+    @Override
+    public void updateProcessInstanceExt(org.activiti.api.process.model.ProcessInstance instance) {
+        BpmProcessInstanceExtDO instanceExtDO = BpmProcessInstanceConvert.INSTANCE.convert(instance);
+        processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
+    }
+
+    @Override
+    public void updateProcessInstanceExtCancel(org.activiti.api.process.model.ProcessInstance instance) {
+        BpmProcessInstanceExtDO instanceExtDO = BpmProcessInstanceConvert.INSTANCE.convert(instance)
+                .setEndTime(new Date()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
+                .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
+                .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult());
+        processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
+    }
+
+    @Override
+    public void updateProcessInstanceExtComplete(org.activiti.api.process.model.ProcessInstance instance) {
+        BpmProcessInstanceExtDO instanceExtDO = BpmProcessInstanceConvert.INSTANCE.convert(instance)
+                .setEndTime(new Date()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
+                .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
+                .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过
+        processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
+    }
+
 }
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/impl/BpmTaskServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/impl/BpmTaskServiceImpl.java
index baca9f41f..ebf36231b 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/impl/BpmTaskServiceImpl.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/impl/BpmTaskServiceImpl.java
@@ -5,7 +5,8 @@ import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.*;
 import cn.iocoder.yudao.adminserver.modules.bpm.convert.task.BpmTaskConvert;
-import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.task.BpmTaskExtMapper;
 import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceService;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmTaskService;
@@ -76,6 +77,9 @@ public class BpmTaskServiceImpl implements BpmTaskService {
     @Lazy // 解决循环依赖
     private BpmProcessInstanceService processInstanceService;
 
+    @Resource
+    private BpmTaskExtMapper taskExtMapper;
+
     @Override
     public List<Task> getTasksByProcessInstanceId(String processInstanceId) {
         return taskService.createTaskQuery().processInstanceId(processInstanceId).list();
@@ -175,12 +179,16 @@ public class BpmTaskServiceImpl implements BpmTaskService {
 
         // 完成任务,审批通过
         taskService.complete(task.getId(), instance.getProcessVariables()); // TODO 芋艿:variables 的选择
+        // 更新任务拓展表为通过
+        taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId())
+                .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()).setComment(reqVO.getComment()));
 
         // TODO 芋艿:添加评论
 //        taskService.addComment(task.getId(), task.getProcessInstanceId(), reqVO.getComment());
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void rejectTask(@Valid BpmTaskRejectReqVO reqVO) {
         // 校验任务存在
         Task task = getTask(reqVO.getId());
@@ -193,12 +201,14 @@ public class BpmTaskServiceImpl implements BpmTaskService {
             throw exception(PROCESS_INSTANCE_NOT_EXISTS);
         }
 
-        // 删除流程实例,以实现驳回任务时,取消整个审批流程
-        processInstanceService.deleteProcessInstance(instance.getId(), BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.getReason());
         // 更新流程实例为不通过
         processInstanceService.updateProcessInstanceResult(instance.getProcessInstanceId(),
                 BpmProcessInstanceResultEnum.REJECT.getResult());
 
+        // 更新任务拓展表为不通过
+        taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId())
+                .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()).setComment(reqVO.getComment()));
+
         // TODO 芋艿:添加评论
 //        taskService.addComment(task.getId(), task.getProcessInstanceId(), reqVO.getComment());
     }
@@ -382,4 +392,35 @@ public class BpmTaskServiceImpl implements BpmTaskService {
         return taskService.createTaskQuery().taskId(id).singleResult();
     }
 
+    // ========== Task 拓展表相关 ==========
+
+    @Override
+    public void createTaskExt(org.activiti.api.task.model.Task task) {
+        BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert(task)
+                .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
+        taskExtMapper.insert(taskExtDO);
+    }
+
+    @Override
+    public void updateTaskExt(org.activiti.api.task.model.Task task) {
+        BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert(task);
+        taskExtMapper.updateByTaskId(taskExtDO);
+    }
+
+    @Override
+    public void updateTaskExtCancel(org.activiti.api.task.model.Task task) {
+        BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert(task)
+                .setEndTime(new Date()) // 由于 Task 里没有办法拿到 endTime,所以这里设置
+                .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult());
+        taskExtMapper.updateByTaskId(taskExtDO);
+    }
+
+    @Override
+    public void updateTaskExtComplete(org.activiti.api.task.model.Task task) {
+        BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert(task)
+                .setEndTime(task.getCompletedDate())
+                .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult());
+        taskExtMapper.updateByTaskId(taskExtDO);
+    }
+
 }
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/listener/BpmProcessInstanceEventListener.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/listener/BpmProcessInstanceEventListener.java
index 2d4279518..3b1268a29 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/listener/BpmProcessInstanceEventListener.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/listener/BpmProcessInstanceEventListener.java
@@ -1,12 +1,13 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.service.task.listener;
 
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
-import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceService;
-import org.activiti.engine.delegate.event.ActivitiEvent;
-import org.activiti.engine.delegate.event.ActivitiEventListener;
-import org.activiti.engine.delegate.event.ActivitiEventType;
-import org.activiti.engine.runtime.ProcessInstance;
+import org.activiti.api.model.shared.event.RuntimeEvent;
+import org.activiti.api.process.model.ProcessInstance;
+import org.activiti.api.process.model.events.ProcessRuntimeEvent;
+import org.activiti.api.process.runtime.events.listener.ProcessEventListener;
+import org.activiti.api.process.runtime.events.listener.ProcessRuntimeEventListener;
+import org.activiti.api.task.model.events.TaskRuntimeEvent;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
 
@@ -18,29 +19,40 @@ import javax.annotation.Resource;
  * @author 芋道源码
  */
 @Component
-public class BpmProcessInstanceEventListener implements ActivitiEventListener {
+public class BpmProcessInstanceEventListener<T extends RuntimeEvent<?, ?>>
+        implements ProcessRuntimeEventListener<T> {
 
     @Resource
     @Lazy // 解决循环依赖
     private BpmProcessInstanceService processInstanceService;
 
     @Override
-    public void onEvent(ActivitiEvent event) {
-        // 不处理 ActivitiEventType.PROCESS_STARTED 事件。原因:事件发布时,流程实例还没进行入库,就已经发布了 ActivitiEvent 事件
-        // 不处理 ActivitiEventType.PROCESS_CANCELLED 事件。原因:直接在 BpmTaskService#cancelProcessInstance 更新记录
-
-        // 正常完成
-        if (event.getType() == ActivitiEventType.PROCESS_COMPLETED
-            || event.getType() == ActivitiEventType.PROCESS_COMPLETED_WITH_ERROR_END_EVENT) {
-            // 正常完成,说明所有流程任务都是审批通过
-            processInstanceService.updateProcessInstanceResult(event.getProcessInstanceId(),
-                    BpmProcessInstanceResultEnum.APPROVE.getResult());
+    @SuppressWarnings("unchecked")
+    public void onEvent(T rawEvent) {
+        // 由于 ProcessRuntimeEventListener 无法保证只监听 ProcessRuntimeEvent 事件,所以通过这样的方式
+        if (!(rawEvent instanceof ProcessRuntimeEvent)) {
+            return;
         }
-    }
+        ProcessRuntimeEvent<ProcessInstance> event = (ProcessRuntimeEvent<ProcessInstance>) rawEvent;
 
-    @Override
-    public boolean isFailOnException() {
-        return true;
+        // 创建时,插入拓展表
+        if (event.getEventType() == ProcessRuntimeEvent.ProcessEvents.PROCESS_CREATED) {
+            processInstanceService.createProcessInstanceExt(event.getEntity());
+            return;
+        }
+        // 取消时,更新拓展表为取消
+        if (event.getEventType() == ProcessRuntimeEvent.ProcessEvents.PROCESS_CANCELLED) {
+            processInstanceService.updateProcessInstanceExtCancel(event.getEntity());
+            return;
+        }
+        // 完成时,更新拓展表为已完成
+        if (event.getEventType() == ProcessRuntimeEvent.ProcessEvents.PROCESS_COMPLETED) {
+            processInstanceService.updateProcessInstanceExtComplete(event.getEntity());
+            return;
+        }
+
+        // 其它事件,进行更新拓展表
+        processInstanceService.updateProcessInstanceExt(event.getEntity());
     }
 
 }
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/listener/BpmTaskEventListener.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/listener/BpmTaskEventListener.java
new file mode 100644
index 000000000..89d51d801
--- /dev/null
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/listener/BpmTaskEventListener.java
@@ -0,0 +1,64 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.service.task.listener;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmTaskService;
+import org.activiti.api.task.model.Task;
+import org.activiti.api.task.model.events.TaskRuntimeEvent;
+import org.activiti.api.task.runtime.events.listener.TaskEventListener;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+/**
+ * 监听 {@link Task} 的开始与完成,创建与更新对应的 {@link BpmTaskExtDO} 记录
+ *
+ * @author 芋道源码
+ */
+@Component
+public class BpmTaskEventListener<T extends TaskRuntimeEvent<? extends Task>>
+        implements TaskEventListener<T> {
+
+    @Resource
+    @Lazy // 解决循环依赖
+    private BpmTaskService taskService;
+
+    @Override
+    public void onEvent(T event) {
+        // 创建时,插入拓展表
+        if (event.getEventType() == TaskRuntimeEvent.TaskEvents.TASK_CREATED) {
+            taskService.createTaskExt(event.getEntity());
+            return;
+        }
+
+        // 取消时,更新拓展表为取消
+        if (event.getEventType() == TaskRuntimeEvent.TaskEvents.TASK_CANCELLED) {
+            taskService.updateTaskExtCancel(event.getEntity());
+            return;
+        }
+        // 完成时,更新拓展表为已完成。要注意,在调用 delete ProcessInstance 才会触发该逻辑
+        if (event.getEventType() == TaskRuntimeEvent.TaskEvents.TASK_COMPLETED) {
+            taskService.updateTaskExtComplete(event.getEntity());
+            return;
+        }
+
+        // 其它事件,进行更新拓展表
+        taskService.updateTaskExt(event.getEntity());
+    }
+
+//    // Task 创建时,插入拓展表
+//        if (event.getType() == ActivitiEventType.TASK_CREATED) {
+//        System.out.println(event);
+//        return;
+//    }
+//
+//        if (event.getType() == ActivitiEventType.TASK_COMPLETED) {
+//        System.out.println(event);
+//        // 不处理;
+//    } else if (event.getType() == ActivitiEventType.ENTITY_DELETED) {
+//        // 假设是
+//        System.out.println(event);
+//    }
+//        System.out.println(event);
+
+}