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,7 +128,7 @@ public class YudaoWebSecurityConfigurerAdapter {
128 // ①:全局共享规则 128 // ①:全局共享规则
129 .authorizeHttpRequests(c -> c 129 .authorizeHttpRequests(c -> c
130 // 1.1 静态资源,可匿名访问 130 // 1.1 静态资源,可匿名访问
131 - .requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll() 131 + .requestMatchers(HttpMethod.GET, "/*.html", "/*.css", "/*.js").permitAll()
132 // 1.2 设置 @PermitAll 无需认证 132 // 1.2 设置 @PermitAll 无需认证
133 .requestMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll() 133 .requestMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll()
134 .requestMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll() 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,6 +38,7 @@ public interface ErrorCodeConstants {
38 ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_003, "任务({})的候选人未配置"); 38 ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_003, "任务({})的候选人未配置");
39 ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在"); 39 ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在");
40 ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); 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 // ========== 流程任务 1-009-005-000 ========== 43 // ========== 流程任务 1-009-005-000 ==========
43 ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); 44 ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你");
@@ -55,6 +56,7 @@ public interface ErrorCodeConstants { @@ -55,6 +56,7 @@ public interface ErrorCodeConstants {
55 ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在"); 56 ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在");
56 ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); 57 ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!");
57 ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!"); 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 // ========== 动态表单模块 1-009-010-000 ========== 61 // ========== 动态表单模块 1-009-010-000 ==========
60 ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在"); 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,6 +161,15 @@ public class BpmModelController {
161 return success(true); 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 @GetMapping("/simple/get") 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,4 +62,7 @@ public class BpmModelMetaInfoVO {
62 @Schema(description = "排序", example = "1") 62 @Schema(description = "排序", example = "1")
63 private Long sort; // 创建时,后端自动生成 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,6 +63,9 @@ public class BpmSimpleModelNodeVO {
63 @Schema(description = "是否需要签名", example = "false") 63 @Schema(description = "是否需要签名", example = "false")
64 private Boolean signEnable; 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,9 +101,8 @@ public class BpmApprovalDetailRespVO {
101 @Schema(description = "审批意见", example = "同意") 101 @Schema(description = "审批意见", example = "同意")
102 private String reason; 102 private String reason;
103 103
104 - // TODO @lesan:改成 signPicUrl 会好点  
105 @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") 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,13 +14,11 @@ public class BpmTaskApproveReqVO {
14 @NotEmpty(message = "任务编号不能为空") 14 @NotEmpty(message = "任务编号不能为空")
15 private String id; 15 private String id;
16 16
17 - @Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!")  
18 - @NotEmpty(message = "审批意见不能为空") 17 + @Schema(description = "审批意见", example = "不错不错!")
19 private String reason; 18 private String reason;
20 19
21 - // TODO @lesan:改成 signPicUrl 会好点  
22 @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") 20 @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png")
23 - private String sign; 21 + private String signPicUrl;
24 22
25 @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED) 23 @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED)
26 private Map<String, Object> variables; 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,7 +14,6 @@ public class BpmTaskRejectReqVO {
14 private String id; 14 private String id;
15 15
16 @Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!") 16 @Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!")
17 - @NotEmpty(message = "审批意见不能为空")  
18 private String reason; 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,9 +78,12 @@ public class BpmTaskRespVO {
78 @Schema(description = "操作按钮设置值") 78 @Schema(description = "操作按钮设置值")
79 private Map<Integer, OperationButtonSetting> buttonsSetting; 79 private Map<Integer, OperationButtonSetting> buttonsSetting;
80 80
81 - @Schema(description = "是否需要签名") 81 + @Schema(description = "是否需要签名", example = "false")
82 private Boolean signEnable; 82 private Boolean signEnable;
83 83
  84 + @Schema(description = "是否填写审批意见", example = "false")
  85 + private Boolean reasonRequire;
  86 +
84 @Data 87 @Data
85 @Schema(description = "流程实例") 88 @Schema(description = "流程实例")
86 public static class ProcessInstance { 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,7 +187,7 @@ public interface BpmProcessInstanceConvert {
187 } 187 }
188 return BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class) 188 return BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class)
189 .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task)) 189 .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task))
190 - .setSign(FlowableUtils.getTaskSign(task)); 190 + .setSignPicUrl(FlowableUtils.getTaskSignPicUrl(task));
191 } 191 }
192 192
193 default Set<Long> parseUserIds(HistoricProcessInstance processInstance, 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,4 +150,9 @@ public class BpmProcessDefinitionInfoDO extends BaseDO {
150 @TableField(typeHandler = StringListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤 150 @TableField(typeHandler = StringListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤
151 private List<Long> managerUserIds; 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,4 +115,9 @@ public interface BpmnModelConstants {
115 */ 115 */
116 String SIGN_ENABLE = "signEnable"; 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,6 +43,13 @@ public class BpmnVariableConstants {
43 * @see ProcessInstance#getProcessVariables() 43 * @see ProcessInstance#getProcessVariables()
44 */ 44 */
45 public static final String PROCESS_INSTANCE_VARIABLE_RETURN_FLAG = "RETURN_FLAG_%s"; 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,8 +65,9 @@ public class BpmnVariableConstants {
58 * @see org.flowable.task.api.Task#getTaskLocalVariables() 65 * @see org.flowable.task.api.Task#getTaskLocalVariables()
59 */ 66 */
60 public static final String TASK_VARIABLE_REASON = "TASK_REASON"; 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,10 +39,10 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent
39 processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity()); 39 processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity());
40 } 40 }
41 41
42 - @Override  
43 - // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法。 42 + @Override // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法
44 protected void processCancelled(FlowableCancelledEvent event) { 43 protected void processCancelled(FlowableCancelledEvent event) {
45 ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId()); 44 ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId());
46 processInstanceService.processProcessInstanceCompleted(processInstance); 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,6 +365,23 @@ public class BpmnModelUtils {
365 return Convert.toBool(extensionElements.get(0).getElementText(), false); 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 public static void addListenerConfig(FlowableListener flowableListener, BpmSimpleModelNodeVO.ListenerHandler handler) { 385 public static void addListenerConfig(FlowableListener flowableListener, BpmSimpleModelNodeVO.ListenerHandler handler) {
369 FieldExtension fieldExtension = new FieldExtension(); 386 FieldExtension fieldExtension = new FieldExtension();
370 fieldExtension.setFieldName("listenerConfig"); 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,9 +213,14 @@ public class FlowableUtils {
213 return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_REASON); 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,6 +441,8 @@ public class SimpleModelUtils {
441 addUserTaskListener(node, userTask); 441 addUserTaskListener(node, userTask);
442 // 添加是否需要签名 442 // 添加是否需要签名
443 addSignEnable(node.getSignEnable(), userTask); 443 addSignEnable(node.getSignEnable(), userTask);
  444 + // 审批意见
  445 + addReasonRequire(node.getReasonRequire(), userTask);
444 return userTask; 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,6 +89,14 @@ public interface BpmModelService {
89 void deleteModel(Long userId, String id); 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 * @param userId 用户编号 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,6 +14,7 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
14 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; 14 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
15 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; 15 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
16 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum; 16 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
  17 +import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
17 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; 18 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
18 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; 19 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
19 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; 20 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
@@ -25,10 +26,14 @@ import org.flowable.bpmn.model.BpmnModel; @@ -25,10 +26,14 @@ import org.flowable.bpmn.model.BpmnModel;
25 import org.flowable.bpmn.model.StartEvent; 26 import org.flowable.bpmn.model.StartEvent;
26 import org.flowable.bpmn.model.UserTask; 27 import org.flowable.bpmn.model.UserTask;
27 import org.flowable.common.engine.impl.db.SuspensionState; 28 import org.flowable.common.engine.impl.db.SuspensionState;
  29 +import org.flowable.engine.HistoryService;
28 import org.flowable.engine.RepositoryService; 30 import org.flowable.engine.RepositoryService;
  31 +import org.flowable.engine.RuntimeService;
  32 +import org.flowable.engine.history.HistoricProcessInstance;
29 import org.flowable.engine.repository.Model; 33 import org.flowable.engine.repository.Model;
30 import org.flowable.engine.repository.ModelQuery; 34 import org.flowable.engine.repository.ModelQuery;
31 import org.flowable.engine.repository.ProcessDefinition; 35 import org.flowable.engine.repository.ProcessDefinition;
  36 +import org.flowable.engine.runtime.ProcessInstance;
32 import org.springframework.stereotype.Service; 37 import org.springframework.stereotype.Service;
33 import org.springframework.transaction.annotation.Transactional; 38 import org.springframework.transaction.annotation.Transactional;
34 import org.springframework.validation.annotation.Validated; 39 import org.springframework.validation.annotation.Validated;
@@ -63,6 +68,11 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -63,6 +68,11 @@ public class BpmModelServiceImpl implements BpmModelService {
63 @Resource 68 @Resource
64 private BpmTaskCandidateInvoker taskCandidateInvoker; 69 private BpmTaskCandidateInvoker taskCandidateInvoker;
65 70
  71 + @Resource
  72 + private HistoryService historyService;
  73 + @Resource
  74 + private RuntimeService runtimeService;
  75 +
66 @Override 76 @Override
67 public List<Model> getModelList(String name) { 77 public List<Model> getModelList(String name) {
68 ModelQuery modelQuery = repositoryService.createModelQuery(); 78 ModelQuery modelQuery = repositoryService.createModelQuery();
@@ -247,6 +257,31 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -247,6 +257,31 @@ public class BpmModelServiceImpl implements BpmModelService {
247 } 257 }
248 258
249 @Override 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 public void updateModelState(Long userId, String id, Integer state) { 285 public void updateModelState(Long userId, String id, Integer state) {
251 // 1.1 校验流程模型存在 286 // 1.1 校验流程模型存在
252 Model model = validateModelManager(id, userId); 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,6 +595,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
595 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, userId); // 设置流程变量,发起人 ID 595 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, userId); // 设置流程变量,发起人 ID
596 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 596 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中
597 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); 597 BpmProcessInstanceStatusEnum.RUNNING.getStatus());
  598 + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 true,不影响没配置 skipExpression 的节点
598 if (CollUtil.isNotEmpty(startUserSelectAssignees)) { 599 if (CollUtil.isNotEmpty(startUserSelectAssignees)) {
599 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); 600 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees);
600 } 601 }
@@ -641,6 +642,13 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -641,6 +642,13 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
641 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { 642 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
642 throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); 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 // 2. 取消流程 653 // 2. 取消流程
646 updateProcessInstanceCancel(cancelReqVO.getId(), 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,6 +64,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
64 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 64 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
65 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; 65 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
66 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG; 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 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseSignEnable; 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,6 +164,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
163 Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonsSetting = BpmnModelUtils.parseButtonsSetting( 164 Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonsSetting = BpmnModelUtils.parseButtonsSetting(
164 bpmnModel, todoTask.getTaskDefinitionKey()); 165 bpmnModel, todoTask.getTaskDefinitionKey());
165 Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey()); 166 Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey());
  167 + Boolean reasonRequire = parseReasonRequire(bpmnModel, todoTask.getTaskDefinitionKey());
166 168
167 // 4. 任务表单 169 // 4. 任务表单
168 BpmFormDO taskForm = null; 170 BpmFormDO taskForm = null;
@@ -171,7 +173,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -171,7 +173,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
171 } 173 }
172 174
173 return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm) 175 return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm)
174 - .setSignEnable(signEnable); 176 + .setSignEnable(signEnable)
  177 + .setReasonRequire(reasonRequire);
175 } 178 }
176 179
177 @Override 180 @Override
@@ -485,9 +488,14 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -485,9 +488,14 @@ public class BpmTaskServiceImpl implements BpmTaskService {
485 // 1.3 校验签名 488 // 1.3 校验签名
486 BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); 489 BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
487 Boolean signEnable = parseSignEnable(bpmnModel, task.getTaskDefinitionKey()); 490 Boolean signEnable = parseSignEnable(bpmnModel, task.getTaskDefinitionKey());
488 - if (signEnable && StrUtil.isEmpty(reqVO.getSign())) { 491 + if (signEnable && StrUtil.isEmpty(reqVO.getSignPicUrl())) {
489 throw exception(TASK_SIGNATURE_NOT_EXISTS); 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 // 情况一:被委派的任务,不调用 complete 去完成任务 500 // 情况一:被委派的任务,不调用 complete 去完成任务
493 if (DelegationState.PENDING.equals(task.getDelegationState())) { 501 if (DelegationState.PENDING.equals(task.getDelegationState())) {
@@ -505,7 +513,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -505,7 +513,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
505 // 2.1 更新 task 状态、原因、签字 513 // 2.1 更新 task 状态、原因、签字
506 updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason()); 514 updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason());
507 if (signEnable) { 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 // 2.2 添加评论 518 // 2.2 添加评论
511 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), 519 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
@@ -859,10 +867,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -859,10 +867,11 @@ public class BpmTaskServiceImpl implements BpmTaskService {
859 .moveActivityIdsToSingleActivityId(activityIds, endEvent.getId()) 867 .moveActivityIdsToSingleActivityId(activityIds, endEvent.getId())
860 .changeState(); 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 runtimeService.deleteProcessInstance(processInstanceId, reason); 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,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 @Slf4j 53 @Slf4j
54 public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientConfig> { 54 public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientConfig> {