Commit d36fc98f017b83f554464fb745205713e231bcc1

Authored by jason
2 parents 96875b1e 081e716e

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

Showing 22 changed files with 144 additions and 26 deletions
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 {
128 128 // ①:全局共享规则
129 129 .authorizeHttpRequests(c -> c
130 130 // 1.1 静态资源,可匿名访问
131   - .requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll()
  131 + .requestMatchers(HttpMethod.GET, "/*.html", "/*.css", "/*.js").permitAll()
132 132 // 1.2 设置 @PermitAll 无需认证
133 133 .requestMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll()
134 134 .requestMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll()
... ...
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 {
38 38 ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_003, "任务({})的候选人未配置");
39 39 ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在");
40 40 ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程");
  41 + ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消");
41 42  
42 43 // ========== 流程任务 1-009-005-000 ==========
43 44 ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你");
... ... @@ -55,6 +56,7 @@ public interface ErrorCodeConstants {
55 56 ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在");
56 57 ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!");
57 58 ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!");
  59 + ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!");
58 60  
59 61 // ========== 动态表单模块 1-009-010-000 ==========
60 62 ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在");
... ...
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 {
161 161 return success(true);
162 162 }
163 163  
  164 + @DeleteMapping("/clean")
  165 + @Operation(summary = "清理模型")
  166 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  167 + @PreAuthorize("@ss.hasPermission('bpm:model:clean')")
  168 + public CommonResult<Boolean> cleanModel(@RequestParam("id") String id) {
  169 + modelService.cleanModel(getLoginUserId(), id);
  170 + return success(true);
  171 + }
  172 +
164 173 // ========== 仿钉钉/飞书的精简模型 =========
165 174  
166 175 @GetMapping("/simple/get")
... ...
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 {
62 62 @Schema(description = "排序", example = "1")
63 63 private Long sort; // 创建时,后端自动生成
64 64  
  65 + @Schema(description = "允许撤销审批中的申请", example = "true")
  66 + private Boolean allowCancelRunningProcess;
  67 +
65 68 }
... ...
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 {
63 63 @Schema(description = "是否需要签名", example = "false")
64 64 private Boolean signEnable;
65 65  
  66 + @Schema(description = "是否填写审批意见", example = "false")
  67 + private Boolean reasonRequire;
  68 +
66 69 /**
67 70 * 审批节点拒绝处理
68 71 */
... ...
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 {
101 101 @Schema(description = "审批意见", example = "同意")
102 102 private String reason;
103 103  
104   - // TODO @lesan:改成 signPicUrl 会好点
105 104 @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png")
106   - private String sign;
  105 + private String signPicUrl;
107 106  
108 107 }
109 108  
... ...
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 {
14 14 @NotEmpty(message = "任务编号不能为空")
15 15 private String id;
16 16  
17   - @Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!")
18   - @NotEmpty(message = "审批意见不能为空")
  17 + @Schema(description = "审批意见", example = "不错不错!")
19 18 private String reason;
20 19  
21   - // TODO @lesan:改成 signPicUrl 会好点
22 20 @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png")
23   - private String sign;
  21 + private String signPicUrl;
24 22  
25 23 @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED)
26 24 private Map<String, Object> variables;
... ...
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 {
14 14 private String id;
15 15  
16 16 @Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!")
17   - @NotEmpty(message = "审批意见不能为空")
18 17 private String reason;
19 18  
20 19 }
... ...
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 {
78 78 @Schema(description = "操作按钮设置值")
79 79 private Map<Integer, OperationButtonSetting> buttonsSetting;
80 80  
81   - @Schema(description = "是否需要签名")
  81 + @Schema(description = "是否需要签名", example = "false")
82 82 private Boolean signEnable;
83 83  
  84 + @Schema(description = "是否填写审批意见", example = "false")
  85 + private Boolean reasonRequire;
  86 +
84 87 @Data
85 88 @Schema(description = "流程实例")
86 89 public static class ProcessInstance {
... ...
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 {
187 187 }
188 188 return BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class)
189 189 .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task))
190   - .setSign(FlowableUtils.getTaskSign(task));
  190 + .setSignPicUrl(FlowableUtils.getTaskSignPicUrl(task));
191 191 }
192 192  
193 193 default Set<Long> parseUserIds(HistoricProcessInstance processInstance,
... ...
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 {
150 150 @TableField(typeHandler = StringListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤
151 151 private List<Long> managerUserIds;
152 152  
  153 + /**
  154 + * 是否允许撤销审批中的申请
  155 + */
  156 + private Boolean allowCancelRunningProcess;
  157 +
153 158 }
... ...
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 {
115 115 */
116 116 String SIGN_ENABLE = "signEnable";
117 117  
  118 + /**
  119 + * 审批意见是否必填
  120 + */
  121 + String REASON_REQUIRE = "reasonRequire";
  122 +
118 123 }
... ...
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 {
43 43 * @see ProcessInstance#getProcessVariables()
44 44 */
45 45 public static final String PROCESS_INSTANCE_VARIABLE_RETURN_FLAG = "RETURN_FLAG_%s";
  46 + /**
  47 + * 流程实例的变量 - 是否跳过表达式
  48 + *
  49 + * @see ProcessInstance#getProcessVariables()
  50 + * @see <a href="https://blog.csdn.net/weixin_42065235/article/details/126039993">Flowable/Activiti之SkipExpression 完成自动审批</a>
  51 + */
  52 + public static final String PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED";
46 53  
47 54 /**
48 55 * 任务的变量 - 状态
... ... @@ -58,8 +65,9 @@ public class BpmnVariableConstants {
58 65 * @see org.flowable.task.api.Task#getTaskLocalVariables()
59 66 */
60 67 public static final String TASK_VARIABLE_REASON = "TASK_REASON";
61   -
62   - // TODO @lesan:TASK_SIGN_PIC_URL 。。。虽然长一点,嘿嘿
63   - public static final String TASK_VARIABLE_SIGN = "TASK_SIGN";
  68 + /**
  69 + * 任务变量 - 签名图片 URL
  70 + */
  71 + public static final String TASK_SIGN_PIC_URL = "TASK_SIGN_PIC_URL";
64 72  
65 73 }
... ...
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
39 39 processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity());
40 40 }
41 41  
42   - @Override
43   - // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法。
  42 + @Override // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法
44 43 protected void processCancelled(FlowableCancelledEvent event) {
45 44 ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId());
46 45 processInstanceService.processProcessInstanceCompleted(processInstance);
47 46 }
  47 +
48 48 }
... ...
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 {
365 365 return Convert.toBool(extensionElements.get(0).getElementText(), false);
366 366 }
367 367  
  368 + public static void addReasonRequire(Boolean reasonRequire, FlowElement userTask) {
  369 + addExtensionElement(userTask, REASON_REQUIRE,
  370 + ObjUtil.isNotNull(reasonRequire) ? reasonRequire.toString() : Boolean.FALSE.toString());
  371 + }
  372 +
  373 + public static Boolean parseReasonRequire(BpmnModel bpmnModel, String flowElementId) {
  374 + FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);
  375 + if (flowElement == null) {
  376 + return false;
  377 + }
  378 + List<ExtensionElement> extensionElements = flowElement.getExtensionElements().get(REASON_REQUIRE);
  379 + if (CollUtil.isEmpty(extensionElements)) {
  380 + return false;
  381 + }
  382 + return Convert.toBool(extensionElements.get(0).getElementText(), false);
  383 + }
  384 +
368 385 public static void addListenerConfig(FlowableListener flowableListener, BpmSimpleModelNodeVO.ListenerHandler handler) {
369 386 FieldExtension fieldExtension = new FieldExtension();
370 387 fieldExtension.setFieldName("listenerConfig");
... ...
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 {
213 213 return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_REASON);
214 214 }
215 215  
216   - // TODO @lesan:这个方法名,也改咧
217   - public static String getTaskSign(TaskInfo task) {
218   - return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_SIGN);
  216 + /**
  217 + * 获得任务的签名图片 URL
  218 + *
  219 + * @param task 任务
  220 + * @return 签名图片 URL
  221 + */
  222 + public static String getTaskSignPicUrl(TaskInfo task) {
  223 + return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_SIGN_PIC_URL);
219 224 }
220 225  
221 226 /**
... ...
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 {
441 441 addUserTaskListener(node, userTask);
442 442 // 添加是否需要签名
443 443 addSignEnable(node.getSignEnable(), userTask);
  444 + // 审批意见
  445 + addReasonRequire(node.getReasonRequire(), userTask);
444 446 return userTask;
445 447 }
446 448  
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
... ... @@ -89,6 +89,14 @@ public interface BpmModelService {
89 89 void deleteModel(Long userId, String id);
90 90  
91 91 /**
  92 + * 清理模型,包括流程实例
  93 + *
  94 + * @param userId 用户编号
  95 + * @param id 编号
  96 + */
  97 + void cleanModel(Long userId, String id);
  98 +
  99 + /**
92 100 * 修改模型的状态,实际更新的部署的流程定义的状态
93 101 *
94 102 * @param userId 用户编号
... ...
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;
14 14 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
15 15 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
16 16 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
  17 +import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
17 18 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
18 19 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
19 20 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
... ... @@ -25,10 +26,14 @@ import org.flowable.bpmn.model.BpmnModel;
25 26 import org.flowable.bpmn.model.StartEvent;
26 27 import org.flowable.bpmn.model.UserTask;
27 28 import org.flowable.common.engine.impl.db.SuspensionState;
  29 +import org.flowable.engine.HistoryService;
28 30 import org.flowable.engine.RepositoryService;
  31 +import org.flowable.engine.RuntimeService;
  32 +import org.flowable.engine.history.HistoricProcessInstance;
29 33 import org.flowable.engine.repository.Model;
30 34 import org.flowable.engine.repository.ModelQuery;
31 35 import org.flowable.engine.repository.ProcessDefinition;
  36 +import org.flowable.engine.runtime.ProcessInstance;
32 37 import org.springframework.stereotype.Service;
33 38 import org.springframework.transaction.annotation.Transactional;
34 39 import org.springframework.validation.annotation.Validated;
... ... @@ -63,6 +68,11 @@ public class BpmModelServiceImpl implements BpmModelService {
63 68 @Resource
64 69 private BpmTaskCandidateInvoker taskCandidateInvoker;
65 70  
  71 + @Resource
  72 + private HistoryService historyService;
  73 + @Resource
  74 + private RuntimeService runtimeService;
  75 +
66 76 @Override
67 77 public List<Model> getModelList(String name) {
68 78 ModelQuery modelQuery = repositoryService.createModelQuery();
... ... @@ -247,6 +257,31 @@ public class BpmModelServiceImpl implements BpmModelService {
247 257 }
248 258  
249 259 @Override
  260 + public void cleanModel(Long userId, String id) {
  261 + // 1. 校验流程模型存在
  262 + Model model = validateModelManager(id, userId);
  263 +
  264 + // 2. 清理所有流程数据
  265 + // TODO @芋艿:这里没有找到批量操作的方法,会不会有性能问题~;
  266 + // TODO @lesan:建议按照顺序?1)List<ProcessInstance> processInstances 循环处理;然后删除删除一个示实例,接着删除它的 history;
  267 + // 2.1 先取消所有正在运行的流程
  268 + List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery()
  269 + .processDefinitionKey(model.getKey()).list();
  270 + processInstances.forEach(processInstance -> {
  271 + runtimeService.deleteProcessInstance(processInstance.getId(),
  272 + BpmReasonEnum.CANCEL_BY_SYSTEM.getReason());
  273 + });
  274 + // 2.2 再从历史中删除所有相关的流程数据
  275 + List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()
  276 + .processDefinitionKey(model.getKey()).list();
  277 + historicProcessInstances.forEach(historicProcessInstance -> {
  278 + historyService.deleteHistoricProcessInstance(historicProcessInstance.getId());
  279 + });
  280 + // TODO @lesan:流程任务,是不是也要清理哈?
  281 + // TODO @lesan:抄送是不是也要清理;
  282 + }
  283 +
  284 + @Override
250 285 public void updateModelState(Long userId, String id, Integer state) {
251 286 // 1.1 校验流程模型存在
252 287 Model model = validateModelManager(id, userId);
... ...
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
595 595 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, userId); // 设置流程变量,发起人 ID
596 596 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中
597 597 BpmProcessInstanceStatusEnum.RUNNING.getStatus());
  598 + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 true,不影响没配置 skipExpression 的节点
598 599 if (CollUtil.isNotEmpty(startUserSelectAssignees)) {
599 600 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees);
600 601 }
... ... @@ -641,6 +642,13 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
641 642 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
642 643 throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
643 644 }
  645 + // 1.3 校验允许撤销审批中的申请
  646 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(instance.getProcessDefinitionId());
  647 + Assert.notNull(processDefinitionInfo, "流程定义({})不存在", processDefinitionInfo);
  648 + if (processDefinitionInfo.getAllowCancelRunningProcess() != null // 防止未配置 AllowCancelRunningProcess , 默认为可取消
  649 + && Boolean.FALSE.equals(processDefinitionInfo.getAllowCancelRunningProcess())) {
  650 + throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW);
  651 + }
644 652  
645 653 // 2. 取消流程
646 654 updateProcessInstanceCancel(cancelReqVO.getId(),
... ...
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
64 64 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
65 65 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
66 66 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG;
  67 +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseReasonRequire;
67 68 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseSignEnable;
68 69  
69 70 /**
... ... @@ -163,6 +164,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
163 164 Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonsSetting = BpmnModelUtils.parseButtonsSetting(
164 165 bpmnModel, todoTask.getTaskDefinitionKey());
165 166 Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey());
  167 + Boolean reasonRequire = parseReasonRequire(bpmnModel, todoTask.getTaskDefinitionKey());
166 168  
167 169 // 4. 任务表单
168 170 BpmFormDO taskForm = null;
... ... @@ -171,7 +173,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
171 173 }
172 174  
173 175 return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm)
174   - .setSignEnable(signEnable);
  176 + .setSignEnable(signEnable)
  177 + .setReasonRequire(reasonRequire);
175 178 }
176 179  
177 180 @Override
... ... @@ -485,9 +488,14 @@ public class BpmTaskServiceImpl implements BpmTaskService {
485 488 // 1.3 校验签名
486 489 BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
487 490 Boolean signEnable = parseSignEnable(bpmnModel, task.getTaskDefinitionKey());
488   - if (signEnable && StrUtil.isEmpty(reqVO.getSign())) {
  491 + if (signEnable && StrUtil.isEmpty(reqVO.getSignPicUrl())) {
489 492 throw exception(TASK_SIGNATURE_NOT_EXISTS);
490 493 }
  494 + // 1.4 校验审批意见
  495 + Boolean reasonRequire = parseReasonRequire(bpmnModel, task.getTaskDefinitionKey());
  496 + if (reasonRequire && StrUtil.isEmpty(reqVO.getReason())) {
  497 + throw exception(TASK_REASON_REQUIRE);
  498 + }
491 499  
492 500 // 情况一:被委派的任务,不调用 complete 去完成任务
493 501 if (DelegationState.PENDING.equals(task.getDelegationState())) {
... ... @@ -505,7 +513,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
505 513 // 2.1 更新 task 状态、原因、签字
506 514 updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason());
507 515 if (signEnable) {
508   - taskService.setVariableLocal(task.getId(), BpmnVariableConstants.TASK_VARIABLE_SIGN, reqVO.getSign());
  516 + taskService.setVariableLocal(task.getId(), BpmnVariableConstants.TASK_SIGN_PIC_URL, reqVO.getSignPicUrl());
509 517 }
510 518 // 2.2 添加评论
511 519 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
... ... @@ -859,10 +867,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
859 867 .moveActivityIdsToSingleActivityId(activityIds, endEvent.getId())
860 868 .changeState();
861 869  
862   - // 3. 如果跳转到 EndEvent 流程还未结束, 执行 deleteProcessInstance 方法。
863   - List<Execution> executionList = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list();
864   - if (CollUtil.isNotEmpty(executionList)) {
865   - log.warn("执行跳转到 EndEvent 后, 流程实例未结束。执行 [deleteProcessInstance] 方法");
  870 + // 3. 特殊:如果跳转到 EndEvent 流程还未结束, 执行 deleteProcessInstance 方法
  871 + // TODO 芋艿:目前发现并行分支情况下,会存在这个情况,后续看看有没更好的方案;
  872 + List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list();
  873 + if (CollUtil.isNotEmpty(executions)) {
  874 + log.warn("[moveTaskToEnd][执行跳转到 EndEvent 后, 流程实例未结束,强制执行 deleteProcessInstance 方法]");
866 875 runtimeService.deleteProcessInstance(processInstanceId, reason);
867 876 }
868 877 }
... ...
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
48 48 /**
49 49 * 微信支付抽象类,实现微信统一的接口、以及部分实现(退款)
50 50 *
51   - * @author 遇到源码
  51 + * @author 芋道源码
52 52 */
53 53 @Slf4j
54 54 public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientConfig> {
... ...