mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-22 15:21:53 +08:00
仿钉钉流程设计- 简化审批拒绝流程, code review 修改
This commit is contained in:
parent
41b9ab2ba5
commit
633a7c50ae
@ -51,9 +51,6 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务");
|
||||
ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人");
|
||||
ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在");
|
||||
ErrorCode TASK_RETURN_NOT_ASSIGN_TARGET_TASK_ID = new ErrorCode(1_009_005_015, "回退任务未指定目标任务编号");
|
||||
ErrorCode TASK_REJECT_HANDLER_TYPE_BY_REJECT_RATIO_ERROR = new ErrorCode(1_009_005_016, "按拒绝人数比例终止流程只能用于会签任务");
|
||||
|
||||
ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!");
|
||||
|
||||
// ========== 动态表单模块 1-009-010-000 ==========
|
||||
|
@ -14,6 +14,7 @@ import lombok.Getter;
|
||||
public enum BpmFieldPermissionEnum {
|
||||
|
||||
// TODO @jason:这个顺序要不要改下,和页面保持一致;只读(1)、编辑(2)、隐藏(3)
|
||||
// @芋艿 我看钉钉页面的顺序 是 可编辑 只读 隐藏
|
||||
WRITE(1, "可编辑"),
|
||||
READ(2, "只读"),
|
||||
NONE(3, "隐藏");
|
||||
|
@ -13,12 +13,8 @@ import lombok.Getter;
|
||||
@AllArgsConstructor
|
||||
public enum BpmUserTaskRejectHandlerType {
|
||||
|
||||
// TODO @jason:是不是收敛成 2 个:FINISH_PROCESS => 1. 直接结束流程;RETURN_PRE_USER_TASK => 2. 驳回到指定节点(RETURN_USER_TASK【去掉 PRE】)
|
||||
FINISH_PROCESS(1, "终止流程"),
|
||||
RETURN_PRE_USER_TASK(2, "驳回到指定任务节点"),
|
||||
|
||||
FINISH_PROCESS_BY_REJECT_NUMBER(3, "按拒绝人数终止流程"), // 用于会签
|
||||
FINISH_TASK(4, "结束任务"); // 待实现,可能会用于意见分支
|
||||
RETURN_USER_TASK(2, "驳回到指定任务节点");
|
||||
|
||||
private final Integer type;
|
||||
private final String name;
|
||||
|
@ -149,11 +149,10 @@ public class BpmModelController {
|
||||
|
||||
// ========== 仿钉钉/飞书的精简模型 =========
|
||||
|
||||
// TODO @jason:modelId => id 哈。一般属于自己的模块,可以简化命名。
|
||||
@GetMapping("/simple/get")
|
||||
@Operation(summary = "获得仿钉钉流程设计模型")
|
||||
@Parameter(name = "modelId", description = "流程模型编号", required = true, example = "a2c5eee0-eb6c-11ee-abf4-0c37967c420a")
|
||||
public CommonResult<BpmSimpleModelNodeVO> getSimpleModel(@RequestParam("modelId") String modelId){
|
||||
public CommonResult<BpmSimpleModelNodeVO> getSimpleModel(@RequestParam("id") String modelId){
|
||||
return success(modelService.getSimpleModel(modelId));
|
||||
}
|
||||
|
||||
|
@ -43,10 +43,9 @@ public class BpmSimpleModelNodeVO {
|
||||
@Schema(description = "节点的属性")
|
||||
private Map<String, Object> attributes; // TODO @jason:建议是字段分拆下;类似说:
|
||||
|
||||
// TODO @jason:看看是不是可以简化;
|
||||
// TODO @jason:看看是不是可以简化;@芋艿: 暂时先放着。不知道后面是否会用到
|
||||
/**
|
||||
* 附加节点 Id, 该节点不从前端传入。 由程序生成. 由于当个节点无法完成功能。 需要附加节点来完成。
|
||||
* 例如: 会签时需要按拒绝人数来终止流程。 需要 userTask + ServiceTask 两个节点配合完成。 serviceTask 由后端生成。
|
||||
*/
|
||||
@JsonIgnore
|
||||
private String attachNodeId;
|
||||
|
@ -11,10 +11,9 @@ import lombok.Data;
|
||||
@Data
|
||||
public class BpmSimpleModelUpdateReqVO {
|
||||
|
||||
// TODO @jason:=> id
|
||||
@Schema(description = "流程模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotEmpty(message = "流程模型编号不能为空")
|
||||
private String modelId; // 对应 Flowable act_re_model 表 ID_ 字段
|
||||
private String id; // 对应 Flowable act_re_model 表 ID_ 字段
|
||||
|
||||
@Schema(description = "仿钉钉流程设计模型对象", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "仿钉钉流程设计模型对象不能为空")
|
||||
|
@ -7,18 +7,17 @@ import jakarta.annotation.Resource;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.JavaDelegate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
// TODO @jason:类名可以改成 BpmCopyTaskDelegate
|
||||
/**
|
||||
* 处理抄送用户的 {@link JavaDelegate} 的实现类
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Service // TODO @jason:这种注解,建议用 @Component
|
||||
public class CopyUserDelegate implements JavaDelegate {
|
||||
@Component
|
||||
public class BpmCopyTaskDelegate implements JavaDelegate {
|
||||
|
||||
@Resource
|
||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
@ -1,40 +0,0 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.custom.delegate;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.JavaDelegate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
// TODO @jason:微信已经讨论,简化哈
|
||||
/**
|
||||
* 处理会签 Service Task 代理
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Component
|
||||
public class MultiInstanceServiceTaskDelegate implements JavaDelegate {
|
||||
|
||||
@Resource
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
|
||||
@Override
|
||||
public void execute(DelegateExecution execution) {
|
||||
|
||||
String attachUserTaskId = BpmnModelUtils.parseExtensionElement(execution.getCurrentFlowElement(),
|
||||
BpmnModelConstants.SERVICE_TASK_ATTACH_USER_TASK_ID); // TODO @jason:上面不需要加空行哈;
|
||||
Assert.notNull(attachUserTaskId, "附属的用户任务 Id 不能为空");
|
||||
// 获取会签任务是否被拒绝
|
||||
Boolean userTaskRejected = execution.getVariable(String.format("%s_reject", attachUserTaskId), Boolean.class);
|
||||
// 如果会签任务被拒绝, 终止流程, 跳转到 EndEvent 节点
|
||||
if (BooleanUtil.isTrue(userTaskRejected)) {
|
||||
processInstanceService.updateProcessInstanceReject(execution.getProcessInstanceId(),
|
||||
execution.getCurrentActivityId(), "会签任务未达到通过比例" );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.custom.expression;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmApproveMethodEnum.APPROVE_BY_RATIO;
|
||||
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_APPROVE_METHOD;
|
||||
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_APPROVE_RATIO;
|
||||
|
||||
// TODO @jason:微信已经讨论,简化哈
|
||||
/**
|
||||
* 按拒绝人数计算会签的完成条件的流程表达式实现
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class CompleteByRejectCountExpression {
|
||||
|
||||
/**
|
||||
* 会签的完成条件
|
||||
*/
|
||||
public boolean completionCondition(DelegateExecution execution) {
|
||||
FlowElement flowElement = execution.getCurrentFlowElement();
|
||||
// 实例总数
|
||||
Integer nrOfInstances = (Integer) execution.getVariable("nrOfInstances");
|
||||
// 完成的实例数
|
||||
Integer nrOfCompletedInstances = (Integer) execution.getVariable("nrOfCompletedInstances");
|
||||
// 审批方式
|
||||
Integer approveMethod = NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_APPROVE_METHOD));
|
||||
Assert.notNull(approveMethod, "审批方式不能空");
|
||||
if (!Objects.equals(APPROVE_BY_RATIO.getMethod(), approveMethod)) {
|
||||
log.error("[completionCondition] the execution is [{}] 审批方式[{}] 不匹配", execution, approveMethod);
|
||||
throw exception(GlobalErrorCodeConstants.ERROR_CONFIGURATION);
|
||||
}
|
||||
// 获取拒绝人数
|
||||
// TODO @jason:CollUtil.filter().size();貌似可以更简洁 @芋艿 CollUtil.filter().size() 使用这个会报错,好坑了.
|
||||
Integer rejectCount = CollectionUtils.getSumValue(execution.getExecutions(),
|
||||
item -> Objects.equals(BpmTaskStatusEnum.REJECT.getStatus(), item.getVariableLocal(BpmConstants.TASK_VARIABLE_STATUS, Integer.class)) ? 1 : 0,
|
||||
Integer::sum, 0);
|
||||
// 同意人数: 完成人数 - 拒绝人数
|
||||
int agreeCount = nrOfCompletedInstances - rejectCount;
|
||||
// 多人会签(按通过比例)
|
||||
Integer approveRatio = NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_APPROVE_RATIO));
|
||||
Assert.notNull(approveRatio, "通过比例不能空");
|
||||
// 判断通过比例
|
||||
double approvePct = approveRatio / (double) 100;
|
||||
double realApprovePct = (double) agreeCount / nrOfInstances;
|
||||
if (realApprovePct >= approvePct) {
|
||||
return true;
|
||||
}
|
||||
double rejectPct = (100 - approveRatio) / (double) 100;
|
||||
double realRejectPct = (double) rejectCount / nrOfInstances;
|
||||
// 判断拒绝比例
|
||||
if (realRejectPct > rejectPct) {
|
||||
execution.setVariable(String.format("%s_reject", flowElement.getId()), Boolean.TRUE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -56,16 +56,6 @@ public interface BpmnModelConstants {
|
||||
*/
|
||||
String USER_TASK_APPROVE_METHOD = "approveMethod";
|
||||
|
||||
/**
|
||||
* BPMN UserTask 的扩展属性,当审批方式为按通过比例时, 标记会签通过比例
|
||||
*/
|
||||
String USER_TASK_APPROVE_RATIO = "approveRatio";
|
||||
|
||||
/**
|
||||
* BPMN ExtensionElement 的扩展属性,用于标记 服务任务附属的用户任务 Id
|
||||
*/
|
||||
String SERVICE_TASK_ATTACH_USER_TASK_ID = "attachUserTaskId";
|
||||
|
||||
/**
|
||||
* BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限
|
||||
*/
|
||||
|
@ -73,48 +73,4 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO @jason:这块如果不需要,可以删除掉~~~
|
||||
// @Override
|
||||
// protected void activityMessageReceived(FlowableMessageEvent event) {
|
||||
// BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(event.getProcessDefinitionId());
|
||||
// FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, event.getActivityId());
|
||||
// if (element instanceof BoundaryEvent) {
|
||||
// BoundaryEvent boundaryEvent = (BoundaryEvent) element;
|
||||
// String boundaryEventType = parseBoundaryEventExtensionElement(boundaryEvent, BpmnModelConstants.BOUNDARY_EVENT_TYPE);
|
||||
// // 如果自定义类型为拒绝后处理,进行拒绝处理
|
||||
// if (Objects.equals(USER_TASK_REJECT_POST_PROCESS.getType(), NumberUtils.parseInt(boundaryEventType))) {
|
||||
// String rejectHandlerType = parseBoundaryEventExtensionElement((BoundaryEvent) element, BpmnModelConstants.USER_TASK_REJECT_HANDLER_TYPE);
|
||||
// rejectHandler(boundaryEvent, event.getProcessInstanceId(), boundaryEvent.getAttachedToRefId(), NumberUtils.parseInt(rejectHandlerType));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void rejectHandler(BoundaryEvent boundaryEvent, String processInstanceId, String taskDefineKey, Integer rejectHandlerType) {
|
||||
// BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType);
|
||||
// if (userTaskRejectHandlerType != null) {
|
||||
// List<Task> taskList = taskService.getAssignedTaskListByConditions(processInstanceId, null, taskDefineKey);
|
||||
// taskList.forEach(task -> {
|
||||
// Integer taskStatus = FlowableUtils.getTaskStatus(task);
|
||||
// // 只有处于拒绝状态下才处理
|
||||
// if (Objects.equals(BpmTaskStatusEnum.REJECT.getStatus(), taskStatus)) {
|
||||
// // 终止流程
|
||||
// if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.TERMINATION) {
|
||||
// processInstanceService.updateProcessInstanceReject(task.getProcessInstanceId(), FlowableUtils.getTaskReason(task));
|
||||
// return;
|
||||
// }
|
||||
// // 驳回
|
||||
// if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_PRE_USER_TASK) {
|
||||
// String returnTaskId = parseBoundaryEventExtensionElement(boundaryEvent, BpmnModelConstants.USER_TASK_REJECT_RETURN_TASK_ID);
|
||||
// if (returnTaskId != null) {
|
||||
// BpmTaskReturnReqVO reqVO = new BpmTaskReturnReqVO().setId(task.getId())
|
||||
// .setTargetTaskDefinitionKey(returnTaskId)
|
||||
// .setReason("任务拒绝回退");
|
||||
// taskService.returnTask(getLoginUserId(), reqVO);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
|
||||
import org.flowable.bpmn.converter.BpmnXMLConverter;
|
||||
import org.flowable.bpmn.model.Process;
|
||||
@ -27,8 +28,7 @@ public class BpmnModelUtils {
|
||||
// TODO @芋艿 尝试从 ExtensionElement 取. 后续相关扩展是否都可以 存 extensionElement。 如表单权限。 按钮权限
|
||||
if (candidateStrategy == null) {
|
||||
ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
|
||||
// TODO @jason:这里可以改成 element != null 看着会简单点 element != null ? NumberUtils.parseInt(element.getElementText()) : null;
|
||||
candidateStrategy = NumberUtils.parseInt(Optional.ofNullable(element).map(ExtensionElement::getElementText).orElse(null));
|
||||
candidateStrategy = element != null ? NumberUtils.parseInt(element.getElementText()) : null;
|
||||
}
|
||||
return candidateStrategy;
|
||||
}
|
||||
@ -38,18 +38,26 @@ public class BpmnModelUtils {
|
||||
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
|
||||
if (candidateParam == null) {
|
||||
ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));
|
||||
// TODO @jason:这里可以改成 element != null 看着会简单点 element != null ? element.getElementText() : null;
|
||||
candidateParam = Optional.ofNullable(element).map(ExtensionElement::getElementText).orElse(null);
|
||||
candidateParam = element != null ? element.getElementText() : null;
|
||||
}
|
||||
return candidateParam;
|
||||
}
|
||||
|
||||
public static BpmUserTaskRejectHandlerType parseRejectHandlerType(FlowElement userTask) {
|
||||
Integer rejectHandlerType = NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE));
|
||||
return BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType);
|
||||
}
|
||||
|
||||
public static String parseReturnTaskId(FlowElement flowElement) {
|
||||
return BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID);
|
||||
}
|
||||
|
||||
public static String parseExtensionElement(FlowElement flowElement, String elementName) {
|
||||
if (flowElement == null) {
|
||||
return null;
|
||||
}
|
||||
ExtensionElement element = CollUtil.getFirst(flowElement.getExtensionElements().get(elementName));
|
||||
return Optional.ofNullable(element).map(ExtensionElement::getElementText).orElse(null);
|
||||
return element != null ? element.getElementText() : null;
|
||||
}
|
||||
|
||||
// TODO @jason:貌似这个没地方调用??? @芋艿 在 BpmTaskConvert里面。暂时注释掉了。
|
||||
|
@ -26,7 +26,6 @@ import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType.USER_TASK_TIMEOUT;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType.FINISH_PROCESS_BY_REJECT_NUMBER;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutActionEnum.AUTO_REMINDER;
|
||||
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;
|
||||
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.*;
|
||||
@ -55,9 +54,9 @@ public class SimpleModelUtils {
|
||||
public static final String ANY_OF_APPROVE_COMPLETE_EXPRESSION = "${ nrOfCompletedInstances > 0 }";
|
||||
|
||||
/**
|
||||
* 按拒绝人数计算多实例完成条件的表达式
|
||||
* 按通过比例完成表达式
|
||||
*/
|
||||
public static final String COMPLETE_BY_REJECT_COUNT_EXPRESSION = "${completeByRejectCountExpression.completionCondition(execution)}";
|
||||
public static final String APPROVE_BY_RATIO_COMPLETE_EXPRESSION = "${ nrOfCompletedInstances/nrOfInstances >= %s}";
|
||||
|
||||
// TODO-DONE @jason:建议方法名,改成 buildBpmnModel
|
||||
// TODO @yunai:注释需要完善下;
|
||||
@ -185,8 +184,9 @@ public class SimpleModelUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建有附加节点的连线
|
||||
* @param nodeId 当前节点 Id
|
||||
* 构建有附加节点的连线
|
||||
*
|
||||
* @param nodeId 当前节点 Id
|
||||
* @param attachNodeId 附属节点 Id
|
||||
* @param targetNodeId 目标节点 Id
|
||||
*/
|
||||
@ -344,28 +344,9 @@ public class SimpleModelUtils {
|
||||
BoundaryEvent boundaryEvent = buildUserTaskTimerBoundaryEvent(userTask, userTaskConfig.getTimeoutHandler());
|
||||
flowElements.add(boundaryEvent);
|
||||
}
|
||||
// 如果按拒绝人数终止流程。需要添加附加的 ServiceTask 处理
|
||||
if (userTaskConfig.getRejectHandler() != null &&
|
||||
Objects.equals(FINISH_PROCESS_BY_REJECT_NUMBER.getType(), userTaskConfig.getRejectHandler().getType())) {
|
||||
ServiceTask serviceTask = buildMultiInstanceServiceTask(node);
|
||||
flowElements.add(serviceTask);
|
||||
}
|
||||
return flowElements;
|
||||
}
|
||||
|
||||
private static ServiceTask buildMultiInstanceServiceTask(BpmSimpleModelNodeVO node) {
|
||||
ServiceTask serviceTask = new ServiceTask();
|
||||
String id = String.format("Activity-%s", IdUtil.fastSimpleUUID());
|
||||
serviceTask.setId(id);
|
||||
serviceTask.setName("会签服务任务");
|
||||
serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
|
||||
serviceTask.setImplementation("${multiInstanceServiceTaskDelegate}");
|
||||
serviceTask.setAsynchronous(false);
|
||||
addExtensionElement(serviceTask, SERVICE_TASK_ATTACH_USER_TASK_ID, node.getId());
|
||||
node.setAttachNodeId(id);
|
||||
return serviceTask;
|
||||
}
|
||||
|
||||
private static BoundaryEvent buildUserTaskTimerBoundaryEvent(UserTask userTask, SimpleModelUserTaskConfig.TimeoutHandler timeoutHandler) {
|
||||
// 定时器边界事件
|
||||
BoundaryEvent boundaryEvent = new BoundaryEvent();
|
||||
@ -406,7 +387,7 @@ public class SimpleModelUtils {
|
||||
serviceTask.setId(node.getId());
|
||||
serviceTask.setName(node.getName());
|
||||
serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
|
||||
serviceTask.setImplementation("${copyUserDelegate}");
|
||||
serviceTask.setImplementation("${bpmCopyTaskDelegate}");
|
||||
|
||||
// 添加抄送候选人元素
|
||||
addCandidateElements(MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY),
|
||||
@ -515,11 +496,10 @@ public class SimpleModelUtils {
|
||||
multiInstanceCharacteristics.setLoopCardinality("1");
|
||||
userTask.setLoopCharacteristics(multiInstanceCharacteristics);
|
||||
} else if (bpmApproveMethodEnum == BpmApproveMethodEnum.APPROVE_BY_RATIO) {
|
||||
multiInstanceCharacteristics.setCompletionCondition(COMPLETE_BY_REJECT_COUNT_EXPRESSION);
|
||||
multiInstanceCharacteristics.setSequential(false);
|
||||
Assert.notNull(approveRatio, "通过比例不能为空");
|
||||
// 添加通过比例的扩展属性
|
||||
addExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_RATIO, approveRatio.toString());
|
||||
double approvePct = approveRatio / (double) 100;
|
||||
multiInstanceCharacteristics.setCompletionCondition(String.format(APPROVE_BY_RATIO_COMPLETE_EXPRESSION, String.format("%.2f", approvePct)));
|
||||
multiInstanceCharacteristics.setSequential(false);
|
||||
}
|
||||
userTask.setLoopCharacteristics(multiInstanceCharacteristics);
|
||||
}
|
||||
|
@ -226,7 +226,7 @@ public class BpmModelServiceImpl implements BpmModelService {
|
||||
@Override
|
||||
public void updateSimpleModel(BpmSimpleModelUpdateReqVO reqVO) {
|
||||
// 1. 校验流程模型存在
|
||||
Model model = getModel(reqVO.getModelId());
|
||||
Model model = getModel(reqVO.getId());
|
||||
if (model == null) {
|
||||
throw exception(MODEL_NOT_EXISTS);
|
||||
}
|
||||
|
@ -13,7 +13,13 @@ import java.util.Collection;
|
||||
*/
|
||||
public interface BpmProcessInstanceCopyService {
|
||||
|
||||
// TODO @jason:要不把 createProcessInstanceCopy 搞 2 个方法,一个方法参数是之前的 userIds、taskId;一个方法是现在 userIds、processInstanceId、taskId、taskName;
|
||||
/**
|
||||
* 流程实例的抄送
|
||||
*
|
||||
* @param userIds 抄送的用户编号
|
||||
* @param taskId 流程任务编号
|
||||
*/
|
||||
void createProcessInstanceCopy(Collection<Long> userIds, String taskId);
|
||||
|
||||
/**
|
||||
* 流程实例的抄送
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.bpm.service.task;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
|
||||
@ -10,6 +11,7 @@ import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@ -44,21 +46,25 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private BpmProcessDefinitionService processDefinitionService;
|
||||
|
||||
@Override
|
||||
public void createProcessInstanceCopy(Collection<Long> userIds, String taskId) {
|
||||
Task task = taskService.getTask(taskId);
|
||||
if (ObjectUtil.isNull(task)) {
|
||||
throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
|
||||
}
|
||||
String processInstanceId = task.getProcessInstanceId();
|
||||
createProcessInstanceCopy(userIds, processInstanceId, task.getId(), task.getName());
|
||||
}
|
||||
|
||||
// TODO @芋艿:这里多加了一个 name;
|
||||
@Override
|
||||
public void createProcessInstanceCopy(Collection<Long> userIds, String processInstanceId, String taskId, String taskName) {
|
||||
// 1.1 校验任务存在 暂时去掉这个校验. 因为任务可能仿钉钉快搭的抄送节点(UserTask) TODO jason:抄送节点,会没有来源的 taskId 么? @芋艿 是否校验一下 传递进来的 id 不为空就行
|
||||
// Task task = taskService.getTask(taskId);
|
||||
// if (ObjectUtil.isNull(task)) {
|
||||
// throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
|
||||
// }
|
||||
// 1.2 校验流程实例存在
|
||||
// String processInstanceId = task.getProcessInstanceId();
|
||||
// 1.1 校验流程实例存在
|
||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
|
||||
if (processInstance == null) {
|
||||
throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
|
||||
}
|
||||
// 1.3 校验流程定义存在
|
||||
// 1.2 校验流程定义存在
|
||||
ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(
|
||||
processInstance.getProcessDefinitionId());
|
||||
if (processDefinition == null) {
|
||||
|
File diff suppressed because one or more lines are too long
@ -54,8 +54,6 @@ import java.util.stream.Stream;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
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.BpmnModelConstants.USER_TASK_REJECT_HANDLER_TYPE;
|
||||
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_REJECT_RETURN_TASK_ID;
|
||||
|
||||
/**
|
||||
* 流程任务实例 Service 实现类
|
||||
@ -189,8 +187,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
|
||||
// 2. 抄送用户
|
||||
if (CollUtil.isNotEmpty(reqVO.getCopyUserIds())) {
|
||||
processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), instance.getProcessInstanceId(),
|
||||
reqVO.getId(), task.getName());
|
||||
processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getId());
|
||||
}
|
||||
|
||||
// 情况一:被委派的任务,不调用 complete 去完成任务
|
||||
@ -338,35 +335,18 @@ public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
|
||||
// 3.1 解析用户任务的拒绝处理类型
|
||||
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
|
||||
// TODO @jason:342 到 344 最好抽象一个方法出来哈。放在 BpmnModelUtils,参照类似 parseCandidateStrategy
|
||||
UserTask flowElement = (UserTask) BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
|
||||
Integer rejectHandlerType = NumberUtils.parseInt(BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_REJECT_HANDLER_TYPE));
|
||||
BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType);
|
||||
// 3.2 类型为驳回到指定的任务节点 TODO @jason:下面这种判断,最好是 JSON
|
||||
if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_PRE_USER_TASK) {
|
||||
// TODO @jason:348 最好抽象一个方法出来哈。放在 BpmnModelUtils,参照类似 parseCandidateStrategy
|
||||
String returnTaskId = BpmnModelUtils.parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID);
|
||||
// TODO @jason:这里如果找不到,直接抛出系统异常;因为说白了,已经不是业务异常啦。
|
||||
if (returnTaskId == null) {
|
||||
throw exception(TASK_RETURN_NOT_ASSIGN_TARGET_TASK_ID);
|
||||
}
|
||||
FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
|
||||
BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement);
|
||||
// 3.2 类型为驳回到指定的任务节点
|
||||
if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) {
|
||||
String returnTaskId = BpmnModelUtils.parseReturnTaskId(flowElement);
|
||||
Assert.notNull(returnTaskId, "回退的节点不能为空");
|
||||
BpmTaskReturnReqVO returnReq = new BpmTaskReturnReqVO().setId(task.getId()).setTargetTaskDefinitionKey(returnTaskId)
|
||||
.setReason(reqVO.getReason());
|
||||
returnTask(userId, returnReq);
|
||||
return;
|
||||
} else if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.FINISH_PROCESS_BY_REJECT_NUMBER) {
|
||||
// TODO @jason:微信沟通,去掉类似的逻辑;
|
||||
// 3.3 按拒绝人数终止流程
|
||||
if (!flowElement.hasMultiInstanceLoopCharacteristics()) {
|
||||
log.error("[rejectTask] 按拒绝人数终止流程类型,只能用于会签任务. 当前任务【{}】不是会签任务", task.getId());
|
||||
throw new IllegalStateException("按拒绝人数终止流程类型,只能用于会签任务");
|
||||
}
|
||||
// 设置变量值为拒绝
|
||||
runtimeService.setVariableLocal(task.getExecutionId(), BpmConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.REJECT.getStatus());
|
||||
taskService.complete(task.getId());
|
||||
return;
|
||||
}
|
||||
// 3.4 其他情况 终止流程。
|
||||
// 3.3 其他情况 终止流程。
|
||||
processInstanceService.updateProcessInstanceReject(instance.getProcessInstanceId(),
|
||||
task.getTaskDefinitionKey(), reqVO.getReason());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user