工作流模块提交
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package com.njcn.bpm;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
|
||||
/**
|
||||
* pqs
|
||||
*
|
||||
* @author cdf
|
||||
* @date 2022/11/10
|
||||
*/
|
||||
@Slf4j
|
||||
@DependsOn("proxyMapperRegister")
|
||||
@MapperScan("com.njcn.**.mapper")
|
||||
@EnableFeignClients(basePackages = "com.njcn")
|
||||
@SpringBootApplication(scanBasePackages = "com.njcn")
|
||||
public class BpmApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(BpmApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.njcn.bpm.behavior;
|
||||
|
||||
import com.njcn.bpm.strategy.BpmTaskCandidateInvoker;
|
||||
import lombok.Setter;
|
||||
import org.flowable.bpmn.model.Activity;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
|
||||
import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory;
|
||||
|
||||
/**
|
||||
* 自定义的 ActivityBehaviorFactory 实现类,目的如下:
|
||||
* 1. 自定义 {@link #createUserTaskActivityBehavior(UserTask)}:实现自定义的流程任务的 assignee 负责人的分配
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Setter
|
||||
public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory {
|
||||
|
||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||
|
||||
@Override
|
||||
public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) {
|
||||
BpmUserTaskActivityBehavior bpmUserTaskActivityBehavior = new BpmUserTaskActivityBehavior(userTask);
|
||||
bpmUserTaskActivityBehavior.setTaskCandidateInvoker(taskCandidateInvoker);
|
||||
return bpmUserTaskActivityBehavior;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParallelMultiInstanceBehavior createParallelMultiInstanceBehavior(Activity activity,
|
||||
AbstractBpmnActivityBehavior behavior) {
|
||||
BpmParallelMultiInstanceBehavior bpmParallelMultiInstanceBehavior = new BpmParallelMultiInstanceBehavior(activity, behavior);
|
||||
bpmParallelMultiInstanceBehavior.setTaskCandidateInvoker(taskCandidateInvoker);
|
||||
return bpmParallelMultiInstanceBehavior;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SequentialMultiInstanceBehavior createSequentialMultiInstanceBehavior(Activity activity,
|
||||
AbstractBpmnActivityBehavior behavior) {
|
||||
BpmSequentialMultiInstanceBehavior bpmSequentialMultiInstanceBehavior = new BpmSequentialMultiInstanceBehavior(activity, behavior);
|
||||
bpmSequentialMultiInstanceBehavior.setTaskCandidateInvoker(taskCandidateInvoker);
|
||||
return bpmSequentialMultiInstanceBehavior;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.njcn.bpm.behavior;
|
||||
|
||||
import com.njcn.bpm.strategy.BpmTaskCandidateInvoker;
|
||||
import com.njcn.bpm.utils.FlowableUtils;
|
||||
import lombok.Setter;
|
||||
import org.flowable.bpmn.model.Activity;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 自定义的【并行】的【多个】流程任务的 assignee 负责人的分配
|
||||
* 第一步,基于分配规则,计算出分配任务的【多个】候选人们。
|
||||
* 第二步,将【多个】任务候选人们,设置到 DelegateExecution 的 collectionVariable 变量中,以便 BpmUserTaskActivityBehavior 使用它
|
||||
*
|
||||
* @author kemengkai
|
||||
* @since 2022-04-21 16:57
|
||||
*/
|
||||
@Setter
|
||||
public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior {
|
||||
|
||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||
|
||||
public BpmParallelMultiInstanceBehavior(Activity activity,
|
||||
AbstractBpmnActivityBehavior innerActivityBehavior) {
|
||||
super(activity, innerActivityBehavior);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写该方法,主要实现两个功能:
|
||||
* 1. 忽略原有的 collectionVariable、collectionElementVariable 表达式,而是采用自己定义的
|
||||
* 2. 获得任务的处理人,并设置到 collectionVariable 中,用于 BpmUserTaskActivityBehavior 从中可以获取任务的处理人
|
||||
*
|
||||
* 注意,多个任务实例,每个任务实例对应一个处理人,所以返回的数量就是任务处理人的数量
|
||||
*
|
||||
* @param execution 执行任务
|
||||
* @return 数量
|
||||
*/
|
||||
@Override
|
||||
protected int resolveNrOfInstances(DelegateExecution execution) {
|
||||
// 第一步,设置 collectionVariable 和 CollectionVariable
|
||||
// 从 execution.getVariable() 读取所有任务处理人的 key
|
||||
super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的
|
||||
super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());
|
||||
// 从 execution.getVariable() 读取当前所有任务处理的人的 key
|
||||
super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
|
||||
|
||||
// 第二步,获取任务的所有处理人
|
||||
List<String> assigneeUserIds = taskCandidateInvoker.calculateUsers(execution);
|
||||
execution.setVariable(super.collectionVariable, assigneeUserIds);
|
||||
return assigneeUserIds.size();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.njcn.bpm.behavior;
|
||||
|
||||
import com.njcn.bpm.strategy.BpmTaskCandidateInvoker;
|
||||
import com.njcn.bpm.utils.FlowableUtils;
|
||||
import lombok.Setter;
|
||||
import org.flowable.bpmn.model.Activity;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
|
||||
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 自定义的【串行】的【多个】流程任务的 assignee 负责人的分配
|
||||
*
|
||||
* 本质上,实现和 {@link BpmParallelMultiInstanceBehavior} 一样,只是继承的类不一样
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Setter
|
||||
public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceBehavior {
|
||||
|
||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||
|
||||
public BpmSequentialMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior innerActivityBehavior) {
|
||||
super(activity, innerActivityBehavior);
|
||||
}
|
||||
|
||||
/**
|
||||
* 逻辑和 {@link BpmParallelMultiInstanceBehavior#resolveNrOfInstances(DelegateExecution)} 类似
|
||||
*
|
||||
* 差异的点:是在【第二步】的时候,需要返回 LinkedHashSet 集合!因为它需要有序!
|
||||
*/
|
||||
@Override
|
||||
protected int resolveNrOfInstances(DelegateExecution execution) {
|
||||
// 第一步,设置 collectionVariable 和 CollectionVariable
|
||||
// 从 execution.getVariable() 读取所有任务处理人的 key
|
||||
super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的
|
||||
super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());
|
||||
// 从 execution.getVariable() 读取当前所有任务处理的人的 key
|
||||
super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
|
||||
|
||||
// 第二步,获取任务的所有处理人
|
||||
List<String> assigneeUserIds = new ArrayList<>(taskCandidateInvoker.calculateUsers(execution)); // 保证有序!!!
|
||||
execution.setVariable(super.collectionVariable, assigneeUserIds);
|
||||
return assigneeUserIds.size();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.njcn.bpm.behavior;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.njcn.bpm.strategy.BpmTaskCandidateInvoker;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.common.engine.impl.el.ExpressionManager;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
|
||||
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
|
||||
import org.flowable.engine.impl.util.TaskHelper;
|
||||
import org.flowable.task.service.TaskService;
|
||||
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 自定义的【单个】流程任务的 assignee 负责人的分配
|
||||
* 第一步,基于分配规则,计算出分配任务的【单个】候选人。如果找不到,则直接报业务异常,不继续执行后续的流程;
|
||||
* 第二步,随机选择一个候选人,则选择作为 assignee 负责人。
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior {
|
||||
|
||||
@Setter
|
||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||
|
||||
public BpmUserTaskActivityBehavior(UserTask userTask) {
|
||||
super(userTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleAssignments(TaskService taskService, String assignee, String owner,
|
||||
List<String> candidateUsers, List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager,
|
||||
DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) {
|
||||
// 第一步,获得任务的候选用户
|
||||
String assigneeUserId = calculateTaskCandidateUsers(execution);
|
||||
Assert.notNull(assigneeUserId, "任务处理人不能为空");
|
||||
// 第二步,设置作为负责人
|
||||
TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId));
|
||||
}
|
||||
|
||||
private String calculateTaskCandidateUsers(DelegateExecution execution) {
|
||||
// 情况一,如果是多实例的任务,例如说会签、或签等情况,则从 Variable 中获取。
|
||||
// 顺序审批可见 BpmSequentialMultiInstanceBehavior,并发审批可见 BpmSequentialMultiInstanceBehavior
|
||||
if (super.multiInstanceActivityBehavior != null) {
|
||||
return execution.getVariable(super.multiInstanceActivityBehavior.getCollectionElementVariable(), String.class);
|
||||
}
|
||||
|
||||
// 情况二,如果非多实例的任务,则计算任务处理人
|
||||
// 第一步,先计算可处理该任务的处理人们
|
||||
List<String> candidateUserIds = taskCandidateInvoker.calculateUsers(execution);
|
||||
// 第二步,后随机选择一个任务的处理人
|
||||
// 疑问:为什么一定要选择一个任务处理人?
|
||||
// 解答:项目对 bpm 的任务是责任到人,所以每个任务有且仅有一个处理人。
|
||||
// 如果希望一个任务可以同时被多个人处理,可以考虑使用 BpmParallelMultiInstanceBehavior 实现的会签 or 或签。
|
||||
int index = RandomUtil.randomInt(candidateUserIds.size());
|
||||
return CollUtil.get(candidateUserIds, index);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.njcn.bpm.config;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import com.njcn.bpm.behavior.BpmActivityBehaviorFactory;
|
||||
import com.njcn.bpm.event.BpmProcessInstanceEventPublisher;
|
||||
import com.njcn.bpm.strategy.BpmTaskCandidateInvoker;
|
||||
import com.njcn.bpm.strategy.IBpmTaskCandidateStrategy;
|
||||
import com.njcn.user.api.UserFeignClient;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
|
||||
import org.flowable.spring.SpringProcessEngineConfiguration;
|
||||
import org.flowable.spring.boot.EngineConfigurationConfigurer;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.AsyncListenableTaskExecutor;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BPM 模块的 Flowable 配置类
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class BpmFlowableConfiguration {
|
||||
|
||||
/**
|
||||
* 参考 {@link org.flowable.spring.boot.FlowableJobConfiguration} 类,创建对应的 AsyncListenableTaskExecutor Bean
|
||||
*
|
||||
* 如果不创建,会导致项目启动时,Flowable 报错的问题
|
||||
*/
|
||||
@Bean(name = "applicationTaskExecutor")
|
||||
@ConditionalOnMissingBean(name = "applicationTaskExecutor")
|
||||
public AsyncListenableTaskExecutor taskExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(8);
|
||||
executor.setMaxPoolSize(8);
|
||||
executor.setQueueCapacity(100);
|
||||
executor.setThreadNamePrefix("flowable-task-Executor-");
|
||||
executor.setAwaitTerminationSeconds(30);
|
||||
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||
executor.setAllowCoreThreadTimeOut(true);
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* BPM 模块的 ProcessEngineConfigurationConfigurer 实现类:
|
||||
*
|
||||
* 1. 设置各种监听器
|
||||
* 2. 设置自定义的 ActivityBehaviorFactory 实现
|
||||
*/
|
||||
@Bean
|
||||
public EngineConfigurationConfigurer<SpringProcessEngineConfiguration> bpmProcessEngineConfigurationConfigurer(
|
||||
ObjectProvider<FlowableEventListener> listeners,
|
||||
BpmActivityBehaviorFactory bpmActivityBehaviorFactory) {
|
||||
return configuration -> {
|
||||
// 注册监听器,例如说 BpmActivityEventListener
|
||||
configuration.setEventListeners(ListUtil.toList(listeners.iterator()));
|
||||
// 设置 ActivityBehaviorFactory 实现类,用于流程任务的审核人的自定义
|
||||
configuration.setActivityBehaviorFactory(bpmActivityBehaviorFactory);
|
||||
};
|
||||
}
|
||||
|
||||
// =========== 审批人相关的 Bean ==========
|
||||
|
||||
@Bean
|
||||
public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskCandidateInvoker bpmTaskCandidateInvoker) {
|
||||
BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory();
|
||||
bpmActivityBehaviorFactory.setTaskCandidateInvoker(bpmTaskCandidateInvoker);
|
||||
return bpmActivityBehaviorFactory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") // adminUserApi 可以注入成功
|
||||
public BpmTaskCandidateInvoker bpmTaskCandidateInvoker(List<IBpmTaskCandidateStrategy> strategyList,
|
||||
UserFeignClient adminUserApi) {
|
||||
return new BpmTaskCandidateInvoker(strategyList, adminUserApi);
|
||||
}
|
||||
|
||||
// =========== 自己拓展的 Bean ==========
|
||||
|
||||
@Bean
|
||||
public BpmProcessInstanceEventPublisher processInstanceEventPublisher(ApplicationEventPublisher publisher) {
|
||||
return new BpmProcessInstanceEventPublisher(publisher);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.njcn.bpm.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.param.BpmCategoryParam;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.vo.BpmCategoryVO;
|
||||
import com.njcn.bpm.service.IBpmCategoryService;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.HttpResultUtil;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/bpm/category")
|
||||
@Validated
|
||||
@Slf4j
|
||||
@Api(tags = "流程分类控制器")
|
||||
@RequiredArgsConstructor
|
||||
public class BpmCategoryController extends BaseController {
|
||||
|
||||
private final IBpmCategoryService categoryService;
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD)
|
||||
@PostMapping("/add")
|
||||
@ApiOperation("创建流程分类")
|
||||
@ApiImplicitParam(name = "bpmCategoryParam", value = "流程分类数据", required = true)
|
||||
public HttpResult<String> add(@Valid @RequestBody BpmCategoryParam bpmCategoryParam) {
|
||||
String methodDescribe = getMethodDescribe("add");
|
||||
String categoryId = categoryService.createCategory(bpmCategoryParam);
|
||||
if (StrUtil.isNotBlank(categoryId)) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, categoryId, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@PostMapping("/update")
|
||||
@ApiOperation("更新流程分类")
|
||||
@ApiImplicitParam(name = "updateParam", value = "流程分类数据", required = true)
|
||||
public HttpResult<Object> update(@RequestBody @Validated BpmCategoryParam.BpmCategoryUpdateParam updateParam) {
|
||||
String methodDescribe = getMethodDescribe("update");
|
||||
categoryService.updateCategory(updateParam);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@PostMapping("/delete")
|
||||
@ApiOperation("删除流程分类")
|
||||
@ApiImplicitParam(name = "ids", value = "流程分类索引", required = true, dataTypeClass = List.class)
|
||||
public HttpResult<Object> delete(@RequestBody List<String> ids) {
|
||||
String methodDescribe = getMethodDescribe("delete");
|
||||
LogUtil.njcnDebug(log, "{},流程分类ID数据为:{}", methodDescribe, String.join(StrUtil.COMMA, ids));
|
||||
categoryService.deleteCategory(ids);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
@GetMapping("/getById")
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@Operation(summary = "获得流程分类")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
public HttpResult<BpmCategory> getById(String id) {
|
||||
String methodDescribe = getMethodDescribe("getById");
|
||||
BpmCategory bpmCategory = categoryService.getById(id);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, bpmCategory, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@PostMapping("/list")
|
||||
@ApiOperation("查询流程分类数据")
|
||||
@ApiImplicitParam(name = "bpmCategoryQueryParam", value = "查询参数", required = true)
|
||||
public HttpResult<Page<BpmCategoryVO>> list(@RequestBody BpmCategoryParam.BpmCategoryQueryParam bpmCategoryQueryParam) {
|
||||
String methodDescribe = getMethodDescribe("list");
|
||||
LogUtil.njcnDebug(log, "{},查询流程分类数据为:{}", methodDescribe, bpmCategoryQueryParam);
|
||||
Page<BpmCategoryVO> result = categoryService.getCategoryPage(bpmCategoryQueryParam);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/simpleList")
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@Operation(summary = "获得动态表单的精简列表", description = "用于表单下拉框")
|
||||
public HttpResult<List<BpmCategory>> getCategorySimpleList() {
|
||||
String methodDescribe = getMethodDescribe("getCategorySimpleList");
|
||||
List<BpmCategory> list = categoryService.getCategoryList();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.njcn.bpm.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.param.BpmFormParam;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.vo.BpmFormVO;
|
||||
import com.njcn.bpm.service.IBpmFormService;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.HttpResultUtil;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/bpm/form")
|
||||
@Validated
|
||||
@Slf4j
|
||||
@Api(tags = "流程表单控制器")
|
||||
@RequiredArgsConstructor
|
||||
public class BpmFormController extends BaseController {
|
||||
|
||||
|
||||
private final IBpmFormService formService;
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@PostMapping("/list")
|
||||
@ApiOperation("查询流程表单数据")
|
||||
@ApiImplicitParam(name = "bpmFormQueryParam", value = "查询参数", required = true)
|
||||
public HttpResult<Page<BpmFormVO>> list(@RequestBody BpmFormParam.BpmFormQueryParam bpmFormQueryParam) {
|
||||
String methodDescribe = getMethodDescribe("list");
|
||||
LogUtil.njcnDebug(log, "{},查询流程表单数据为:{}", methodDescribe, bpmFormQueryParam);
|
||||
Page<BpmFormVO> result = formService.getFormPage(bpmFormQueryParam);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD)
|
||||
@PostMapping("/add")
|
||||
@ApiOperation("新增流程表单")
|
||||
@ApiImplicitParam(name = "bpmFormParam", value = "流程表单数据", required = true)
|
||||
public HttpResult<String> add(@Valid @RequestBody BpmFormParam bpmFormParam) {
|
||||
String methodDescribe = getMethodDescribe("add");
|
||||
String wfFormId = formService.createForm(bpmFormParam);
|
||||
if (StrUtil.isNotBlank(wfFormId)) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, wfFormId, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@PostMapping("/update")
|
||||
@ApiOperation("更新流程表单")
|
||||
@ApiImplicitParam(name = "updateParam", value = "流程表单数据", required = true)
|
||||
public HttpResult<Object> update(@RequestBody @Validated BpmFormParam.BpmFormUpdateParam updateParam) {
|
||||
String methodDescribe = getMethodDescribe("update");
|
||||
formService.updateForm(updateParam);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@PostMapping("/delete")
|
||||
@ApiOperation("删除流程表单")
|
||||
@ApiImplicitParam(name = "ids", value = "流程表单索引", required = true, dataTypeClass = List.class)
|
||||
public HttpResult<Object> delete(@RequestBody List<String> ids) {
|
||||
String methodDescribe = getMethodDescribe("delete");
|
||||
LogUtil.njcnDebug(log, "{},流程表单ID数据为:{}", methodDescribe, String.join(StrUtil.COMMA, ids));
|
||||
formService.deleteForm(ids);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@GetMapping("/getById")
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@Operation(summary = "获得动态表单")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
public HttpResult<BpmForm> getById(String id) {
|
||||
String methodDescribe = getMethodDescribe("getById");
|
||||
BpmForm form = formService.getById(id);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, form, methodDescribe);
|
||||
}
|
||||
|
||||
@GetMapping("/simpleList")
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@Operation(summary = "获得动态表单的精简列表", description = "用于表单下拉框")
|
||||
public HttpResult<List<BpmForm>> getFormSimpleList() {
|
||||
String methodDescribe = getMethodDescribe("getFormSimpleList");
|
||||
List<BpmForm> list = formService.getFormList();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, list, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package com.njcn.bpm.controller;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO;
|
||||
import com.njcn.bpm.pojo.param.BpmCategoryParam;
|
||||
import com.njcn.bpm.pojo.param.BpmModelParam;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.vo.BpmModelRespVO;
|
||||
import com.njcn.bpm.service.IBpmCategoryService;
|
||||
import com.njcn.bpm.service.IBpmModelService;
|
||||
import com.njcn.bpm.service.IBpmFormService;
|
||||
import com.njcn.bpm.service.IBpmProcessDefinitionService;
|
||||
import com.njcn.bpm.utils.BpmModelConvert;
|
||||
import com.njcn.bpm.utils.CollectionUtils;
|
||||
import com.njcn.bpm.utils.JsonUtils;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.HttpResultUtil;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.Model;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.njcn.bpm.pojo.dto.CommonResult.success;
|
||||
import static com.njcn.bpm.utils.CollectionUtils.convertMap;
|
||||
import static com.njcn.bpm.utils.CollectionUtils.convertSet;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/bpm/model")
|
||||
@Validated
|
||||
@Slf4j
|
||||
@Api(tags = "流程模型控制器")
|
||||
@RequiredArgsConstructor
|
||||
public class BpmModelController extends BaseController {
|
||||
|
||||
private final IBpmModelService modelService;
|
||||
|
||||
private final IBpmFormService formService;
|
||||
|
||||
private final IBpmCategoryService categoryService;
|
||||
|
||||
private final IBpmProcessDefinitionService processDefinitionService;
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@PostMapping("/list")
|
||||
@ApiOperation("获得模型分页")
|
||||
@ApiImplicitParam(name = "bpmModelQueryParam", value = "查询参数", required = true)
|
||||
public HttpResult<Page<BpmModelRespVO>> getModelPage(@RequestBody BpmModelParam.BpmModelQueryParam bpmModelQueryParam) {
|
||||
String methodDescribe = getMethodDescribe("list");
|
||||
LogUtil.njcnDebug(log, "{},查询流程表单数据为:{}", methodDescribe, bpmModelQueryParam);
|
||||
Page<Model> pageResult = modelService.getModelPage(bpmModelQueryParam);
|
||||
if (CollUtil.isEmpty(pageResult.getRecords())) {
|
||||
Page<BpmModelRespVO> result = new Page<>();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
// 拼接数据
|
||||
// 获得 Form 表单
|
||||
Set<String> formIds = convertSet(pageResult.getRecords(), model -> {
|
||||
BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
|
||||
return metaInfo != null ? metaInfo.getFormId() : null;
|
||||
});
|
||||
Map<String, BpmForm> formMap = formService.getFormMap(formIds);
|
||||
// 获得 Category Map
|
||||
Map<String, BpmCategory> categoryMap = categoryService.getCategoryMap(
|
||||
convertSet(pageResult.getRecords(), Model::getCategory));
|
||||
// 获得 Deployment Map
|
||||
Set<String> deploymentIds = new HashSet<>();
|
||||
pageResult.getRecords().forEach(model -> CollectionUtils.addIfNotNull(deploymentIds, model.getDeploymentId()));
|
||||
|
||||
Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(deploymentIds);
|
||||
// 获得 ProcessDefinition Map
|
||||
List<ProcessDefinition> processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds(deploymentIds);
|
||||
Map<String, ProcessDefinition> processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmModelConvert.INSTANCE.buildModelPage(pageResult, formMap, categoryMap, deploymentMap, processDefinitionMap), methodDescribe);
|
||||
}
|
||||
|
||||
@GetMapping("/getById")
|
||||
@Operation(summary = "获得模型")
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
public HttpResult<BpmModelRespVO> getById(String id) {
|
||||
Model model = modelService.getModel(id);
|
||||
String methodDescribe = getMethodDescribe("getById");
|
||||
if (model == null) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
byte[] bpmnBytes = modelService.getModelBpmnXML(id);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmModelConvert.INSTANCE.buildModel(model, bpmnBytes), methodDescribe);
|
||||
}
|
||||
|
||||
@Operation(summary = "新建模型")
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD)
|
||||
@PostMapping("/add")
|
||||
@ApiOperation("新建模型")
|
||||
@ApiImplicitParam(name = "createRetVO", value = "模型数据", required = true)
|
||||
public HttpResult<String> createModel(@Validated @RequestBody BpmModelParam bpmModelParam) {
|
||||
String methodDescribe = getMethodDescribe("createModel");
|
||||
String modelId = modelService.createModel(bpmModelParam, null);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, modelId, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@PostMapping("/update")
|
||||
@ApiOperation("修改模型")
|
||||
@ApiImplicitParam(name = "updateParam", value = "流程分类数据", required = true)
|
||||
public HttpResult<Object> update( @Validated @RequestBody BpmModelParam.BpmModelUpdateParam updateParam) {
|
||||
String methodDescribe = getMethodDescribe("update");
|
||||
modelService.updateModel(updateParam);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
// @PostMapping("/import")
|
||||
// @Operation(summary = "导入模型")
|
||||
// @PreAuthorize("@ss.hasPermission('bpm:model:import')")
|
||||
// public CommonResult<String> importModel(@Valid BpmModeImportReqVO importReqVO) throws IOException {
|
||||
// BpmModelCreateReqVO createReqVO = BeanUtils.toBean(importReqVO, BpmModelCreateReqVO.class);
|
||||
// // 读取文件
|
||||
// String bpmnXml = IoUtils.readUtf8(importReqVO.getBpmnFile().getInputStream(), false);
|
||||
// return success(modelService.createModel(createReqVO, bpmnXml));
|
||||
// }
|
||||
|
||||
|
||||
@PostMapping("/deploy")
|
||||
@Operation(summary = "部署模型")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
public HttpResult<Boolean> deployModel(String id) {
|
||||
String methodDescribe = getMethodDescribe("deployModel");
|
||||
modelService.deployModel(id);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
//
|
||||
// @PutMapping("/update-state")
|
||||
// @Operation(summary = "修改模型的状态", description = "实际更新的部署的流程定义的状态")
|
||||
// @PreAuthorize("@ss.hasPermission('bpm:model:update')")
|
||||
// public CommonResult<Boolean> updateModelState(@Valid @RequestBody BpmModelUpdateStateReqVO reqVO) {
|
||||
// modelService.updateModelState(reqVO.getId(), reqVO.getState());
|
||||
// return success(true);
|
||||
// }
|
||||
//
|
||||
// @DeleteMapping("/delete")
|
||||
// @Operation(summary = "删除模型")
|
||||
// @Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
// @PreAuthorize("@ss.hasPermission('bpm:model:delete')")
|
||||
// public CommonResult<Boolean> deleteModel(@RequestParam("id") String id) {
|
||||
// modelService.deleteModel(id);
|
||||
// return success(true);
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.njcn.bpm.controller;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.param.BpmProcessDefinitionInfoParam;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
|
||||
import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO;
|
||||
import com.njcn.bpm.service.IBpmCategoryService;
|
||||
import com.njcn.bpm.service.IBpmFormService;
|
||||
import com.njcn.bpm.service.IBpmProcessDefinitionService;
|
||||
import com.njcn.bpm.strategy.BpmTaskCandidateStartUserSelectStrategy;
|
||||
import com.njcn.bpm.utils.BpmProcessDefinitionConvert;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.HttpResultUtil;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.njcn.bpm.utils.CollectionUtils.convertSet;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/bpm/processDefinition")
|
||||
@Validated
|
||||
@Slf4j
|
||||
@Api(tags = "管理后台 - 流程定义")
|
||||
@RequiredArgsConstructor
|
||||
public class BpmProcessDefinitionController extends BaseController {
|
||||
|
||||
private final IBpmProcessDefinitionService processDefinitionService;
|
||||
|
||||
private final IBpmFormService formService;
|
||||
|
||||
private final IBpmCategoryService categoryService;
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@PostMapping("/page")
|
||||
@ApiOperation("获得流程定义分页")
|
||||
@ApiImplicitParam(name = "bpmProcessDefinitionInfoQueryParam", value = "查询参数", required = true)
|
||||
public HttpResult<Page<BpmProcessDefinitionInfoVO>> getProcessDefinitionPage(
|
||||
BpmProcessDefinitionInfoParam.BpmProcessDefinitionInfoQueryParam bpmProcessDefinitionInfoQueryParam) {
|
||||
String methodDescribe = getMethodDescribe("getProcessDefinitionPage");
|
||||
Page<ProcessDefinition> pageResult = processDefinitionService.getProcessDefinitionPage(bpmProcessDefinitionInfoQueryParam);
|
||||
if (CollUtil.isEmpty(pageResult.getRecords())) {
|
||||
Page<BpmProcessDefinitionInfoVO> result = new Page<>();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
// 获得 Category Map
|
||||
Map<String, BpmCategory> categoryMap = categoryService.getCategoryMap(
|
||||
convertSet(pageResult.getRecords(), ProcessDefinition::getCategory));
|
||||
// 获得 Deployment Map
|
||||
Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(
|
||||
convertSet(pageResult.getRecords(), ProcessDefinition::getDeploymentId));
|
||||
// 获得 BpmProcessDefinitionInfoDO Map
|
||||
Map<String, BpmProcessDefinitionInfo> processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap(
|
||||
convertSet(pageResult.getRecords(), ProcessDefinition::getId));
|
||||
// 获得 Form Map
|
||||
Map<String, BpmForm> formMap = formService.getFormMap(
|
||||
convertSet(processDefinitionMap.values(), BpmProcessDefinitionInfo::getFormId));
|
||||
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionPage(
|
||||
pageResult, deploymentMap, processDefinitionMap, formMap, categoryMap), methodDescribe);
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得流程定义列表")
|
||||
@Parameter(name = "suspensionState", description = "挂起状态", required = true) // 参见 Flowable SuspensionState 枚举
|
||||
public HttpResult<List<BpmProcessDefinitionInfoVO>> getProcessDefinitionList(Integer suspensionState) {
|
||||
String methodDescribe = getMethodDescribe("getProcessDefinitionList");
|
||||
List<ProcessDefinition> list = processDefinitionService.getProcessDefinitionListBySuspensionState(suspensionState);
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, new ArrayList<>(), methodDescribe);
|
||||
}
|
||||
|
||||
// 获得 BpmProcessDefinitionInfoDO Map
|
||||
Map<String, BpmProcessDefinitionInfo> processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap(
|
||||
convertSet(list, ProcessDefinition::getId));
|
||||
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionList(
|
||||
list, null, processDefinitionMap, null, null), methodDescribe);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得流程定义")
|
||||
@Parameter(name = "id", description = "流程编号", required = true, example = "1024")
|
||||
@Parameter(name = "key", description = "流程定义标识", required = true, example = "1024")
|
||||
public HttpResult<BpmProcessDefinitionInfoVO> getProcessDefinition(
|
||||
@RequestParam(value = "id", required = false) String id,
|
||||
@RequestParam(value = "key", required = false) String key) {
|
||||
String methodDescribe = getMethodDescribe("getProcessDefinition");
|
||||
ProcessDefinition processDefinition = id != null ? processDefinitionService.getProcessDefinition(id)
|
||||
: processDefinitionService.getActiveProcessDefinition(key);
|
||||
if (processDefinition == null) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId());
|
||||
List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(
|
||||
processDefinition, null, null, null, null, bpmnModel, userTaskList), methodDescribe);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.njcn.bpm.event;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Validated
|
||||
public class BpmProcessInstanceEventPublisher {
|
||||
|
||||
private final ApplicationEventPublisher publisher;
|
||||
|
||||
public void sendProcessInstanceResultEvent(@Valid BpmProcessInstanceStatusEvent event) {
|
||||
publisher.publishEvent(event);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.njcn.bpm.event;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 流程实例的状态(结果)发生变化的 Event
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@SuppressWarnings("ALL")
|
||||
@Data
|
||||
public class BpmProcessInstanceStatusEvent extends ApplicationEvent {
|
||||
|
||||
/**
|
||||
* 流程实例的编号
|
||||
*/
|
||||
@NotNull(message = "流程实例的编号不能为空")
|
||||
private String id;
|
||||
/**
|
||||
* 流程实例的 key
|
||||
*/
|
||||
@NotNull(message = "流程实例的 key 不能为空")
|
||||
private String processDefinitionKey;
|
||||
/**
|
||||
* 流程实例的结果
|
||||
*/
|
||||
@NotNull(message = "流程实例的状态不能为空")
|
||||
private Integer status;
|
||||
/**
|
||||
* 流程实例对应的业务标识
|
||||
* 例如说,请假
|
||||
*/
|
||||
private String businessKey;
|
||||
|
||||
public BpmProcessInstanceStatusEvent(Object source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.njcn.bpm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.vo.BpmCategoryVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
|
||||
/**
|
||||
* BPM 流程分类 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface BpmCategoryMapper extends BaseMapper<BpmCategory> {
|
||||
|
||||
|
||||
Page<BpmCategoryVO> page(@Param("page") Page<Object> objectPage, @Param("ew") QueryWrapper<BpmCategoryVO> categoryVOQueryWrapper);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.njcn.bpm.mapper;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.vo.BpmFormVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 动态表单 Mapper
|
||||
*
|
||||
* @author 风里雾里
|
||||
*/
|
||||
@Mapper
|
||||
public interface BpmFormMapper extends BaseMapper<BpmForm> {
|
||||
Page<BpmFormVO> page(@Param("page")Page<Object> objectPage, @Param("ew") QueryWrapper<BpmFormVO> categoryVOQueryWrapper);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.njcn.bpm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
|
||||
@Mapper
|
||||
public interface BpmProcessDefinitionInfoMapper extends BaseMapper<BpmProcessDefinitionInfo> {
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.bpm.mapper.BpmCategoryMapper">
|
||||
|
||||
<!--获取流程表单分页列表-->
|
||||
<select id="page" resultType="BpmCategoryVO">
|
||||
SELECT
|
||||
bpm_category.id,
|
||||
bpm_category.name,
|
||||
bpm_category.code,
|
||||
bpm_category.status,
|
||||
bpm_category.create_time,
|
||||
bpm_category.remark
|
||||
FROM bpm_category bpm_category
|
||||
WHERE ${ew.sqlSegment}
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.bpm.mapper.BpmFormMapper">
|
||||
|
||||
<!--获取流程表单分页列表-->
|
||||
<select id="page" resultType="BpmFormVO">
|
||||
SELECT
|
||||
bpm_form.id,
|
||||
bpm_form.name,
|
||||
bpm_form.status,
|
||||
bpm_form.create_time,
|
||||
bpm_form.remark
|
||||
FROM bpm_form bpm_form
|
||||
WHERE ${ew.sqlSegment}
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.njcn.bpm.service;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.bpm.pojo.param.BpmCategoryParam;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.vo.BpmCategoryVO;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.njcn.bpm.utils.CollectionUtils.convertMap;
|
||||
|
||||
|
||||
/**
|
||||
* BPM 流程分类 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface IBpmCategoryService extends IService<BpmCategory> {
|
||||
|
||||
/**
|
||||
* 创建流程分类
|
||||
*
|
||||
* @return 编号
|
||||
*/
|
||||
String createCategory( BpmCategoryParam bpmCategoryParam);
|
||||
|
||||
/**
|
||||
* 更新流程分类
|
||||
*
|
||||
*/
|
||||
void updateCategory( BpmCategoryParam.BpmCategoryUpdateParam updateParam);
|
||||
|
||||
/**
|
||||
* 删除流程分类
|
||||
*/
|
||||
void deleteCategory(List<String> ids);
|
||||
|
||||
|
||||
/**
|
||||
* 获得流程分类分页
|
||||
*
|
||||
* @return 流程分类分页
|
||||
*/
|
||||
Page<BpmCategoryVO> getCategoryPage(BpmCategoryParam.BpmCategoryQueryParam bpmCategoryQueryParam);
|
||||
|
||||
/**
|
||||
* 获得流程分类 Map,基于指定编码
|
||||
*
|
||||
* @param codes 编号数组
|
||||
* @return 流程分类 Map
|
||||
*/
|
||||
default Map<String, BpmCategory> getCategoryMap(Collection<String> codes) {
|
||||
return convertMap(getCategoryListByCode(codes), BpmCategory::getCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程分类列表,基于指定编码
|
||||
*
|
||||
* @return 流程分类列表
|
||||
*/
|
||||
List<BpmCategory> getCategoryListByCode(Collection<String> codes);
|
||||
|
||||
|
||||
|
||||
List<BpmCategory> getCategoryList();
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.njcn.bpm.service;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.bpm.pojo.param.BpmFormParam;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.vo.BpmFormVO;
|
||||
import com.njcn.bpm.utils.CollectionUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* 动态表单 Service 接口
|
||||
*
|
||||
* @author @风里雾里
|
||||
*/
|
||||
public interface IBpmFormService extends IService<BpmForm> {
|
||||
|
||||
/**
|
||||
* 创建动态表单
|
||||
*
|
||||
* @return 编号
|
||||
*/
|
||||
String createForm(BpmFormParam bpmFormParam);
|
||||
|
||||
/**
|
||||
* 更新动态表单
|
||||
*
|
||||
*/
|
||||
void updateForm(BpmFormParam.BpmFormUpdateParam updateParam);
|
||||
|
||||
/**
|
||||
* 删除动态表单
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteForm(List<String> id);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获得动态表单列表
|
||||
*
|
||||
* @return 动态表单列表
|
||||
*/
|
||||
List<BpmForm> getFormList();
|
||||
|
||||
/**
|
||||
* 获得动态表单列表
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 动态表单列表
|
||||
*/
|
||||
List<BpmForm> getFormList(Collection<String> ids);
|
||||
|
||||
/**
|
||||
* 获得动态表单 Map
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 动态表单 Map
|
||||
*/
|
||||
default Map<String, BpmForm> getFormMap(Collection<String> ids) {
|
||||
return CollectionUtils.convertMap(this.getFormList(ids), BpmForm::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得动态表单分页
|
||||
*
|
||||
* @return 动态表单分页
|
||||
*/
|
||||
Page<BpmFormVO> getFormPage(BpmFormParam.BpmFormQueryParam bpmFormQueryParam);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.njcn.bpm.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.param.BpmModelParam;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.engine.repository.Model;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* Flowable流程模型接口
|
||||
*
|
||||
* @author yunlongn
|
||||
*/
|
||||
public interface IBpmModelService {
|
||||
|
||||
/**
|
||||
* 获得流程模型分页
|
||||
*
|
||||
* @return 流程模型分页
|
||||
*/
|
||||
Page<Model> getModelPage(BpmModelParam.BpmModelQueryParam bpmModelQueryParam);
|
||||
|
||||
/**
|
||||
* 创建流程模型
|
||||
*
|
||||
* @param bpmnXml BPMN XML
|
||||
* @return 创建的流程模型的编号
|
||||
*/
|
||||
String createModel(BpmModelParam bpmModelParam, String bpmnXml);
|
||||
|
||||
/**
|
||||
* 获得流程模块
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 流程模型
|
||||
*/
|
||||
Model getModel(String id);
|
||||
|
||||
/**
|
||||
* 获得流程模型的 BPMN XML
|
||||
*
|
||||
* @param id 编号
|
||||
* @return BPMN XML
|
||||
*/
|
||||
byte[] getModelBpmnXML(String id);
|
||||
|
||||
/**
|
||||
* 修改流程模型
|
||||
*
|
||||
*/
|
||||
void updateModel(BpmModelParam.BpmModelUpdateParam updateParam);
|
||||
|
||||
/**
|
||||
* 将流程模型,部署成一个流程定义
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deployModel(String id);
|
||||
|
||||
/**
|
||||
* 删除模型
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteModel(String id);
|
||||
|
||||
/**
|
||||
* 修改模型的状态,实际更新的部署的流程定义的状态
|
||||
*
|
||||
* @param id 编号
|
||||
* @param state 状态
|
||||
*/
|
||||
void updateModelState(String id, Integer state);
|
||||
|
||||
/**
|
||||
* 获得流程定义编号对应的 BPMN Model
|
||||
*
|
||||
* @param processDefinitionId 流程定义编号
|
||||
* @return BPMN Model
|
||||
*/
|
||||
BpmnModel getBpmnModelByDefinitionId(String processDefinitionId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.njcn.bpm.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO;
|
||||
import com.njcn.bpm.pojo.param.BpmProcessDefinitionInfoParam;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.Model;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.njcn.bpm.utils.CollectionUtils.convertMap;
|
||||
|
||||
|
||||
/**
|
||||
* Flowable流程定义接口
|
||||
*
|
||||
* @author yunlong.li
|
||||
* @author ZJQ
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface IBpmProcessDefinitionService extends IService<BpmProcessDefinitionInfo> {
|
||||
|
||||
/**
|
||||
* 获得流程定义分页
|
||||
*
|
||||
* @return 流程定义 Page
|
||||
*/
|
||||
Page<ProcessDefinition> getProcessDefinitionPage(BpmProcessDefinitionInfoParam.BpmProcessDefinitionInfoQueryParam bpmProcessDefinitionInfoQueryParam);
|
||||
|
||||
/**
|
||||
* 获得流程定义列表
|
||||
*
|
||||
* @param suspensionState 中断状态
|
||||
* @return 流程定义列表
|
||||
*/
|
||||
List<ProcessDefinition> getProcessDefinitionListBySuspensionState(Integer suspensionState);
|
||||
|
||||
/**
|
||||
* 基于流程模型,创建流程定义
|
||||
*
|
||||
* @param model 流程模型
|
||||
* @param modelMetaInfo 流程模型元信息
|
||||
* @param bpmnBytes BPMN XML 字节数组
|
||||
* @param form 表单
|
||||
* @return 流程编号
|
||||
*/
|
||||
String createProcessDefinition(Model model, BpmModelMetaInfoRespDTO modelMetaInfo, byte[] bpmnBytes, BpmForm form);
|
||||
|
||||
/**
|
||||
* 更新流程定义状态
|
||||
*
|
||||
* @param id 流程定义的编号
|
||||
* @param state 状态
|
||||
*/
|
||||
void updateProcessDefinitionState(String id, Integer state);
|
||||
|
||||
/**
|
||||
* 获得流程定义对应的 BPMN
|
||||
*
|
||||
* @param id 流程定义编号
|
||||
* @return BPMN
|
||||
*/
|
||||
BpmnModel getProcessDefinitionBpmnModel(String id);
|
||||
|
||||
/**
|
||||
* 获得流程定义的信息
|
||||
*
|
||||
* @param id 流程定义编号
|
||||
* @return 流程定义信息
|
||||
*/
|
||||
BpmProcessDefinitionInfo getProcessDefinitionInfo(String id);
|
||||
|
||||
/**
|
||||
* 获得流程定义的信息 List
|
||||
*
|
||||
* @param ids 流程定义编号数组
|
||||
* @return 流程额定义信息数组
|
||||
*/
|
||||
List<BpmProcessDefinitionInfo> getProcessDefinitionInfoList(Collection<String> ids);
|
||||
|
||||
default Map<String, BpmProcessDefinitionInfo> getProcessDefinitionInfoMap(Set<String> ids) {
|
||||
return convertMap(getProcessDefinitionInfoList(ids), BpmProcessDefinitionInfo::getProcessDefinitionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程定义编号对应的 ProcessDefinition
|
||||
*
|
||||
* @param id 流程定义编号
|
||||
* @return 流程定义
|
||||
*/
|
||||
ProcessDefinition getProcessDefinition(String id);
|
||||
|
||||
/**
|
||||
* 获得 ids 对应的 ProcessDefinition 数组
|
||||
*
|
||||
* @param ids 编号的数组
|
||||
* @return 流程定义的数组
|
||||
*/
|
||||
List<ProcessDefinition> getProcessDefinitionList(Set<String> ids);
|
||||
|
||||
default Map<String, ProcessDefinition> getProcessDefinitionMap(Set<String> ids) {
|
||||
return convertMap(getProcessDefinitionList(ids), ProcessDefinition::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 deploymentId 对应的 ProcessDefinition
|
||||
*
|
||||
* @param deploymentId 部署编号
|
||||
* @return 流程定义
|
||||
*/
|
||||
ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId);
|
||||
|
||||
/**
|
||||
* 获得 deploymentIds 对应的 ProcessDefinition 数组
|
||||
*
|
||||
* @param deploymentIds 部署编号的数组
|
||||
* @return 流程定义的数组
|
||||
*/
|
||||
List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds);
|
||||
|
||||
/**
|
||||
* 获得流程定义标识对应的激活的流程定义
|
||||
*
|
||||
* @param key 流程定义的标识
|
||||
* @return 流程定义
|
||||
*/
|
||||
ProcessDefinition getActiveProcessDefinition(String key);
|
||||
|
||||
/**
|
||||
* 获得 ids 对应的 Deployment Map
|
||||
*
|
||||
* @param ids 部署编号的数组
|
||||
* @return 流程部署 Map
|
||||
*/
|
||||
default Map<String, Deployment> getDeploymentMap(Set<String> ids) {
|
||||
return convertMap(getDeploymentList(ids), Deployment::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 ids 对应的 Deployment 数组
|
||||
*
|
||||
* @param ids 部署编号的数组
|
||||
* @return 流程部署的数组
|
||||
*/
|
||||
List<Deployment> getDeploymentList(Set<String> ids);
|
||||
|
||||
/**
|
||||
* 获得 id 对应的 Deployment
|
||||
*
|
||||
* @param id 部署编号
|
||||
* @return 流程部署
|
||||
*/
|
||||
Deployment getDeployment(String id);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.njcn.bpm.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.bpm.enums.BpmResponseEnum;
|
||||
import com.njcn.bpm.mapper.BpmCategoryMapper;
|
||||
import com.njcn.bpm.pojo.param.BpmCategoryParam;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.vo.BpmCategoryVO;
|
||||
import com.njcn.bpm.pojo.vo.BpmFormVO;
|
||||
import com.njcn.bpm.service.IBpmCategoryService;
|
||||
import com.njcn.bpm.utils.BeanUtils;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BPM 流程分类 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class BpmCategoryServiceImpl extends ServiceImpl<BpmCategoryMapper, BpmCategory> implements IBpmCategoryService {
|
||||
|
||||
|
||||
@Override
|
||||
public String createCategory(BpmCategoryParam bpmCategoryParam) {
|
||||
// 校验唯一
|
||||
checkCategoryName(bpmCategoryParam, false);
|
||||
checkCategoryCode(bpmCategoryParam, false);
|
||||
// 插入
|
||||
BpmCategory category = BeanUtils.toBean(bpmCategoryParam, BpmCategory.class);
|
||||
this.baseMapper.insert(category);
|
||||
return category.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCategory(BpmCategoryParam.BpmCategoryUpdateParam updateParam) {
|
||||
// 校验唯一
|
||||
checkCategoryName(updateParam, true);
|
||||
checkCategoryCode(updateParam, true);
|
||||
// 更新
|
||||
BpmCategory updateObj = BeanUtils.toBean(updateParam, BpmCategory.class);
|
||||
this.baseMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void deleteCategory(List<String> ids) {
|
||||
this.lambdaUpdate().set(BpmCategory::getState, DataStateEnum.DELETED.getCode())
|
||||
.in(BpmCategory::getId, ids)
|
||||
.update();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Page<BpmCategoryVO> getCategoryPage(BpmCategoryParam.BpmCategoryQueryParam bpmCategoryQueryParam) {
|
||||
QueryWrapper<BpmCategoryVO> categoryVOQueryWrapper = new QueryWrapper<>();
|
||||
if (StrUtil.isNotBlank(bpmCategoryQueryParam.getName())) {
|
||||
categoryVOQueryWrapper.like("bpm_category.name", bpmCategoryQueryParam.getName());
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(bpmCategoryQueryParam.getCode())) {
|
||||
categoryVOQueryWrapper.like("bpm_category.name", bpmCategoryQueryParam.getCode());
|
||||
}
|
||||
categoryVOQueryWrapper.eq("bpm_category.state", DataStateEnum.ENABLE.getCode());
|
||||
categoryVOQueryWrapper.orderByDesc("bpm_category.update_time");
|
||||
return this.baseMapper.page(new Page<>(PageFactory.getPageNum(bpmCategoryQueryParam), PageFactory.getPageSize(bpmCategoryQueryParam)), categoryVOQueryWrapper);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BpmCategory> getCategoryListByCode(Collection<String> codes) {
|
||||
if (CollUtil.isEmpty(codes)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
LambdaQueryWrapper<BpmCategory> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.in(BpmCategory::getCode,codes);
|
||||
return this.baseMapper.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BpmCategory> getCategoryList() {
|
||||
LambdaQueryWrapper<BpmCategory> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.select(BpmCategory::getId,BpmCategory::getName)
|
||||
.eq(BpmCategory::getState,DataStateEnum.ENABLE.getCode());
|
||||
return this.baseMapper.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
|
||||
private void checkCategoryName(BpmCategoryParam bpmCategoryParam, boolean isExcludeSelf) {
|
||||
LambdaQueryWrapper<BpmCategory> categoryLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
//判断流程表单的名称
|
||||
categoryLambdaQueryWrapper
|
||||
.eq(BpmCategory::getName, bpmCategoryParam.getName())
|
||||
.eq(BpmCategory::getState, DataStateEnum.ENABLE.getCode());
|
||||
//更新的时候,需排除当前记录
|
||||
if (isExcludeSelf) {
|
||||
if (bpmCategoryParam instanceof BpmCategoryParam.BpmCategoryUpdateParam) {
|
||||
categoryLambdaQueryWrapper.ne(BpmCategory::getId, ((BpmCategoryParam.BpmCategoryUpdateParam) bpmCategoryParam).getId());
|
||||
}
|
||||
}
|
||||
int nameCountByAccount = this.count(categoryLambdaQueryWrapper);
|
||||
//大于等于1个则表示重复
|
||||
if (nameCountByAccount >= 1) {
|
||||
throw new BusinessException(BpmResponseEnum.REPEAT_CATEGORY_NAME_FORM);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkCategoryCode(BpmCategoryParam bpmCategoryParam, boolean isExcludeSelf) {
|
||||
LambdaQueryWrapper<BpmCategory> categoryLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
//判断流程表单的名称
|
||||
categoryLambdaQueryWrapper
|
||||
.eq(BpmCategory::getCode, bpmCategoryParam.getCode())
|
||||
.eq(BpmCategory::getState, DataStateEnum.ENABLE.getCode());
|
||||
//更新的时候,需排除当前记录
|
||||
if (isExcludeSelf) {
|
||||
if (bpmCategoryParam instanceof BpmCategoryParam.BpmCategoryUpdateParam) {
|
||||
categoryLambdaQueryWrapper.ne(BpmCategory::getId, ((BpmCategoryParam.BpmCategoryUpdateParam) bpmCategoryParam).getId());
|
||||
}
|
||||
}
|
||||
int nameCountByAccount = this.count(categoryLambdaQueryWrapper);
|
||||
//大于等于1个则表示重复
|
||||
if (nameCountByAccount >= 1) {
|
||||
throw new BusinessException(BpmResponseEnum.REPEAT_CATEGORY_NAME_FORM);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.njcn.bpm.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.bpm.enums.BpmResponseEnum;
|
||||
import com.njcn.bpm.mapper.BpmFormMapper;
|
||||
import com.njcn.bpm.pojo.param.BpmFormParam;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.vo.BpmFormVO;
|
||||
import com.njcn.bpm.service.IBpmFormService;
|
||||
import com.njcn.bpm.utils.BeanUtils;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* 动态表单 Service 实现类
|
||||
*
|
||||
* @author 风里雾里
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class BpmFormServiceImpl extends ServiceImpl<BpmFormMapper, BpmForm> implements IBpmFormService {
|
||||
|
||||
@Override
|
||||
public Page<BpmFormVO> getFormPage(BpmFormParam.BpmFormQueryParam bpmFormQueryParam) {
|
||||
QueryWrapper<BpmFormVO> bpmFormVOQueryWrapper = new QueryWrapper<>();
|
||||
if (StrUtil.isNotBlank(bpmFormQueryParam.getName())) {
|
||||
bpmFormVOQueryWrapper.like("bpm_form.name", bpmFormQueryParam.getName());
|
||||
}
|
||||
bpmFormVOQueryWrapper.eq("bpm_form.state", DataStateEnum.ENABLE.getCode());
|
||||
bpmFormVOQueryWrapper.orderByDesc("bpm_form.update_time");
|
||||
return this.baseMapper.page(new Page<>(PageFactory.getPageNum(bpmFormQueryParam), PageFactory.getPageSize(bpmFormQueryParam)), bpmFormVOQueryWrapper);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String createForm(BpmFormParam bpmFormParam) {
|
||||
// this.validateFields(createReqVO.getFields());
|
||||
checkFormName(bpmFormParam, false);
|
||||
// 插入
|
||||
BpmForm form = BeanUtils.toBean(bpmFormParam, BpmForm.class);
|
||||
this.baseMapper.insert(form);
|
||||
// 返回
|
||||
return form.getId();
|
||||
}
|
||||
|
||||
private void checkFormName(BpmFormParam bpmFormParam, boolean isExcludeSelf) {
|
||||
LambdaQueryWrapper<BpmForm> categoryLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
//判断流程表单的名称
|
||||
categoryLambdaQueryWrapper
|
||||
.eq(BpmForm::getName, bpmFormParam.getName())
|
||||
.eq(BpmForm::getState, DataStateEnum.ENABLE.getCode());
|
||||
//更新的时候,需排除当前记录
|
||||
if (isExcludeSelf) {
|
||||
if (bpmFormParam instanceof BpmFormParam.BpmFormUpdateParam) {
|
||||
categoryLambdaQueryWrapper.ne(BpmForm::getId, ((BpmFormParam.BpmFormUpdateParam) bpmFormParam).getId());
|
||||
}
|
||||
}
|
||||
int nameCountByAccount = this.count(categoryLambdaQueryWrapper);
|
||||
//大于等于1个则表示重复
|
||||
if (nameCountByAccount >= 1) {
|
||||
throw new BusinessException(BpmResponseEnum.REPEAT_NAME_FORM);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateForm(BpmFormParam.BpmFormUpdateParam updateParam) {
|
||||
checkFormName(updateParam, true);
|
||||
BpmForm updateObj = BeanUtils.toBean(updateParam, BpmForm.class);
|
||||
this.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteForm(List<String> ids) {
|
||||
this.lambdaUpdate().set(BpmForm::getState, DataStateEnum.DELETED.getCode())
|
||||
.in(BpmForm::getId, ids)
|
||||
.update();
|
||||
}
|
||||
|
||||
// private void validateFormExists(Long id) {
|
||||
// if (this.baseMapper.selectById(id) == null) {
|
||||
// throw exception(ErrorCodeConstants.FORM_NOT_EXISTS);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public List<BpmForm> getFormList() {
|
||||
LambdaQueryWrapper<BpmForm> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.select(BpmForm::getId,BpmForm::getName)
|
||||
.eq(BpmForm::getState,DataStateEnum.ENABLE.getCode());
|
||||
return this.baseMapper.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BpmForm> getFormList(Collection<String> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return this.baseMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 校验 Field,避免 field 重复
|
||||
*
|
||||
* @param fields field 数组
|
||||
*/
|
||||
private void validateFields(List<String> fields) {
|
||||
if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验
|
||||
return;
|
||||
}
|
||||
// Map<String, String> fieldMap = new HashMap<>(); // key 是 vModel,value 是 label
|
||||
// for (String field : fields) {
|
||||
// BpmFormFieldRespDTO fieldDTO = JsonUtils.parseObject(field, BpmFormFieldRespDTO.class);
|
||||
// Assert.notNull(fieldDTO);
|
||||
// String oldLabel = fieldMap.put(fieldDTO.getVModel(), fieldDTO.getLabel());
|
||||
// // 如果不存在,则直接返回
|
||||
// if (oldLabel == null) {
|
||||
// continue;
|
||||
// }
|
||||
// // 如果存在,则报错
|
||||
// throw exception(ErrorCodeConstants.FORM_FIELD_REPEAT, oldLabel, fieldDTO.getLabel(), fieldDTO.getVModel());
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
package com.njcn.bpm.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.enums.BpmModelFormTypeEnum;
|
||||
import com.njcn.bpm.enums.BpmResponseEnum;
|
||||
import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO;
|
||||
import com.njcn.bpm.pojo.param.BpmModelParam;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.service.IBpmFormService;
|
||||
import com.njcn.bpm.service.IBpmModelService;
|
||||
import com.njcn.bpm.service.IBpmProcessDefinitionService;
|
||||
import com.njcn.bpm.strategy.BpmTaskCandidateInvoker;
|
||||
import com.njcn.bpm.utils.*;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.ProcessEngineConfiguration;
|
||||
import org.flowable.engine.RepositoryService;
|
||||
import org.flowable.engine.repository.Model;
|
||||
import org.flowable.engine.repository.ModelQuery;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/**
|
||||
* Flowable流程模型实现
|
||||
* 主要进行 Flowable {@link Model} 的维护
|
||||
*
|
||||
* @author yunlongn
|
||||
* @author 芋道源码
|
||||
* @author jason
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class BpmModelServiceImpl implements IBpmModelService {
|
||||
|
||||
@Resource
|
||||
private RepositoryService repositoryService;
|
||||
@Resource
|
||||
private IBpmProcessDefinitionService processDefinitionService;
|
||||
@Resource
|
||||
private IBpmFormService bpmFormService;
|
||||
@Resource
|
||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||
|
||||
@Override
|
||||
public Page<Model> getModelPage(BpmModelParam.BpmModelQueryParam bpmModelQueryParam) {
|
||||
ModelQuery modelQuery = repositoryService.createModelQuery();
|
||||
if (StrUtil.isNotBlank(bpmModelQueryParam.getKey())) {
|
||||
modelQuery.modelKey(bpmModelQueryParam.getKey());
|
||||
}
|
||||
if (StrUtil.isNotBlank(bpmModelQueryParam.getName())) {
|
||||
modelQuery.modelNameLike("%" + bpmModelQueryParam.getName() + "%"); // 模糊匹配
|
||||
}
|
||||
if (StrUtil.isNotBlank(bpmModelQueryParam.getCategory())) {
|
||||
modelQuery.modelCategory(bpmModelQueryParam.getCategory());
|
||||
}
|
||||
// 执行查询
|
||||
long count = modelQuery.count();
|
||||
if (count == 0) {
|
||||
return new Page<>();
|
||||
}
|
||||
int offset = PageFactory.getPageSize(bpmModelQueryParam) * (PageFactory.getPageNum(bpmModelQueryParam) - 1);
|
||||
List<Model> models = modelQuery
|
||||
.modelTenantId(ProcessEngineConfiguration.NO_TENANT_ID)
|
||||
.orderByCreateTime().desc()
|
||||
.listPage(offset, PageFactory.getPageSize(bpmModelQueryParam));
|
||||
|
||||
Page<Model> page = new Page<>();
|
||||
page.setRecords(models);
|
||||
page.setTotal(count);
|
||||
page.setSize(PageFactory.getPageSize(bpmModelQueryParam));
|
||||
page.setCurrent(PageFactory.getPageNum(bpmModelQueryParam));
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String createModel(BpmModelParam bpmModelParam, String bpmnXml) {
|
||||
if (!ValidationUtils.isXmlNCName(bpmModelParam.getKey())) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_XML_ERROR);
|
||||
}
|
||||
// 校验流程标识已经存在
|
||||
Model keyModel = getModelByKey(bpmModelParam.getKey());
|
||||
if (keyModel != null) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_MODEL_REPEAT);
|
||||
}
|
||||
// 创建流程定义
|
||||
Model model = repositoryService.newModel();
|
||||
BpmModelConvert.INSTANCE.copyToCreateModel(model, bpmModelParam);
|
||||
model.setTenantId(FlowableUtils.getTenantId());
|
||||
// 保存流程定义
|
||||
repositoryService.saveModel(model);
|
||||
// 保存 BPMN XML
|
||||
saveModelBpmnXml(model, bpmnXml);
|
||||
return model.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务
|
||||
public void updateModel(BpmModelParam.BpmModelUpdateParam updateParam) {
|
||||
// 校验流程模型存在
|
||||
Model model = getModel(updateParam.getId());
|
||||
if (model == null) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST);
|
||||
}
|
||||
// 修改流程定义
|
||||
BpmModelConvert.INSTANCE.copyToUpdateModel(model, updateParam);
|
||||
// 更新模型
|
||||
repositoryService.saveModel(model);
|
||||
// 更新 BPMN XML
|
||||
saveModelBpmnXml(model, updateParam.getBpmnXml());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务
|
||||
public void deployModel(String id) {
|
||||
// 1.1 校验流程模型存在
|
||||
Model model = getModel(id);
|
||||
if (ObjectUtils.isEmpty(model)) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST);
|
||||
}
|
||||
// 1.2 校验流程图
|
||||
byte[] bpmnBytes = getModelBpmnXML(model.getId());
|
||||
validateBpmnXml(bpmnBytes);
|
||||
// 1.3 校验表单已配
|
||||
BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
|
||||
BpmForm form = validateFormConfig(metaInfo);
|
||||
|
||||
// 1.4 校验任务分配规则已配置
|
||||
taskCandidateInvoker.validateBpmnConfig(bpmnBytes);
|
||||
|
||||
// 2.1 创建流程定义
|
||||
String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, form);
|
||||
|
||||
// 2.2 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。
|
||||
updateProcessDefinitionSuspended(model.getDeploymentId());
|
||||
|
||||
// 2.3 更新 model 的 deploymentId,进行关联
|
||||
ProcessDefinition definition = processDefinitionService.getProcessDefinition(definitionId);
|
||||
model.setDeploymentId(definition.getDeploymentId());
|
||||
repositoryService.saveModel(model);
|
||||
}
|
||||
|
||||
private void validateBpmnXml(byte[] bpmnBytes) {
|
||||
BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);
|
||||
if (bpmnModel == null) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST);
|
||||
}
|
||||
// 1. 没有 StartEvent
|
||||
StartEvent startEvent = BpmnModelUtils.getStartEvent(bpmnModel);
|
||||
if (startEvent == null) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_START_EVENT_NOT_EXIST);
|
||||
}
|
||||
// 2. 校验 UserTask 的 name 都配置了
|
||||
List<UserTask> userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
|
||||
UserTask userTask1 = userTasks.get(0);
|
||||
userTask1.getName();
|
||||
userTasks.forEach(userTask -> {
|
||||
if (StrUtil.isEmpty(userTask.getName())) {
|
||||
throw new BusinessException(BpmResponseEnum.MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteModel(String id) {
|
||||
// 校验流程模型存在
|
||||
Model model = getModel(id);
|
||||
if (model == null) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST);
|
||||
}
|
||||
// 执行删除
|
||||
repositoryService.deleteModel(id);
|
||||
// 禁用流程定义
|
||||
updateProcessDefinitionSuspended(model.getDeploymentId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateModelState(String id, Integer state) {
|
||||
// 1.1 校验流程模型存在
|
||||
Model model = getModel(id);
|
||||
if (model == null) {
|
||||
throw new BusinessException(BpmResponseEnum.BPM_MODEL_NOT_EXIST);
|
||||
}
|
||||
// 1.2 校验流程定义存在
|
||||
ProcessDefinition definition = processDefinitionService.getProcessDefinitionByDeploymentId(model.getDeploymentId());
|
||||
if (definition == null) {
|
||||
throw new BusinessException(BpmResponseEnum.PROCESS_DEFINITION_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 2. 更新状态
|
||||
processDefinitionService.updateProcessDefinitionState(definition.getId(), state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmnModel getBpmnModelByDefinitionId(String processDefinitionId) {
|
||||
return repositoryService.getBpmnModel(processDefinitionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验流程表单已配置
|
||||
*
|
||||
* @param metaInfo 流程模型元数据
|
||||
* @return 表单配置
|
||||
*/
|
||||
private BpmForm validateFormConfig(BpmModelMetaInfoRespDTO metaInfo) {
|
||||
if (metaInfo == null || metaInfo.getFormType() == null) {
|
||||
throw new BusinessException(BpmResponseEnum.MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
|
||||
}
|
||||
// 校验表单存在
|
||||
if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) {
|
||||
if (metaInfo.getFormId() == null) {
|
||||
throw new BusinessException(BpmResponseEnum.MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
|
||||
}
|
||||
BpmForm form = bpmFormService.getById(metaInfo.getFormId());
|
||||
if (form == null) {
|
||||
throw new BusinessException(BpmResponseEnum.FORM_NOT_EXISTS);
|
||||
}
|
||||
return form;
|
||||
} else {
|
||||
if (StrUtil.isEmpty(metaInfo.getFormCustomCreatePath()) || StrUtil.isEmpty(metaInfo.getFormCustomViewPath())) {
|
||||
throw new BusinessException(BpmResponseEnum.MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void saveModelBpmnXml(Model model, String bpmnXml) {
|
||||
if (StrUtil.isEmpty(bpmnXml)) {
|
||||
return;
|
||||
}
|
||||
repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(bpmnXml));
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂起 deploymentId 对应的流程定义
|
||||
* <p>
|
||||
* 注意:这里一个 deploymentId 只关联一个流程定义
|
||||
*
|
||||
* @param deploymentId 流程发布Id
|
||||
*/
|
||||
private void updateProcessDefinitionSuspended(String deploymentId) {
|
||||
if (StrUtil.isEmpty(deploymentId)) {
|
||||
return;
|
||||
}
|
||||
ProcessDefinition oldDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(deploymentId);
|
||||
if (oldDefinition == null) {
|
||||
return;
|
||||
}
|
||||
processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), SuspensionState.SUSPENDED.getStateCode());
|
||||
}
|
||||
|
||||
private Model getModelByKey(String key) {
|
||||
return repositoryService.createModelQuery().modelKey(key).singleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Model getModel(String id) {
|
||||
return repositoryService.getModel(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getModelBpmnXML(String id) {
|
||||
return repositoryService.getModelEditorSource(id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package com.njcn.bpm.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.bpm.enums.BpmnModelConstants;
|
||||
import com.njcn.bpm.mapper.BpmCategoryMapper;
|
||||
import com.njcn.bpm.mapper.BpmProcessDefinitionInfoMapper;
|
||||
import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO;
|
||||
import com.njcn.bpm.pojo.param.BpmProcessDefinitionInfoParam;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
|
||||
import com.njcn.bpm.service.IBpmProcessDefinitionService;
|
||||
import com.njcn.bpm.utils.BeanUtils;
|
||||
import com.njcn.bpm.utils.FlowableUtils;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.common.engine.impl.db.SuspensionState;
|
||||
import org.flowable.engine.RepositoryService;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.Model;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.flowable.engine.repository.ProcessDefinitionQuery;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
|
||||
import static com.njcn.bpm.utils.CollectionUtils.addIfNotNull;
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
/**
|
||||
* 流程定义实现
|
||||
* 主要进行 Flowable {@link ProcessDefinition} 和 {@link Deployment} 的维护
|
||||
*
|
||||
* @author yunlongn
|
||||
* @author ZJQ
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class BpmProcessDefinitionServiceImpl extends ServiceImpl<BpmProcessDefinitionInfoMapper, BpmProcessDefinitionInfo> implements IBpmProcessDefinitionService {
|
||||
|
||||
@Resource
|
||||
private RepositoryService repositoryService;
|
||||
|
||||
|
||||
@Override
|
||||
public Page<ProcessDefinition> getProcessDefinitionPage(BpmProcessDefinitionInfoParam.BpmProcessDefinitionInfoQueryParam bpmProcessDefinitionInfoQueryParam) {
|
||||
ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
|
||||
if (StrUtil.isNotBlank(bpmProcessDefinitionInfoQueryParam.getKey())) {
|
||||
query.processDefinitionKey(bpmProcessDefinitionInfoQueryParam.getKey());
|
||||
}
|
||||
// 执行查询
|
||||
long count = query.count();
|
||||
if (count == 0) {
|
||||
return new Page<>();
|
||||
}
|
||||
int offset = PageFactory.getPageSize(bpmProcessDefinitionInfoQueryParam) * (PageFactory.getPageNum(bpmProcessDefinitionInfoQueryParam) - 1);
|
||||
List<ProcessDefinition> processDefinitionList = query.orderByProcessDefinitionVersion().desc()
|
||||
.listPage(offset, PageFactory.getPageSize(bpmProcessDefinitionInfoQueryParam));
|
||||
Page<ProcessDefinition> page = new Page<>();
|
||||
page.setRecords(processDefinitionList);
|
||||
page.setTotal(count);
|
||||
page.setSize(PageFactory.getPageSize(bpmProcessDefinitionInfoQueryParam));
|
||||
page.setCurrent(PageFactory.getPageNum(bpmProcessDefinitionInfoQueryParam));
|
||||
return page;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ProcessDefinition getProcessDefinition(String id) {
|
||||
return repositoryService.getProcessDefinition(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProcessDefinition> getProcessDefinitionList(Set<String> ids) {
|
||||
return repositoryService.createProcessDefinitionQuery().processDefinitionIds(ids).list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId) {
|
||||
if (StrUtil.isEmpty(deploymentId)) {
|
||||
return null;
|
||||
}
|
||||
return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds) {
|
||||
if (CollUtil.isEmpty(deploymentIds)) {
|
||||
return emptyList();
|
||||
}
|
||||
return repositoryService.createProcessDefinitionQuery().deploymentIds(deploymentIds).list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessDefinition getActiveProcessDefinition(String key) {
|
||||
return repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).active().singleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Deployment> getDeploymentList(Set<String> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return emptyList();
|
||||
}
|
||||
List<Deployment> list = new ArrayList<>(ids.size());
|
||||
for (String id : ids) {
|
||||
addIfNotNull(list, getDeployment(id));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Deployment getDeployment(String id) {
|
||||
if (StrUtil.isEmpty(id)) {
|
||||
return null;
|
||||
}
|
||||
return repositoryService.createDeploymentQuery().deploymentId(id).singleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createProcessDefinition(Model model, BpmModelMetaInfoRespDTO modelMetaInfo,
|
||||
byte[] bpmnBytes, BpmForm form) {
|
||||
// 创建 Deployment 部署
|
||||
Deployment deploy = repositoryService.createDeployment()
|
||||
.key(model.getKey()).name(model.getName()).category(model.getCategory())
|
||||
.addBytes(model.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, bpmnBytes)
|
||||
.tenantId(FlowableUtils.getTenantId())
|
||||
.disableSchemaValidation() // 禁用 XML Schema 验证,因为有自定义的属性
|
||||
.deploy();
|
||||
|
||||
// 设置 ProcessDefinition 的 category 分类
|
||||
ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
|
||||
.deploymentId(deploy.getId()).singleResult();
|
||||
repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory());
|
||||
// 注意 1,ProcessDefinition 的 key 和 name 是通过 BPMN 中的 <bpmn2:process /> 的 id 和 name 决定
|
||||
// 注意 2,目前该项目的设计上,需要保证 Model、Deployment、ProcessDefinition 使用相同的 key,保证关联性。
|
||||
// 否则,会导致 ProcessDefinition 的分页无法查询到。
|
||||
if (!Objects.equals(definition.getKey(), model.getKey())) {
|
||||
throw new BusinessException("流程定义的标识期望是(" + model.getKey() + "),当前是(" + definition.getKey() + "),请修改 BPMN 流程图");
|
||||
}
|
||||
if (!Objects.equals(definition.getName(), model.getName())) {
|
||||
throw new BusinessException("流程定义的名字期望是(" + model.getName() + "),当前是(" + definition.getName() + "),请修改 BPMN 流程图");
|
||||
}
|
||||
|
||||
// 插入拓展表
|
||||
BpmProcessDefinitionInfo definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfo.class);
|
||||
definitionDO.setModelId(model.getId());
|
||||
definitionDO.setProcessDefinitionId(definition.getId());
|
||||
if (form != null) {
|
||||
definitionDO.setFormFields(form.getFields());
|
||||
definitionDO.setFormConf(form.getConf());
|
||||
}
|
||||
this.baseMapper.insert(definitionDO);
|
||||
return definition.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProcessDefinitionState(String id, Integer state) {
|
||||
// 激活
|
||||
if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) {
|
||||
repositoryService.activateProcessDefinitionById(id, false, null);
|
||||
return;
|
||||
}
|
||||
// 挂起
|
||||
if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) {
|
||||
// suspendProcessInstances = false,进行中的任务,不进行挂起。
|
||||
// 原因:只要新的流程不允许发起即可,老流程继续可以执行。
|
||||
repositoryService.suspendProcessDefinitionById(id, false, null);
|
||||
return;
|
||||
}
|
||||
log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmnModel getProcessDefinitionBpmnModel(String id) {
|
||||
return repositoryService.getBpmnModel(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BpmProcessDefinitionInfo getProcessDefinitionInfo(String id) {
|
||||
LambdaQueryWrapper<BpmProcessDefinitionInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(BpmProcessDefinitionInfo::getProcessDefinitionId, id);
|
||||
return this.baseMapper.selectOne(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BpmProcessDefinitionInfo> getProcessDefinitionInfoList(Collection<String> ids) {
|
||||
LambdaQueryWrapper<BpmProcessDefinitionInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||
lambdaQueryWrapper.eq(BpmProcessDefinitionInfo::getProcessDefinitionId, ids)
|
||||
.eq(BpmProcessDefinitionInfo::getState, DataStateEnum.ENABLE.getCode());
|
||||
return this.baseMapper.selectList(lambdaQueryWrapper);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<ProcessDefinition> getProcessDefinitionListBySuspensionState(Integer suspensionState) {
|
||||
// 拼接查询条件
|
||||
ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
|
||||
if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), suspensionState)) {
|
||||
query.suspended();
|
||||
} else if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), suspensionState)) {
|
||||
query.active();
|
||||
}
|
||||
// 执行查询
|
||||
query.processDefinitionTenantId(FlowableUtils.getTenantId());
|
||||
return query.list();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.njcn.bpm.service.task;
|
||||
|
||||
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.njcn.bpm.utils.CollectionUtils.convertMap;
|
||||
|
||||
|
||||
/**
|
||||
* 流程实例 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface IBpmProcessInstanceService {
|
||||
|
||||
/**
|
||||
* 获得流程实例
|
||||
*
|
||||
* @param id 流程实例的编号
|
||||
* @return 流程实例
|
||||
*/
|
||||
ProcessInstance getProcessInstance(String id);
|
||||
//
|
||||
// /**
|
||||
// * 获得流程实例列表
|
||||
// *
|
||||
// * @param ids 流程实例的编号集合
|
||||
// * @return 流程实例列表
|
||||
// */
|
||||
// List<ProcessInstance> getProcessInstances(Set<String> ids);
|
||||
//
|
||||
// /**
|
||||
// * 获得流程实例 Map
|
||||
// *
|
||||
// * @param ids 流程实例的编号集合
|
||||
// * @return 流程实例列表 Map
|
||||
// */
|
||||
// default Map<String, ProcessInstance> getProcessInstanceMap(Set<String> ids) {
|
||||
// return convertMap(getProcessInstances(ids), ProcessInstance::getProcessInstanceId);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获得历史的流程实例
|
||||
// *
|
||||
// * @param id 流程实例的编号
|
||||
// * @return 历史的流程实例
|
||||
// */
|
||||
// HistoricProcessInstance getHistoricProcessInstance(String id);
|
||||
//
|
||||
// /**
|
||||
// * 获得历史的流程实例列表
|
||||
// *
|
||||
// * @param ids 流程实例的编号集合
|
||||
// * @return 历史的流程实例列表
|
||||
// */
|
||||
// List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids);
|
||||
//
|
||||
// /**
|
||||
// * 获得历史的流程实例 Map
|
||||
// *
|
||||
// * @param ids 流程实例的编号集合
|
||||
// * @return 历史的流程实例列表 Map
|
||||
// */
|
||||
// default Map<String, HistoricProcessInstance> getHistoricProcessInstanceMap(Set<String> ids) {
|
||||
// return convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获得流程实例的分页
|
||||
// *
|
||||
// * @param userId 用户编号
|
||||
// * @param pageReqVO 分页请求
|
||||
// * @return 流程实例的分页
|
||||
// */
|
||||
// PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId, @Valid BpmProcessInstancePageReqVO pageReqVO);
|
||||
//
|
||||
// /**
|
||||
// * 创建流程实例(提供给前端)
|
||||
// *
|
||||
// * @param userId 用户编号
|
||||
// * @param createReqVO 创建信息
|
||||
// * @return 实例的编号
|
||||
// */
|
||||
// String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO);
|
||||
//
|
||||
// /**
|
||||
// * 创建流程实例(提供给内部)
|
||||
// *
|
||||
// * @param userId 用户编号
|
||||
// * @param createReqDTO 创建信息
|
||||
// * @return 实例的编号
|
||||
// */
|
||||
// String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO);
|
||||
//
|
||||
// /**
|
||||
// * 发起人取消流程实例
|
||||
// *
|
||||
// * @param userId 用户编号
|
||||
// * @param cancelReqVO 取消信息
|
||||
// */
|
||||
// void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO);
|
||||
//
|
||||
// /**
|
||||
// * 管理员取消流程实例
|
||||
// *
|
||||
// * @param userId 用户编号
|
||||
// * @param cancelReqVO 取消信息
|
||||
// */
|
||||
// void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO);
|
||||
//
|
||||
// /**
|
||||
// * 更新 ProcessInstance 拓展记录为取消
|
||||
// *
|
||||
// * @param event 流程取消事件
|
||||
// */
|
||||
// void updateProcessInstanceWhenCancel(FlowableCancelledEvent event);
|
||||
//
|
||||
// /**
|
||||
// * 更新 ProcessInstance 拓展记录为完成
|
||||
// *
|
||||
// * @param instance 流程任务
|
||||
// */
|
||||
// void updateProcessInstanceWhenApprove(ProcessInstance instance);
|
||||
//
|
||||
// /**
|
||||
// * 更新 ProcessInstance 拓展记录为不通过
|
||||
// *
|
||||
// * @param id 流程编号
|
||||
// * @param reason 理由。例如说,审批不通过时,需要传递该值
|
||||
// */
|
||||
// void updateProcessInstanceReject(String id, String reason);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
package com.njcn.bpm.service.task.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.njcn.bpm.service.task.IBpmProcessInstanceService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.HistoryService;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.history.HistoricProcessInstanceQuery;
|
||||
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;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* 流程实例 Service 实现类
|
||||
*
|
||||
* ProcessDefinition & ProcessInstance & Execution & Task 的关系:
|
||||
* 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" />
|
||||
*
|
||||
* HistoricProcessInstance & ProcessInstance 的关系:
|
||||
* 1. <a href=" https://my.oschina.net/843294669/blog/71902" />
|
||||
*
|
||||
* 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class BpmProcessInstanceServiceImpl implements IBpmProcessInstanceService {
|
||||
|
||||
@Resource
|
||||
private RuntimeService runtimeService;
|
||||
@Resource
|
||||
private HistoryService historyService;
|
||||
|
||||
// @Resource
|
||||
// private BpmProcessDefinitionService processDefinitionService;
|
||||
// @Resource
|
||||
// private BpmMessageService messageService;
|
||||
//
|
||||
// @Resource
|
||||
// private AdminUserApi adminUserApi;
|
||||
//
|
||||
// @Resource
|
||||
// private BpmProcessInstanceEventPublisher processInstanceEventPublisher;
|
||||
|
||||
@Override
|
||||
public ProcessInstance getProcessInstance(String id) {
|
||||
return runtimeService.createProcessInstanceQuery()
|
||||
.includeProcessVariables()
|
||||
.processInstanceId(id)
|
||||
.singleResult();
|
||||
}
|
||||
//
|
||||
// @Override
|
||||
// public List<ProcessInstance> getProcessInstances(Set<String> ids) {
|
||||
// return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public HistoricProcessInstance getHistoricProcessInstance(String id) {
|
||||
// return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) {
|
||||
// return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public PageResult<HistoricProcessInstance> getProcessInstancePage(Long userId,
|
||||
// BpmProcessInstancePageReqVO pageReqVO) {
|
||||
// // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
|
||||
// HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery()
|
||||
// .includeProcessVariables()
|
||||
// .processInstanceTenantId(FlowableUtils.getTenantId())
|
||||
// .orderByProcessInstanceStartTime().desc();
|
||||
// if (userId != null) { // 【我的流程】菜单时,需要传递该字段
|
||||
// processInstanceQuery.startedBy(String.valueOf(userId));
|
||||
// } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段
|
||||
// processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId()));
|
||||
// }
|
||||
// if (StrUtil.isNotEmpty(pageReqVO.getName())) {
|
||||
// processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%");
|
||||
// }
|
||||
// if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionId())) {
|
||||
// processInstanceQuery.processDefinitionId("%" + pageReqVO.getProcessDefinitionId() + "%");
|
||||
// }
|
||||
// if (StrUtil.isNotEmpty(pageReqVO.getCategory())) {
|
||||
// processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory());
|
||||
// }
|
||||
// if (pageReqVO.getStatus() != null) {
|
||||
// processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus());
|
||||
// }
|
||||
// if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) {
|
||||
// processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0]));
|
||||
// processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1]));
|
||||
// }
|
||||
// // 查询数量
|
||||
// long processInstanceCount = processInstanceQuery.count();
|
||||
// if (processInstanceCount == 0) {
|
||||
// return PageResult.empty(processInstanceCount);
|
||||
// }
|
||||
// // 查询列表
|
||||
// List<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize());
|
||||
// return new PageResult<>(processInstanceList, processInstanceCount);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// @Transactional(rollbackFor = Exception.class)
|
||||
// public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {
|
||||
// // 获得流程定义
|
||||
// ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
|
||||
// // 发起流程
|
||||
// return createProcessInstance0(userId, definition, createReqVO.getVariables(), null,
|
||||
// createReqVO.getStartUserSelectAssignees());
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
|
||||
// // 获得流程定义
|
||||
// ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
|
||||
// // 发起流程
|
||||
// return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(),
|
||||
// createReqDTO.getStartUserSelectAssignees());
|
||||
// }
|
||||
//
|
||||
// private String createProcessInstance0(Long userId, ProcessDefinition definition,
|
||||
// Map<String, Object> variables, String businessKey,
|
||||
// Map<String, List<Long>> startUserSelectAssignees) {
|
||||
// // 1.1 校验流程定义
|
||||
// if (definition == null) {
|
||||
// throw exception(PROCESS_DEFINITION_NOT_EXISTS);
|
||||
// }
|
||||
// if (definition.isSuspended()) {
|
||||
// throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
|
||||
// }
|
||||
// // 1.2 校验发起人自选审批人
|
||||
// validateStartUserSelectAssignees(definition, startUserSelectAssignees);
|
||||
//
|
||||
// // 2. 创建流程实例
|
||||
// if (variables == null) {
|
||||
// variables = new HashMap<>();
|
||||
// }
|
||||
// FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用
|
||||
// variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中
|
||||
// BpmProcessInstanceStatusEnum.RUNNING.getStatus());
|
||||
// if (CollUtil.isNotEmpty(startUserSelectAssignees)) {
|
||||
// variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees);
|
||||
// }
|
||||
// ProcessInstance instance = runtimeService.createProcessInstanceBuilder()
|
||||
// .processDefinitionId(definition.getId())
|
||||
// .businessKey(businessKey)
|
||||
// .name(definition.getName().trim())
|
||||
// .variables(variables)
|
||||
// .start();
|
||||
// return instance.getId();
|
||||
// }
|
||||
//
|
||||
// private void validateStartUserSelectAssignees(ProcessDefinition definition, Map<String, List<Long>> startUserSelectAssignees) {
|
||||
// // 1. 获得发起人自选审批人的 UserTask 列表
|
||||
// BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId());
|
||||
// List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel);
|
||||
// if (CollUtil.isEmpty(userTaskList)) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 2. 校验发起人自选审批人的 UserTask 是否都配置了
|
||||
// userTaskList.forEach(userTask -> {
|
||||
// List<Long> assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(userTask.getId()) : null;
|
||||
// if (CollUtil.isEmpty(assignees)) {
|
||||
// throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, userTask.getName());
|
||||
// }
|
||||
// Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assignees);
|
||||
// assignees.forEach(assignee -> {
|
||||
// if (userMap.get(assignee) == null) {
|
||||
// throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, userTask.getName(), assignee);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {
|
||||
// // 1.1 校验流程实例存在
|
||||
// ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
|
||||
// if (instance == null) {
|
||||
// throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
|
||||
// }
|
||||
// // 1.2 只能取消自己的
|
||||
// if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
|
||||
// throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
|
||||
// }
|
||||
//
|
||||
// // 2. 通过删除流程实例,实现流程实例的取消,
|
||||
// // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。
|
||||
// deleteProcessInstance(cancelReqVO.getId(),
|
||||
// BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason()));
|
||||
//
|
||||
// // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) {
|
||||
// // 1.1 校验流程实例存在
|
||||
// ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
|
||||
// if (instance == null) {
|
||||
// throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
|
||||
// }
|
||||
// // 1.2 管理员取消,不用校验是否为自己的
|
||||
// AdminUserRespDTO user = adminUserApi.getUser(userId);
|
||||
//
|
||||
// // 2. 通过删除流程实例,实现流程实例的取消,
|
||||
// // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。
|
||||
// deleteProcessInstance(cancelReqVO.getId(),
|
||||
// BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason()));
|
||||
//
|
||||
// // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void updateProcessInstanceWhenCancel(FlowableCancelledEvent event) {
|
||||
// // 1. 判断是否为 Reject 不通过。如果是,则不进行更新.
|
||||
// // 因为,updateProcessInstanceReject 方法(审批不通过),已经进行更新了
|
||||
// if (BpmDeleteReasonEnum.isRejectReason((String) event.getCause())) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 2. 更新流程实例 status
|
||||
// runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
|
||||
// BpmProcessInstanceStatusEnum.CANCEL.getStatus());
|
||||
//
|
||||
// // 3. 发送流程实例的状态事件
|
||||
// // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
|
||||
// HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId());
|
||||
// // 发送流程实例的状态事件
|
||||
// processInstanceEventPublisher.sendProcessInstanceResultEvent(
|
||||
// BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.CANCEL.getStatus()));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void updateProcessInstanceWhenApprove(ProcessInstance instance) {
|
||||
// // 1. 更新流程实例 status
|
||||
// runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS,
|
||||
// BpmProcessInstanceStatusEnum.APPROVE.getStatus());
|
||||
//
|
||||
// // 2. 发送流程被【通过】的消息
|
||||
// messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance));
|
||||
//
|
||||
// // 3. 发送流程实例的状态事件
|
||||
// // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
|
||||
// HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId());
|
||||
// processInstanceEventPublisher.sendProcessInstanceResultEvent(
|
||||
// BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.APPROVE.getStatus()));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// @Transactional(rollbackFor = Exception.class)
|
||||
// public void updateProcessInstanceReject(String id, String reason) {
|
||||
// // 1. 更新流程实例 status
|
||||
// runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus());
|
||||
//
|
||||
// // 2. 删除流程实例,以实现驳回任务时,取消整个审批流程
|
||||
// ProcessInstance processInstance = getProcessInstance(id);
|
||||
// deleteProcessInstance(id, StrUtil.format(BpmDeleteReasonEnum.REJECT_TASK.format(reason)));
|
||||
//
|
||||
// // 3. 发送流程被【不通过】的消息
|
||||
// messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(processInstance, reason));
|
||||
//
|
||||
// // 4. 发送流程实例的状态事件
|
||||
// processInstanceEventPublisher.sendProcessInstanceResultEvent(
|
||||
// BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus()));
|
||||
// }
|
||||
//
|
||||
// private void deleteProcessInstance(String id, String reason) {
|
||||
// runtimeService.deleteProcessInstance(id, reason);
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.njcn.bpm.strategy;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum;
|
||||
import com.njcn.bpm.utils.BpmnModelUtils;
|
||||
import com.njcn.bpm.utils.CollectionUtils;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.user.api.UserFeignClient;
|
||||
import com.njcn.user.pojo.po.User;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
public class BpmTaskCandidateInvoker {
|
||||
|
||||
private final Map<BpmTaskCandidateStrategyEnum, IBpmTaskCandidateStrategy> strategyMap = new HashMap<>();
|
||||
|
||||
private final UserFeignClient adminUserApi;
|
||||
|
||||
public BpmTaskCandidateInvoker(List<IBpmTaskCandidateStrategy> strategyList,
|
||||
UserFeignClient adminUserApi) {
|
||||
strategyList.forEach(strategy -> {
|
||||
IBpmTaskCandidateStrategy oldStrategy = strategyMap.put(strategy.getStrategy(), strategy);
|
||||
Assert.isNull(oldStrategy, "策略(%s) 重复", strategy.getStrategy());
|
||||
});
|
||||
this.adminUserApi = adminUserApi;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验流程模型的任务分配规则全部都配置了
|
||||
* 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去!
|
||||
*
|
||||
* @param bpmnBytes BPMN XML
|
||||
*/
|
||||
public void validateBpmnConfig(byte[] bpmnBytes) {
|
||||
BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);
|
||||
assert bpmnModel != null;
|
||||
List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
|
||||
// 遍历所有的 UserTask,校验审批人配置
|
||||
userTaskList.forEach(userTask -> {
|
||||
// 1. 非空校验
|
||||
Integer strategy = BpmnModelUtils.parseCandidateStrategy(userTask);
|
||||
String param = BpmnModelUtils.parseCandidateParam(userTask);
|
||||
if (strategy == null) {
|
||||
throw new BusinessException("部署流程失败,原因:BPMN 流程图中,用户任务(" + userTask.getName() + ")的名字不存在");
|
||||
}
|
||||
IBpmTaskCandidateStrategy candidateStrategy = getCandidateStrategy(strategy);
|
||||
if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) {
|
||||
throw new BusinessException("部署流程失败,原因:用户任务(" + userTask.getName() + ")未配置审批人,请点击【流程设计】按钮,选择该它的【任务(审批人)】进行配置");
|
||||
}
|
||||
// 2. 具体策略校验
|
||||
getCandidateStrategy(strategy).validateParam(param);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算任务的候选人
|
||||
*
|
||||
* @param execution 执行任务
|
||||
* @return 用户编号集合
|
||||
*/
|
||||
public List<String> calculateUsers(DelegateExecution execution) {
|
||||
Integer strategy = BpmnModelUtils.parseCandidateStrategy(execution.getCurrentFlowElement());
|
||||
String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement());
|
||||
// 1.1 计算任务的候选人
|
||||
List<String> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
|
||||
// 1.2 移除被禁用的用户
|
||||
removeDisableUsers(userIds);
|
||||
|
||||
// 2. 校验是否有候选人
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
log.error("[calculateUsers][流程任务({}/{}/{}) 任务规则({}/{}) 找不到候选人]", execution.getId(),
|
||||
execution.getProcessDefinitionId(), execution.getCurrentActivityId(), strategy, param);
|
||||
throw new BusinessException("操作失败,原因:找不到任务的审批人!");
|
||||
}
|
||||
return userIds;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void removeDisableUsers(List<String> assigneeUserIds) {
|
||||
if (CollUtil.isEmpty(assigneeUserIds)) {
|
||||
return;
|
||||
}
|
||||
List<User> users = adminUserApi.getUserByIdList(assigneeUserIds).getData();
|
||||
Map<String, User> userMap = CollectionUtils.convertMap(users, User::getId);
|
||||
assigneeUserIds.removeIf(id -> {
|
||||
User user = userMap.get(id);
|
||||
return user == null || !DataStateEnum.ENABLE.getCode().equals(user.getState());
|
||||
});
|
||||
}
|
||||
|
||||
private IBpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
|
||||
BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
|
||||
Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);
|
||||
IBpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum);
|
||||
Assert.notNull(strategyObj, "策略(%s) 不存在", strategy);
|
||||
return strategyObj;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.njcn.bpm.strategy;
|
||||
|
||||
import com.njcn.user.api.UserFeignClient;
|
||||
import com.njcn.bpm.utils.StrUtils;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 角色 {@link IBpmTaskCandidateStrategy} 实现类
|
||||
*
|
||||
* @author kyle
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateRoleStrategy implements IBpmTaskCandidateStrategy {
|
||||
|
||||
@Resource
|
||||
private UserFeignClient userFeignClient;
|
||||
|
||||
// @Resource
|
||||
// private PermissionApi permissionApi;
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
return BpmTaskCandidateStrategyEnum.ROLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateParam(String param) {
|
||||
// Set<Long> roleIds = StrUtils.splitToLongSet(param);
|
||||
// roleApi.validRoleList(roleIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> calculateUsers(DelegateExecution execution, String param) {
|
||||
List<String> roleIds = StrUtils.splitToStringList(param);
|
||||
return userFeignClient.getUserIdByRoleId(roleIds).getData();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.njcn.bpm.strategy;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum;
|
||||
import com.njcn.bpm.service.task.IBpmProcessInstanceService;
|
||||
import com.njcn.bpm.utils.BpmnModelUtils;
|
||||
import com.njcn.bpm.utils.FlowableUtils;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateStartUserSelectStrategy implements IBpmTaskCandidateStrategy {
|
||||
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private IBpmProcessInstanceService processInstanceService;
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
return BpmTaskCandidateStrategyEnum.START_USER_SELECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateParam(String param) {}
|
||||
|
||||
@Override
|
||||
public List<String> calculateUsers(DelegateExecution execution, String param) {
|
||||
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
|
||||
Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId());
|
||||
Map<String, List<String>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance);
|
||||
Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空",
|
||||
execution.getProcessInstanceId());
|
||||
// 获得审批人
|
||||
List<String> assignees = startUserSelectAssignees.get(execution.getCurrentActivityId());
|
||||
return new ArrayList<>(assignees);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParamRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得发起人自选审批人的 UserTask 列表
|
||||
*
|
||||
* @param bpmnModel BPMN 模型
|
||||
* @return UserTask 列表
|
||||
*/
|
||||
public static List<UserTask> getStartUserSelectUserTaskList(BpmnModel bpmnModel) {
|
||||
if (bpmnModel == null) {
|
||||
return null;
|
||||
}
|
||||
List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
|
||||
if (CollUtil.isEmpty(userTaskList)) {
|
||||
return null;
|
||||
}
|
||||
userTaskList.removeIf(userTask -> !Objects.equals(BpmnModelUtils.parseCandidateStrategy(userTask),
|
||||
BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()));
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.njcn.bpm.strategy;
|
||||
|
||||
import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum;
|
||||
import com.njcn.bpm.utils.StrUtils;
|
||||
import com.njcn.user.api.UserFeignClient;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户 {@link IBpmTaskCandidateStrategy} 实现类
|
||||
*
|
||||
* @author kyle
|
||||
*/
|
||||
@Component
|
||||
public class BpmTaskCandidateUserStrategy implements IBpmTaskCandidateStrategy {
|
||||
|
||||
@Resource
|
||||
private UserFeignClient adminUserApi;
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
return BpmTaskCandidateStrategyEnum.USER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateParam(String param) {
|
||||
// adminUserApi.validateUserList(StrUtils.splitToLongSet(param));
|
||||
//暂时默认均有效,后期可以加强管理,todo...
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> calculateUsers(DelegateExecution execution, String param) {
|
||||
return StrUtils.splitToStringList(param);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.njcn.bpm.strategy;
|
||||
|
||||
import com.njcn.bpm.enums.BpmTaskCandidateStrategyEnum;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* BPM 任务的候选人的策略接口
|
||||
*
|
||||
* 例如说:分配审批人
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface IBpmTaskCandidateStrategy {
|
||||
|
||||
/**
|
||||
* 对应策略
|
||||
*
|
||||
* @return 策略
|
||||
*/
|
||||
BpmTaskCandidateStrategyEnum getStrategy();
|
||||
|
||||
/**
|
||||
* 校验参数
|
||||
*
|
||||
* @param param 参数
|
||||
*/
|
||||
void validateParam(String param);
|
||||
|
||||
/**
|
||||
* 基于执行任务,获得任务的候选用户们
|
||||
*
|
||||
* @param execution 执行任务
|
||||
* @return 用户编号集合
|
||||
*/
|
||||
List<String> calculateUsers(DelegateExecution execution, String param);
|
||||
|
||||
/**
|
||||
* 是否一定要输入参数
|
||||
*
|
||||
* @return 是否
|
||||
*/
|
||||
default boolean isParamRequired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.enums.BpmModelFormTypeEnum;
|
||||
import com.njcn.bpm.pojo.dto.BpmModelMetaInfoRespDTO;
|
||||
import com.njcn.bpm.pojo.dto.PageResult;
|
||||
import com.njcn.bpm.pojo.param.BpmModelParam;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
|
||||
import com.njcn.bpm.pojo.vo.BpmModelRespVO;
|
||||
import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO;
|
||||
import org.flowable.common.engine.impl.db.SuspensionState;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.Model;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 流程模型 Convert
|
||||
*
|
||||
* @author yunlongn
|
||||
*/
|
||||
@Mapper
|
||||
public interface BpmModelConvert {
|
||||
|
||||
BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class);
|
||||
|
||||
default Page<BpmModelRespVO> buildModelPage(Page<Model> pageResult,
|
||||
Map<String, BpmForm> formMap,
|
||||
Map<String, BpmCategory> categoryMap, Map<String, Deployment> deploymentMap,
|
||||
Map<String, ProcessDefinition> processDefinitionMap) {
|
||||
List<BpmModelRespVO> list = CollectionUtils.convertList(pageResult.getRecords(), model -> {
|
||||
BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model);
|
||||
BpmForm form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null;
|
||||
BpmCategory category = categoryMap.get(model.getCategory());
|
||||
Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null;
|
||||
ProcessDefinition processDefinition = model.getDeploymentId() != null ? processDefinitionMap.get(model.getDeploymentId()) : null;
|
||||
return buildModel0(model, metaInfo, form, category, deployment, processDefinition);
|
||||
});
|
||||
Page<BpmModelRespVO> voPage = new Page<>();
|
||||
voPage.setRecords(list);
|
||||
voPage.setTotal(pageResult.getTotal());
|
||||
voPage.setSize(pageResult.getSize());
|
||||
voPage.setCurrent(pageResult.getCurrent());
|
||||
return voPage;
|
||||
}
|
||||
|
||||
default BpmModelRespVO buildModel(Model model,
|
||||
byte[] bpmnBytes) {
|
||||
BpmModelMetaInfoRespDTO metaInfo = buildMetaInfo(model);
|
||||
BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null);
|
||||
if (ArrayUtil.isNotEmpty(bpmnBytes)) {
|
||||
modelVO.setBpmnXml(new String(bpmnBytes));
|
||||
}
|
||||
return modelVO;
|
||||
}
|
||||
|
||||
default BpmModelRespVO buildModel0(Model model,
|
||||
BpmModelMetaInfoRespDTO metaInfo, BpmForm form, BpmCategory category,
|
||||
Deployment deployment, ProcessDefinition processDefinition) {
|
||||
BpmModelRespVO modelRespVO = new BpmModelRespVO();
|
||||
modelRespVO.setId(model.getId());
|
||||
modelRespVO.setName(model.getName());
|
||||
modelRespVO.setKey(model.getKey());
|
||||
modelRespVO.setCategory(model.getCategory());
|
||||
modelRespVO.setCreateTime(DateUtils.of(model.getCreateTime()));
|
||||
// Form
|
||||
if (metaInfo != null) {
|
||||
modelRespVO.setFormType(metaInfo.getFormType());
|
||||
modelRespVO.setFormId(metaInfo.getFormId());
|
||||
modelRespVO.setFormCustomCreatePath(metaInfo.getFormCustomCreatePath());
|
||||
modelRespVO.setFormCustomViewPath(metaInfo.getFormCustomViewPath());
|
||||
modelRespVO.setIcon(metaInfo.getIcon());
|
||||
modelRespVO.setDescription(metaInfo.getDescription());
|
||||
}
|
||||
if (form != null) {
|
||||
modelRespVO.setFormId(form.getId());
|
||||
modelRespVO.setFormName(form.getName());
|
||||
}
|
||||
// Category
|
||||
if (category != null) {
|
||||
modelRespVO.setCategoryName(category.getName());
|
||||
}
|
||||
// ProcessDefinition
|
||||
if (processDefinition != null) {
|
||||
modelRespVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionInfoVO.class));
|
||||
modelRespVO.getProcessDefinition().setSuspensionState(processDefinition.isSuspended() ?
|
||||
SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
|
||||
if (deployment != null) {
|
||||
modelRespVO.getProcessDefinition().setDeploymentTime(DateUtils.of(deployment.getDeploymentTime()));
|
||||
}
|
||||
}
|
||||
return modelRespVO;
|
||||
}
|
||||
|
||||
default void copyToCreateModel(Model model, BpmModelParam bean) {
|
||||
model.setName(bean.getName());
|
||||
model.setKey(bean.getKey());
|
||||
model.setCategory(bean.getCategory());
|
||||
// model.setMetaInfo(buildMetaInfoStr(null,
|
||||
// null, bean.getDescription(),
|
||||
// null, null, null, null));
|
||||
//类型暂时写死为表单 todo...
|
||||
model.setMetaInfo(buildMetaInfoStr(buildMetaInfo(model),
|
||||
bean.getIcon(), bean.getDescription(),
|
||||
BpmModelFormTypeEnum.NORMAL.getType(), bean.getFormId(), bean.getFormCustomCreatePath(), bean.getFormCustomViewPath()));
|
||||
}
|
||||
|
||||
default void copyToUpdateModel(Model model, BpmModelParam.BpmModelUpdateParam bean) {
|
||||
model.setName(bean.getName());
|
||||
model.setCategory(bean.getCategory());
|
||||
model.setMetaInfo(buildMetaInfoStr(buildMetaInfo(model),
|
||||
bean.getIcon(), bean.getDescription(),
|
||||
bean.getFormType(), bean.getFormId(), bean.getFormCustomCreatePath(), bean.getFormCustomViewPath()));
|
||||
}
|
||||
|
||||
default String buildMetaInfoStr(BpmModelMetaInfoRespDTO metaInfo,
|
||||
String icon, String description,
|
||||
Integer formType, String formId, String formCustomCreatePath, String formCustomViewPath) {
|
||||
if (metaInfo == null) {
|
||||
metaInfo = new BpmModelMetaInfoRespDTO();
|
||||
}
|
||||
// 只有非空,才进行设置,避免更新时的覆盖
|
||||
if (StrUtil.isNotEmpty(icon)) {
|
||||
metaInfo.setIcon(icon);
|
||||
}
|
||||
if (StrUtil.isNotEmpty(description)) {
|
||||
metaInfo.setDescription(description);
|
||||
}
|
||||
if (Objects.nonNull(formType)) {
|
||||
metaInfo.setFormType(formType);
|
||||
metaInfo.setFormId(formId);
|
||||
metaInfo.setFormCustomCreatePath(formCustomCreatePath);
|
||||
metaInfo.setFormCustomViewPath(formCustomViewPath);
|
||||
}
|
||||
return JsonUtils.toJsonString(metaInfo);
|
||||
}
|
||||
|
||||
default BpmModelMetaInfoRespDTO buildMetaInfo(Model model) {
|
||||
return JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.bpm.pojo.po.BpmCategory;
|
||||
import com.njcn.bpm.pojo.po.BpmForm;
|
||||
import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo;
|
||||
import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.common.engine.impl.db.SuspensionState;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Bpm 流程定义的 Convert
|
||||
*
|
||||
* @author yunlong.li
|
||||
*/
|
||||
@Mapper
|
||||
public interface BpmProcessDefinitionConvert {
|
||||
|
||||
BpmProcessDefinitionConvert INSTANCE = Mappers.getMapper(BpmProcessDefinitionConvert.class);
|
||||
|
||||
default Page<BpmProcessDefinitionInfoVO> buildProcessDefinitionPage(Page<ProcessDefinition> page,
|
||||
Map<String, Deployment> deploymentMap,
|
||||
Map<String, BpmProcessDefinitionInfo> processDefinitionInfoMap,
|
||||
Map<String, BpmForm> formMap,
|
||||
Map<String, BpmCategory> categoryMap) {
|
||||
List<BpmProcessDefinitionInfoVO> list = buildProcessDefinitionList(page.getRecords(), deploymentMap, processDefinitionInfoMap, formMap, categoryMap);
|
||||
Page<BpmProcessDefinitionInfoVO> voPage = new Page<>();
|
||||
voPage.setRecords(list);
|
||||
voPage.setTotal(page.getTotal());
|
||||
voPage.setSize(page.getSize());
|
||||
voPage.setCurrent(page.getCurrent());
|
||||
return voPage;
|
||||
}
|
||||
|
||||
default List<BpmProcessDefinitionInfoVO> buildProcessDefinitionList(List<ProcessDefinition> list,
|
||||
Map<String, Deployment> deploymentMap,
|
||||
Map<String, BpmProcessDefinitionInfo> processDefinitionInfoMap,
|
||||
Map<String, BpmForm> formMap,
|
||||
Map<String, BpmCategory> categoryMap) {
|
||||
return CollectionUtils.convertList(list, definition -> {
|
||||
Deployment deployment = MapUtil.get(deploymentMap, definition.getDeploymentId(), Deployment.class);
|
||||
BpmProcessDefinitionInfo processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfo.class);
|
||||
BpmForm form = null;
|
||||
if (processDefinitionInfo != null) {
|
||||
form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmForm.class);
|
||||
}
|
||||
BpmCategory category = MapUtil.get(categoryMap, definition.getCategory(), BpmCategory.class);
|
||||
return buildProcessDefinition(definition, deployment, processDefinitionInfo, form, category, null, null);
|
||||
});
|
||||
}
|
||||
|
||||
default BpmProcessDefinitionInfoVO buildProcessDefinition(ProcessDefinition definition,
|
||||
Deployment deployment,
|
||||
BpmProcessDefinitionInfo processDefinitionInfo,
|
||||
BpmForm form,
|
||||
BpmCategory category,
|
||||
BpmnModel bpmnModel,
|
||||
List<UserTask> startUserSelectUserTaskList) {
|
||||
BpmProcessDefinitionInfoVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionInfoVO.class);
|
||||
respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
|
||||
// Deployment
|
||||
if (deployment != null) {
|
||||
respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime()));
|
||||
}
|
||||
// BpmProcessDefinitionInfoDO
|
||||
if (processDefinitionInfo != null) {
|
||||
copyTo(processDefinitionInfo, respVO);
|
||||
// Form
|
||||
if (form != null) {
|
||||
respVO.setFormName(form.getName());
|
||||
}
|
||||
}
|
||||
// Category
|
||||
if (category != null) {
|
||||
respVO.setCategoryName(category.getName());
|
||||
}
|
||||
// BpmnModel
|
||||
if (bpmnModel != null) {
|
||||
respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));
|
||||
respVO.setStartUserSelectTasks(BeanUtils.toBean(startUserSelectUserTaskList, BpmProcessDefinitionInfoVO.UserTask.class));
|
||||
}
|
||||
return respVO;
|
||||
}
|
||||
|
||||
@Mapping(source = "from.id", target = "to.id", ignore = true)
|
||||
void copyTo(BpmProcessDefinitionInfo from, @MappingTarget BpmProcessDefinitionInfoVO to);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.njcn.bpm.enums.BpmnModelConstants;
|
||||
import org.flowable.bpmn.converter.BpmnXMLConverter;
|
||||
import org.flowable.bpmn.model.Process;
|
||||
import org.flowable.bpmn.model.*;
|
||||
import org.flowable.common.engine.impl.util.io.BytesStreamSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 流程模型转操作工具类
|
||||
*/
|
||||
public class BpmnModelUtils {
|
||||
|
||||
public static Integer parseCandidateStrategy(FlowElement userTask) {
|
||||
return NumberUtils.parseInt(userTask.getAttributeValue(
|
||||
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
|
||||
}
|
||||
|
||||
public static String parseCandidateParam(FlowElement userTask) {
|
||||
return userTask.getAttributeValue(
|
||||
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据节点,获取入口连线
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @return 入口连线列表
|
||||
*/
|
||||
public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) {
|
||||
if (source instanceof FlowNode) {
|
||||
return ((FlowNode) source).getIncomingFlows();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据节点,获取出口连线
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @return 出口连线列表
|
||||
*/
|
||||
public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) {
|
||||
if (source instanceof FlowNode) {
|
||||
return ((FlowNode) source).getOutgoingFlows();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程元素信息
|
||||
*
|
||||
* @param model bpmnModel 对象
|
||||
* @param flowElementId 元素 ID
|
||||
* @return 元素信息
|
||||
*/
|
||||
public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) {
|
||||
Process process = model.getMainProcess();
|
||||
return process.getFlowElement(flowElementId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 BPMN 流程中,指定的元素们
|
||||
*
|
||||
* @param model 模型
|
||||
* @param clazz 指定元素。例如说,{@link UserTask}、{@link Gateway} 等等
|
||||
* @return 元素们
|
||||
*/
|
||||
public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {
|
||||
List<T> result = new ArrayList<>();
|
||||
model.getProcesses().forEach(process -> {
|
||||
process.getFlowElements().forEach(flowElement -> {
|
||||
if (flowElement.getClass().isAssignableFrom(clazz)) {
|
||||
result.add((T) flowElement);
|
||||
}
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public static StartEvent getStartEvent(BpmnModel model) {
|
||||
Process process = model.getMainProcess();
|
||||
// 从 initialFlowElement 找
|
||||
FlowElement startElement = process.getInitialFlowElement();
|
||||
if (startElement instanceof StartEvent) {
|
||||
return (StartEvent) startElement;
|
||||
}
|
||||
// 从 flowElementList 找
|
||||
return (StartEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof StartEvent);
|
||||
}
|
||||
|
||||
public static BpmnModel getBpmnModel(byte[] bpmnBytes) {
|
||||
if (ArrayUtil.isEmpty(bpmnBytes)) {
|
||||
return null;
|
||||
}
|
||||
BpmnXMLConverter converter = new BpmnXMLConverter();
|
||||
// 补充说明:由于在 Flowable 中自定义了属性,所以 validateSchema 传递 false
|
||||
return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false);
|
||||
}
|
||||
|
||||
public static String getBpmnXml(BpmnModel model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
BpmnXMLConverter converter = new BpmnXMLConverter();
|
||||
return new String(converter.convertToXML(model));
|
||||
}
|
||||
|
||||
// ========== 遍历相关的方法 ==========
|
||||
|
||||
/**
|
||||
* 找到 source 节点之前的所有用户任务节点
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param userTaskList 已找到的用户任务节点
|
||||
* @return 用户任务节点 数组
|
||||
*/
|
||||
public static List<UserTask> getPreviousUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
|
||||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
||||
if (source instanceof StartEvent && source.getSubProcess() != null) {
|
||||
userTaskList = getPreviousUserTaskList(source.getSubProcess(), hasSequenceFlow, userTaskList);
|
||||
}
|
||||
|
||||
// 根据类型,获取入口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
|
||||
if (sequenceFlows == null) {
|
||||
return userTaskList;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 类型为用户节点,则新增父级节点
|
||||
if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
|
||||
userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement());
|
||||
}
|
||||
// 类型为子流程,则添加子流程开始节点出口处相连的节点
|
||||
if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
|
||||
// 获取子流程用户任务节点
|
||||
List<UserTask> childUserTaskList = findChildProcessUserTaskList((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (CollUtil.isNotEmpty(childUserTaskList)) {
|
||||
userTaskList.addAll(childUserTaskList);
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
userTaskList = getPreviousUserTaskList(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList);
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 迭代获取子流程用户任务节点
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param userTaskList 需要撤回的用户任务列表
|
||||
* @return 用户任务节点
|
||||
*/
|
||||
public static List<UserTask> findChildProcessUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
|
||||
|
||||
// 根据类型,获取出口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
|
||||
if (sequenceFlows == null) {
|
||||
return userTaskList;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof UserTask) {
|
||||
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
|
||||
continue;
|
||||
}
|
||||
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
|
||||
List<UserTask> childUserTaskList = findChildProcessUserTaskList((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (CollUtil.isNotEmpty(childUserTaskList)) {
|
||||
userTaskList.addAll(childUserTaskList);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
userTaskList = findChildProcessUserTaskList(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList);
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行
|
||||
* 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param target 目标节点
|
||||
* @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set<String> visitedElements) {
|
||||
visitedElements = visitedElements == null ? new HashSet<>() : visitedElements;
|
||||
// 不能是开始事件和子流程
|
||||
if (source instanceof StartEvent && isInEventSubprocess(source)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 根据类型,获取入口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
|
||||
if (CollUtil.isEmpty(sequenceFlows)) {
|
||||
return true;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (visitedElements.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
visitedElements.add(sequenceFlow.getId());
|
||||
// 这条线路存在目标节点,这条线路完成,进入下个线路
|
||||
FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement();
|
||||
if (target.getId().equals(sourceFlowElement.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 如果目标节点为并行网关,则不继续
|
||||
if (sourceFlowElement instanceof ParallelGateway) {
|
||||
return false;
|
||||
}
|
||||
// 否则就继续迭代
|
||||
if (!isSequentialReachable(sourceFlowElement, target, visitedElements)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前节点是否属于不同的子流程
|
||||
*
|
||||
* @param flowElement 被判断的节点
|
||||
* @return true 表示属于子流程
|
||||
*/
|
||||
private static boolean isInEventSubprocess(FlowElement flowElement) {
|
||||
FlowElementsContainer flowElementsContainer = flowElement.getParentContainer();
|
||||
while (flowElementsContainer != null) {
|
||||
if (flowElementsContainer instanceof EventSubProcess) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flowElementsContainer instanceof FlowElement) {
|
||||
flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer();
|
||||
} else {
|
||||
flowElementsContainer = null;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找
|
||||
*
|
||||
* @param source 起始节点
|
||||
* @param runTaskKeyList 正在运行的任务 Key,用于校验任务节点是否是正在运行的节点
|
||||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
|
||||
* @param userTaskList 需要撤回的用户任务列表
|
||||
* @return 子级任务节点列表
|
||||
*/
|
||||
public static List<UserTask> iteratorFindChildUserTasks(FlowElement source, List<String> runTaskKeyList,
|
||||
Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
|
||||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
|
||||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
|
||||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
||||
if (source instanceof StartEvent && source.getSubProcess() != null) {
|
||||
userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList);
|
||||
}
|
||||
|
||||
// 根据类型,获取出口连线
|
||||
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
|
||||
if (sequenceFlows == null) {
|
||||
return userTaskList;
|
||||
}
|
||||
// 循环找到目标元素
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
// 如果发现连线重复,说明循环了,跳过这个循环
|
||||
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 添加已经走过的连线
|
||||
hasSequenceFlow.add(sequenceFlow.getId());
|
||||
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) {
|
||||
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
|
||||
continue;
|
||||
}
|
||||
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
|
||||
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
|
||||
List<UserTask> childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null);
|
||||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
||||
if (CollUtil.isNotEmpty(childUserTaskList)) {
|
||||
userTaskList.addAll(childUserTaskList);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 继续迭代
|
||||
userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList);
|
||||
}
|
||||
return userTaskList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import com.njcn.bpm.enums.BpmConstants;
|
||||
import org.flowable.common.engine.api.delegate.Expression;
|
||||
import org.flowable.common.engine.api.variable.VariableContainer;
|
||||
import org.flowable.common.engine.impl.el.ExpressionManager;
|
||||
import org.flowable.common.engine.impl.identity.Authentication;
|
||||
import org.flowable.engine.ProcessEngineConfiguration;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
|
||||
import org.flowable.engine.impl.util.CommandContextUtil;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.task.api.TaskInfo;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Flowable 相关的工具方法
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class FlowableUtils {
|
||||
|
||||
// ========== User 相关的工具方法 ==========
|
||||
|
||||
public static void setAuthenticatedUserId(Long userId) {
|
||||
Authentication.setAuthenticatedUserId(String.valueOf(userId));
|
||||
}
|
||||
|
||||
public static void clearAuthenticatedUserId() {
|
||||
Authentication.setAuthenticatedUserId(null);
|
||||
}
|
||||
|
||||
public static String getTenantId() {
|
||||
return ProcessEngineConfiguration.NO_TENANT_ID;
|
||||
}
|
||||
|
||||
// ========== Execution 相关的工具方法 ==========
|
||||
|
||||
/**
|
||||
* 格式化多实例(并签、或签)的 collectionVariable 变量(多实例对应的多审批人列表)
|
||||
*
|
||||
* @param activityId 活动编号
|
||||
* @return collectionVariable 变量
|
||||
*/
|
||||
public static String formatExecutionCollectionVariable(String activityId) {
|
||||
return activityId + "_assignees";
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化多实例(并签、或签)的 collectionElementVariable 变量(当前实例对应的一个审批人)
|
||||
*
|
||||
* @param activityId 活动编号
|
||||
* @return collectionElementVariable 变量
|
||||
*/
|
||||
public static String formatExecutionCollectionElementVariable(String activityId) {
|
||||
return activityId + "_assignee";
|
||||
}
|
||||
|
||||
// ========== ProcessInstance 相关的工具方法 ==========
|
||||
|
||||
public static Integer getProcessInstanceStatus(ProcessInstance processInstance) {
|
||||
return getProcessInstanceStatus(processInstance.getProcessVariables());
|
||||
}
|
||||
|
||||
public static Integer getProcessInstanceStatus(HistoricProcessInstance processInstance) {
|
||||
return getProcessInstanceStatus(processInstance.getProcessVariables());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程实例的状态
|
||||
*
|
||||
* @param processVariables 流程实例的 variables
|
||||
* @return 状态
|
||||
*/
|
||||
private static Integer getProcessInstanceStatus(Map<String, Object> processVariables) {
|
||||
return (Integer) processVariables.get(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程实例的表单
|
||||
*
|
||||
* @param processInstance 流程实例
|
||||
* @return 表单
|
||||
*/
|
||||
public static Map<String, Object> getProcessInstanceFormVariable(HistoricProcessInstance processInstance) {
|
||||
Map<String, Object> formVariables = new HashMap<>(processInstance.getProcessVariables());
|
||||
filterProcessInstanceFormVariable(formVariables);
|
||||
return formVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤流程实例的表单
|
||||
*
|
||||
* 为什么要过滤?目前使用 processVariables 存储所有流程实例的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示
|
||||
*
|
||||
* @param processVariables 流程实例的 variables
|
||||
* @return 过滤后的表单
|
||||
*/
|
||||
public static Map<String, Object> filterProcessInstanceFormVariable(Map<String, Object> processVariables) {
|
||||
processVariables.remove(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
|
||||
return processVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得流程实例的发起用户选择的审批人 Map
|
||||
*
|
||||
* @param processInstance 流程实例
|
||||
* @return 发起用户选择的审批人 Map
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map<String, List<String>> getStartUserSelectAssignees(ProcessInstance processInstance) {
|
||||
return (Map<String, List<String>>) processInstance.getProcessVariables().get(
|
||||
BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
|
||||
}
|
||||
|
||||
// ========== Task 相关的工具方法 ==========
|
||||
|
||||
/**
|
||||
* 获得任务的状态
|
||||
*
|
||||
* @param task 任务
|
||||
* @return 状态
|
||||
*/
|
||||
public static Integer getTaskStatus(TaskInfo task) {
|
||||
return (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得任务的审批原因
|
||||
*
|
||||
* @param task 任务
|
||||
* @return 审批原因
|
||||
*/
|
||||
public static String getTaskReason(TaskInfo task) {
|
||||
return (String) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_REASON);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得任务的表单
|
||||
*
|
||||
* @param task 任务
|
||||
* @return 表单
|
||||
*/
|
||||
public static Map<String, Object> getTaskFormVariable(TaskInfo task) {
|
||||
Map<String, Object> formVariables = new HashMap<>(task.getTaskLocalVariables());
|
||||
filterTaskFormVariable(formVariables);
|
||||
return formVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤任务的表单
|
||||
*
|
||||
* 为什么要过滤?目前使用 taskLocalVariables 存储所有任务的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示
|
||||
*
|
||||
* @param taskLocalVariables 任务的 taskLocalVariables
|
||||
* @return 过滤后的表单
|
||||
*/
|
||||
public static Map<String, Object> filterTaskFormVariable(Map<String, Object> taskLocalVariables) {
|
||||
taskLocalVariables.remove(BpmConstants.TASK_VARIABLE_STATUS);
|
||||
taskLocalVariables.remove(BpmConstants.TASK_VARIABLE_REASON);
|
||||
return taskLocalVariables;
|
||||
}
|
||||
|
||||
// ========== Expression 相关的工具方法 ==========
|
||||
|
||||
public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) {
|
||||
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
|
||||
assert processEngineConfiguration != null;
|
||||
ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
|
||||
assert expressionManager != null;
|
||||
Expression expression = expressionManager.createExpression(expressionString);
|
||||
return expression.getValue(variableContainer);
|
||||
}
|
||||
|
||||
}
|
||||
202
pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/JsonUtils.java
Normal file
202
pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/JsonUtils.java
Normal file
@@ -0,0 +1,202 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JSON 工具类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
public class JsonUtils {
|
||||
|
||||
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
static {
|
||||
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 值
|
||||
objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 objectMapper 属性
|
||||
* <p>
|
||||
* 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean
|
||||
*
|
||||
* @param objectMapper ObjectMapper 对象
|
||||
*/
|
||||
public static void init(ObjectMapper objectMapper) {
|
||||
JsonUtils.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String toJsonString(Object object) {
|
||||
return objectMapper.writeValueAsString(object);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static byte[] toJsonByte(Object object) {
|
||||
return objectMapper.writeValueAsBytes(object);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String toJsonPrettyString(Object object) {
|
||||
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(text, clazz);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, String path, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JsonNode treeNode = objectMapper.readTree(text);
|
||||
JsonNode pathNode = treeNode.path(path);
|
||||
return objectMapper.readValue(pathNode.toString(), clazz);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, Type type) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type));
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串解析成指定类型的对象
|
||||
* 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下,
|
||||
* 如果 text 没有 class 属性,则会报错。此时,使用这个方法,可以解决。
|
||||
*
|
||||
* @param text 字符串
|
||||
* @param clazz 类型
|
||||
* @return 对象
|
||||
*/
|
||||
public static <T> T parseObject2(String text, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
return JSONUtil.toBean(text, clazz);
|
||||
}
|
||||
|
||||
public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
|
||||
if (ArrayUtil.isEmpty(bytes)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(bytes, clazz);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", bytes, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T parseObject(String text, TypeReference<T> typeReference) {
|
||||
try {
|
||||
return objectMapper.readValue(text, typeReference);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 JSON 字符串成指定类型的对象,如果解析失败,则返回 null
|
||||
*
|
||||
* @param text 字符串
|
||||
* @param typeReference 类型引用
|
||||
* @return 指定类型的对象
|
||||
*/
|
||||
public static <T> T parseObjectQuietly(String text, TypeReference<T> typeReference) {
|
||||
try {
|
||||
return objectMapper.readValue(text, typeReference);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> List<T> parseArray(String text, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> List<T> parseArray(String text, String path, Class<T> clazz) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JsonNode treeNode = objectMapper.readTree(text);
|
||||
JsonNode pathNode = treeNode.path(path);
|
||||
return objectMapper.readValue(pathNode.toString(), objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonNode parseTree(String text) {
|
||||
try {
|
||||
return objectMapper.readTree(text);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonNode parseTree(byte[] text) {
|
||||
try {
|
||||
return objectMapper.readTree(text);
|
||||
} catch (IOException e) {
|
||||
log.error("json parse err,json:{}", text, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isJson(String text) {
|
||||
return JSONUtil.isTypeJSON(text);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.njcn.bpm.utils;
|
||||
|
||||
import cn.hutool.core.text.StrPool;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class StrUtils {
|
||||
|
||||
public static String maxLength(CharSequence str, int maxLength) {
|
||||
return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定字符串是否以任何一个字符串开始
|
||||
* 给定字符串和数组为空都返回 false
|
||||
*
|
||||
* @param str 给定字符串
|
||||
* @param prefixes 需要检测的开始字符串
|
||||
* @since 3.0.6
|
||||
*/
|
||||
public static boolean startWithAny(String str, Collection<String> prefixes) {
|
||||
if (StrUtil.isEmpty(str) || ArrayUtil.isEmpty(prefixes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (CharSequence suffix : prefixes) {
|
||||
if (StrUtil.startWith(str, suffix, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<Long> splitToLong(String value, CharSequence separator) {
|
||||
long[] longs = StrUtil.splitToLong(value, separator);
|
||||
return Arrays.stream(longs).boxed().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static Set<Long> splitToLongSet(String value) {
|
||||
return splitToLongSet(value, StrPool.COMMA);
|
||||
}
|
||||
|
||||
public static Set<Long> splitToLongSet(String value, CharSequence separator) {
|
||||
long[] longs = StrUtil.splitToLong(value, separator);
|
||||
return Arrays.stream(longs).boxed().collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static List<Integer> splitToInteger(String value, CharSequence separator) {
|
||||
int[] integers = StrUtil.splitToInt(value, separator);
|
||||
return Arrays.stream(integers).boxed().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除字符串中,包含指定字符串的行
|
||||
*
|
||||
* @param content 字符串
|
||||
* @param sequence 包含的字符串
|
||||
* @return 移除后的字符串
|
||||
*/
|
||||
public static String removeLineContains(String content, String sequence) {
|
||||
if (StrUtil.isEmpty(content) || StrUtil.isEmpty(sequence)) {
|
||||
return content;
|
||||
}
|
||||
return Arrays.stream(content.split("\n"))
|
||||
.filter(line -> !line.contains(sequence))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
public static List<String> splitToStringList(String value) {
|
||||
return StrUtil.split(value, StrPool.COMMA);
|
||||
}
|
||||
}
|
||||
69
pqs-bpm/bpm-boot/src/main/resources/bootstrap.yml
Normal file
69
pqs-bpm/bpm-boot/src/main/resources/bootstrap.yml
Normal file
@@ -0,0 +1,69 @@
|
||||
#当前服务的基本信息
|
||||
microservice:
|
||||
ename: @artifactId@
|
||||
name: '@name@'
|
||||
version: @version@
|
||||
sentinel:
|
||||
url: @sentinel.url@
|
||||
gateway:
|
||||
url: @gateway.url@
|
||||
server:
|
||||
port: 10321
|
||||
#feign接口开启服务熔断降级处理
|
||||
feign:
|
||||
sentinel:
|
||||
enabled: true
|
||||
spring:
|
||||
application:
|
||||
name: @artifactId@
|
||||
#nacos注册中心以及配置中心的指定
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
ip: @service.server.url@
|
||||
server-addr: @nacos.url@
|
||||
namespace: @nacos.namespace@
|
||||
config:
|
||||
server-addr: @nacos.url@
|
||||
namespace: @nacos.namespace@
|
||||
file-extension: yaml
|
||||
shared-configs:
|
||||
- data-id: share-config.yaml
|
||||
refresh: true
|
||||
- data-Id: bpm-config.yaml
|
||||
refresh: true
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 100MB
|
||||
max-request-size: 100MB
|
||||
|
||||
flowable:
|
||||
database-schema-update: false
|
||||
db-history-used: true # flowable6 默认 true 生成信息表,无需手动设置
|
||||
check-process-definitions: false # 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程
|
||||
history-level: full # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数
|
||||
async-executor-activate: false
|
||||
|
||||
#项目日志的配置
|
||||
logging:
|
||||
config: http://@nacos.url@/nacos/v1/cs/configs?tenant=@nacos.namespace@&group=DEFAULT_GROUP&dataId=logback.xml
|
||||
level:
|
||||
root: info
|
||||
|
||||
|
||||
#mybatis配置信息
|
||||
mybatis-plus:
|
||||
type-aliases-package: com.njcn.bpm.pojo
|
||||
|
||||
gw:
|
||||
url: dwzyywzt-pms3-proxy.com
|
||||
code: 13B9B47F1E483324E05338297A0A0595
|
||||
|
||||
mqtt:
|
||||
client-id: @artifactId@${random.value}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user