Эх сурвалжийг харах

Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm

jason 6 сар өмнө
parent
commit
d36fc98f01
22 өөрчлөгдсөн 144 нэмэгдсэн , 26 устгасан
  1. 1 1
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
  2. 2 0
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
  3. 9 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java
  4. 3 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java
  5. 3 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
  6. 1 2
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java
  7. 2 4
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java
  8. 0 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java
  9. 4 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java
  10. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
  11. 5 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java
  12. 5 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
  13. 11 3
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java
  14. 2 2
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java
  15. 17 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
  16. 8 3
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java
  17. 2 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
  18. 8 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
  19. 35 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
  20. 8 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
  21. 16 7
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
  22. 1 1
      yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java

@@ -128,7 +128,7 @@ public class YudaoWebSecurityConfigurerAdapter {
                 // ①:全局共享规则
                 .authorizeHttpRequests(c -> c
                     // 1.1 静态资源,可匿名访问
-                    .requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll()
+                    .requestMatchers(HttpMethod.GET, "/*.html", "/*.css", "/*.js").permitAll()
                     // 1.2 设置 @PermitAll 无需认证
                     .requestMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll()
                     .requestMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll()

+ 2 - 0
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java

@@ -38,6 +38,7 @@ public interface ErrorCodeConstants {
     ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_003, "任务({})的候选人未配置");
     ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在");
     ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程");
+    ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消");
 
     // ========== 流程任务 1-009-005-000 ==========
     ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你");
@@ -55,6 +56,7 @@ public interface ErrorCodeConstants {
     ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在");
     ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!");
     ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!");
+    ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!");
 
     // ========== 动态表单模块 1-009-010-000 ==========
     ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在");

+ 9 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java

@@ -161,6 +161,15 @@ public class BpmModelController {
         return success(true);
     }
 
+    @DeleteMapping("/clean")
+    @Operation(summary = "清理模型")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('bpm:model:clean')")
+    public CommonResult<Boolean> cleanModel(@RequestParam("id") String id) {
+        modelService.cleanModel(getLoginUserId(), id);
+        return success(true);
+    }
+
     // ========== 仿钉钉/飞书的精简模型 =========
 
     @GetMapping("/simple/get")

+ 3 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java

@@ -62,4 +62,7 @@ public class BpmModelMetaInfoVO {
     @Schema(description = "排序", example = "1")
     private Long sort; // 创建时,后端自动生成
 
+    @Schema(description = "允许撤销审批中的申请", example = "true")
+    private Boolean allowCancelRunningProcess;
+
 }

+ 3 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java

@@ -63,6 +63,9 @@ public class BpmSimpleModelNodeVO {
     @Schema(description = "是否需要签名", example = "false")
     private Boolean signEnable;
 
+    @Schema(description = "是否填写审批意见", example = "false")
+    private Boolean reasonRequire;
+
     /**
      * 审批节点拒绝处理
      */

+ 1 - 2
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java

@@ -101,9 +101,8 @@ public class BpmApprovalDetailRespVO {
         @Schema(description = "审批意见", example = "同意")
         private String reason;
 
-        // TODO @lesan:改成 signPicUrl 会好点
         @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png")
-        private String sign;
+        private String signPicUrl;
 
     }
 

+ 2 - 4
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java

@@ -14,13 +14,11 @@ public class BpmTaskApproveReqVO {
     @NotEmpty(message = "任务编号不能为空")
     private String id;
 
-    @Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!")
-    @NotEmpty(message = "审批意见不能为空")
+    @Schema(description = "审批意见", example = "不错不错!")
     private String reason;
 
-    // TODO @lesan:改成 signPicUrl 会好点
     @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png")
-    private String sign;
+    private String signPicUrl;
 
     @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED)
     private Map<String, Object> variables;

+ 0 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java

@@ -14,7 +14,6 @@ public class BpmTaskRejectReqVO {
     private String id;
 
     @Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!")
-    @NotEmpty(message = "审批意见不能为空")
     private String reason;
 
 }

+ 4 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java

@@ -78,9 +78,12 @@ public class BpmTaskRespVO {
     @Schema(description = "操作按钮设置值")
     private Map<Integer, OperationButtonSetting> buttonsSetting;
 
-    @Schema(description = "是否需要签名")
+    @Schema(description = "是否需要签名", example = "false")
     private Boolean signEnable;
 
+    @Schema(description = "是否填写审批意见", example = "false")
+    private Boolean reasonRequire;
+
     @Data
     @Schema(description = "流程实例")
     public static class ProcessInstance {

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java

@@ -187,7 +187,7 @@ public interface BpmProcessInstanceConvert {
         }
         return BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class)
                 .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task))
-                .setSign(FlowableUtils.getTaskSign(task));
+                .setSignPicUrl(FlowableUtils.getTaskSignPicUrl(task));
     }
 
     default Set<Long> parseUserIds(HistoricProcessInstance processInstance,

+ 5 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java

@@ -150,4 +150,9 @@ public class BpmProcessDefinitionInfoDO extends BaseDO {
     @TableField(typeHandler = StringListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤
     private List<Long> managerUserIds;
 
+    /**
+     * 是否允许撤销审批中的申请
+     */
+    private Boolean allowCancelRunningProcess;
+
 }

+ 5 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java

@@ -115,4 +115,9 @@ public interface BpmnModelConstants {
      */
     String SIGN_ENABLE = "signEnable";
 
+    /**
+     * 审批意见是否必填
+     */
+    String REASON_REQUIRE = "reasonRequire";
+
 }

+ 11 - 3
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java

@@ -43,6 +43,13 @@ public class BpmnVariableConstants {
      * @see ProcessInstance#getProcessVariables()
      */
     public static final String PROCESS_INSTANCE_VARIABLE_RETURN_FLAG = "RETURN_FLAG_%s";
+    /**
+     * 流程实例的变量 - 是否跳过表达式
+     *
+     * @see ProcessInstance#getProcessVariables()
+     * @see <a href="https://blog.csdn.net/weixin_42065235/article/details/126039993">Flowable/Activiti之SkipExpression 完成自动审批</a>
+     */
+    public static final String PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED";
 
     /**
      * 任务的变量 - 状态
@@ -58,8 +65,9 @@ public class BpmnVariableConstants {
      * @see org.flowable.task.api.Task#getTaskLocalVariables()
      */
     public static final String TASK_VARIABLE_REASON = "TASK_REASON";
-
-    // TODO @lesan:TASK_SIGN_PIC_URL 。。。虽然长一点,嘿嘿
-    public static final String TASK_VARIABLE_SIGN = "TASK_SIGN";
+    /**
+     * 任务变量 - 签名图片 URL
+     */
+    public static final String TASK_SIGN_PIC_URL = "TASK_SIGN_PIC_URL";
 
 }

+ 2 - 2
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java

@@ -39,10 +39,10 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent
         processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity());
     }
 
-    @Override
-    // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法。
+    @Override // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法
     protected void processCancelled(FlowableCancelledEvent event) {
         ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId());
         processInstanceService.processProcessInstanceCompleted(processInstance);
     }
+
 }

+ 17 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java

@@ -365,6 +365,23 @@ public class BpmnModelUtils {
         return Convert.toBool(extensionElements.get(0).getElementText(), false);
     }
 
+    public static void addReasonRequire(Boolean reasonRequire, FlowElement userTask) {
+        addExtensionElement(userTask, REASON_REQUIRE,
+                ObjUtil.isNotNull(reasonRequire) ? reasonRequire.toString() : Boolean.FALSE.toString());
+    }
+
+    public static Boolean parseReasonRequire(BpmnModel bpmnModel, String flowElementId) {
+        FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);
+        if (flowElement == null) {
+            return false;
+        }
+        List<ExtensionElement> extensionElements = flowElement.getExtensionElements().get(REASON_REQUIRE);
+        if (CollUtil.isEmpty(extensionElements)) {
+            return false;
+        }
+        return Convert.toBool(extensionElements.get(0).getElementText(), false);
+    }
+
     public static void addListenerConfig(FlowableListener flowableListener, BpmSimpleModelNodeVO.ListenerHandler handler) {
         FieldExtension fieldExtension = new FieldExtension();
         fieldExtension.setFieldName("listenerConfig");

+ 8 - 3
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java

@@ -213,9 +213,14 @@ public class FlowableUtils {
         return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_REASON);
     }
 
-    // TODO @lesan:这个方法名,也改咧
-    public static String getTaskSign(TaskInfo task) {
-        return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_SIGN);
+    /**
+     * 获得任务的签名图片 URL
+     *
+     * @param task 任务
+     * @return 签名图片 URL
+     */
+    public static String getTaskSignPicUrl(TaskInfo task) {
+        return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_SIGN_PIC_URL);
     }
 
     /**

+ 2 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java

@@ -441,6 +441,8 @@ public class SimpleModelUtils {
             addUserTaskListener(node, userTask);
             // 添加是否需要签名
             addSignEnable(node.getSignEnable(), userTask);
+            // 审批意见
+            addReasonRequire(node.getReasonRequire(), userTask);
             return userTask;
         }
 

+ 8 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java

@@ -88,6 +88,14 @@ public interface BpmModelService {
      */
     void deleteModel(Long userId, String id);
 
+    /**
+     * 清理模型,包括流程实例
+     *
+     * @param userId  用户编号
+     * @param id 编号
+     */
+    void cleanModel(Long userId, String id);
+
     /**
      * 修改模型的状态,实际更新的部署的流程定义的状态
      *

+ 35 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java

@@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
@@ -25,10 +26,14 @@ import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.bpmn.model.StartEvent;
 import org.flowable.bpmn.model.UserTask;
 import org.flowable.common.engine.impl.db.SuspensionState;
+import org.flowable.engine.HistoryService;
 import org.flowable.engine.RepositoryService;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.history.HistoricProcessInstance;
 import org.flowable.engine.repository.Model;
 import org.flowable.engine.repository.ModelQuery;
 import org.flowable.engine.repository.ProcessDefinition;
+import org.flowable.engine.runtime.ProcessInstance;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
@@ -63,6 +68,11 @@ public class BpmModelServiceImpl implements BpmModelService {
     @Resource
     private BpmTaskCandidateInvoker taskCandidateInvoker;
 
+    @Resource
+    private HistoryService historyService;
+    @Resource
+    private RuntimeService runtimeService;
+
     @Override
     public List<Model> getModelList(String name) {
         ModelQuery modelQuery = repositoryService.createModelQuery();
@@ -246,6 +256,31 @@ public class BpmModelServiceImpl implements BpmModelService {
         updateProcessDefinitionSuspended(model.getDeploymentId());
     }
 
+    @Override
+    public void cleanModel(Long userId, String id) {
+        // 1. 校验流程模型存在
+        Model model = validateModelManager(id, userId);
+
+        // 2. 清理所有流程数据
+        // TODO @芋艿:这里没有找到批量操作的方法,会不会有性能问题~;
+        // TODO @lesan:建议按照顺序?1)List<ProcessInstance> processInstances 循环处理;然后删除删除一个示实例,接着删除它的 history;
+        // 2.1 先取消所有正在运行的流程
+        List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery()
+                .processDefinitionKey(model.getKey()).list();
+        processInstances.forEach(processInstance -> {
+            runtimeService.deleteProcessInstance(processInstance.getId(),
+                    BpmReasonEnum.CANCEL_BY_SYSTEM.getReason());
+        });
+        // 2.2 再从历史中删除所有相关的流程数据
+        List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()
+                .processDefinitionKey(model.getKey()).list();
+        historicProcessInstances.forEach(historicProcessInstance -> {
+            historyService.deleteHistoricProcessInstance(historicProcessInstance.getId());
+        });
+        // TODO @lesan:流程任务,是不是也要清理哈?
+        // TODO @lesan:抄送是不是也要清理;
+    }
+
     @Override
     public void updateModelState(Long userId, String id, Integer state) {
         // 1.1 校验流程模型存在

+ 8 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java

@@ -595,6 +595,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
         variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, userId); // 设置流程变量,发起人 ID
         variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中
                 BpmProcessInstanceStatusEnum.RUNNING.getStatus());
+        variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 true,不影响没配置 skipExpression 的节点
         if (CollUtil.isNotEmpty(startUserSelectAssignees)) {
             variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees);
         }
@@ -641,6 +642,13 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
         if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
             throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
         }
+        // 1.3 校验允许撤销审批中的申请
+        BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(instance.getProcessDefinitionId());
+        Assert.notNull(processDefinitionInfo, "流程定义({})不存在", processDefinitionInfo);
+        if (processDefinitionInfo.getAllowCancelRunningProcess() != null // 防止未配置 AllowCancelRunningProcess , 默认为可取消
+                && Boolean.FALSE.equals(processDefinitionInfo.getAllowCancelRunningProcess())) {
+            throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW);
+        }
 
         // 2. 取消流程
         updateProcessInstanceCancel(cancelReqVO.getId(),

+ 16 - 7
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java

@@ -64,6 +64,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG;
+import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseReasonRequire;
 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseSignEnable;
 
 /**
@@ -163,6 +164,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
         Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonsSetting = BpmnModelUtils.parseButtonsSetting(
                 bpmnModel, todoTask.getTaskDefinitionKey());
         Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey());
+        Boolean reasonRequire = parseReasonRequire(bpmnModel, todoTask.getTaskDefinitionKey());
 
         // 4. 任务表单
         BpmFormDO taskForm = null;
@@ -171,7 +173,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
         }
 
         return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm)
-                .setSignEnable(signEnable);
+                .setSignEnable(signEnable)
+                .setReasonRequire(reasonRequire);
     }
 
     @Override
@@ -485,9 +488,14 @@ public class BpmTaskServiceImpl implements BpmTaskService {
         // 1.3 校验签名
         BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
         Boolean signEnable = parseSignEnable(bpmnModel, task.getTaskDefinitionKey());
-        if (signEnable && StrUtil.isEmpty(reqVO.getSign())) {
+        if (signEnable && StrUtil.isEmpty(reqVO.getSignPicUrl())) {
             throw exception(TASK_SIGNATURE_NOT_EXISTS);
         }
+        // 1.4 校验审批意见
+        Boolean reasonRequire = parseReasonRequire(bpmnModel, task.getTaskDefinitionKey());
+        if (reasonRequire && StrUtil.isEmpty(reqVO.getReason())) {
+            throw exception(TASK_REASON_REQUIRE);
+        }
 
         // 情况一:被委派的任务,不调用 complete 去完成任务
         if (DelegationState.PENDING.equals(task.getDelegationState())) {
@@ -505,7 +513,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
         // 2.1 更新 task 状态、原因、签字
         updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason());
         if (signEnable) {
-            taskService.setVariableLocal(task.getId(), BpmnVariableConstants.TASK_VARIABLE_SIGN, reqVO.getSign());
+            taskService.setVariableLocal(task.getId(), BpmnVariableConstants.TASK_SIGN_PIC_URL, reqVO.getSignPicUrl());
         }
         // 2.2 添加评论
         taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
@@ -859,10 +867,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
                 .moveActivityIdsToSingleActivityId(activityIds, endEvent.getId())
                 .changeState();
 
-        // 3. 如果跳转到 EndEvent 流程还未结束, 执行 deleteProcessInstance 方法。
-        List<Execution> executionList = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list();
-        if (CollUtil.isNotEmpty(executionList)) {
-            log.warn("执行跳转到 EndEvent 后, 流程实例未结束。执行 [deleteProcessInstance] 方法");
+        // 3. 特殊:如果跳转到 EndEvent 流程还未结束, 执行 deleteProcessInstance 方法
+        // TODO 芋艿:目前发现并行分支情况下,会存在这个情况,后续看看有没更好的方案;
+        List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list();
+        if (CollUtil.isNotEmpty(executions)) {
+            log.warn("[moveTaskToEnd][执行跳转到 EndEvent 后, 流程实例未结束,强制执行 deleteProcessInstance 方法]");
             runtimeService.deleteProcessInstance(processInstanceId, reason);
         }
     }

+ 1 - 1
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java

@@ -48,7 +48,7 @@ import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClient
 /**
  * 微信支付抽象类,实现微信统一的接口、以及部分实现(退款)
  *
- * @author 遇到源码
+ * @author 芋道源码
  */
 @Slf4j
 public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientConfig> {