feat : 模块抽离 + 自定义查分表模块基本完成
This commit is contained in:
142
workflow/pom.xml
Normal file
142
workflow/pom.xml
Normal file
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>visual</artifactId>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>workflow</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<flowable.version>6.8.0</flowable.version>
|
||||
<maven.deploy.skip>true</maven.deploy.skip>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<!--安全组件-->
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-security</artifactId>
|
||||
</dependency>
|
||||
<!--swagger文档-->
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-swagger</artifactId>
|
||||
</dependency>
|
||||
<!--日志记录-->
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-log</artifactId>
|
||||
</dependency>
|
||||
<!--mybatis组件-->
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-mybatis</artifactId>
|
||||
</dependency>
|
||||
<!--文件组件-->
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-file</artifactId>
|
||||
</dependency>
|
||||
<!--邮件发送组件-->
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-email</artifactId>
|
||||
</dependency>
|
||||
<!--软件消息通知-->
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>notice-api</artifactId>
|
||||
</dependency>
|
||||
<!-- 代码执行引擎 -->
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-code</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>xalan</groupId>
|
||||
<artifactId>serializer</artifactId>
|
||||
<version>2.7.2</version>
|
||||
</dependency>
|
||||
<!-- flowable -->
|
||||
<dependency>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-spring-boot-starter</artifactId>
|
||||
<version>${flowable.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-spring-boot-starter-app</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-form-spring-configurator</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-idm-spring-configurator</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>mybatis</artifactId>
|
||||
<groupId>org.mybatis</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.liquibase</groupId>
|
||||
<artifactId>liquibase-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.liquibase</groupId>
|
||||
<artifactId>liquibase-core</artifactId>
|
||||
<version>4.9.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-bpmn-model</artifactId>
|
||||
<version>${flowable.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-json-converter</artifactId>
|
||||
<version>${flowable.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-bpmn-converter</artifactId>
|
||||
<version>${flowable.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-excel</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.7.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,20 @@
|
||||
package cn.fateverse.workflow;
|
||||
|
||||
import cn.fateverse.common.security.annotation.EnableSecurity;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/22
|
||||
*/
|
||||
@EnableSecurity
|
||||
@EnableDiscoveryClient
|
||||
@SpringBootApplication
|
||||
public class WorkFlowApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(WorkFlowApplication.class, args);
|
||||
System.out.println("工作流模块启动成功");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package cn.fateverse.workflow.constant;
|
||||
|
||||
/**
|
||||
* 引擎场景
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/23
|
||||
*/
|
||||
public class ProcessConstant {
|
||||
/**
|
||||
* root 节点id
|
||||
*/
|
||||
public static final String ROOT_NODE = "root";
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
public static final String FORM_VAR = "formData";
|
||||
/**
|
||||
* 流程状态
|
||||
*/
|
||||
public static final String PROCESS_STATUS = "processStatus";
|
||||
/**
|
||||
* 发起人
|
||||
*/
|
||||
public static final String START_USER_INFO = "startUser";
|
||||
/**
|
||||
* 自选用户
|
||||
*/
|
||||
public static final String OPTIONAL_USER = "optionalUser";
|
||||
/**
|
||||
* 审批人
|
||||
*/
|
||||
public static final String ASSIGNEE_NAME = "assigneeName";
|
||||
/**
|
||||
* 用户任务类型
|
||||
*/
|
||||
public static final String USER_TASK = "userTask";
|
||||
/**
|
||||
* 发起人id
|
||||
*/
|
||||
public static final String START_USER_ID = "startUserId";
|
||||
/**
|
||||
* 流程执行过程中需要使用到的工具类
|
||||
*/
|
||||
public static final String PROCESS_UTIL_CLASS = "processUtil.";
|
||||
/**
|
||||
* gateway结束名称
|
||||
*/
|
||||
public static final String GATEWAY_END = "gatewayEnd";
|
||||
/**
|
||||
* 正在处理
|
||||
*/
|
||||
public static final String BUSINESS_STATUS_DEAL = "1";
|
||||
/**
|
||||
* 撤销
|
||||
*/
|
||||
public static final String BUSINESS_STATUS_REVOCATION = "2";
|
||||
/**
|
||||
* 驳回
|
||||
*/
|
||||
public static final String BUSINESS_STATUS_REJECT = "3";
|
||||
/**
|
||||
* 已结束
|
||||
*/
|
||||
public static final String BUSINESS_STATUS_ENDED = "4";
|
||||
/**
|
||||
* 审批意见
|
||||
*/
|
||||
public static final String COMMENT_OPINION = "opinion";
|
||||
/**
|
||||
* 评论
|
||||
*/
|
||||
public static final String COMMENT_COMMENT = "comment";
|
||||
/**
|
||||
* 签名
|
||||
*/
|
||||
public static final String COMMENT_SIGN = "sign";
|
||||
/**
|
||||
* 拒绝
|
||||
*/
|
||||
public static final String COMMENT_REFUSE = "refuse";
|
||||
/**
|
||||
* 流程运行状态
|
||||
*/
|
||||
public static final String PROCESS_RUNNING = "running";
|
||||
/**
|
||||
* 流程同意
|
||||
*/
|
||||
public static final String PROCESS_AGREE = "agree";
|
||||
/**
|
||||
* 通过
|
||||
*/
|
||||
public static final String PROCESS_PASS = "pass";
|
||||
/**
|
||||
* 未激活
|
||||
*/
|
||||
public static final String PROCESS_UNACTIVATED = "unactivated";
|
||||
/**
|
||||
* 创建
|
||||
*/
|
||||
public static final String PROCESS_CREATE = "create";
|
||||
/**
|
||||
* 审批人列表
|
||||
*/
|
||||
public static final String ASSIGNEE_LIST = "assigneeList";
|
||||
/**
|
||||
* bpmn流程表单数据
|
||||
*/
|
||||
public static final String BPMN_FORM_ITEMS = "formItems";
|
||||
/**
|
||||
* bpmn流程json数据
|
||||
*/
|
||||
public static final String BPMN_FORM_PROCESS = "process";
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package cn.fateverse.workflow.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.common.core.result.Result;
|
||||
import cn.fateverse.common.core.result.page.TableDataInfo;
|
||||
import cn.fateverse.common.log.annotation.Log;
|
||||
import cn.fateverse.common.log.enums.BusinessType;
|
||||
import cn.fateverse.workflow.entity.dto.ProcessDto;
|
||||
import cn.fateverse.workflow.entity.query.ProcessQuery;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessDefinitionHistoryVo;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessDefinitionInfoVo;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessDefinitionVo;
|
||||
import cn.fateverse.workflow.service.ProcessDefinitionService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工作流流程部署
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/22
|
||||
*/
|
||||
@Api(value = "工作流流程部署", tags = "工作流流程部署")
|
||||
@RestController
|
||||
@RequestMapping("/process/definition")
|
||||
public class ProcessDefinitionController {
|
||||
|
||||
private final ProcessDefinitionService processDefinitionService;
|
||||
|
||||
public ProcessDefinitionController(ProcessDefinitionService processDefinitionService) {
|
||||
this.processDefinitionService = processDefinitionService;
|
||||
}
|
||||
|
||||
@ApiOperation("工作流流程查询")
|
||||
@GetMapping
|
||||
@PreAuthorize("@ss.hasPermission('workflow:definition:list')")
|
||||
public Result<TableDataInfo<ProcessDefinitionVo>> list(ProcessQuery query) {
|
||||
TableDataInfo<ProcessDefinitionVo> dataTable = processDefinitionService.searchList(query);
|
||||
return Result.ok(dataTable);
|
||||
}
|
||||
|
||||
@ApiOperation("工作流查询")
|
||||
@GetMapping("/{deploymentId}")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:definition:info')")
|
||||
public Result<ProcessDefinitionInfoVo> info(@PathVariable String deploymentId){
|
||||
ProcessDefinitionInfoVo infoVo = processDefinitionService.searchByDeploymentId(deploymentId);
|
||||
return Result.ok(infoVo);
|
||||
}
|
||||
|
||||
@ApiOperation("工作流查询")
|
||||
@GetMapping("/key/{processDefinitionKey}")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:definition:info')")
|
||||
public Result<ProcessDefinitionInfoVo> initiateInfo(@PathVariable String processDefinitionKey){
|
||||
ProcessDefinitionInfoVo infoVo = processDefinitionService.searchInitiateInfoByKey(processDefinitionKey);
|
||||
return Result.ok(infoVo);
|
||||
}
|
||||
|
||||
@ApiOperation("工作流历史流程查询")
|
||||
@GetMapping("/history/{processDefinitionKey}")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:definition:history')")
|
||||
public Result<List<ProcessDefinitionHistoryVo>> history(@PathVariable String processDefinitionKey) {
|
||||
List<ProcessDefinitionHistoryVo> processVos = processDefinitionService.searchHistoryList(processDefinitionKey);
|
||||
return Result.ok(processVos);
|
||||
}
|
||||
|
||||
@ApiOperation("工作流挂起")
|
||||
@PutMapping("/suspend")
|
||||
@Log(title = "工作流挂起",businessType = BusinessType.UPDATE)
|
||||
@PreAuthorize("@ss.hasPermission('workflow:definition:suspend')")
|
||||
public Result<Void> suspend(@RequestBody String processDefinitionId) {
|
||||
checkProcessDefinitionId(processDefinitionId);
|
||||
processDefinitionService.suspend(processDefinitionId);
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
@ApiOperation("工作流激活")
|
||||
@PutMapping("/activate")
|
||||
@Log(title = "工作流激活",businessType = BusinessType.UPDATE)
|
||||
@PreAuthorize("@ss.hasPermission('workflow:definition:activate')")
|
||||
public Result<Void> activate(@RequestBody String processDefinitionId) {
|
||||
checkProcessDefinitionId(processDefinitionId);
|
||||
processDefinitionService.activate(processDefinitionId);
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
@ApiOperation("删除工作流")
|
||||
@DeleteMapping("/{deploymentId}")
|
||||
@Log(title = "删除工作流",businessType = BusinessType.UPDATE)
|
||||
@PreAuthorize("@ss.hasPermission('workflow:definition:activate')")
|
||||
public Result<Void> delete(@PathVariable String deploymentId) {
|
||||
checkProcessDefinitionId(deploymentId);
|
||||
processDefinitionService.delete(deploymentId);
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
@ApiOperation("工作流流程部署")
|
||||
@PostMapping
|
||||
@Log(title = "工作流流程部署",businessType = BusinessType.UPDATE)
|
||||
@PreAuthorize("@ss.hasPermission('workflow:definition:add')")
|
||||
public Result<Void> add(@RequestBody ProcessDto processDto) {
|
||||
processDefinitionService.add(processDto);
|
||||
return Result.ok("工作流程部署成功!");
|
||||
}
|
||||
|
||||
private void checkProcessDefinitionId(String processDefinitionId) {
|
||||
if (StrUtil.isEmpty(processDefinitionId)) {
|
||||
throw new CustomException("流程id不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package cn.fateverse.workflow.controller;
|
||||
|
||||
import cn.fateverse.common.core.result.Result;
|
||||
import cn.fateverse.workflow.entity.bpmn.Attachment;
|
||||
import cn.fateverse.workflow.service.ProcessFileService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 工作流模块文件上传
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023/1/10
|
||||
*/
|
||||
|
||||
@Api(value = "工作流文件上传", tags = "工作流文件上传")
|
||||
@RestController
|
||||
@RequestMapping("/process/file")
|
||||
public class ProcessFileController {
|
||||
|
||||
private final ProcessFileService processFileService;
|
||||
|
||||
public ProcessFileController(ProcessFileService processFileService) {
|
||||
this.processFileService = processFileService;
|
||||
}
|
||||
|
||||
@ApiOperation("上传文件")
|
||||
@PostMapping
|
||||
public Result<Attachment> uploadFile(MultipartFile file) throws Exception {
|
||||
Attachment attachment = processFileService.uploadFile(file);
|
||||
return Result.ok(attachment);
|
||||
}
|
||||
|
||||
@ApiOperation("删除文件")
|
||||
@DeleteMapping("/{fileId}")
|
||||
public Result<Void> deleteFile(@PathVariable Long fileId) {
|
||||
processFileService.deleteFile(fileId);
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package cn.fateverse.workflow.controller;
|
||||
|
||||
import cn.fateverse.common.core.result.Result;
|
||||
import cn.fateverse.common.core.result.page.TableDataInfo;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessInstanceDetailVo;
|
||||
import cn.fateverse.workflow.entity.dto.ProcessInstanceDto;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessInstanceVo;
|
||||
import cn.fateverse.workflow.service.ProcessInstanceService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 工作流流程实例
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/24
|
||||
*/
|
||||
@Api(value = "工作流流程实例" , tags = "工作流流程实例")
|
||||
@RestController
|
||||
@RequestMapping("/process/instance")
|
||||
public class ProcessInstanceController {
|
||||
|
||||
private final ProcessInstanceService processInstanceService;
|
||||
|
||||
public ProcessInstanceController(ProcessInstanceService processInstanceService) {
|
||||
this.processInstanceService = processInstanceService;
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation("工作流流程查询")
|
||||
@GetMapping
|
||||
@PreAuthorize("@ss.hasPermission('workflow:instance:list')")
|
||||
public Result<TableDataInfo<ProcessInstanceVo>> list(String state) {
|
||||
TableDataInfo<ProcessInstanceVo> dataInfo = processInstanceService.searchList(state);
|
||||
return Result.ok(dataInfo);
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation("工作流流程查询")
|
||||
@GetMapping("/self")
|
||||
public Result<TableDataInfo<ProcessInstanceVo>> listSelf(String state, String departmentName) {
|
||||
TableDataInfo<ProcessInstanceVo> dataInfo = processInstanceService.searchSelfList(state, departmentName);
|
||||
return Result.ok(dataInfo);
|
||||
}
|
||||
|
||||
@ApiOperation("工作流流程查询")
|
||||
@GetMapping("/about")
|
||||
public Result<TableDataInfo<ProcessInstanceVo>> aboutMeList(String state, String deploymentName) {
|
||||
TableDataInfo<ProcessInstanceVo> dataInfo = processInstanceService.searchAboutMeList(state, deploymentName);
|
||||
return Result.ok(dataInfo);
|
||||
}
|
||||
|
||||
@ApiOperation("工作流流程查询")
|
||||
@GetMapping("/info/{processInstanceId}")
|
||||
public Result<ProcessInstanceDetailVo> selfInfo(@PathVariable String processInstanceId) {
|
||||
ProcessInstanceDetailVo detailVo = processInstanceService.searchSelfInfo(processInstanceId);
|
||||
return Result.ok(detailVo);
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation("以现有流程数据为基础,重新启动流程(获取数据信息)")
|
||||
@GetMapping("/re/info/{instanceId}")
|
||||
public Result<ProcessInstanceDetailVo> reInfo(@PathVariable String instanceId) {
|
||||
ProcessInstanceDetailVo detailVo = processInstanceService.searchReInfo(instanceId);
|
||||
return Result.ok(detailVo);
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation("开始工作流实例")
|
||||
@PostMapping("/start")
|
||||
public Result<Void> start(@RequestBody @Validated ProcessInstanceDto processInstanceDto) {
|
||||
processInstanceService.start(processInstanceDto);
|
||||
return Result.ok("流程启动成功");
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation("重新工作流实例")
|
||||
@PostMapping("/restart")
|
||||
public Result<Void> restart(@RequestBody @Validated ProcessInstanceDto processInstanceDto) {
|
||||
processInstanceService.restart(processInstanceDto);
|
||||
return Result.ok("流程启动成功");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package cn.fateverse.workflow.controller;
|
||||
|
||||
import cn.fateverse.common.excel.utils.ExcelUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.fateverse.common.core.entity.Option;
|
||||
import cn.fateverse.workflow.entity.dto.ProcessListenerDto;
|
||||
import cn.fateverse.workflow.entity.query.ProcessListenerQuery;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessListenerVo;
|
||||
import cn.fateverse.workflow.service.ProcessListenerService;
|
||||
import cn.fateverse.common.core.result.Result;
|
||||
import cn.fateverse.common.core.result.page.TableDataInfo;
|
||||
import cn.fateverse.common.core.utils.ObjectUtils;
|
||||
import cn.fateverse.common.log.annotation.Log;
|
||||
import cn.fateverse.common.log.enums.BusinessType;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统内置监听器 Controller
|
||||
*
|
||||
* @author clay
|
||||
* @date 2023-03-27
|
||||
*/
|
||||
@Api(value = "系统内置监听器管理",tags = "系统内置监听器管理")
|
||||
@RestController
|
||||
@RequestMapping("/process/listener")
|
||||
public class ProcessListenerController {
|
||||
|
||||
private final ProcessListenerService processListenerService;
|
||||
|
||||
public ProcessListenerController(ProcessListenerService processListenerService) {
|
||||
this.processListenerService = processListenerService;
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation("获取系统内置监听器列表")
|
||||
@GetMapping
|
||||
@PreAuthorize("@ss.hasPermission('flowable:listener:list')")
|
||||
public Result<TableDataInfo<ProcessListenerVo>> list(ProcessListenerQuery query) {
|
||||
TableDataInfo<ProcessListenerVo> dataInfo = processListenerService.searchList(query);
|
||||
return Result.ok(dataInfo);
|
||||
}
|
||||
|
||||
@ApiOperation("获取系统内置监听器选项")
|
||||
@GetMapping("option/{type}")
|
||||
public Result<List<Option>> option(@PathVariable String type) {
|
||||
if (StrUtil.isEmpty(type)){
|
||||
return Result.error("请求参数不能为空!");
|
||||
}
|
||||
List<Option> optionList = processListenerService.searchOptionList(type);
|
||||
return Result.ok(optionList);
|
||||
}
|
||||
|
||||
@ApiOperation("导出excel数据")
|
||||
@GetMapping("/export")
|
||||
@PreAuthorize("@ss.hasPermission('flowable:listener:export')")
|
||||
public void export(ProcessListenerQuery query){
|
||||
List<ProcessListenerVo> list = processListenerService.exportList(query);
|
||||
ExcelUtil.exportExcel(list,ProcessListenerVo.class);
|
||||
}
|
||||
|
||||
@ApiOperation("获取系统内置监听器详细信息")
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize("@ss.hasPermission('flowable:listener:info')")
|
||||
public Result<ProcessListenerVo> info(@PathVariable String id) {
|
||||
ObjectUtils.checkPk(id);
|
||||
ProcessListenerVo processListener = processListenerService.searchById(id);
|
||||
return Result.ok(processListener);
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation("新增系统内置监听器")
|
||||
@PostMapping
|
||||
@Log(title = "新增系统内置监听器",businessType = BusinessType.INSERT)
|
||||
@PreAuthorize("@ss.hasPermission('flowable:listener:add')")
|
||||
public Result<Void> add(@RequestBody @Validated ProcessListenerDto processListener){
|
||||
processListenerService.save(processListener);
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
@ApiOperation("修改系统内置监听器")
|
||||
@PutMapping
|
||||
@Log(title = "修改系统内置监听器",businessType = BusinessType.UPDATE)
|
||||
@PreAuthorize("@ss.hasPermission('flowable:listener:edit')")
|
||||
public Result<Void> edit(@RequestBody @Validated ProcessListenerDto processListener){
|
||||
ObjectUtils.checkPk(processListener.getId());
|
||||
processListenerService.edit(processListener);
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
@ApiOperation("删除系统内置监听器")
|
||||
@DeleteMapping("/{idList}")
|
||||
@Log(title = "删除系统内置监听器",businessType = BusinessType.DELETE)
|
||||
@PreAuthorize("@ss.hasPermission('flowable:listener:del')")
|
||||
public Result<Void> batchDel(@PathVariable List<String> idList){
|
||||
ObjectUtils.checkPkList(idList);
|
||||
processListenerService.removeBatch(idList);
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package cn.fateverse.workflow.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.fateverse.common.core.result.Result;
|
||||
import cn.fateverse.common.core.result.page.TableDataInfo;
|
||||
import cn.fateverse.common.core.utils.ObjectUtils;
|
||||
import cn.fateverse.workflow.entity.bpmn.CommentInfo;
|
||||
import cn.fateverse.workflow.entity.dto.TaskDto;
|
||||
import cn.fateverse.workflow.entity.vo.task.TaskResultVo;
|
||||
import cn.fateverse.workflow.entity.vo.task.TaskVo;
|
||||
import cn.fateverse.workflow.service.ProcessTaskService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
||||
/**
|
||||
* 工作流任务处理
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/24
|
||||
*/
|
||||
@Api(value = "任务实例", tags = "任务实例")
|
||||
@RestController
|
||||
@RequestMapping("/process/task")
|
||||
public class ProcessTaskController {
|
||||
|
||||
private final ProcessTaskService processTaskService;
|
||||
|
||||
public ProcessTaskController(ProcessTaskService processTaskService) {
|
||||
this.processTaskService = processTaskService;
|
||||
}
|
||||
|
||||
@ApiOperation("工作流流程查询")
|
||||
@GetMapping
|
||||
@PreAuthorize("@ss.hasPermission('workflow:task:list')")
|
||||
public Result<TableDataInfo<TaskVo>> list() {
|
||||
TableDataInfo<TaskVo> dataInfo = processTaskService.searchTaskList();
|
||||
return Result.ok(dataInfo);
|
||||
}
|
||||
|
||||
@ApiOperation("工作流流程查询")
|
||||
@GetMapping("/{taskId}")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:task:info')")
|
||||
public Result<TaskResultVo> info(@PathVariable String taskId) {
|
||||
TaskResultVo resultVo = processTaskService.searchTaskByTaskId(taskId);
|
||||
return Result.ok(resultVo);
|
||||
}
|
||||
|
||||
@ApiOperation("完成任务")
|
||||
@PutMapping("/complete")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:task:complete')")
|
||||
public Result<Void> complete(@RequestBody @Validated TaskDto taskDto) {
|
||||
processTaskService.completeTask(taskDto);
|
||||
return Result.ok("任务完成!");
|
||||
}
|
||||
|
||||
@ApiOperation("拒绝任务")
|
||||
@PutMapping("/refuse")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:task:delegate')")
|
||||
public Result<Void> refuse(@RequestBody @Validated TaskDto taskDto) {
|
||||
processTaskService.refuseTask(taskDto);
|
||||
return Result.ok("操作完成!");
|
||||
}
|
||||
|
||||
@ApiOperation("委派")
|
||||
@PutMapping("/delegate")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:task:delegate')")
|
||||
public Result<Void> delegate(@RequestBody @Validated TaskDto taskDto) {
|
||||
if (ObjectUtils.isEmpty(taskDto.getDelegateUserInfo())){
|
||||
return Result.error("委托人信息不能为空!");
|
||||
}
|
||||
processTaskService.delegateTas(taskDto);
|
||||
return Result.ok("委派成功!");
|
||||
}
|
||||
|
||||
@ApiOperation("委派人完成")
|
||||
@PutMapping("/resolve/complete")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:task:resolve')")
|
||||
public Result<Void> resolveComplete(@RequestBody @Validated TaskDto taskDto) {
|
||||
processTaskService.resolveCompleteTask(taskDto);
|
||||
return Result.ok("任务完成!");
|
||||
}
|
||||
|
||||
@ApiOperation("撤销")
|
||||
@PutMapping("/revoke")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:task:revoke')")
|
||||
public Result<Void> revoke(@RequestBody @Validated TaskDto taskDto) {
|
||||
processTaskService.revokeTask(taskDto);
|
||||
return Result.ok("撤销成功!");
|
||||
}
|
||||
|
||||
@ApiOperation("移交转办")
|
||||
@PutMapping("/assignee")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:task:assignee')")
|
||||
public Result<Void> assignee(@RequestBody @Validated TaskDto taskDto) {
|
||||
if (ObjectUtils.isEmpty(taskDto.getTransferUserInfo())){
|
||||
return Result.error("移交转办人信息不能为空!");
|
||||
}
|
||||
processTaskService.assigneeTask(taskDto);
|
||||
return Result.ok("移交转办成功!");
|
||||
}
|
||||
|
||||
@ApiOperation("退回")
|
||||
@PutMapping("/rollback")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:task:rollback')")
|
||||
public Result<Void> rollback(@RequestBody @Validated TaskDto taskDto) {
|
||||
if (StrUtil.isEmpty(taskDto.getRollBackId())){
|
||||
return Result.error("退回节点不能为空!");
|
||||
}
|
||||
processTaskService.rollbackTask(taskDto);
|
||||
return Result.ok("退回成功!");
|
||||
}
|
||||
|
||||
@ApiOperation("加签")
|
||||
@PutMapping("/add/multi")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:task:addmulti')")
|
||||
public Result<Void> addMulti(@RequestBody @Validated TaskDto taskDto) {
|
||||
if (ObjectUtils.isEmpty(taskDto.getMultiAddUserInfo())){
|
||||
return Result.error("加签人信息不能为空!");
|
||||
}
|
||||
processTaskService.addMultiTask(taskDto);
|
||||
return Result.ok("加签成功!");
|
||||
}
|
||||
|
||||
@ApiOperation("查看签上的人")
|
||||
@PutMapping("/list/multi")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:task:listmulti')")
|
||||
public Result<Void> listMulti(@RequestBody String taskId) {
|
||||
if (ObjectUtils.isEmpty(taskId)){
|
||||
return Result.error("任务节点不能为空!");
|
||||
}
|
||||
processTaskService.searchMultiUsersInfo(taskId);
|
||||
return Result.ok("");
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation("减签")
|
||||
@DeleteMapping("/multi/{taskId}")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:task:delmulti')")
|
||||
public Result<Void> delMulti(@PathVariable String taskId) {
|
||||
if (ObjectUtils.isEmpty(taskId)){
|
||||
return Result.error("任务节点不能为空!");
|
||||
}
|
||||
processTaskService.deleteMultiTask(taskId);
|
||||
return Result.ok("减签成功!");
|
||||
}
|
||||
|
||||
@ApiOperation("添加评论")
|
||||
@PostMapping("/comment")
|
||||
@PreAuthorize("@ss.hasPermission('workflow:task:comment')")
|
||||
public Result<Void> comment(@RequestBody @Validated CommentInfo commentInfo) {
|
||||
processTaskService.addCommentTask(commentInfo);
|
||||
return Result.ok("提交成功!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package cn.fateverse.workflow.entity;
|
||||
|
||||
import cn.fateverse.workflow.enums.FormPermEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 表单权限对象
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/24
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class FormPerm {
|
||||
/**
|
||||
* todo 前端生成id,后期改为itemId
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 实例id
|
||||
*/
|
||||
private String deploymentId;
|
||||
|
||||
/**
|
||||
* task的表单权限key
|
||||
*/
|
||||
private String fromKey;
|
||||
/**
|
||||
* 表单权限
|
||||
*/
|
||||
private Boolean required;
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
private FormPermEnum perm;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package cn.fateverse.workflow.entity;
|
||||
|
||||
import cn.fateverse.workflow.entity.bpmn.UserInfo;
|
||||
import cn.fateverse.workflow.enums.OperationStateEnums;
|
||||
import cn.fateverse.workflow.enums.OperationEnums;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.task.service.delegate.DelegateTask;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 操作历史对象
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-02-24
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class HistoricalOperation {
|
||||
|
||||
//任务id
|
||||
private String taskId;
|
||||
//节点id
|
||||
private String nodeId;
|
||||
//流程实例id
|
||||
private String processInstanceId;
|
||||
//操作名称
|
||||
private String operationName;
|
||||
//行为
|
||||
private OperationEnums operation;
|
||||
//审批模式
|
||||
private String approvalMode;
|
||||
//评论json数据
|
||||
private String message;
|
||||
//完成时间
|
||||
private Date finishTime;
|
||||
//节点类型
|
||||
private String nodeType;
|
||||
//返回类型
|
||||
private OperationStateEnums state;
|
||||
//开始时间
|
||||
private Long startTime;
|
||||
//用户json数据
|
||||
private String userInfo;
|
||||
//是否被标记, 标记表示当前操作不纳入到节点分析列表,0表示纳入
|
||||
private Integer mark;
|
||||
|
||||
|
||||
public static HistoricalOperation toHistoricalOperation(DelegateTask delegateTask, UserInfo userInfo) {
|
||||
return HistoricalOperation.builder()
|
||||
.taskId(makeTaskId())
|
||||
.nodeId(delegateTask.getTaskDefinitionKey())
|
||||
.processInstanceId(delegateTask.getProcessInstanceId())
|
||||
.operation(OperationEnums.OPINION)
|
||||
.operationName(delegateTask.getName())
|
||||
.state(OperationStateEnums.RUNNING)
|
||||
.startTime(new Date().getTime())
|
||||
.userInfo(JSON.toJSONString(userInfo))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static HistoricalOperation toHistoricalOperation(DelegateTask delegateTask, List<UserInfo> userInfos) {
|
||||
return HistoricalOperation.builder()
|
||||
.taskId(makeTaskId())
|
||||
.nodeId(delegateTask.getTaskDefinitionKey())
|
||||
.processInstanceId(delegateTask.getProcessInstanceId())
|
||||
.operation(OperationEnums.OPINION)
|
||||
.operationName(delegateTask.getName())
|
||||
.state(OperationStateEnums.RUNNING)
|
||||
.startTime(new Date().getTime())
|
||||
.userInfo(JSON.toJSONString(userInfos))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static HistoricalOperation toHistoricalOperation(DelegateExecution execution, List<UserInfo> userInfos, String nodeName, String approvalMode) {
|
||||
return HistoricalOperation.builder()
|
||||
.taskId(makeTaskId())
|
||||
.nodeId(execution.getCurrentActivityId())
|
||||
.processInstanceId(execution.getProcessInstanceId())
|
||||
.approvalMode(approvalMode)
|
||||
.operation(OperationEnums.OPINION)
|
||||
.operationName(nodeName)
|
||||
.state(OperationStateEnums.RUNNING)
|
||||
.startTime(new Date().getTime())
|
||||
.userInfo(JSON.toJSONString(userInfos))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static HistoricalOperation toHistoricalOperation(DelegateExecution execution, UserInfo userInfo) {
|
||||
return toHistoricalOperation(execution, userInfo, OperationEnums.CREATE);
|
||||
}
|
||||
|
||||
public static HistoricalOperation toHistoricalOperation(DelegateExecution execution, List<UserInfo> list) {
|
||||
return toHistoricalOperation(execution, list, OperationEnums.CREATE);
|
||||
}
|
||||
|
||||
public static HistoricalOperation toHistoricalOperation(DelegateExecution execution, UserInfo userInfo, OperationEnums operation) {
|
||||
return toHistoricalOperation(execution, Collections.singletonList(userInfo), operation);
|
||||
|
||||
}
|
||||
|
||||
public static HistoricalOperation toHistoricalOperation(DelegateExecution execution, List<UserInfo> list, OperationEnums operation) {
|
||||
Date date = new Date();
|
||||
return HistoricalOperation.builder()
|
||||
.taskId(makeTaskId())
|
||||
.nodeId(execution.getCurrentActivityId())
|
||||
.processInstanceId(execution.getProcessInstanceId())
|
||||
.operation(operation)
|
||||
.operationName("提交申请")
|
||||
.state(OperationStateEnums.AGREE)
|
||||
.startTime(date.getTime())
|
||||
.finishTime(date)
|
||||
.userInfo(JSON.toJSONString(list))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static String makeTaskId() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package cn.fateverse.workflow.entity;
|
||||
|
||||
import cn.fateverse.common.core.annotaion.EnableAutoField;
|
||||
import cn.fateverse.common.core.entity.BaseEntity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 流程业务对象
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/23
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@EnableAutoField
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ProcessBusiness extends BaseEntity {
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
private Long businessId;
|
||||
/**
|
||||
* 分组id
|
||||
*/
|
||||
private Long groupId;
|
||||
/**
|
||||
* 部署id
|
||||
*/
|
||||
private String deploymentId;
|
||||
/**
|
||||
* 部署的key
|
||||
*/
|
||||
private String processDefinitionKey;
|
||||
/**
|
||||
* 部署名称
|
||||
*/
|
||||
private String deploymentName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package cn.fateverse.workflow.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 流程部署数据信息
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/23
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ProcessData {
|
||||
/**
|
||||
* 部署id
|
||||
*/
|
||||
private String deploymentId;
|
||||
/**
|
||||
* 流程部署名称
|
||||
*/
|
||||
private String deploymentName;
|
||||
/**
|
||||
* logo
|
||||
*/
|
||||
private String logo;
|
||||
/**
|
||||
* 设置数据
|
||||
*/
|
||||
private String settings;
|
||||
/**
|
||||
* 流程数据
|
||||
*/
|
||||
private String processStr;
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
private String formItemStr;
|
||||
/**
|
||||
* 描述信息
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package cn.fateverse.workflow.entity;
|
||||
|
||||
import cn.fateverse.common.core.annotaion.EnableAutoField;
|
||||
import cn.fateverse.common.core.entity.BaseEntity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023/1/10
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@EnableAutoField
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ProcessFile extends BaseEntity {
|
||||
|
||||
private Long id;
|
||||
/**
|
||||
* 文件名称
|
||||
*/
|
||||
private String fileName;
|
||||
/**
|
||||
* 文件原始名称
|
||||
*/
|
||||
private String originalFilename;
|
||||
/**
|
||||
* 文件url
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* 资源路径
|
||||
*/
|
||||
private String uri;
|
||||
/**
|
||||
* 文件类型
|
||||
*/
|
||||
private String fileType;
|
||||
/**
|
||||
* 文件大小
|
||||
*/
|
||||
private Long size;
|
||||
|
||||
/**
|
||||
* 是否是图片
|
||||
*/
|
||||
private Boolean isImage;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package cn.fateverse.workflow.entity;
|
||||
|
||||
import cn.fateverse.common.core.annotaion.EnableAutoField;
|
||||
import cn.fateverse.common.core.entity.BaseEntity;
|
||||
import cn.fateverse.common.core.entity.Option;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-03-27
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@EnableAutoField
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ProcessListener extends BaseEntity {
|
||||
//监听器id
|
||||
private String id;
|
||||
//监听器类型 1 : 任务监听 2: 执行监听
|
||||
private String listenerType;
|
||||
//监听类型 create assignment complete end take
|
||||
private String eventType;
|
||||
//监听器名称
|
||||
private String listenerName;
|
||||
//监听器值
|
||||
private String listenerValue;
|
||||
//监听器数据类型 0: Java类 1: 表达式 2: 代理表达式
|
||||
private String listenerValueType;
|
||||
|
||||
public Option toOption(){
|
||||
return Option.builder()
|
||||
.value(id)
|
||||
.label(listenerName)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package cn.fateverse.workflow.entity;
|
||||
|
||||
import cn.fateverse.common.core.annotaion.AutoTime;
|
||||
import cn.fateverse.common.core.annotaion.AutoUser;
|
||||
import cn.fateverse.common.core.annotaion.EnableAutoField;
|
||||
import cn.fateverse.common.core.entity.BaseEntity;
|
||||
import cn.fateverse.common.core.enums.AutoUserEnum;
|
||||
import cn.fateverse.common.core.enums.MethodEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 用户业务层面的流程实例
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-22
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@EnableAutoField
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class UserInstance extends BaseEntity {
|
||||
//实例id
|
||||
private String instanceId;
|
||||
//用户id
|
||||
@AutoUser(value = AutoUserEnum.USER_ID, method = MethodEnum.INSERT)
|
||||
private String userId;
|
||||
//流程实例id
|
||||
private String processInstanceId;
|
||||
//部署流程名称
|
||||
private String deploymentName;
|
||||
//状态
|
||||
private String state;
|
||||
//发起人
|
||||
@AutoUser(value = AutoUserEnum.NICK_NAME, method = MethodEnum.INSERT)
|
||||
private String initiator;
|
||||
//提交时间
|
||||
@AutoTime(method = MethodEnum.INSERT)
|
||||
private Date submitTime;
|
||||
//完成时间
|
||||
private Date finishTime;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cn.fateverse.workflow.entity.bo;
|
||||
|
||||
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 流程数据分析业务对象
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-02-27
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class AnalysisProcessBo {
|
||||
//历史操作节点集合
|
||||
private Set<String> hiOperationNodeSet;
|
||||
//流程节点父id映射
|
||||
private Map<String, ProcessNode> processNodeParentIdMap;
|
||||
//流程节点id映射
|
||||
private Map<String, ProcessNode> processNodeIdMap;
|
||||
//需要改变的节点集合
|
||||
private Set<String> changeNodeSet;
|
||||
//需要回滚的节点集合
|
||||
private Set<String> rollbackNodeSet;
|
||||
//离开合并节点的节点信息
|
||||
private Set<String> leaveMergeNodeSet;
|
||||
//进入网关的节点信息
|
||||
private Set<String> enterMergeNodeSet;
|
||||
//真在运行的节点信息
|
||||
private Set<String> activityIdSet;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.fateverse.workflow.entity.bo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 流程参数业务数数据
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023/1/11
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class VariablesBo {
|
||||
//流程中携带的参数信息
|
||||
private Map<String, Object> variables;
|
||||
//是否进行了表单权限的过滤
|
||||
private Boolean flag;
|
||||
|
||||
|
||||
public static VariablesBo ok(Map<String, Object> variables) {
|
||||
return VariablesBo.builder()
|
||||
.variables(variables)
|
||||
.flag(Boolean.TRUE)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static VariablesBo no(Map<String, Object> variables) {
|
||||
return VariablesBo.builder()
|
||||
.variables(variables)
|
||||
.flag(Boolean.FALSE)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 附件相关信息
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023/1/10
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Attachment {
|
||||
//附件id
|
||||
private Long id;
|
||||
//附件名称
|
||||
private String name;
|
||||
//附件路径
|
||||
private String url;
|
||||
//是否是图片
|
||||
private Boolean isImage;
|
||||
//附件大小
|
||||
private Long size;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 评论信息
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023/1/10
|
||||
*/
|
||||
@Data
|
||||
public class CommentInfo {
|
||||
|
||||
@NotNull(message = "任务id不能为空")
|
||||
private String taskId;
|
||||
//评论内容
|
||||
private String context;
|
||||
//附件列表
|
||||
private List<Attachment> attachments;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 条件对象
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class ConditionInfo {
|
||||
//条件id
|
||||
private String id;
|
||||
//条件名称
|
||||
private String title;
|
||||
//比较类型
|
||||
private String valueType;
|
||||
//比较类型
|
||||
private String compare;
|
||||
//值
|
||||
private List<Object> value;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 邮件对象
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class EmailInfo {
|
||||
/**
|
||||
* 主题
|
||||
*/
|
||||
private String subject;
|
||||
/**
|
||||
* 发送对象
|
||||
*/
|
||||
private List<String> to;
|
||||
/**
|
||||
* 发送对象
|
||||
*/
|
||||
private List<String> cc;
|
||||
/**
|
||||
* 发送内容
|
||||
*/
|
||||
private String content;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 表单项
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class FormItem {
|
||||
/**
|
||||
* 前端id
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 表单title
|
||||
*/
|
||||
private String title;
|
||||
/**
|
||||
* 表单名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
private String icon;
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
private Object value;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
private String perm;
|
||||
/**
|
||||
* 值的类型
|
||||
*/
|
||||
private String valueType;
|
||||
/**
|
||||
* 配置信息
|
||||
*/
|
||||
private JSONObject props;
|
||||
//private FormItemProps props;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 表单权限
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class FormItemProps {
|
||||
/**
|
||||
* 是否必填
|
||||
*/
|
||||
private Boolean required;
|
||||
/**
|
||||
* 是否打印
|
||||
*/
|
||||
private Boolean enablePrint;
|
||||
/**
|
||||
* 描述信息
|
||||
*/
|
||||
private Object placeholder;
|
||||
/**
|
||||
* 展示时长
|
||||
*/
|
||||
private Boolean showLength;
|
||||
/**
|
||||
* 是否多选
|
||||
*/
|
||||
private Boolean multiple;
|
||||
/**
|
||||
* 展示分数
|
||||
*/
|
||||
private Boolean showScore;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private Boolean enableHalf;
|
||||
/**
|
||||
* 最大值
|
||||
*/
|
||||
private Integer maxSize;
|
||||
private Boolean summaryColumns;
|
||||
private Boolean showSummary;
|
||||
/**
|
||||
* 行布局
|
||||
*/
|
||||
private Boolean rowLayout;
|
||||
private Boolean showBorder;
|
||||
/**
|
||||
* 颜色
|
||||
*/
|
||||
private String color;
|
||||
/**
|
||||
* 日期格式
|
||||
*/
|
||||
private String format;
|
||||
/**
|
||||
* 子节点
|
||||
*/
|
||||
private List<FormItem> items;
|
||||
/**
|
||||
* 列信息
|
||||
*/
|
||||
private List<FormItem> columns;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 表单的操作权限
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class FormOperates {
|
||||
//id
|
||||
private String id;
|
||||
//名称
|
||||
private String title;
|
||||
//是否选择
|
||||
private Boolean required;
|
||||
//权限
|
||||
private String perm;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 条件组对象
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class GroupsInfo {
|
||||
private String groupType;
|
||||
private List<ConditionInfo> conditions;
|
||||
private List<String> cids;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* http请求属性
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class HttpInfo {
|
||||
//请求类型
|
||||
private String method;
|
||||
//请求url
|
||||
private String url;
|
||||
//请求头信息
|
||||
private List<HttpParam> headers;
|
||||
//请求类型
|
||||
private String contentType;
|
||||
//请求参数
|
||||
private List<HttpParam> params;
|
||||
private Integer retry;
|
||||
//是否启用自定义脚本
|
||||
private Boolean handlerByScript;
|
||||
//成功是的脚本
|
||||
private String success;
|
||||
//失败时的脚本
|
||||
private String fail;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-03-25
|
||||
*/
|
||||
public class HttpParam {
|
||||
//名称
|
||||
private String name;
|
||||
//是否是字段
|
||||
private Boolean isField;
|
||||
//值
|
||||
private String value;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Boolean getIsField() {
|
||||
return isField;
|
||||
}
|
||||
|
||||
public void setIsField(Boolean field) {
|
||||
isField = field;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 监听器相关信息
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-27
|
||||
*/
|
||||
@Data
|
||||
public class ListenerInfo {
|
||||
/**
|
||||
* 是否使用
|
||||
*/
|
||||
private Boolean state;
|
||||
/**
|
||||
* 监听器列表
|
||||
*/
|
||||
private List<ListenerItem> list;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-03-27
|
||||
*/
|
||||
@Data
|
||||
public class ListenerItem {
|
||||
//监听器类型 1 : 任务监听 2: 执行监听
|
||||
private String listenerType;
|
||||
//监听类型 create assignment complete end take
|
||||
private List<String> eventType;
|
||||
//监听器名称
|
||||
private String listenerName;
|
||||
//监听器值
|
||||
private String listenerValue;
|
||||
//监听器数据类型 0: Java类 1: 表达式 2: 代理表达式
|
||||
private String listenerValueType;
|
||||
//是否系统内置
|
||||
private Boolean isSys;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* logo对象
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class LogoInfo {
|
||||
private String icon;
|
||||
private String background;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 通知类型
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class NotifyType {
|
||||
//通知类型
|
||||
private String type;
|
||||
//通知名称
|
||||
private String name;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 通知类型的信息
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class NotifyTypeInfo {
|
||||
//通知标题
|
||||
private String title;
|
||||
//通知配置信息
|
||||
private JSONObject types;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import cn.fateverse.workflow.enums.ProcessNodeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 节点信息数据
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class ProcessNode {
|
||||
/**
|
||||
* 节点id
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 父节点id
|
||||
*/
|
||||
private String parentId;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String desc;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 节点类型
|
||||
*/
|
||||
private ProcessNodeEnum type;
|
||||
/**
|
||||
* 属性配置
|
||||
*/
|
||||
private Properties props;
|
||||
/**
|
||||
* 分支信息
|
||||
*/
|
||||
private List<ProcessNode> branchs;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import cn.fateverse.workflow.enums.AssigneeTypeEnums;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 节点配置信息
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class Properties {
|
||||
/**
|
||||
* 审批人类型
|
||||
*/
|
||||
private AssigneeTypeEnums assignedType;
|
||||
/**
|
||||
* 审批用户
|
||||
*/
|
||||
private List<UserInfo> assignedUser;
|
||||
/**
|
||||
* 发起人自旋 multiple true false
|
||||
*/
|
||||
private Map<String,Object> selfSelect;
|
||||
/**
|
||||
* 连续主管 endCondition TOP LEAVE endLevel level
|
||||
*/
|
||||
private Map<String,Object> leaderTop=new HashMap<>();
|
||||
/**
|
||||
* 指定主管审批
|
||||
*/
|
||||
private Map<String,Object> leader=new HashMap<>();
|
||||
/**
|
||||
* 系统角色
|
||||
*/
|
||||
private List<RoleInfo> roleList;
|
||||
/**
|
||||
* 表单人员
|
||||
*/
|
||||
private String formUser;
|
||||
/**
|
||||
* 审批人为空的规则 hander 和 assignedUser
|
||||
*/
|
||||
private Map<String,Object> nobody;
|
||||
/**
|
||||
* 会签模式
|
||||
* NEXT: 会签 (按选择顺序审批,每个人必须同意)
|
||||
* AND: 会签(可同时审批,每个人必须同意)
|
||||
* OR: 或签(有一人同意即可)
|
||||
*/
|
||||
private String mode;
|
||||
/**
|
||||
* 签字
|
||||
*/
|
||||
private Boolean sign;
|
||||
/**
|
||||
* 审批超时
|
||||
*/
|
||||
private JSONObject timeLimit;
|
||||
|
||||
private Map<String,Object> refuse;
|
||||
/**
|
||||
* 表单权限
|
||||
*/
|
||||
private List<FormOperates> formPerms;
|
||||
|
||||
/**
|
||||
* 条件配置类型
|
||||
*/
|
||||
private String groupsType;
|
||||
/**
|
||||
* 条件表达式
|
||||
*/
|
||||
private String expression;
|
||||
/**
|
||||
* 条件组
|
||||
*/
|
||||
private List<GroupsInfo> groups;
|
||||
|
||||
/**
|
||||
* 允许发起人添加抄送人
|
||||
*/
|
||||
private Boolean shouldAdd;
|
||||
/**
|
||||
* 延时类型
|
||||
*/
|
||||
private String type;
|
||||
/**
|
||||
* 时间,时间戳
|
||||
*/
|
||||
private Long time;
|
||||
/**
|
||||
* 计时单位
|
||||
*/
|
||||
private String unit;
|
||||
/**
|
||||
* 时间
|
||||
*/
|
||||
private String dateTime;
|
||||
|
||||
/**
|
||||
* http节点相关配置信息
|
||||
*/
|
||||
private HttpInfo http;
|
||||
|
||||
/**
|
||||
* 监听器信息
|
||||
*/
|
||||
private ListenerInfo listener;
|
||||
/**
|
||||
* email对象相关细腻系
|
||||
*/
|
||||
private EmailInfo email;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 角色信息
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023/1/11
|
||||
*/
|
||||
@Data
|
||||
public class RoleInfo {
|
||||
private Long roleId;
|
||||
private String roleName;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 配置信息
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class SettingsInfo {
|
||||
|
||||
private List<String> commiter;
|
||||
/**
|
||||
* 可以管理当前流程的人员,分配权限
|
||||
*/
|
||||
private List<UserInfo> admin;
|
||||
/**
|
||||
* 审批时是否需要签字
|
||||
*/
|
||||
private Boolean sign;
|
||||
/**
|
||||
* 通知方式
|
||||
*/
|
||||
private JSONObject notify;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package cn.fateverse.workflow.entity.bpmn;
|
||||
|
||||
import cn.fateverse.admin.entity.User;
|
||||
import cn.fateverse.admin.vo.UserVo;
|
||||
import cn.fateverse.workflow.enums.OperationStateEnums;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用户的简单信息
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class UserInfo {
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 用户审批状态
|
||||
*/
|
||||
private OperationStateEnums state;
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
/**
|
||||
* 性别
|
||||
*/
|
||||
private String sex;
|
||||
/**
|
||||
* 是否选中
|
||||
*/
|
||||
private Boolean selected;
|
||||
|
||||
|
||||
public static UserInfo toUserInfo(User user){
|
||||
return toUserInfo(user, OperationStateEnums.RUNNING);
|
||||
}
|
||||
public static UserInfo toUserInfo(User user, OperationStateEnums state){
|
||||
return UserInfo.builder()
|
||||
.id(user.getUserId().toString())
|
||||
.avatar(user.getAvatar())
|
||||
.name(user.getNickName())
|
||||
.state(state)
|
||||
.sex(user.getSex())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static UserInfo toUserInfo(UserVo user, OperationStateEnums state){
|
||||
return UserInfo.builder()
|
||||
.id(user.getUserId().toString())
|
||||
.avatar(user.getAvatar())
|
||||
.name(user.getNickName())
|
||||
.state(state)
|
||||
.sex(user.getSex())
|
||||
.build();
|
||||
}
|
||||
public static UserInfo toUserInfo(UserVo user){
|
||||
return toUserInfo(user, OperationStateEnums.RUNNING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package cn.fateverse.workflow.entity.dto;
|
||||
|
||||
import cn.fateverse.workflow.enums.FormItemEnum;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 表单项接受对象
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/24
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class FormItemDto implements Serializable {
|
||||
/**
|
||||
* todo 前端生成id,后期改为itemId
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 表单名称
|
||||
*/
|
||||
private FormItemEnum name;
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
private String icon;
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
private String value;
|
||||
/**
|
||||
* 值的类型
|
||||
*/
|
||||
private String valueType;
|
||||
/**
|
||||
* 配置信息
|
||||
*/
|
||||
private JSONObject props;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package cn.fateverse.workflow.entity.dto;
|
||||
|
||||
import cn.fateverse.workflow.entity.bpmn.FormItem;
|
||||
import cn.fateverse.workflow.entity.bpmn.LogoInfo;
|
||||
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
|
||||
import cn.fateverse.workflow.entity.bpmn.SettingsInfo;
|
||||
import cn.fateverse.workflow.entity.ProcessBusiness;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程接收对象
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class ProcessDto extends ProcessBusiness {
|
||||
/**
|
||||
* logo
|
||||
*/
|
||||
private LogoInfo logo;
|
||||
|
||||
/**
|
||||
* 设置信息
|
||||
*/
|
||||
private SettingsInfo settings;
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
private Integer version;
|
||||
/**
|
||||
* 流程节点数据
|
||||
*/
|
||||
private List<ProcessNode> process;
|
||||
/**
|
||||
* 表单权限数据
|
||||
*/
|
||||
private List<FormItem> formItems;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package cn.fateverse.workflow.entity.dto;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
|
||||
/**
|
||||
* 流程启动接受对象
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/24
|
||||
*/
|
||||
@Data
|
||||
public class ProcessInstanceDto {
|
||||
//@NotNull(message = "key不能为空")
|
||||
//private String processDefinitionKey;
|
||||
@NotNull(message = "id不能为空")
|
||||
private String processDefinitionId;
|
||||
@NotNull(message = "表单数据不能为空")
|
||||
private String formData;
|
||||
//@NotNull(message = "用户自选的userMap信息")
|
||||
private JSONObject optionalUser;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package cn.fateverse.workflow.entity.dto;
|
||||
|
||||
import cn.fateverse.workflow.entity.ProcessListener;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统内置监听器对象 wk_re_process_listener
|
||||
*
|
||||
* @author clay
|
||||
* @date 2023-03-27
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ApiModel("系统内置监听器Dto")
|
||||
public class ProcessListenerDto {
|
||||
|
||||
/**
|
||||
* 监听器id
|
||||
*/
|
||||
@ApiModelProperty("监听器id")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 监听器类型 1 : 任务监听 2: 执行监听
|
||||
*/
|
||||
@ApiModelProperty("监听器类型 1 : 任务监听 2: 执行监听")
|
||||
private String listenerType;
|
||||
|
||||
/**
|
||||
* 监听类型 create assignment complete end take
|
||||
*/
|
||||
@ApiModelProperty("监听类型 create assignment complete end take")
|
||||
private List<String> eventType;
|
||||
|
||||
/**
|
||||
* 监听器名称
|
||||
*/
|
||||
@ApiModelProperty("监听器名称")
|
||||
private String listenerName;
|
||||
|
||||
/**
|
||||
* 监听器值
|
||||
*/
|
||||
@ApiModelProperty("监听器值")
|
||||
private String listenerValue;
|
||||
|
||||
/**
|
||||
* 监听器数据类型 0: Java类 1: 表达式 2: 代理表达式
|
||||
*/
|
||||
@ApiModelProperty("监听器数据类型 0: Java类 1: 表达式 2: 代理表达式")
|
||||
private String listenerValueType;
|
||||
|
||||
|
||||
public static ProcessListener toProcessListener(ProcessListenerDto processListener) {
|
||||
return ProcessListener.builder()
|
||||
.id(processListener.getId())
|
||||
.listenerType(processListener.getListenerType())
|
||||
.eventType(JSON.toJSONString(processListener.getEventType()))
|
||||
.listenerName(processListener.getListenerName())
|
||||
.listenerValue(processListener.getListenerValue())
|
||||
.listenerValueType(processListener.getListenerValueType())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package cn.fateverse.workflow.entity.dto;
|
||||
|
||||
import cn.fateverse.workflow.entity.bpmn.CommentInfo;
|
||||
import cn.fateverse.workflow.entity.bpmn.UserInfo;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 任务接受对象
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
@Data
|
||||
public class TaskDto {
|
||||
@NotNull(message = "任务id不能为空")
|
||||
private String taskId;
|
||||
|
||||
private String formData;
|
||||
|
||||
private String signInfo;
|
||||
|
||||
private String rollBackId;
|
||||
/**
|
||||
* todo 询问王老师是否需要加评论
|
||||
* 评论 or 拒绝理由
|
||||
*/
|
||||
private CommentInfo comment;
|
||||
/**
|
||||
* 委派用户信息
|
||||
*/
|
||||
private UserInfo delegateUserInfo;
|
||||
/**
|
||||
* 加签用户信息
|
||||
*/
|
||||
private UserInfo multiAddUserInfo;
|
||||
/**
|
||||
* 转办用户信息
|
||||
*/
|
||||
private UserInfo transferUserInfo;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.fateverse.workflow.entity.query;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 系统内置监听器对象 wk_re_process_listener
|
||||
*
|
||||
* @author clay
|
||||
* @date 2023-03-27
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ApiModel("系统内置监听器Query")
|
||||
public class ProcessListenerQuery {
|
||||
|
||||
/**
|
||||
* 监听器类型 1 : 任务监听 2: 执行监听
|
||||
*/
|
||||
@ApiModelProperty("监听器类型 1 : 任务监听 2: 执行监听")
|
||||
private String listenerType;
|
||||
|
||||
/**
|
||||
* 监听类型 create assignment complete end take
|
||||
*/
|
||||
@ApiModelProperty("监听类型 create assignment complete end take")
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 监听器名称
|
||||
*/
|
||||
@ApiModelProperty("监听器名称")
|
||||
private String listenerName;
|
||||
|
||||
/**
|
||||
* 监听器数据类型 0: Java类 1: 表达式 2: 代理表达式
|
||||
*/
|
||||
@ApiModelProperty("监听器数据类型 0: Java类 1: 表达式 2: 代理表达式")
|
||||
private String listenerValueType;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.fateverse.workflow.entity.query;
|
||||
|
||||
import cn.fateverse.common.core.entity.QueryTime;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 部署流程查询
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/23
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ApiModel("流程部署查询")
|
||||
public class ProcessQuery extends QueryTime {
|
||||
|
||||
@ApiModelProperty("部署名称")
|
||||
private String deploymentName;
|
||||
|
||||
@ApiModelProperty("状态")
|
||||
private Integer state;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package cn.fateverse.workflow.entity.vo;
|
||||
|
||||
import cn.fateverse.common.core.annotaion.AutoTime;
|
||||
import cn.fateverse.common.core.annotaion.EnableAutoField;
|
||||
import cn.fateverse.common.core.enums.MethodEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 自定义表单项
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/23
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@EnableAutoField
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class FormItemData {
|
||||
|
||||
/**
|
||||
* todo 前端生成id,后期改为itemId
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 实例id
|
||||
*/
|
||||
private String processInstanceId;
|
||||
/**
|
||||
* 是否为启动流程是的数据
|
||||
*/
|
||||
private Boolean isStart;
|
||||
/**
|
||||
* 是否当前task需要的数据
|
||||
*/
|
||||
private Boolean isPresent;
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
private String formData;
|
||||
/**
|
||||
* 自选用户
|
||||
*/
|
||||
private String optionalUser;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@AutoTime(method = MethodEnum.INSERT)
|
||||
private Date createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package cn.fateverse.workflow.entity.vo;
|
||||
|
||||
import cn.fateverse.workflow.entity.bpmn.CommentInfo;
|
||||
import cn.fateverse.workflow.entity.bpmn.UserInfo;
|
||||
import cn.fateverse.workflow.enums.OperationStateEnums;
|
||||
import cn.fateverse.workflow.enums.OperationEnums;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 操作历史
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-02-24
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class HistoricalOperationVo {
|
||||
|
||||
/**
|
||||
* 任务id
|
||||
*/
|
||||
private String taskId;
|
||||
/**
|
||||
* 流程实例id
|
||||
*/
|
||||
private String processInstanceId;
|
||||
/**
|
||||
* 操作名称
|
||||
*/
|
||||
private String operationName;
|
||||
/**
|
||||
* 行为
|
||||
*/
|
||||
private OperationEnums operation;
|
||||
/**
|
||||
* 评论json数据
|
||||
*/
|
||||
private CommentInfo comment;
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
private Date finishTime;
|
||||
/**
|
||||
* 节点id
|
||||
*/
|
||||
private String nodeId;
|
||||
/**
|
||||
* 返回类型
|
||||
*/
|
||||
private OperationStateEnums state;
|
||||
/**
|
||||
* 标志
|
||||
*/
|
||||
private Integer mark;
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private Date startTime;
|
||||
/**
|
||||
* 用户json数据
|
||||
*/
|
||||
private List<UserInfo> userInfo;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package cn.fateverse.workflow.entity.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 部署流程历史信息
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/23
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ProcessDefinitionHistoryVo {
|
||||
/**
|
||||
* 分组id
|
||||
*/
|
||||
private String processDefinitionId;
|
||||
/**
|
||||
* 部署id
|
||||
*/
|
||||
private String deploymentId;
|
||||
/**
|
||||
* 部署的key
|
||||
*/
|
||||
private String processDefinitionKey;
|
||||
/**
|
||||
* 部署名称
|
||||
*/
|
||||
private String deploymentName;
|
||||
/**
|
||||
* logo
|
||||
*/
|
||||
private String logo;
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String state;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package cn.fateverse.workflow.entity.vo;
|
||||
|
||||
import cn.fateverse.workflow.entity.bpmn.FormItem;
|
||||
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程部署的详细信息
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/26
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class ProcessDefinitionInfoVo {
|
||||
//部署流程key
|
||||
private String processDefinitionKey;
|
||||
//部署名称
|
||||
private String deploymentName;
|
||||
//部署logo
|
||||
private Object logo;
|
||||
//基础设置
|
||||
private Object settings;
|
||||
//动态表单信息
|
||||
private List<FormItem> formItems;
|
||||
//流程节点信息
|
||||
private List<ProcessNode> process;
|
||||
//备注
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package cn.fateverse.workflow.entity.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 查询部署流程简单信息
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/23
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ProcessDefinitionVo {
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
private Long businessId;
|
||||
/**
|
||||
* 分组id
|
||||
*/
|
||||
private Long groupId;
|
||||
/**
|
||||
* 部署id
|
||||
*/
|
||||
private String deploymentId;
|
||||
/**
|
||||
* 实例id
|
||||
*/
|
||||
private String processDefinitionId;
|
||||
/**
|
||||
* 部署的key
|
||||
*/
|
||||
private String processDefinitionKey;
|
||||
/**
|
||||
* 部署名称
|
||||
*/
|
||||
private String deploymentName;
|
||||
/**
|
||||
* logo
|
||||
*/
|
||||
private String logo;
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String state;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package cn.fateverse.workflow.entity.vo;
|
||||
|
||||
import cn.fateverse.workflow.entity.bpmn.FormItem;
|
||||
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
|
||||
import cn.fateverse.workflow.entity.bpmn.UserInfo;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程任务详细信息返回实体
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023/1/24
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class ProcessInstanceDetailVo {
|
||||
|
||||
//表单数据
|
||||
private List<FormItem> formItems;
|
||||
//表单数据
|
||||
private JSONObject formData;
|
||||
//用户自选数据信息
|
||||
private JSONObject optionalUser;
|
||||
//用户信息
|
||||
private UserInfo userInfo;
|
||||
//流程数据
|
||||
private List<ProcessNode> process;
|
||||
//运行中的节点
|
||||
private List<String> runningList;
|
||||
//拒绝的节点
|
||||
private List<String> refuseList;
|
||||
//被pass的节点列表
|
||||
private List<String> passList;
|
||||
//结束的节点
|
||||
private List<String> endList;
|
||||
//还没有激活的节点
|
||||
private List<String> noTakeList;
|
||||
//操作历史
|
||||
private List<HistoricalOperationVo> operationList;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package cn.fateverse.workflow.entity.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/25
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ProcessInstanceVo {
|
||||
//当前任务id
|
||||
private String taskId;
|
||||
//部署流程名称
|
||||
private String deploymentName;
|
||||
//流程实例id
|
||||
private String processInstanceId;
|
||||
//当前审批人
|
||||
private String approveName;
|
||||
//当前任务名
|
||||
private String taskName;
|
||||
//当前状态
|
||||
private String state;
|
||||
//发起人
|
||||
private String initiator;
|
||||
//是否是自己
|
||||
private Boolean isSelf;
|
||||
//提交时间
|
||||
private Date submitTime;
|
||||
//结束时间
|
||||
private Date endTime;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package cn.fateverse.workflow.entity.vo;
|
||||
|
||||
import cn.fateverse.workflow.entity.ProcessListener;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统内置监听器对象 wk_re_process_listener
|
||||
*
|
||||
* @author clay
|
||||
* @date 2023-03-27
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ApiModel("系统内置监听器Vo")
|
||||
public class ProcessListenerVo {
|
||||
|
||||
/**
|
||||
* 监听器id
|
||||
*/
|
||||
@ApiModelProperty("监听器id")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 监听器类型 1 : 任务监听 2: 执行监听
|
||||
*/
|
||||
@ApiModelProperty("监听器类型 1 : 任务监听 2: 执行监听")
|
||||
private String listenerType;
|
||||
|
||||
/**
|
||||
* 监听类型 create assignment complete end take
|
||||
*/
|
||||
@ApiModelProperty("监听类型 create assignment complete end take")
|
||||
private List<String> eventType;
|
||||
|
||||
/**
|
||||
* 监听器名称
|
||||
*/
|
||||
@ApiModelProperty("监听器名称")
|
||||
private String listenerName;
|
||||
|
||||
/**
|
||||
* 监听器值
|
||||
*/
|
||||
@ApiModelProperty("监听器值")
|
||||
private String listenerValue;
|
||||
|
||||
/**
|
||||
* 监听器数据类型 0: Java类 1: 表达式 2: 代理表达式
|
||||
*/
|
||||
@ApiModelProperty("监听器数据类型 0: Java类 1: 表达式 2: 代理表达式")
|
||||
private String listenerValueType;
|
||||
|
||||
public static ProcessListenerVo toProcessListenerVo(ProcessListener processListener) {
|
||||
return ProcessListenerVo.builder()
|
||||
.id(processListener.getId())
|
||||
.listenerType(processListener.getListenerType())
|
||||
.eventType(JSON.parseArray(processListener.getEventType(),String.class))
|
||||
.listenerName(processListener.getListenerName())
|
||||
.listenerValue(processListener.getListenerValue())
|
||||
.listenerValueType(processListener.getListenerValueType())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package cn.fateverse.workflow.entity.vo.task;
|
||||
|
||||
import cn.fateverse.workflow.entity.bpmn.CommentInfo;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 任务详细信息返回
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023/1/13
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class TaskDetailVo {
|
||||
//任务id
|
||||
private String taskId;
|
||||
//激活id
|
||||
private String activityId;
|
||||
//名称
|
||||
private String name;
|
||||
//创建时间
|
||||
private Date createTime;
|
||||
//结束时间
|
||||
private Date endTime;
|
||||
//签字信息
|
||||
private String signInfo;
|
||||
//评论信息
|
||||
private List<CommentInfo> commentList;
|
||||
//操作信息
|
||||
private List<CommentInfo> optionList;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package cn.fateverse.workflow.entity.vo.task;
|
||||
|
||||
import cn.fateverse.common.core.entity.Option;
|
||||
import cn.fateverse.workflow.entity.bpmn.FormItem;
|
||||
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
|
||||
import cn.fateverse.workflow.entity.bpmn.UserInfo;
|
||||
import cn.fateverse.workflow.entity.vo.HistoricalOperationVo;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/25
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class TaskResultVo {
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
private List<FormItem> formItems;
|
||||
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
private JSONObject formData;
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
private UserInfo userInfo;
|
||||
/**
|
||||
* 流程数据
|
||||
*/
|
||||
private List<ProcessNode> process;
|
||||
|
||||
/**
|
||||
* 用户task的option选择
|
||||
*/
|
||||
private List<Option> userTaskOption;
|
||||
/**
|
||||
* 节点流程中的详细信息
|
||||
*/
|
||||
private Map<String, TaskDetailVo> taskDetailMap;
|
||||
|
||||
/**
|
||||
* 操作历史
|
||||
*/
|
||||
private List<HistoricalOperationVo> operationList;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cn.fateverse.workflow.entity.vo.task;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/24
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class TaskVo {
|
||||
//任务id
|
||||
private String taskId;
|
||||
//任务名称
|
||||
private String taskName;
|
||||
//流程名称
|
||||
private String processName;
|
||||
//发起人
|
||||
private String initiatorName;
|
||||
//提交时间
|
||||
private Date submitTime;
|
||||
//到达时间
|
||||
private Date arriveDate;
|
||||
//创建时间
|
||||
private Date createdDate;
|
||||
//流程流转的状态
|
||||
private String state;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package cn.fateverse.workflow.enums;
|
||||
|
||||
/**
|
||||
* 审批人类型枚举
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023/1/2
|
||||
*/
|
||||
public enum AssigneeTypeEnums {
|
||||
|
||||
/**
|
||||
* 指定人员
|
||||
*/
|
||||
ASSIGN_USER("ASSIGN_USER"),
|
||||
/**
|
||||
* 发起人自选
|
||||
*/
|
||||
SELF_SELECT("SELF_SELECT"),
|
||||
/**
|
||||
* 连续多级主管
|
||||
*/
|
||||
LEADER_TOP("LEADER_TOP"),
|
||||
/**
|
||||
* 主管
|
||||
*/
|
||||
LEADER("LEADER"),
|
||||
/**
|
||||
* 角色
|
||||
*/
|
||||
ROLE("ROLE"),
|
||||
/**
|
||||
* 发起人自己
|
||||
*/
|
||||
SELF("SELF"),
|
||||
/**
|
||||
* 表单内联系人
|
||||
*/
|
||||
FORM_USER("FORM_USER");
|
||||
|
||||
private String typeName;
|
||||
|
||||
AssigneeTypeEnums(String typeName) {
|
||||
this.typeName = typeName;
|
||||
}
|
||||
|
||||
public String getTypeName() {
|
||||
return typeName;
|
||||
}
|
||||
|
||||
public void setTypeName(String typeName) {
|
||||
this.typeName = typeName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package cn.fateverse.workflow.enums;
|
||||
|
||||
/**
|
||||
* 表单类型枚举
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/25
|
||||
*/
|
||||
public enum FormItemEnum {
|
||||
|
||||
/**
|
||||
* 控件枚举
|
||||
*/
|
||||
SpanLayout("SpanLayout", "分栏布局"),
|
||||
TextInput("TextInput", "单行文本输入"),
|
||||
TextareaInput("TextareaInput", "多行文本输入"),
|
||||
NumberInput("NumberInput", "数字输入框"),
|
||||
AmountInput("AmountInput", "金额输入框"),
|
||||
SelectInput("SelectInput", "单选框"),
|
||||
MultipleSelect("MultipleSelect", "多选框"),
|
||||
DateTime("DateTime", "日期时间点"),
|
||||
DateTimeRange("DateTimeRange", "日期时间区间"),
|
||||
ImageUpload("SpanLayout", "ImageUpload"),
|
||||
FileUpload("FileUpload", "上传附件"),
|
||||
UserPicker("UserPicker", "人员选择"),
|
||||
DeptPicker("DeptPicker", "部门选择"),
|
||||
Description("Description", "说明文字"),
|
||||
TableList("TableList", "明细表"),
|
||||
;
|
||||
|
||||
|
||||
private final String name;
|
||||
private final String title;
|
||||
|
||||
|
||||
FormItemEnum(String name, String title) {
|
||||
this.name = name;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package cn.fateverse.workflow.enums;
|
||||
|
||||
/**
|
||||
* 表单权限枚举
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/24
|
||||
*/
|
||||
public enum FormPermEnum {
|
||||
/**
|
||||
* 权限类型
|
||||
*/
|
||||
READ("R", "可读"),
|
||||
EDIT("E", "可编辑"),
|
||||
HIDE("H", "影藏");
|
||||
|
||||
|
||||
private final String type;
|
||||
|
||||
private final String name;
|
||||
|
||||
FormPermEnum(String type, String name) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package cn.fateverse.workflow.enums;
|
||||
|
||||
/**
|
||||
* 审批节点模式枚举
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
public enum ModeEnums {
|
||||
/**
|
||||
* 会签(可同时审批,每个人必须同意)
|
||||
*/
|
||||
AND("AND"),
|
||||
|
||||
NORMAL("NORMAL"),
|
||||
/**
|
||||
* 或签(有一人同意即可)
|
||||
*/
|
||||
OR("OR"),
|
||||
/**
|
||||
* 会签 (按选择顺序审批,每个人必须同意)
|
||||
*/
|
||||
NEXT("NEXT");
|
||||
|
||||
private String typeName;
|
||||
|
||||
ModeEnums(String typeName) {
|
||||
this.typeName = typeName;
|
||||
}
|
||||
|
||||
public String getTypeName() {
|
||||
return typeName;
|
||||
}
|
||||
|
||||
public void setTypeName(String typeName) {
|
||||
this.typeName = typeName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.fateverse.workflow.enums;
|
||||
|
||||
/**
|
||||
* 操作类型枚举
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-20
|
||||
*/
|
||||
public enum OperationEnums {
|
||||
PASS("直接略过"),
|
||||
UNACTIVATED("未激活"),
|
||||
CREATE("创建"),
|
||||
OPINION("审批操作"),
|
||||
COMMENT("评论"),
|
||||
CC("抄送"),
|
||||
TRIGGER_WEBHOOK("HTTP请求"),
|
||||
TRIGGER_EMAIL("邮件"),
|
||||
;
|
||||
|
||||
private final String desc;
|
||||
|
||||
OperationEnums(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.fateverse.workflow.enums;
|
||||
|
||||
/**
|
||||
* 操作状态枚举
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-19
|
||||
*/
|
||||
public enum OperationStateEnums {
|
||||
PASS("pass"),
|
||||
UNACTIVATED("unactivated"),
|
||||
CREATE("create"),
|
||||
AGREE("agree"),
|
||||
SUCCESS("success"),
|
||||
FAILURE("failure"),
|
||||
RUNNING("running"),
|
||||
REFUSE("refuse"),
|
||||
SIGN("sign"),
|
||||
COMMENT("comment"),
|
||||
ROLLBACK("rollback"),
|
||||
AUTO_REFUSE("autoRefuse"),
|
||||
AUTO_PASS("autoPass"),
|
||||
;
|
||||
|
||||
|
||||
private final String state;
|
||||
|
||||
|
||||
OperationStateEnums(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package cn.fateverse.workflow.enums;
|
||||
|
||||
import org.flowable.bpmn.model.*;
|
||||
|
||||
/**
|
||||
* 流程节点枚举
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/23
|
||||
*/
|
||||
public enum ProcessNodeEnum {
|
||||
/**
|
||||
* 节点类型
|
||||
*/
|
||||
ROOT("ROOT", "开始分支", UserTask.class),
|
||||
END("END", "结束分支", Object.class),
|
||||
APPROVAL("APPROVAL", "审核节点", UserTask.class),
|
||||
CC("CC", "抄送", ServiceTask.class),
|
||||
DELAY("DELAY", "延时等待", IntermediateCatchEvent.class),
|
||||
TRIGGER("TRIGGER", "触发器", ServiceTask.class),
|
||||
MERGE("MERGE", "并行节点", Object.class),
|
||||
EMPTY("EMPTY", "空节点", Object.class),
|
||||
CONDITION("CONDITION", "条件分支", ExclusiveGateway.class),
|
||||
CONDITIONS("CONDITIONS", "多条件分支", ExclusiveGateway.class),
|
||||
CONCURRENTS("CONCURRENTS", "并行分支", ParallelGateway.class),
|
||||
CONCURRENT("CONCURRENT", "并行分支", SequenceFlow.class),
|
||||
;
|
||||
|
||||
private final String type;
|
||||
private final String name;
|
||||
private final Class<?> typeClass;
|
||||
|
||||
|
||||
ProcessNodeEnum(String type, String name, Class<?> typeClass) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.typeClass = typeClass;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Class<?> getTypeClass() {
|
||||
return typeClass;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.fateverse.workflow.mapper;
|
||||
|
||||
import cn.fateverse.workflow.entity.vo.FormItemData;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/25
|
||||
*/
|
||||
public interface FormItemDataMapper {
|
||||
/**
|
||||
* 插入表单的数据
|
||||
*
|
||||
* @param data 表单数据
|
||||
* @return 返回插入结果
|
||||
*/
|
||||
int insert(FormItemData data);
|
||||
|
||||
/**
|
||||
* 通过实例id查询表单数据
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @param isPresent 是否为当前数据
|
||||
* @return 获取到当前表单项对应的数据信息
|
||||
*/
|
||||
FormItemData selectByProcessInstanceId(@Param("processInstanceId") String processInstanceId,
|
||||
@Param("isPresent") Boolean isPresent);
|
||||
|
||||
/**
|
||||
* 批量取消当前选中状态
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @return 结果
|
||||
*/
|
||||
int updatePresentStart(@Param("processInstanceId") String processInstanceId);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package cn.fateverse.workflow.mapper;
|
||||
|
||||
import cn.fateverse.workflow.entity.FormPerm;
|
||||
import org.apache.ibatis.annotations.MapKey;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/24
|
||||
*/
|
||||
public interface FormPermMapper {
|
||||
|
||||
/**
|
||||
* 批量添加流程表单数据
|
||||
*
|
||||
* @param list 表单列表
|
||||
* @return 结果
|
||||
*/
|
||||
int batchInsert(List<FormPerm> list);
|
||||
|
||||
/**
|
||||
* 通过FromKey查询当前task的表单权限
|
||||
*
|
||||
* @param fromKey 表单关键词
|
||||
* @param deploymentId 流程部署id
|
||||
* @return 查询结果
|
||||
*/
|
||||
@MapKey("id")
|
||||
Map<String, FormPerm> selectListByFormKeyAndDeploymentId(@Param("fromKey") String fromKey,
|
||||
@Param("deploymentId") String deploymentId);
|
||||
|
||||
/**
|
||||
* 根据表单key的list查询到所有的权限
|
||||
*
|
||||
* @param fromKeys 表单关键词列表
|
||||
* @param deploymentId 流程部署id
|
||||
* @return 查询结果
|
||||
*/
|
||||
List<FormPerm> selectListByFormKeyListAndDeploymentId(@Param("fromKeys") List<String> fromKeys,
|
||||
@Param("deploymentId") String deploymentId);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package cn.fateverse.workflow.mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-03-19
|
||||
*/
|
||||
public interface HisFlowableActinstMapper {
|
||||
/**
|
||||
* 删除节点信息
|
||||
* @param ids 需要删除的id集合
|
||||
*/
|
||||
int deleteByIds(List<String> ids);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package cn.fateverse.workflow.mapper;
|
||||
|
||||
import cn.fateverse.workflow.entity.HistoricalOperation;
|
||||
import cn.fateverse.workflow.enums.OperationStateEnums;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-02-24
|
||||
*/
|
||||
public interface HistoricalOperationMapper {
|
||||
/**
|
||||
* 根据流程实例id查询到操作历史
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @return 操作历史数据
|
||||
*/
|
||||
List<HistoricalOperation> selectListByProcessInstanceId(@Param("processInstanceId") String processInstanceId, @Param("mark") Integer mark);
|
||||
|
||||
/**
|
||||
* 根据任务id查询到操作历史
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @return 操作历史
|
||||
*/
|
||||
HistoricalOperation selectByTaskId(String taskId);
|
||||
|
||||
/**
|
||||
* 根据流程实例id和nodeid获取到操作历史
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @param nodeId 节点id
|
||||
* @return 操作历史
|
||||
*/
|
||||
HistoricalOperation selectByProcessInstanceIdAndNodeId(@Param("processInstanceId") String processInstanceId, @Param("nodeId") String nodeId);
|
||||
|
||||
/**
|
||||
* 获取到其他正在运行的节点
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @return 操作历史
|
||||
*/
|
||||
List<HistoricalOperation> selectByProcessInstanceIdAndNotNodeId(@Param("processInstanceId") String processInstanceId, @Param("state") String state);
|
||||
|
||||
/**
|
||||
* 新增操作历史
|
||||
*
|
||||
* @param historicalOperation 操作历史
|
||||
* @return 结果
|
||||
*/
|
||||
int insert(HistoricalOperation historicalOperation);
|
||||
|
||||
/**
|
||||
* @param historicalOperation 操作历史
|
||||
* @return 结果
|
||||
*/
|
||||
int update(HistoricalOperation historicalOperation);
|
||||
|
||||
/**
|
||||
* 批量更新操作历史
|
||||
*
|
||||
* @param operationList 操作历史
|
||||
* @return 结果
|
||||
*/
|
||||
int batchUpdate(List<HistoricalOperation> operationList);
|
||||
|
||||
/**
|
||||
* 根据当前流程实例id和节点id查询是否存在数据
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @param nodeId 节点id
|
||||
* @return 数量
|
||||
*/
|
||||
int selectCountByProcessInstanceIdAndNodeId(@Param("processInstanceId") String processInstanceId, @Param("nodeId") String nodeId);
|
||||
|
||||
/**
|
||||
* 根据当前接地那实例id和节点id更新操作历史状态
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @param nodeId 节点id
|
||||
* @param state 状态
|
||||
* @return 改变数据
|
||||
*/
|
||||
int updateStatusByProcessInstanceIdAndNodeId(@Param("processInstanceId") String processInstanceId,
|
||||
@Param("nodeId") String nodeId,
|
||||
@Param("state") OperationStateEnums state);
|
||||
|
||||
/**
|
||||
* 获取到所有运行中的节点信息
|
||||
*
|
||||
* @param processInstanceListIds 流程实例ids
|
||||
* @return 流程实例数据
|
||||
*/
|
||||
List<HistoricalOperation> selectRunningByProcessInstanceId(List<String> processInstanceListIds);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package cn.fateverse.workflow.mapper;
|
||||
|
||||
import cn.fateverse.workflow.entity.ProcessBusiness;
|
||||
import cn.fateverse.workflow.entity.query.ProcessQuery;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessDefinitionVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/23
|
||||
*/
|
||||
public interface ProcessBusinessMapper {
|
||||
|
||||
/**
|
||||
* 查询workflow业务数据列表
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @return 结果
|
||||
*/
|
||||
List<ProcessDefinitionVo> selectWorkflowBusinessSimple(ProcessQuery query);
|
||||
|
||||
/**
|
||||
* 根据部署的key查询workflow业务数据
|
||||
*
|
||||
* @param processDefinitionKey 流程部署key
|
||||
* @return 结果
|
||||
*/
|
||||
ProcessBusiness selectByDeploymentKey(String processDefinitionKey);
|
||||
|
||||
/**
|
||||
* 根据部署id查想流程业务数据
|
||||
*
|
||||
* @param deploymentId 部署id
|
||||
* @return 结果
|
||||
*/
|
||||
ProcessBusiness selectByDeploymentId(String deploymentId);
|
||||
|
||||
/**
|
||||
* 新增workflow业务数据
|
||||
*
|
||||
* @param business 流程业务数据
|
||||
* @return 结果
|
||||
*/
|
||||
int insert(ProcessBusiness business);
|
||||
|
||||
/**
|
||||
* 更新workflow业务数据
|
||||
*
|
||||
* @param business 流程业务数据
|
||||
* @return 结果
|
||||
*/
|
||||
int update(ProcessBusiness business);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package cn.fateverse.workflow.mapper;
|
||||
|
||||
import cn.fateverse.workflow.entity.ProcessData;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessDefinitionHistoryVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/23
|
||||
*/
|
||||
public interface ProcessDataMapper {
|
||||
|
||||
/**
|
||||
* 插入流程的辅助数据
|
||||
*
|
||||
* @param data 流程实例附带的辅助数据
|
||||
* @return 变化值
|
||||
*/
|
||||
int insert(ProcessData data);
|
||||
|
||||
/**
|
||||
* 通过ke查询流程的历史版本数据
|
||||
*
|
||||
* @param processDefinitionKey 流程实例部署的key
|
||||
* @return 历史记录
|
||||
*/
|
||||
List<ProcessDefinitionHistoryVo> selectByDeploymentKey(String processDefinitionKey);
|
||||
|
||||
/**
|
||||
* 根据部署id获取到流程部署的表单数据
|
||||
*
|
||||
* @param processDefinitionId 流程实例部署id
|
||||
* @return 流程实例附带的数据
|
||||
*/
|
||||
ProcessData selectByProcessDefinitionId(String processDefinitionId);
|
||||
|
||||
/**
|
||||
* 根据
|
||||
*
|
||||
* @param processDefinitionKey 流程id
|
||||
* @return 流程数据
|
||||
*/
|
||||
ProcessData selectByProcessDefinitionKey(String processDefinitionKey);
|
||||
|
||||
/**
|
||||
* 根据部署id查询 部署的相关数据信息
|
||||
*
|
||||
* @param deploymentId 部署实例id
|
||||
* @return 流程实例附带的数据
|
||||
*/
|
||||
ProcessData selectByDeploymentId(String deploymentId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package cn.fateverse.workflow.mapper;
|
||||
|
||||
import cn.fateverse.workflow.entity.ProcessFile;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023/1/10
|
||||
*/
|
||||
public interface ProcessFileMapper {
|
||||
|
||||
/**
|
||||
* 新增文件信息
|
||||
*
|
||||
* @param file 流程专用文件对象
|
||||
* @return 新增结果
|
||||
*/
|
||||
int insert(ProcessFile file);
|
||||
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param fileId 文件id
|
||||
* @return 操作结果
|
||||
*/
|
||||
int delete(Long fileId);
|
||||
|
||||
/**
|
||||
* 通过id查询文件信息
|
||||
*
|
||||
* @param fileId 文件第
|
||||
* @return 流程文件对象
|
||||
*/
|
||||
ProcessFile selectById(Long fileId);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package cn.fateverse.workflow.mapper;
|
||||
|
||||
import cn.fateverse.workflow.entity.ProcessListener;
|
||||
import cn.fateverse.workflow.entity.query.ProcessListenerQuery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统内置监听器 Mapper
|
||||
*
|
||||
* @author clay
|
||||
* @date 2023-03-27
|
||||
*/
|
||||
public interface ProcessListenerMapper {
|
||||
|
||||
/**
|
||||
* 查询系统内置监听器
|
||||
*
|
||||
* @param id 系统内置监听器Id
|
||||
* @return 系统内置监听器
|
||||
*/
|
||||
ProcessListener selectById(String id);
|
||||
|
||||
/**
|
||||
* 查询系统内置监听器列表
|
||||
*
|
||||
* @param query 系统内置监听器查询
|
||||
* @return 系统内置监听器集合
|
||||
*/
|
||||
List<ProcessListener> selectList(ProcessListenerQuery query);
|
||||
|
||||
/**
|
||||
* 通过ids 批量查询监听器信息
|
||||
*
|
||||
* @param idList 监听器id列表
|
||||
* @return 系统内置监听器集合
|
||||
*/
|
||||
List<ProcessListener> selectIds(List<String> idList);
|
||||
|
||||
/**
|
||||
* 新增系统内置监听器
|
||||
*
|
||||
* @param processListener 系统内置监听器
|
||||
* @return 结果
|
||||
*/
|
||||
int insert(ProcessListener processListener);
|
||||
|
||||
/**
|
||||
* 修改系统内置监听器
|
||||
*
|
||||
* @param processListener 系统内置监听器
|
||||
* @return 结果
|
||||
*/
|
||||
int update(ProcessListener processListener);
|
||||
|
||||
/**
|
||||
* 删除系统内置监听器
|
||||
*
|
||||
* @param id 需要删除的系统内置监听器Id
|
||||
* @return 结果
|
||||
*/
|
||||
int deleteById(String id);
|
||||
|
||||
/**
|
||||
* 批量删除系统内置监听器
|
||||
*
|
||||
* @param idList 需要删除的系统内置监听器Id 集合
|
||||
* @return 结果
|
||||
*/
|
||||
int deleteBatchByIdList(List<String> idList);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package cn.fateverse.workflow.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.MapKey;
|
||||
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
|
||||
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/24
|
||||
*/
|
||||
public interface ProcessMapper {
|
||||
|
||||
|
||||
/**
|
||||
* 根据list查询ProcessInstance信息
|
||||
*
|
||||
* @param list 流程节点id
|
||||
* @return 结果
|
||||
*/
|
||||
@MapKey("id")
|
||||
Map<String, ExecutionEntityImpl> selectProcessInstanceMapByList(List<String> list);
|
||||
|
||||
/**
|
||||
* 向用户任务表中插入一条历史记录,用于抄送
|
||||
*
|
||||
* @param task 任务
|
||||
* @return 操作结果
|
||||
*/
|
||||
int insertUserTaskInHistorical(TaskEntityImpl task);
|
||||
|
||||
/**
|
||||
* 批量新增
|
||||
*
|
||||
* @param taskList 任务列表
|
||||
* @return 新增结果
|
||||
*/
|
||||
int batchInsertUserTaskInHistorical(List<TaskEntityImpl> taskList);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.fateverse.workflow.mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-03-19
|
||||
*/
|
||||
public interface RunFlowableActinstMapper {
|
||||
|
||||
/**
|
||||
* 删除节点信息
|
||||
*
|
||||
* @param ids 节点id列表
|
||||
* @return 删除结果
|
||||
*/
|
||||
int deleteRunActinstsByIds(List<String> ids);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package cn.fateverse.workflow.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.MapKey;
|
||||
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/25
|
||||
*/
|
||||
public interface TaskRuntimeMapper {
|
||||
|
||||
/**
|
||||
* 根据实例id查询当前运行的task节点信息
|
||||
*
|
||||
* @param list 实例id列表
|
||||
* @return 任务信息
|
||||
*/
|
||||
@MapKey("processInstanceId")
|
||||
Map<String, TaskEntityImpl> searchTaskListByProcessInstanceIds(List<String> list);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package cn.fateverse.workflow.mapper;
|
||||
|
||||
import cn.fateverse.workflow.entity.UserInstance;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户实例mapper层
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-22
|
||||
*/
|
||||
public interface UserInstanceMapper {
|
||||
|
||||
|
||||
/**
|
||||
* 根据实例id查询用户实例
|
||||
*
|
||||
* @param instanceId 实例id
|
||||
* @return 用户实例
|
||||
*/
|
||||
UserInstance selectByInstanceId(String instanceId);
|
||||
|
||||
/**
|
||||
* 根据用户d查询用户实例
|
||||
*
|
||||
* @param userId 实例id
|
||||
* @param state 流程状态
|
||||
* @return 用户实例
|
||||
*/
|
||||
List<UserInstance> selectByUserId(@Param("userId") String userId, @Param("state") String state, @Param("deploymentName") String deploymentName);
|
||||
|
||||
|
||||
/**
|
||||
* 根据用户id和状态查询到关于我的流程记录
|
||||
*
|
||||
* @param userId 用户id
|
||||
* @param state 流程记录装填
|
||||
* @return 用户流程记录
|
||||
*/
|
||||
List<UserInstance> selectProcessInstanceListByUserIdAndState(@Param("userId") Long userId, @Param("state") String state, @Param("deploymentName") String deploymentName);
|
||||
|
||||
/**
|
||||
* 根据流程实例id查询用户实例
|
||||
*
|
||||
* @param processInstanceId 实例id
|
||||
* @return 用户实例
|
||||
*/
|
||||
UserInstance selectByProcessInstanceId(String processInstanceId);
|
||||
|
||||
/**
|
||||
* 新增用户实例
|
||||
*
|
||||
* @param userInstance 用户实例
|
||||
* @return 新增数量
|
||||
*/
|
||||
int insert(UserInstance userInstance);
|
||||
|
||||
/**
|
||||
* 更新用户实例
|
||||
*
|
||||
* @param userInstance 用户实例
|
||||
* @return 更新数量
|
||||
*/
|
||||
int update(UserInstance userInstance);
|
||||
|
||||
/**
|
||||
* 更新用户实例状态
|
||||
*
|
||||
* @param processInstanceId 流程id
|
||||
* @param state 需要更新的状态
|
||||
*/
|
||||
int updateState(@Param("processInstanceId") String processInstanceId, @Param("state") String state);
|
||||
|
||||
/**
|
||||
* 批量更新用户实例的状态
|
||||
*
|
||||
* @param processInstanceIds 流程id数组
|
||||
* @param businessStatusReject 需要更新的状态
|
||||
* @return 更新的数量
|
||||
*/
|
||||
int updateStateByProcessInstanceIdList(List<String> processInstanceIds, String businessStatusReject);
|
||||
}
|
||||
@@ -0,0 +1,831 @@
|
||||
package cn.fateverse.workflow.process;
|
||||
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.common.core.utils.ObjectUtils;
|
||||
import cn.fateverse.workflow.constant.ProcessConstant;
|
||||
import cn.fateverse.workflow.entity.ProcessListener;
|
||||
import cn.fateverse.workflow.entity.bpmn.*;
|
||||
import cn.fateverse.workflow.entity.bpmn.Properties;
|
||||
import cn.fateverse.workflow.enums.FormPermEnum;
|
||||
import cn.fateverse.workflow.enums.ModeEnums;
|
||||
import cn.fateverse.workflow.enums.ProcessNodeEnum;
|
||||
import cn.fateverse.workflow.entity.FormPerm;
|
||||
import cn.fateverse.workflow.entity.dto.ProcessDto;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.flowable.bpmn.model.*;
|
||||
import org.flowable.bpmn.model.Process;
|
||||
import org.flowable.engine.delegate.ExecutionListener;
|
||||
import org.flowable.engine.delegate.TaskListener;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.flowable.bpmn.model.ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION;
|
||||
|
||||
/**
|
||||
* json 转 bpmn对象工具类
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/27
|
||||
*/
|
||||
public class BpmnParseUtil {
|
||||
|
||||
/**
|
||||
* 流程dto
|
||||
*/
|
||||
private final ProcessDto processDto;
|
||||
|
||||
/**
|
||||
* 表单权限
|
||||
*/
|
||||
private List<FormPerm> formPerms;
|
||||
|
||||
/**
|
||||
* bpmn流程文件
|
||||
*/
|
||||
private final Process process;
|
||||
|
||||
/**
|
||||
* bpmn流程模型
|
||||
*/
|
||||
private final BpmnModel bpmnModel;
|
||||
|
||||
/**
|
||||
* 自定义流程监听器映射map
|
||||
*/
|
||||
private final Map<String, ProcessListener> processListenerMap;
|
||||
|
||||
/**
|
||||
* 父级id的流程节点映射
|
||||
*/
|
||||
private Map<String, ProcessNode> parentMap = null;
|
||||
|
||||
/**
|
||||
* 自己id的流程节点映射
|
||||
*/
|
||||
private Map<String, ProcessNode> oneselfMap = null;
|
||||
|
||||
public BpmnParseUtil(ProcessDto processDto, List<ProcessListener> processListeners) {
|
||||
process = new Process();
|
||||
bpmnModel = new BpmnModel();
|
||||
this.processDto = processDto;
|
||||
if (null == processListeners || processListeners.isEmpty()) {
|
||||
this.processListenerMap = new HashMap<>();
|
||||
} else {
|
||||
processListenerMap = processListeners.stream().collect(Collectors.toMap(ProcessListener::getId, Function.identity()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表单权限
|
||||
*
|
||||
* @return 表单权限信息
|
||||
*/
|
||||
public List<FormPerm> getFormPerms() {
|
||||
return formPerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行函数
|
||||
*
|
||||
* @return bpmn模型对象
|
||||
*/
|
||||
public BpmnModel run() {
|
||||
formPerms = new ArrayList<>();
|
||||
//bpmn文件
|
||||
//流程对象
|
||||
//设置唯一的流程key
|
||||
process.setId(processDto.getProcessDefinitionKey());
|
||||
//设置流程名称
|
||||
process.setName(processDto.getDeploymentName());
|
||||
//设置备注名称
|
||||
process.setDocumentation(processDto.getRemark());
|
||||
|
||||
ExtensionAttribute processData = new ExtensionAttribute();
|
||||
processData.setName(ProcessConstant.BPMN_FORM_PROCESS);
|
||||
processData.setNamespace("http://flowable.org/bpmn");
|
||||
processData.setValue(JSONObject.toJSONString(processDto.getProcess()));
|
||||
process.addAttribute(processData);
|
||||
|
||||
ExtensionAttribute formItems = new ExtensionAttribute();
|
||||
formItems.setName(ProcessConstant.BPMN_FORM_ITEMS);
|
||||
formItems.setNamespace("http://flowable.org/bpmn");
|
||||
formItems.setValue(JSONObject.toJSONString(processDto.getFormItems()));
|
||||
process.addAttribute(formItems);
|
||||
//将流程对象加入到bpmn模型中
|
||||
bpmnModel.addProcess(process);
|
||||
//开始解析流程
|
||||
List<ProcessNode> processNodeList = processDto.getProcess();
|
||||
//将流程中的node根据他们的parentId进行分组,方便递归处理
|
||||
parentMap = processNodeList.stream().collect(Collectors.toMap(ProcessNode::getParentId, Function.identity()));
|
||||
oneselfMap = processNodeList.stream().collect(Collectors.toMap(ProcessNode::getId, Function.identity()));
|
||||
try {
|
||||
//开始解析json,创建bpmn模型对象
|
||||
crete("admin");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new CustomException(e.getMessage());
|
||||
}
|
||||
//流程结束监听器
|
||||
List<FlowableListener> executionListeners = new ArrayList<>();
|
||||
FlowableListener flowableListener = new FlowableListener();
|
||||
flowableListener.setEvent(ExecutionListener.EVENTNAME_END);
|
||||
flowableListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
|
||||
//使用spring 的 ioc bean管理获取到ProcessListener类,触发监听
|
||||
flowableListener.setImplementation("${processListener}");
|
||||
executionListeners.add(flowableListener);
|
||||
process.setExecutionListeners(executionListeners);
|
||||
return bpmnModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建分支list
|
||||
*
|
||||
* @param branch 分支节点
|
||||
*/
|
||||
private String creteBranch(ProcessNode branch) {
|
||||
ProcessNode node = parentMap.get(branch.getId());
|
||||
if (null == node) {
|
||||
throw new CustomException("解析失败,网关下没有节点,请检查流程图!");
|
||||
}
|
||||
String parentId = branch.getParentId();
|
||||
switch (node.getType()) {
|
||||
//审批人节点
|
||||
case APPROVAL:
|
||||
createUserTask(parentId, node);
|
||||
break;
|
||||
//触发器
|
||||
case TRIGGER:
|
||||
//抄送节点
|
||||
case CC:
|
||||
creteServiceTask(parentId, node);
|
||||
break;
|
||||
//延时等待节点
|
||||
case DELAY:
|
||||
createDelayService(parentId, node);
|
||||
break;
|
||||
//并行分支 和 多条件分支
|
||||
case CONCURRENTS:
|
||||
case CONDITION:
|
||||
createGatewayBuilder(parentId, node);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return crete(node.getId());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建节点
|
||||
*
|
||||
* @param parentId 父id
|
||||
*/
|
||||
private String crete(String parentId) {
|
||||
ProcessNode node = parentMap.get(parentId);
|
||||
if (null == node) {
|
||||
return parentId;
|
||||
}
|
||||
String processNodeId = node.getId();
|
||||
switch (node.getType()) {
|
||||
//root节点
|
||||
case ROOT:
|
||||
createStartEvent(node);
|
||||
break;
|
||||
//审批人节点
|
||||
case APPROVAL:
|
||||
createUserTask(parentId, node);
|
||||
break;
|
||||
//结束节点
|
||||
case END:
|
||||
createEndProcessNode(parentId, node);
|
||||
break;
|
||||
//触发器
|
||||
case TRIGGER:
|
||||
//抄送节点
|
||||
case CC:
|
||||
creteServiceTask(parentId, node);
|
||||
break;
|
||||
//延时等待节点
|
||||
case DELAY:
|
||||
createDelayService(parentId, node);
|
||||
break;
|
||||
//并行分支 和 多条件分支
|
||||
case CONCURRENTS:
|
||||
case CONDITIONS:
|
||||
createGatewayBuilder(parentId, node);
|
||||
ProcessNode nextNode = parentMap.get(node.getId());
|
||||
processNodeId = nextNode.getId();
|
||||
System.out.println("条件分支解析");
|
||||
break;
|
||||
default:
|
||||
throw new CustomException("节点类型错误!");
|
||||
}
|
||||
return crete(processNodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建网关节点
|
||||
*
|
||||
* @param parentId 父级id
|
||||
* @param node 节点对象信息
|
||||
*/
|
||||
private void createGatewayBuilder(String parentId, ProcessNode node) {
|
||||
Gateway gateway = null;
|
||||
//获取到合并节点
|
||||
ProcessNode merge = parentMap.get(node.getId());
|
||||
if (node.getType() == ProcessNodeEnum.CONCURRENTS) {
|
||||
ParallelGateway parallelGateway = new ParallelGateway();
|
||||
parallelGateway.setId(node.getId());
|
||||
parallelGateway.setName(node.getName());
|
||||
process.addFlowElement(parallelGateway);
|
||||
//创建并行网关的结束网关节点
|
||||
gateway = createParallelGateWayEnd(merge.getId());
|
||||
} else if (node.getType() == ProcessNodeEnum.CONDITIONS) {
|
||||
ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
|
||||
exclusiveGateway.setId(node.getId());
|
||||
exclusiveGateway.setName(node.getName());
|
||||
process.addFlowElement(exclusiveGateway);
|
||||
//创建并行网关的结束网关节点
|
||||
gateway = createExclusiveGateWayEnd(merge.getId());
|
||||
}
|
||||
connect(parentId, node.getId());
|
||||
List<ProcessNode> branchs = node.getBranchs();
|
||||
if (Objects.isNull(branchs)) {
|
||||
throw new CustomException("解析失败,网关下没有节点,请检查流程图!");
|
||||
}
|
||||
//添加到process中
|
||||
process.addFlowElement(gateway);
|
||||
for (ProcessNode branch : branchs) {
|
||||
//创建分支节点
|
||||
String endId = creteBranch(branch);
|
||||
//连接节点信息
|
||||
connect(endId, merge.getId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建条件网关的结束节点
|
||||
*
|
||||
* @param id 结束节点id
|
||||
* @return 条件网关对象
|
||||
*/
|
||||
private ExclusiveGateway createExclusiveGateWayEnd(String id) {
|
||||
ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
|
||||
exclusiveGateway.setName(ProcessConstant.GATEWAY_END);
|
||||
exclusiveGateway.setId(id);
|
||||
return exclusiveGateway;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建并行网关的结束节点
|
||||
*
|
||||
* @param id 结束节点id
|
||||
* @return 并行网关对象
|
||||
*/
|
||||
private ParallelGateway createParallelGateWayEnd(String id) {
|
||||
ParallelGateway parallelGateway = new ParallelGateway();
|
||||
parallelGateway.setId(id);
|
||||
return parallelGateway;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建结束节点
|
||||
*
|
||||
* @param parentId 父节点id
|
||||
* @param node 节点对象
|
||||
*/
|
||||
private void createEndProcessNode(String parentId, ProcessNode node) {
|
||||
EndEvent endEvent = new EndEvent();
|
||||
endEvent.setId(node.getId());
|
||||
process.addFlowElement(endEvent);
|
||||
connect(parentId, node.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接节点
|
||||
*
|
||||
* @param sourceRef 源
|
||||
* @param targetRef 目标
|
||||
*/
|
||||
private void connect(String sourceRef, String targetRef) {
|
||||
FlowElement flowElement = process.getFlowElement(sourceRef);
|
||||
SequenceFlow connect = connect(sourceRef, targetRef, flowElement);
|
||||
process.addFlowElement(connect);
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接节点
|
||||
*
|
||||
* @param sourceRef 源
|
||||
* @param targetRef 目标
|
||||
* @param flowElement 流对象
|
||||
* @return 线对象
|
||||
*/
|
||||
private SequenceFlow connect(String sourceRef, String targetRef, FlowElement flowElement) {
|
||||
String flowId = getEdgeId();
|
||||
SequenceFlow flow = new SequenceFlow();
|
||||
if (flowElement instanceof ExclusiveGateway && !ProcessConstant.GATEWAY_END.equals(flowElement.getName())) {
|
||||
parseCondition(sourceRef, targetRef, flow);
|
||||
}
|
||||
flow.setId(flowId);
|
||||
flow.setSourceRef(sourceRef);
|
||||
flow.setTargetRef(targetRef);
|
||||
return flow;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析条件表达式
|
||||
*
|
||||
* @param sourceRef 源
|
||||
* @param targetRef 目标
|
||||
* @param flow 连线
|
||||
*/
|
||||
private void parseCondition(String sourceRef, String targetRef, SequenceFlow flow) {
|
||||
ProcessNode processNode = oneselfMap.get(targetRef);
|
||||
ProcessNode branch = oneselfMap.get(sourceRef).getBranchs().stream().filter(item -> item.getId().equals(processNode.getParentId())).findAny().get();
|
||||
if (branch.getType() != ProcessNodeEnum.CONDITION) {
|
||||
throw new CustomException("流程图有误,请检查流程图!");
|
||||
}
|
||||
//解析表达式
|
||||
Properties props = branch.getProps();
|
||||
//将表达式组取出
|
||||
List<GroupsInfo> groups = props.getGroups();
|
||||
if (ObjectUtils.isEmpty(groups)) {
|
||||
throw new CustomException(branch.getName() + "条件分支的条件组数据为空,请检查");
|
||||
}
|
||||
//构建条件表达式
|
||||
StringBuilder conditionsStrBu = new StringBuilder();
|
||||
conditionsStrBu.append("${ ");
|
||||
//遍历条件组
|
||||
List<String> conditionGroupsList = new ArrayList<>();
|
||||
for (GroupsInfo info : groups) {
|
||||
List<ConditionInfo> conditions = info.getConditions();
|
||||
if (ObjectUtils.isEmpty(conditions)) {
|
||||
throw new CustomException(branch.getName() + "条件分支,条件组中条件为空,请检查");
|
||||
}
|
||||
//遍历条件表达式
|
||||
List<String> conditionList = new ArrayList<>();
|
||||
for (ConditionInfo condition : conditions) {
|
||||
String conditionStr;
|
||||
switch (condition.getValueType()) {
|
||||
//用户
|
||||
case "User":
|
||||
conditionStr = parseConditionUser(condition);
|
||||
break;
|
||||
//字符串
|
||||
case "String":
|
||||
conditionStr = parseConditionString(condition);
|
||||
break;
|
||||
//数值
|
||||
case "Number":
|
||||
conditionStr = parseConditionNumber(condition);
|
||||
break;
|
||||
//时间
|
||||
case "Date":
|
||||
//部门
|
||||
case "Dept":
|
||||
default:
|
||||
throw new CustomException("其他占时还未实现!");
|
||||
}
|
||||
conditionList.add(conditionStr);
|
||||
}
|
||||
//组合条件
|
||||
String relation = "OR".equals(info.getGroupType()) ? " || " : " && ";
|
||||
conditionGroupsList.add("(" + StringUtils.join(conditionList, relation) + ")");
|
||||
}
|
||||
//组合条件组表达式
|
||||
String relationGroups = "OR".equals(props.getGroupsType()) ? " || " : " && ";
|
||||
conditionsStrBu.append(StringUtils.join(conditionGroupsList, relationGroups)).append(" } ");
|
||||
flow.setConditionExpression(conditionsStrBu.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析条件表达式中数据number类型
|
||||
*
|
||||
* @param condition 条件对象
|
||||
* @return 条件表达式
|
||||
*/
|
||||
private String parseConditionNumber(ConditionInfo condition) {
|
||||
String id = condition.getId();
|
||||
String str;
|
||||
switch (condition.getCompare().trim()) {
|
||||
case "=":
|
||||
case ">":
|
||||
case "<":
|
||||
case ">=":
|
||||
case "<=":
|
||||
str = condition.getValue().get(0).toString();
|
||||
return " " + id + condition.getCompare() + str + " ";
|
||||
case "IN":
|
||||
return conditionValueToConditionStr(condition);
|
||||
case "B":
|
||||
case "AB":
|
||||
case "BA":
|
||||
case "ABA":
|
||||
List<String> valueList = valueObjectToString(condition.getValue());
|
||||
str = StringUtils.join(valueList, ",");
|
||||
return " " + ProcessConstant.PROCESS_UTIL_CLASS + condition.getCompare().trim().toUpperCase() + "( " + id + "," + str + ") ";
|
||||
default:
|
||||
throw new CustomException("条件类型错误!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* object 的 list 转String list
|
||||
*
|
||||
* @param list object list
|
||||
* @return String list
|
||||
*/
|
||||
private List<String> valueObjectToString(List<Object> list) {
|
||||
return list.stream().map(Object::toString).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将条件数据转为为String类型的
|
||||
*
|
||||
* @param condition 条件对象
|
||||
* @return 条件表达式
|
||||
*/
|
||||
private String conditionValueToConditionStr(ConditionInfo condition) {
|
||||
List<String> valueList = valueObjectToString(condition.getValue());
|
||||
String str = StringUtils.join(valueList, ",");
|
||||
return " " + ProcessConstant.PROCESS_UTIL_CLASS + " strArrContainsMethod(" + condition.getId() + "," + str + ") ";
|
||||
}
|
||||
|
||||
/**
|
||||
* 将条件对象转换为String
|
||||
*
|
||||
* @param condition 条件对象
|
||||
* @return 条件表达式
|
||||
*/
|
||||
private String parseConditionString(ConditionInfo condition) {
|
||||
if ("=".equals(condition.getValueType().trim())) {
|
||||
String str = "'" + condition.getValue().get(0).toString() + "'";
|
||||
return " " + ProcessConstant.PROCESS_UTIL_CLASS + " strEqualsMethod(" + condition.getId() + "," + str + ") ";
|
||||
} else {
|
||||
return conditionValueToConditionStr(condition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将条件对象的用户信息转化为条件表达式
|
||||
*
|
||||
* @param condition 条件对象
|
||||
* @return 条件表达式
|
||||
*/
|
||||
private String parseConditionUser(ConditionInfo condition) {
|
||||
List<Object> valueList = condition.getValue();
|
||||
List<String> userIds = new ArrayList<>(valueList.size());
|
||||
for (Object value : valueList) {
|
||||
JSONObject user = JSON.parseObject(JSON.toJSONString(value));
|
||||
userIds.add(user.getString("id"));
|
||||
}
|
||||
String str = StringUtils.join(userIds, ",");
|
||||
return " " + ProcessConstant.PROCESS_UTIL_CLASS + " strArrContainsMethod(" + condition.getId() + "," + str + ") ";
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建任务节点
|
||||
*
|
||||
* @param parentId 父节点id
|
||||
* @param node 节点对象
|
||||
*/
|
||||
private void createUserTask(String parentId, ProcessNode node) {
|
||||
UserTask userTask = new UserTask();
|
||||
userTask.setName(node.getName());
|
||||
userTask.setId(node.getId());
|
||||
|
||||
|
||||
//定义任务监听器
|
||||
List<FlowableListener> taskListeners = new ArrayList<>();
|
||||
FlowableListener taskListener = new FlowableListener();
|
||||
// 事件类型,
|
||||
taskListener.setEvent(TaskListener.EVENTNAME_CREATE);
|
||||
// 监听器类型
|
||||
taskListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
|
||||
// 设置实现了,这里设置监听器的类型是delegateExpression,这样可以在实现类注入Spring bean.
|
||||
taskListener.setImplementation("${taskCreatedListener}");
|
||||
taskListeners.add(taskListener);
|
||||
userTask.setTaskListeners(taskListeners);
|
||||
//定义执行监听器
|
||||
FlowableListener flowableListener = new FlowableListener();
|
||||
// 事件类型,
|
||||
flowableListener.setEvent(ExecutionListener.EVENTNAME_START);
|
||||
// 监听器类型
|
||||
flowableListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
|
||||
// 设置实现了,这里设置监听器的类型是delegateExpression,这样可以在实现类注入Spring bean.,会签监听器
|
||||
flowableListener.setImplementation("${counterSignListener}");
|
||||
userTask.setExecutionListeners(Collections.singletonList(flowableListener));
|
||||
createUserTaskBoundaryEvent(node, userTask);
|
||||
MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = new MultiInstanceLoopCharacteristics();
|
||||
// 审批人集合参数 assigneeList 为 集合,例如["kermit", "gonzo", "fozzie"];
|
||||
multiInstanceLoopCharacteristics.setInputDataItem(userTask.getId() + ProcessConstant.ASSIGNEE_LIST);
|
||||
// 迭代集合 activiti:elementVariable="assignee" 为 接收 loop 中的值的变量名;
|
||||
multiInstanceLoopCharacteristics.setElementVariable("assigneeName");
|
||||
// 串行
|
||||
multiInstanceLoopCharacteristics.setSequential(false);
|
||||
//方便系统开发,直接设置为当前登录用户 activiti:assignee="${assignee}" 相当于将认领人指定为loop中取得的变量对象,就和java 中 foreach 差不多的意思;
|
||||
userTask.setAssignee("${assigneeName}");
|
||||
// 设置多实例属性
|
||||
Properties props = node.getProps();
|
||||
String mode = props.getMode();
|
||||
userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);
|
||||
if (ModeEnums.AND.getTypeName().equals(mode)) {
|
||||
// nrOfInstances :实例总数
|
||||
// nrOfActiveInstances:当前活动(即尚未完成)实例的数量。对于顺序多实例,这将始终为1。
|
||||
// nrOfCompletedInstances:已完成实例的数量。
|
||||
multiInstanceLoopCharacteristics.setCompletionCondition("${nrOfCompletedInstances==nrOfInstances}");
|
||||
} else if (ModeEnums.OR.getTypeName().equals(mode)) {
|
||||
multiInstanceLoopCharacteristics.setSequential(false);
|
||||
multiInstanceLoopCharacteristics.setCompletionCondition("${nrOfCompletedInstances > 0}");
|
||||
} else if (ModeEnums.NEXT.getTypeName().equals(mode)) {
|
||||
multiInstanceLoopCharacteristics.setSequential(true);
|
||||
multiInstanceLoopCharacteristics.setCompletionCondition("${nrOfCompletedInstances==nrOfInstances}");
|
||||
}
|
||||
setUserTaskProcessListener(props.getListener(), userTask);
|
||||
String fromKey = getId();
|
||||
userTask.setFormKey(fromKey);
|
||||
createFromPerms(node, fromKey);
|
||||
process.addFlowElement(userTask);
|
||||
connect(parentId, node.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置监听器
|
||||
*
|
||||
* @param info 监听器信息
|
||||
* @param userTask 用户任务节点
|
||||
*/
|
||||
private void setUserTaskProcessListener(ListenerInfo info, UserTask userTask) {
|
||||
if (!info.getState()) {
|
||||
return;
|
||||
}
|
||||
List<FlowableListener> taskListeners = userTask.getTaskListeners();
|
||||
List<FlowableListener> executionListeners = userTask.getExecutionListeners();
|
||||
for (ListenerItem listenerItem : info.getList()) {
|
||||
if (listenerItem.getIsSys()) {
|
||||
ProcessListener processListener = processListenerMap.get(listenerItem.getListenerValue());
|
||||
if (null == processListener) {
|
||||
continue;
|
||||
}
|
||||
List<String> eventList = JSON.parseArray(processListener.getEventType(), String.class);
|
||||
markFlowableListener(taskListeners, executionListeners, eventList, processListener.getListenerValueType(), processListener.getListenerType());
|
||||
} else {
|
||||
List<String> eventList = listenerItem.getEventType();
|
||||
markFlowableListener(taskListeners, executionListeners, eventList, listenerItem.getListenerValueType(), listenerItem.getListenerType());
|
||||
}
|
||||
}
|
||||
userTask.setTaskListeners(taskListeners);
|
||||
userTask.setExecutionListeners(executionListeners);
|
||||
}
|
||||
|
||||
/**
|
||||
* 制作监听器
|
||||
*
|
||||
* @param taskListeners 用户监听器列表
|
||||
* @param executionListeners 执行器监听列表
|
||||
* @param eventList 事件列表
|
||||
* @param listenerValueType 监听器值类型
|
||||
* @param listenerType 监听器类型
|
||||
*/
|
||||
private void markFlowableListener(List<FlowableListener> taskListeners, List<FlowableListener> executionListeners, List<String> eventList, String listenerValueType, String listenerType) {
|
||||
for (String event : eventList) {
|
||||
FlowableListener listener = new FlowableListener();
|
||||
listener.setEvent(event);
|
||||
listener.setImplementationType(getImplementationType(listenerValueType));
|
||||
if ("0".equals(listenerType)) {
|
||||
executionListeners.add(listener);
|
||||
} else {
|
||||
taskListeners.add(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据监听器值类型获取到对应的加载类型
|
||||
*
|
||||
* @param listenerValueType 值类型
|
||||
* @return 加载类型
|
||||
*/
|
||||
private String getImplementationType(String listenerValueType) {
|
||||
switch (listenerValueType) {
|
||||
case "0":
|
||||
return "class";
|
||||
case "1":
|
||||
return "expression";
|
||||
case "2":
|
||||
return "delegateExpression";
|
||||
default:
|
||||
throw new CustomException("类型异常");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建延时处理的service服务
|
||||
*
|
||||
* @param parentId 父级id
|
||||
* @param node 节点信息
|
||||
*/
|
||||
private void createDelayService(String parentId, ProcessNode node) {
|
||||
//创建定时任务
|
||||
IntermediateCatchEvent intermediateCatchEvent = new IntermediateCatchEvent();
|
||||
intermediateCatchEvent.setId(node.getId());
|
||||
intermediateCatchEvent.setName(node.getName());
|
||||
List<FlowableListener> flowableListenerList = new ArrayList<>();
|
||||
//添加定时任务触发的开始监听器
|
||||
FlowableListener catchEventListenerStart = new FlowableListener();
|
||||
catchEventListenerStart.setEvent(ExecutionListener.EVENTNAME_START);
|
||||
catchEventListenerStart.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
|
||||
catchEventListenerStart.setImplementation("${intermediateCatchEventListener}");
|
||||
flowableListenerList.add(catchEventListenerStart);
|
||||
//添加定时任务触发的结束监听器
|
||||
FlowableListener catchEventListenerEnd = new FlowableListener();
|
||||
catchEventListenerEnd.setEvent(ExecutionListener.EVENTNAME_END);
|
||||
catchEventListenerEnd.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
|
||||
catchEventListenerEnd.setImplementation("${intermediateCatchEventListener}");
|
||||
flowableListenerList.add(catchEventListenerEnd);
|
||||
intermediateCatchEvent.setExecutionListeners(flowableListenerList);
|
||||
|
||||
TimerEventDefinition timerEventDefinition = new TimerEventDefinition();
|
||||
|
||||
Properties props = node.getProps();
|
||||
switch (props.getType()) {
|
||||
case "FIXED":
|
||||
//指定时间触发
|
||||
timerEventDefinition.setTimeDuration("PT" + props.getTime() + props.getUnit());
|
||||
break;
|
||||
case "AUTO":
|
||||
//自动到当天的时间,如果已经过来当天的时间,则需要指定到下一天
|
||||
timerEventDefinition.setTimeDate("${" + node.getId() + "}");
|
||||
break;
|
||||
default:
|
||||
throw new CustomException("模式还未实现!");
|
||||
}
|
||||
////设置好时间事件
|
||||
intermediateCatchEvent.setEventDefinitions(Collections.singletonList(timerEventDefinition));
|
||||
//将流程添加到process文件中
|
||||
process.addFlowElement(intermediateCatchEvent);
|
||||
//将节点连接起来
|
||||
connect(parentId, node.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建userTask的边界事件
|
||||
*
|
||||
* @param node 节点信息
|
||||
* @param userTask 用户任务
|
||||
*/
|
||||
public void createUserTaskBoundaryEvent(ProcessNode node, UserTask userTask) {
|
||||
Properties props = node.getProps();
|
||||
JSONObject timeLimit = props.getTimeLimit();
|
||||
JSONObject timeout = timeLimit.getJSONObject("timeout");
|
||||
long value = timeout.getLongValue("value");
|
||||
if (value <= 0) {
|
||||
return;
|
||||
}
|
||||
//定义边界事件
|
||||
BoundaryEvent boundaryEvent = new BoundaryEvent();
|
||||
boundaryEvent.setId(userTask.getId().replace("node_", "boundary_"));
|
||||
boundaryEvent.setAttachedToRef(userTask);
|
||||
boundaryEvent.setAttachedToRefId(userTask.getId());
|
||||
|
||||
//边界事件监听器
|
||||
List<FlowableListener> boundaryEventList = new ArrayList<>();
|
||||
//监听器
|
||||
FlowableListener boundaryEventListener = new FlowableListener();
|
||||
boundaryEventListener.setEvent(ExecutionListener.EVENTNAME_END);
|
||||
boundaryEventListener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
|
||||
boundaryEventListener.setImplementation("${boundaryEvenListener}");
|
||||
boundaryEventList.add(boundaryEventListener);
|
||||
//边界事件结束,设置不关闭主流程
|
||||
boundaryEvent.setCancelActivity(false);
|
||||
//todo 方便测试,占时不做真的数据解析
|
||||
TimerEventDefinition eventDefinition = new TimerEventDefinition();
|
||||
//添加时间表达式
|
||||
eventDefinition.setTimeDuration("PT" + props.getTime() + props.getUnit());
|
||||
//eventDefinition.setTimeDuration("PT10S");
|
||||
JSONObject notify = timeLimit.getJSONObject("handler").getJSONObject("notify");
|
||||
if (!notify.getBoolean("once")) {
|
||||
String hour = notify.getString("hour");
|
||||
eventDefinition.setTimeCycle("R/PT" + hour + props.getUnit() + "/2100-01-01");
|
||||
//eventDefinition.setTimeCycle("R/PT10S/2100-01-01");
|
||||
}
|
||||
//时间表达式
|
||||
boundaryEvent.setEventDefinitions(Collections.singletonList(eventDefinition));
|
||||
boundaryEvent.setExecutionListeners(boundaryEventList);
|
||||
process.addFlowElement(boundaryEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建service Task (CC, TRIGGER)
|
||||
*
|
||||
* @param parentId 父级id
|
||||
* @param node 节点信息
|
||||
*/
|
||||
private void creteServiceTask(String parentId, ProcessNode node) {
|
||||
ServiceTask serviceTask = new ServiceTask();
|
||||
serviceTask.setId(node.getId());
|
||||
serviceTask.setName(node.getName());
|
||||
serviceTask.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
|
||||
serviceTask.setImplementation("${serviceListener}");
|
||||
process.addFlowElement(serviceTask);
|
||||
connect(parentId, node.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建开始节点
|
||||
*
|
||||
* @param node 节点对象
|
||||
*/
|
||||
public void createStartEvent(ProcessNode node) {
|
||||
StartEvent startEvent = new StartEvent();
|
||||
startEvent.setId(node.getId());
|
||||
startEvent.setInitiator("applyUserId");
|
||||
//定义任务监听器
|
||||
FlowableListener listener = new FlowableListener();
|
||||
// 事件类型,
|
||||
listener.setEvent(ExecutionListener.EVENTNAME_START);
|
||||
// 监听器类型
|
||||
listener.setImplementationType(IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
|
||||
// 设置实现了,这里设置监听器的类型是delegateExpression,这样可以在实现类注入Spring bean.
|
||||
listener.setImplementation("${createStartEventListener}");
|
||||
startEvent.setExecutionListeners(Collections.singletonList(listener));
|
||||
|
||||
createFromPerms(node, ProcessConstant.ROOT_NODE);
|
||||
process.addFlowElement(startEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建表单权限
|
||||
*
|
||||
* @param node 节点对象
|
||||
* @param fromKey 表单对应的key
|
||||
*/
|
||||
private void createFromPerms(ProcessNode node, String fromKey) {
|
||||
Properties props = node.getProps();
|
||||
List<FormOperates> formPerms = props.getFormPerms();
|
||||
if (null != formPerms && !formPerms.isEmpty()) {
|
||||
System.out.println(formPerms);
|
||||
for (FormOperates formPerm : formPerms) {
|
||||
createFromPerm(formPerm, fromKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建表单权限对象并存储
|
||||
*
|
||||
* @param formOperates 表单对象
|
||||
* @param fromKey 表单对应的key
|
||||
*/
|
||||
private void createFromPerm(FormOperates formOperates, String fromKey) {
|
||||
FormPerm formPerm = FormPerm.builder()
|
||||
.id(formOperates.getId())
|
||||
.fromKey(fromKey)
|
||||
.required(formOperates.getRequired())
|
||||
.build();
|
||||
switch (formOperates.getPerm()) {
|
||||
//可读
|
||||
case "R":
|
||||
formPerm.setPerm(FormPermEnum.READ);
|
||||
break;
|
||||
//可写
|
||||
case "E":
|
||||
formPerm.setPerm(FormPermEnum.EDIT);
|
||||
break;
|
||||
//影藏
|
||||
case "H":
|
||||
formPerm.setPerm(FormPermEnum.HIDE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
formPerms.add(formPerm);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连线的随机id
|
||||
*
|
||||
* @return 返回连线id
|
||||
*/
|
||||
public String getEdgeId() {
|
||||
return "flow_" + getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取uuid
|
||||
*
|
||||
* @return 返回uuid
|
||||
*/
|
||||
public String getId() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package cn.fateverse.workflow.process;
|
||||
|
||||
import cn.fateverse.workflow.entity.bpmn.FormItem;
|
||||
import cn.fateverse.workflow.enums.FormItemEnum;
|
||||
import cn.fateverse.workflow.enums.FormPermEnum;
|
||||
import cn.fateverse.workflow.entity.FormPerm;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* 表单工具类
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/25
|
||||
*/
|
||||
public class FormItemParseUtil {
|
||||
|
||||
|
||||
/**
|
||||
* 检查表单
|
||||
*
|
||||
* @param formItems 需要简单的对象
|
||||
* @param fromPermMap 表单权限数据
|
||||
* @param formData 表单数据
|
||||
* @return 检查完成后的表单数据
|
||||
*/
|
||||
public static List<FormItem> checkFromItemPermsNew(List<FormItem> formItems, Map<String, FormPerm> fromPermMap, JSONObject formData) {
|
||||
return checkFromItemPermsNew(formItems, fromPermMap, formData, false);
|
||||
}
|
||||
|
||||
public static JSONArray checkFromItemPerms(JSONArray objects, Map<String, FormPerm> fromPermMap, JSONObject formData) {
|
||||
return checkFromItemPerms(objects, fromPermMap, formData, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查表单
|
||||
*
|
||||
* @param formItems 需要简单的对象
|
||||
* @param fromPermMap 表单权限数据
|
||||
* @param formData 表单数据
|
||||
* @param read 当表单没有对应的权限对象是,指定是否是可读权限,true 可读, false 可编辑
|
||||
* @return 检查完成后的表单数据
|
||||
*/
|
||||
public static List<FormItem> checkFromItemPermsNew(List<FormItem> formItems, Map<String, FormPerm> fromPermMap, JSONObject formData, Boolean read) {
|
||||
List<FormItem> result = new ArrayList<>();
|
||||
//判断当前传参是否为空的list
|
||||
if (null == formItems || formItems.isEmpty()){
|
||||
return new ArrayList<>();
|
||||
}
|
||||
for (FormItem formItem : formItems) {
|
||||
boolean flag = true;
|
||||
//如果是布局组件则需要递归
|
||||
if (FormItemEnum.SpanLayout.getName().equals(formItem.getName())){
|
||||
//获取到配置信息
|
||||
JSONObject props = formItem.getProps();
|
||||
//获取到布局下的items
|
||||
List<FormItem> items = props.getList("items",FormItem.class);
|
||||
//递归检查items
|
||||
List<FormItem> resultItems = checkFromItemPermsNew(items, fromPermMap, formData, read);
|
||||
props.put("items",resultItems);
|
||||
formItem.setProps(props);
|
||||
}else {
|
||||
//进行权限检查
|
||||
String id = formItem.getId();
|
||||
FormPerm formPerm = fromPermMap.get(id);
|
||||
if (null == formPerm) {
|
||||
if (read) {
|
||||
formItem.setPerm(FormPermEnum.READ.getType());
|
||||
} else {
|
||||
formItem.setPerm(FormPermEnum.EDIT.getType());
|
||||
}
|
||||
}else {
|
||||
switch (formPerm.getPerm()) {
|
||||
//编辑和可读状态进行权限赋值
|
||||
case EDIT:
|
||||
case READ:
|
||||
formItem.setPerm(formPerm.getPerm().getType());
|
||||
break;
|
||||
//影藏则同上,将表单数据清除,当前表单组件不显示
|
||||
case HIDE:
|
||||
if (null != formData) {
|
||||
formData.remove(id);
|
||||
}
|
||||
flag = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flag){
|
||||
result.add(formItem);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static JSONArray checkFromItemPerms(JSONArray objects, Map<String, FormPerm> fromPermMap, JSONObject formData, Boolean read) {
|
||||
//判断当前传参是否为空的list
|
||||
if (null == objects || objects.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
//构建需要返回的对象
|
||||
JSONArray result = new JSONArray();
|
||||
for (int i = 0; i < objects.size(); i++) {
|
||||
//获取到当前需要检查的对象
|
||||
JSONObject fromItem = objects.getJSONObject(i);
|
||||
String id = fromItem.getString("id");
|
||||
//如果是布局组件则需要递归
|
||||
if (FormItemEnum.SpanLayout.getName().equals(fromItem.getString("name"))) {
|
||||
//获取到配置信息
|
||||
JSONObject props = fromItem.getJSONObject("props");
|
||||
//获取到布局下的items
|
||||
JSONArray items = props.getJSONArray("items");
|
||||
//递归检查items
|
||||
JSONArray resultItems = checkFromItemPerms(items, fromPermMap, formData, read);
|
||||
//如果为空,则清除当前的组件
|
||||
if (null != resultItems && !resultItems.isEmpty()) {
|
||||
//不为空则更新当前的组件信息
|
||||
result.addAll(resultItems);
|
||||
}
|
||||
} else {
|
||||
//进行权限检查
|
||||
FormPerm formPerm = fromPermMap.get(id);
|
||||
if (null == formPerm) {
|
||||
if (read) {
|
||||
fromItem.put("perm", FormPermEnum.READ.getType());
|
||||
} else {
|
||||
fromItem.put("perm", FormPermEnum.EDIT.getType());
|
||||
}
|
||||
result.add(fromItem);
|
||||
continue;
|
||||
}
|
||||
switch (formPerm.getPerm()) {
|
||||
//编辑和可读状态进行权限赋值
|
||||
case EDIT:
|
||||
case READ:
|
||||
fromItem.put("perm", formPerm.getPerm().getType());
|
||||
result.add(fromItem);
|
||||
break;
|
||||
//影藏则同上,将表单数据清除,当前表单组件不显示
|
||||
case HIDE:
|
||||
if (null != formData) {
|
||||
formData.remove(id);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查表单的最新权限
|
||||
*
|
||||
* @param formData 表单数据
|
||||
* @param formDataOld 历史表单数据
|
||||
* @param fromPermMap 表单权限数据
|
||||
* @return 校验完成的表单数据
|
||||
*/
|
||||
public static JSONObject checkFromDataPrem(JSONObject formData, JSONObject formDataOld, Map<String, FormPerm> fromPermMap) {
|
||||
for (String key : formData.keySet()) {
|
||||
FormPerm formPerm = fromPermMap.get(key);
|
||||
if (null != formPerm && formPerm.getPerm() == FormPermEnum.EDIT) {
|
||||
formDataOld.remove(key);
|
||||
formDataOld.put(key, formData.get(key));
|
||||
}
|
||||
}
|
||||
return formDataOld;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package cn.fateverse.workflow.process;
|
||||
|
||||
import cn.fateverse.workflow.entity.HistoricalOperation;
|
||||
import cn.fateverse.workflow.entity.bpmn.CommentInfo;
|
||||
import cn.fateverse.workflow.entity.bpmn.UserInfo;
|
||||
import cn.fateverse.workflow.entity.vo.HistoricalOperationVo;
|
||||
import cn.fateverse.workflow.mapper.HistoricalOperationMapper;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import org.flowable.bpmn.model.*;
|
||||
import org.flowable.bpmn.model.Process;
|
||||
import org.flowable.engine.RepositoryService;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 流程服务工具类
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023/1/24
|
||||
*/
|
||||
public class ProcessServiceUtils {
|
||||
|
||||
/**
|
||||
* 获取操作历史数据
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @return 返回的vo对象
|
||||
*/
|
||||
public static List<HistoricalOperationVo> getHistoricalOperations(String processInstanceId, HistoricalOperationMapper operationMapper) {
|
||||
return getHistoricalOperations(processInstanceId, operationMapper, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到操作记录
|
||||
*
|
||||
* @param processInstanceId 部署流程id
|
||||
* @param operationMapper 操作映射
|
||||
* @param mark 标记
|
||||
* @return 返回的vo对象
|
||||
*/
|
||||
public static List<HistoricalOperationVo> getHistoricalOperations(String processInstanceId, HistoricalOperationMapper operationMapper, Integer mark) {
|
||||
List<HistoricalOperation> operationList = operationMapper.selectListByProcessInstanceId(processInstanceId, mark);
|
||||
return getHistoricalOperationVoList(operationList);
|
||||
}
|
||||
|
||||
/**
|
||||
* HistoricalOperation 转换为 Vo对象
|
||||
*
|
||||
* @param operationList 操作历史
|
||||
* @return 返回的vo对象
|
||||
*/
|
||||
@NotNull
|
||||
public static List<HistoricalOperationVo> getHistoricalOperationVoList(List<HistoricalOperation> operationList) {
|
||||
return operationList.stream().map(operation -> {
|
||||
HistoricalOperationVo vo = HistoricalOperationVo.builder()
|
||||
.taskId(operation.getTaskId())
|
||||
.processInstanceId(operation.getProcessInstanceId())
|
||||
.operationName(operation.getOperationName())
|
||||
.operation(operation.getOperation())
|
||||
.finishTime(operation.getFinishTime())
|
||||
.nodeId(operation.getNodeId())
|
||||
.state(operation.getState())
|
||||
.mark(operation.getMark())
|
||||
.startTime(new Date(operation.getStartTime()))
|
||||
.build();
|
||||
if (null != operation.getMessage()) {
|
||||
CommentInfo comment = JSON.parseObject(operation.getMessage(), CommentInfo.class);
|
||||
vo.setComment(comment);
|
||||
}
|
||||
if (null != operation.getUserInfo()) {
|
||||
List<UserInfo> userInfo = JSON.parseArray(operation.getUserInfo(), UserInfo.class);
|
||||
vo.setUserInfo(userInfo);
|
||||
}
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取到bpmn的数据
|
||||
*
|
||||
* @param repositoryService 引擎获取模型对象
|
||||
* @param processDefinitionId 流程id
|
||||
* @param key 参数关键词
|
||||
* @return 参数值
|
||||
*/
|
||||
public static String getBpmnValue(RepositoryService repositoryService, String processDefinitionId, String key) {
|
||||
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
|
||||
Process process = bpmnModel.getProcesses().get(0);
|
||||
ExtensionAttribute attribute = process.getAttributes().get(key).get(0);
|
||||
return attribute.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到bpmn的数据
|
||||
*
|
||||
* @param repositoryService 引擎获取模型对象
|
||||
* @param processDefinitionId 流程id
|
||||
* @return 参数值
|
||||
*/
|
||||
public static Map<String, String> getBpmnValueAttribute(RepositoryService repositoryService, String processDefinitionId) {
|
||||
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
|
||||
Process process = bpmnModel.getProcesses().get(0);
|
||||
return getBpmnValueAttribute(process);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到bpmn的数据
|
||||
*
|
||||
* @param process 流程信息
|
||||
* @return 参数
|
||||
*/
|
||||
public static Map<String, String> getBpmnValueAttribute(Process process) {
|
||||
Map<String, List<ExtensionAttribute>> attributes = process.getAttributes();
|
||||
return attributes.keySet().stream().map(key -> attributes.get(key).get(0)).collect(Collectors.toMap(ExtensionAttribute::getName, ExtensionAttribute::getValue));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package cn.fateverse.workflow.process;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程引擎函数工具类
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/30
|
||||
*/
|
||||
@Component
|
||||
public class ProcessUtil {
|
||||
|
||||
/**
|
||||
* 字符串是否等于
|
||||
*
|
||||
* @param controlId 控制id
|
||||
* @param value 需要检测的值
|
||||
* @return 返回结果
|
||||
*/
|
||||
public Boolean strEqualsMethod(String controlId, String value) {
|
||||
return value.equals(controlId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 字符串是否包含函数
|
||||
*
|
||||
* @param controlId 控制id
|
||||
* @param values 需要检测的值
|
||||
* @return 返回结果
|
||||
*/
|
||||
public static Boolean strArrContainsMethod(String controlId, String... values) {
|
||||
List<String> valueList = Arrays.asList(values);
|
||||
return valueList.contains(controlId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数值类型是否包含函数
|
||||
*
|
||||
* @param controlId 控制id
|
||||
* @param values 需要检测的值
|
||||
* @return 返回结果
|
||||
*/
|
||||
public static Boolean numberContainsMethod(Number controlId, Number... values) {
|
||||
List<Number> valueList = Arrays.asList(values);
|
||||
return valueList.contains(controlId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断B模式
|
||||
*
|
||||
* @param controlId 控制id
|
||||
* @param values 需要检测的值
|
||||
* @return 返回结果
|
||||
*/
|
||||
public static Boolean b(String controlId, Number... values) {
|
||||
List<Number> valueList = Arrays.asList(values);
|
||||
long value = Long.parseLong(controlId);
|
||||
return value > valueList.get(0).longValue() && value < valueList.get(1).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断AB模式
|
||||
*
|
||||
* @param controlId 控制id
|
||||
* @param values 需要检测的值
|
||||
* @return 返回结果
|
||||
*/
|
||||
public static Boolean ab(String controlId, Number... values) {
|
||||
List<Number> valueList = Arrays.asList(values);
|
||||
long value = Long.parseLong(controlId);
|
||||
return value >= valueList.get(0).longValue() && value < valueList.get(1).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断BA模式
|
||||
*
|
||||
* @param controlId 控制id
|
||||
* @param values 需要检测的值
|
||||
* @return 返回结果
|
||||
*/
|
||||
public static Boolean ba(String controlId, Number... values) {
|
||||
List<Number> valueList = Arrays.asList(values);
|
||||
long value = Long.parseLong(controlId);
|
||||
return value > valueList.get(0).longValue() && value <= valueList.get(1).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断ABA模式
|
||||
*
|
||||
* @param controlId 控制id
|
||||
* @param values 需要检测的值
|
||||
* @return 返回结果
|
||||
*/
|
||||
public static Boolean aba(String controlId, Number... values) {
|
||||
List<Number> valueList = Arrays.asList(values);
|
||||
long value = Long.parseLong(controlId);
|
||||
return value >= valueList.get(0).longValue() && value <= valueList.get(1).longValue();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
package cn.fateverse.workflow.process;
|
||||
|
||||
import cn.fateverse.common.code.engine.JavaScriptEngine;
|
||||
import cn.fateverse.workflow.constant.ProcessConstant;
|
||||
import cn.fateverse.workflow.entity.bpmn.*;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.common.email.entity.SendEmailInfo;
|
||||
import cn.fateverse.common.email.service.EmailService;
|
||||
import cn.fateverse.workflow.entity.HistoricalOperation;
|
||||
import cn.fateverse.workflow.enums.OperationEnums;
|
||||
import cn.fateverse.workflow.enums.OperationStateEnums;
|
||||
import cn.fateverse.workflow.mapper.HistoricalOperationMapper;
|
||||
import cn.fateverse.workflow.mapper.UserInstanceMapper;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import jdk.nashorn.api.scripting.ScriptObjectMirror;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-03-22
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TriggerService {
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
private final EmailService emailService;
|
||||
|
||||
private final RuntimeService runtimeService;
|
||||
|
||||
private final UserInstanceMapper userInstanceMapper;
|
||||
|
||||
private final HistoricalOperationMapper operationMapper;
|
||||
|
||||
private final PlatformTransactionManager txManager;
|
||||
|
||||
public TriggerService(EmailService emailService,
|
||||
RuntimeService runtimeService,
|
||||
UserInstanceMapper userInstanceMapper,
|
||||
HistoricalOperationMapper operationMapper,
|
||||
PlatformTransactionManager txManager) {
|
||||
this.emailService = emailService;
|
||||
this.runtimeService = runtimeService;
|
||||
this.userInstanceMapper = userInstanceMapper;
|
||||
this.operationMapper = operationMapper;
|
||||
this.txManager = txManager;
|
||||
this.restTemplate = new RestTemplate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送http请求(异步)
|
||||
*
|
||||
* @param processNode 节点信息
|
||||
* @param operation 流程实例id
|
||||
*/
|
||||
@Async
|
||||
public void asyncSendHttpRequest(ProcessNode processNode, HistoricalOperation operation, DelegateExecution execution) {
|
||||
log.info("开始异步发送请求");
|
||||
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
|
||||
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
TransactionStatus status = txManager.getTransaction(def);
|
||||
try {
|
||||
sendHttpRequest(processNode, operation, execution);
|
||||
txManager.commit(status);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
txManager.rollback(status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送http请求
|
||||
*
|
||||
* @param processNode 节点信息
|
||||
* @param operation 流程实例id
|
||||
* @param execution 执行器中的对象
|
||||
*/
|
||||
public void sendHttpRequest(ProcessNode processNode, HistoricalOperation operation, DelegateExecution execution) {
|
||||
log.info("发送请求");
|
||||
//获取到节点中http相关的数据信息
|
||||
HttpInfo http = processNode.getProps().getHttp();
|
||||
//选取当前http的请求类型
|
||||
HttpMethod httpMethod;
|
||||
switch (http.getMethod()) {
|
||||
case "GET":
|
||||
httpMethod = HttpMethod.GET;
|
||||
break;
|
||||
case "POST":
|
||||
httpMethod = HttpMethod.POST;
|
||||
break;
|
||||
case "PUT":
|
||||
httpMethod = HttpMethod.PUT;
|
||||
break;
|
||||
case "DELETE":
|
||||
httpMethod = HttpMethod.DELETE;
|
||||
break;
|
||||
default:
|
||||
throw new CustomException("请求类型错误!");
|
||||
}
|
||||
HttpHeaders headers = getHttpHeaders(http);
|
||||
HttpEntity<String> entity = new HttpEntity<>(headers);
|
||||
//制作响应头
|
||||
ParameterizedTypeReference<HashMap<String, Object>> responseType = new ParameterizedTypeReference<HashMap<String, Object>>() {
|
||||
};
|
||||
|
||||
//发起请求
|
||||
ResponseEntity<HashMap<String, Object>> response;
|
||||
try {
|
||||
switch (httpMethod) {
|
||||
case GET:
|
||||
case DELETE:
|
||||
String url = getGetAndDeleteParams(http, execution.getVariables());
|
||||
response = restTemplate.exchange(url, httpMethod, entity, responseType);
|
||||
break;
|
||||
case POST:
|
||||
case PUT:
|
||||
Map<String, Object> params = getPostAndPutParams(http, execution.getVariables());
|
||||
response = restTemplate.exchange(http.getUrl(), httpMethod, entity, responseType, params);
|
||||
break;
|
||||
default:
|
||||
throw new CustomException("请求类型不支持");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e instanceof CustomException) {
|
||||
CustomException custom = (CustomException) e;
|
||||
String message = custom.getMessage();
|
||||
operation.setState(OperationStateEnums.REFUSE);
|
||||
changeOperation(operation, message);
|
||||
if (http.getHandlerByScript()) {
|
||||
rejectionProcess(execution, "http请求异常,流程自动终止!");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!http.getHandlerByScript()) {
|
||||
changeOperation(operation, true);
|
||||
return;
|
||||
}
|
||||
//获取到请求的返回结果
|
||||
Map<String, Object> result = response.getBody();
|
||||
ScriptObjectMirror jsResult;
|
||||
try {
|
||||
//判断请求是否有效
|
||||
if (response.getStatusCode() == HttpStatus.OK) {
|
||||
jsResult = JavaScriptEngine.executeScript(http.getSuccess(), "handlerSuccess", result);
|
||||
operation.setState(OperationStateEnums.SUCCESS);
|
||||
} else {
|
||||
jsResult = JavaScriptEngine.executeScript(http.getFail(), "handlerFail", result);
|
||||
operation.setState(OperationStateEnums.FAILURE);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
operation.setState(OperationStateEnums.REFUSE);
|
||||
changeOperation(operation, message);
|
||||
if (http.getHandlerByScript()) {
|
||||
rejectionProcess(execution, "http请求异常,流程自动终止!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
//获取到自定义脚本的状态
|
||||
boolean state = (Boolean) jsResult.get("state");
|
||||
//获取到js脚本中的内容
|
||||
changeOperation(operation, jsResult.get("msg").toString());
|
||||
//如果返回结果为false,需要停止流程的执行
|
||||
if (!state) {
|
||||
rejectionProcess(execution, "http请求发送失败,驳回流程");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取post和pur的参数
|
||||
*
|
||||
* @param http 请求对象信息
|
||||
* @param variables 流程参数
|
||||
* @return 参数信息
|
||||
*/
|
||||
private Map<String, Object> getPostAndPutParams(HttpInfo http, Map<String, Object> variables) {
|
||||
List<HttpParam> params = http.getParams();
|
||||
if (null == params) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
Map<String, Object> resultParams = new HashMap<>();
|
||||
for (HttpParam param : params) {
|
||||
if (!StrUtil.isEmpty(param.getName()) && !StrUtil.isEmpty(param.getValue())) {
|
||||
Object value;
|
||||
if (param.getIsField()) {
|
||||
value = variables.get(param.getValue());
|
||||
} else {
|
||||
value = param.getValue();
|
||||
}
|
||||
resultParams.put(param.getName(), value);
|
||||
}
|
||||
}
|
||||
return resultParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取到动态参数名称
|
||||
*
|
||||
* @param url 请求路由
|
||||
* @return 返回参数名称
|
||||
*/
|
||||
public Set<String> getDynamicParamNames(String url) {
|
||||
Set<String> paramNames = new HashSet<>();
|
||||
String regex = "\\{(\\w+)}";
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
Matcher matcher = pattern.matcher(url);
|
||||
while (matcher.find()) {
|
||||
String paramName = matcher.group(1);
|
||||
paramNames.add(paramName);
|
||||
}
|
||||
return paramNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取get或者delete请求的url
|
||||
*
|
||||
* @param http http信息
|
||||
* @param variables 流程参数
|
||||
* @return url
|
||||
*/
|
||||
private String getGetAndDeleteParams(HttpInfo http, Map<String, Object> variables) {
|
||||
StringBuffer url = new StringBuffer();
|
||||
Map<String, HttpParam> httpParamMap = null;
|
||||
if (null != http.getParams()) {
|
||||
httpParamMap = http.getParams().stream().filter(param -> !StrUtil.isEmpty(param.getName()) && !StrUtil.isEmpty(param.getValue())).collect(Collectors.toMap(HttpParam::getName, Function.identity()));
|
||||
}
|
||||
Set<String> dynamicParamSet = getDynamicParamNames(http.getUrl());
|
||||
if (!dynamicParamSet.isEmpty()) {
|
||||
if (null == httpParamMap || httpParamMap.isEmpty()) {
|
||||
throw new CustomException("url含有动态参数,但是参数列表为空!");
|
||||
}
|
||||
Map<String, String> replaceParams = new HashMap<>();
|
||||
for (String param : dynamicParamSet) {
|
||||
HttpParam httpParam = httpParamMap.get(param);
|
||||
if (null == httpParam) {
|
||||
throw new CustomException(param + "参数未设置");
|
||||
}
|
||||
Object value;
|
||||
if (httpParam.getIsField()) {
|
||||
value = variables.get(httpParam.getValue());
|
||||
} else {
|
||||
value = httpParam.getValue();
|
||||
}
|
||||
replaceParams.put(param, value.toString());
|
||||
}
|
||||
url = replaceDynamicParams(http.getUrl(), replaceParams);
|
||||
}
|
||||
if (http.getMethod().equals("DELETE")) {
|
||||
return url.toString();
|
||||
}
|
||||
if (null == httpParamMap || httpParamMap.isEmpty()) {
|
||||
return http.getUrl();
|
||||
}
|
||||
List<String> getMethodsParamsList = new ArrayList<>();
|
||||
for (String paramName : httpParamMap.keySet()) {
|
||||
if (!dynamicParamSet.contains(paramName)) {
|
||||
String paramStr = paramName + "=" + httpParamMap.get(paramName).getValue();
|
||||
getMethodsParamsList.add(paramStr);
|
||||
}
|
||||
}
|
||||
if (!getMethodsParamsList.isEmpty()) {
|
||||
url.append("?");
|
||||
for (int i = 0; i < getMethodsParamsList.size(); i++) {
|
||||
if (i != getMethodsParamsList.size() - 1) {
|
||||
url.append(getMethodsParamsList.get(i)).append("&");
|
||||
} else {
|
||||
url.append(getMethodsParamsList.get(i));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换
|
||||
*
|
||||
* @param url 请求路
|
||||
* @param dynamicParams 动态参数信息
|
||||
* @return 替换好的url
|
||||
*/
|
||||
private StringBuffer replaceDynamicParams(String url, Map<String, String> dynamicParams) {
|
||||
String regex = "\\{(\\w+)}";
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
Matcher matcher = pattern.matcher(url);
|
||||
StringBuffer newUrl = new StringBuffer();
|
||||
while (matcher.find()) {
|
||||
String paramName = matcher.group(1);
|
||||
String paramValue = dynamicParams.get(paramName);
|
||||
if (paramValue != null) {
|
||||
matcher.appendReplacement(newUrl, paramValue);
|
||||
} else {
|
||||
matcher.appendReplacement(newUrl, "0");
|
||||
}
|
||||
}
|
||||
matcher.appendTail(newUrl);
|
||||
return newUrl;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
private HttpHeaders getHttpHeaders(HttpInfo http) {
|
||||
//选择 Content-Type
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
if (http.getContentType().equals("FORM")) {
|
||||
headers.set("Content-Type", "multipart/form-data");
|
||||
} else {
|
||||
headers.set("Content-Type", "application/json");
|
||||
}
|
||||
//组装header
|
||||
List<HttpParam> headerParams = http.getHeaders();
|
||||
if (null != headerParams && !headerParams.isEmpty()) {
|
||||
for (HttpParam param : headerParams) {
|
||||
if (StrUtil.isEmpty(param.getName()) || StrUtil.isEmpty(param.getValue())) {
|
||||
continue;
|
||||
}
|
||||
headers.set(param.getName(), param.getValue());
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变操作历史
|
||||
*
|
||||
* @param operation 操作历史
|
||||
* @param msg 消息
|
||||
*/
|
||||
private void changeOperation(HistoricalOperation operation, String msg) {
|
||||
CommentInfo comment = new CommentInfo();
|
||||
comment.setContext(msg);
|
||||
operation.setMessage(JSON.toJSONString(comment));
|
||||
//对当前的操作进行更新
|
||||
int resultNum = operationMapper.update(operation);
|
||||
if (resultNum == 0) {
|
||||
operationMapper.insert(operation);
|
||||
}
|
||||
}
|
||||
|
||||
private void rejectionProcess(DelegateExecution execution, String reason) {
|
||||
//对流程进行结束的操作
|
||||
Date date = new Date();
|
||||
changVariables(execution, ProcessConstant.BUSINESS_STATUS_REJECT);
|
||||
HistoricalOperation failOperation = HistoricalOperation.builder()
|
||||
.taskId(HistoricalOperation.makeTaskId())
|
||||
.processInstanceId(execution.getProcessInstanceId())
|
||||
.operation(OperationEnums.COMMENT)
|
||||
.state(OperationStateEnums.FAILURE)
|
||||
.startTime(date.getTime())
|
||||
.finishTime(date)
|
||||
.build();
|
||||
failOperation.setNodeId(HistoricalOperation.makeTaskId());
|
||||
CommentInfo failComment = new CommentInfo();
|
||||
failComment.setContext(reason);
|
||||
failOperation.setMessage(JSON.toJSONString(failComment));
|
||||
//添加结束操作的操作记录
|
||||
operationMapper.insert(failOperation);
|
||||
//停止当前流程
|
||||
runtimeService.deleteProcessInstance(execution.getProcessInstanceId(), "http请求发送失败,驳回流程");
|
||||
userInstanceMapper.updateState(execution.getProcessInstanceId(), ProcessConstant.BUSINESS_STATUS_REJECT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 改变运行装填
|
||||
*
|
||||
* @param execution 执行器任务信息
|
||||
* @param state 需要修改的状态
|
||||
*/
|
||||
private void changVariables(DelegateExecution execution, String state) {
|
||||
//获取流程数据,并设置为拒绝状态
|
||||
Map<String, Object> variables = execution.getTransientVariables();
|
||||
variables.put(ProcessConstant.PROCESS_STATUS, state);
|
||||
//将表单数据设置到流程中
|
||||
runtimeService.setVariables(execution.getProcessInstanceId(), variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮件(异步)
|
||||
*
|
||||
* @param processNode 节点信息
|
||||
* @param operation 流程实例id
|
||||
*/
|
||||
@Async
|
||||
public void asyncSendEmail(ProcessNode processNode, HistoricalOperation operation) {
|
||||
log.info("开始异步发送邮件");
|
||||
sendEmail(processNode, operation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮件
|
||||
*
|
||||
* @param processNode 节点信息
|
||||
* @param operation 流程实例id
|
||||
*/
|
||||
public void sendEmail(ProcessNode processNode, HistoricalOperation operation) {
|
||||
log.info("发送邮件");
|
||||
EmailInfo email = processNode.getProps().getEmail();
|
||||
SendEmailInfo send = new SendEmailInfo(email.getSubject(), email.getTo(), email.getCc(), email.getContent());
|
||||
boolean sendMailState = false;
|
||||
try {
|
||||
sendMailState = emailService.sendMail(send);
|
||||
} catch (Exception e) {
|
||||
log.error("邮件发送异常", e);
|
||||
}
|
||||
changeOperation(operation, sendMailState);
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变操作历史状态
|
||||
*
|
||||
* @param oldOperation 流程实例id
|
||||
* @param sendState 发送状态
|
||||
*/
|
||||
private void changeOperation(HistoricalOperation oldOperation, boolean sendState) {
|
||||
HistoricalOperation operation = operationMapper.selectByTaskId(oldOperation.getTaskId());
|
||||
if (null == operation) {
|
||||
operation = oldOperation;
|
||||
}
|
||||
CommentInfo comment = new CommentInfo();
|
||||
if (sendState) {
|
||||
comment.setContext(oldOperation.getOperation().getDesc() + "发送成功");
|
||||
operation.setState(OperationStateEnums.SUCCESS);
|
||||
} else {
|
||||
comment.setContext(oldOperation.getOperation().getDesc() + "发送失败");
|
||||
operation.setState(OperationStateEnums.FAILURE);
|
||||
}
|
||||
log.info(comment.getContext());
|
||||
operation.setMessage(JSON.toJSONString(comment));
|
||||
int resultNum = operationMapper.update(operation);
|
||||
if (resultNum == 0) {
|
||||
operationMapper.insert(oldOperation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package cn.fateverse.workflow.process.cmd;
|
||||
|
||||
import cn.fateverse.workflow.entity.bo.AnalysisProcessBo;
|
||||
import cn.fateverse.workflow.entity.dto.TaskDto;
|
||||
import org.flowable.bpmn.model.FlowNode;
|
||||
import org.flowable.bpmn.model.Process;
|
||||
import org.flowable.common.engine.impl.interceptor.Command;
|
||||
import org.flowable.common.engine.impl.interceptor.CommandContext;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.impl.delegate.ActivityBehavior;
|
||||
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
|
||||
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
|
||||
import org.flowable.engine.impl.util.CommandContextUtil;
|
||||
import org.flowable.engine.runtime.Execution;
|
||||
import org.flowable.task.api.Task;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 驳回用户审批执行器
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-21
|
||||
*/
|
||||
public class RollbackUserTaskCmd implements Command<String>, Serializable {
|
||||
|
||||
private final RuntimeService runtimeService;
|
||||
private final Task task;
|
||||
private final TaskDto taskDto;
|
||||
private final AnalysisProcessBo processBo;
|
||||
private final Process process;
|
||||
|
||||
public RollbackUserTaskCmd(RuntimeService runtimeService,
|
||||
Task task, TaskDto taskDto,
|
||||
AnalysisProcessBo processBo, Process process) {
|
||||
this.runtimeService = runtimeService;
|
||||
this.task = task;
|
||||
this.taskDto = taskDto;
|
||||
this.processBo = processBo;
|
||||
this.process = process;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String execute(CommandContext commandContext) {
|
||||
List<Execution> executions = runtimeService.createExecutionQuery().parentId(task.getProcessInstanceId()).list();
|
||||
//过滤出需要删除的执行器
|
||||
List<String> executionIds = executions.stream()
|
||||
.filter(execution -> processBo.getLeaveMergeNodeSet().contains(execution.getActivityId()) || processBo.getRollbackNodeSet().contains(execution.getActivityId()))
|
||||
.map(Execution::getId).collect(Collectors.toList());
|
||||
|
||||
//通过执行器就行驳回
|
||||
runtimeService.createChangeActivityStateBuilder()
|
||||
.processInstanceId(task.getProcessInstanceId())
|
||||
.moveExecutionsToSingleActivityId(executionIds, taskDto.getRollBackId())
|
||||
.changeState();
|
||||
// 需要解析当前节点和目标节点的相对位置
|
||||
//1. 当前节点和目标节点都不在网关内 (无需处理)
|
||||
//2. 当前节点在网关内,目标节点在上一级网关外,或理解为目标节点相对当前节点在并行网关外 (无需处理)
|
||||
//3. 当前节点不在网关内,目标节点在网关内 (需要处理)
|
||||
//4. 当前节点和目标节点在不同的网关类 (需要处理)
|
||||
if (!processBo.getEnterMergeNodeSet().isEmpty()) {
|
||||
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
|
||||
String parentId = executions.iterator().next().getParentId();
|
||||
ExecutionEntity parentExecutionEntity = executionEntityManager.findById(parentId);
|
||||
for (String targetInSpecialGatewayEndId : processBo.getEnterMergeNodeSet()) {
|
||||
FlowNode targetInSpecialGatewayEnd = (FlowNode) process.getFlowElement(targetInSpecialGatewayEndId);
|
||||
int nbrOfExecutionsToJoin = targetInSpecialGatewayEnd.getIncomingFlows().size();
|
||||
// 处理目标节点所处的分支以外的分支,即 总分枝数-1 = nbrOfExecutionsToJoin - 1
|
||||
for (int i = 0; i < nbrOfExecutionsToJoin - 1; i++) {
|
||||
ExecutionEntity childExecution = executionEntityManager.createChildExecution(parentExecutionEntity);
|
||||
childExecution.setCurrentFlowElement(targetInSpecialGatewayEnd);
|
||||
ActivityBehavior activityBehavior = (ActivityBehavior) targetInSpecialGatewayEnd.getBehavior();
|
||||
activityBehavior.execute(childExecution);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package cn.fateverse.workflow.process.listener;
|
||||
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.workflow.constant.ProcessConstant;
|
||||
import cn.fateverse.workflow.service.NotifyService;
|
||||
import cn.fateverse.workflow.entity.HistoricalOperation;
|
||||
import cn.fateverse.workflow.entity.ProcessData;
|
||||
import cn.fateverse.workflow.entity.bpmn.CommentInfo;
|
||||
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
|
||||
import cn.fateverse.workflow.entity.bpmn.SettingsInfo;
|
||||
import cn.fateverse.workflow.entity.bpmn.UserInfo;
|
||||
import cn.fateverse.workflow.enums.OperationStateEnums;
|
||||
import cn.fateverse.workflow.mapper.HistoricalOperationMapper;
|
||||
import cn.fateverse.workflow.mapper.ProcessDataMapper;
|
||||
import cn.fateverse.workflow.mapper.UserInstanceMapper;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.ExecutionListener;
|
||||
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 用户定时边界事件监听器
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023/1/4
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class BoundaryEvenListener implements ExecutionListener {
|
||||
|
||||
private final TaskService taskService;
|
||||
|
||||
private final RuntimeService runtimeService;
|
||||
|
||||
private final NotifyService notifyService;
|
||||
|
||||
private final ProcessDataMapper processDataMapper;
|
||||
|
||||
private final UserInstanceMapper userInstanceMapper;
|
||||
|
||||
private final HistoricalOperationMapper operationMapper;
|
||||
|
||||
public BoundaryEvenListener(TaskService taskService,
|
||||
RuntimeService runtimeService,
|
||||
NotifyService notifyService,
|
||||
ProcessDataMapper processDataMapper,
|
||||
UserInstanceMapper userInstanceMapper, HistoricalOperationMapper operationMapper) {
|
||||
this.taskService = taskService;
|
||||
this.runtimeService = runtimeService;
|
||||
this.notifyService = notifyService;
|
||||
this.processDataMapper = processDataMapper;
|
||||
this.userInstanceMapper = userInstanceMapper;
|
||||
this.operationMapper = operationMapper;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void notify(DelegateExecution execution) {
|
||||
ExecutionEntityImpl entity = (ExecutionEntityImpl) execution;
|
||||
String processDefinitionKey = entity.getProcessDefinitionKey();
|
||||
ProcessData processData = processDataMapper.selectByProcessDefinitionKey(processDefinitionKey);
|
||||
Optional<ProcessNode> nodeOptional = JSON.parseArray(processData.getProcessStr(), ProcessNode.class).stream().filter(processNode -> {
|
||||
String activityId = execution.getCurrentActivityId().replace("boundary_" , "node_");
|
||||
return processNode.getId().equals(activityId);
|
||||
}).findAny();
|
||||
if (nodeOptional.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
ProcessNode processNode = nodeOptional.get();
|
||||
JSONObject timeLimit = processNode.getProps().getTimeLimit();
|
||||
String type = timeLimit.getJSONObject("handler").getString("type");
|
||||
List<Task> list = taskService.createTaskQuery()
|
||||
.processInstanceId(execution.getProcessInstanceId())
|
||||
.includeProcessVariables()
|
||||
.taskDefinitionKey(processNode.getId())
|
||||
.list();
|
||||
switch (type) {
|
||||
case "PASS":
|
||||
passExecution(list);
|
||||
break;
|
||||
case "REFUSE":
|
||||
refuseExecution(list);
|
||||
break;
|
||||
case "NOTIFY":
|
||||
String deploymentName = processData.getDeploymentName();
|
||||
notifyService.notifyTaskList(deploymentName, list, JSON.parseObject(processData.getSettings(), SettingsInfo.class));
|
||||
break;
|
||||
default:
|
||||
throw new CustomException("事件还未实现");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动通过
|
||||
*
|
||||
* @param list 任务对象列表
|
||||
*/
|
||||
private void passExecution(List<Task> list) {
|
||||
list.forEach(task -> {
|
||||
changeOperation(task, OperationStateEnums.AUTO_PASS, "任务超时,自动通过");
|
||||
taskService.complete(task.getId());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动驳回
|
||||
*
|
||||
* @param list 任务对象列表
|
||||
*/
|
||||
private void refuseExecution(List<Task> list) {
|
||||
Set<String> processInstanceIdSet = new HashSet<>(list.size());
|
||||
list.forEach(task -> {
|
||||
changeOperation(task, OperationStateEnums.AUTO_REFUSE, "任务超时,自动驳回");
|
||||
changVariables(task, ProcessConstant.BUSINESS_STATUS_REJECT);
|
||||
runtimeService.deleteProcessInstance(task.getProcessInstanceId(), "任务超时,自动驳回");
|
||||
processInstanceIdSet.add(task.getProcessInstanceId());
|
||||
});
|
||||
userInstanceMapper.updateStateByProcessInstanceIdList(new ArrayList<>(processInstanceIdSet), ProcessConstant.BUSINESS_STATUS_REJECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变运行装填
|
||||
*
|
||||
* @param task 任务节点
|
||||
* @param state 需要修改的状态
|
||||
*/
|
||||
private void changVariables(Task task, String state) {
|
||||
//获取流程数据,并设置为拒绝状态
|
||||
Map<String, Object> variables = task.getProcessVariables();
|
||||
variables.put(ProcessConstant.PROCESS_STATUS, state);
|
||||
//将表单数据设置到流程中
|
||||
runtimeService.setVariables(task.getProcessInstanceId(), variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变操作历史
|
||||
*
|
||||
* @param task task任务节点信息
|
||||
* @param state 需要修改的状态
|
||||
* @param msg 需要添加的提示信息
|
||||
*/
|
||||
private void changeOperation(Task task, OperationStateEnums state, String msg) {
|
||||
//获取到操作历史
|
||||
HistoricalOperation operation = operationMapper.selectByProcessInstanceIdAndNodeId(task.getProcessInstanceId(), task.getTaskDefinitionKey());
|
||||
//设置状态
|
||||
operation.setState(state);
|
||||
CommentInfo commentInfo = new CommentInfo();
|
||||
commentInfo.setContext(msg);
|
||||
operation.setMessage(JSON.toJSONString(commentInfo));
|
||||
List<UserInfo> userInfos = JSON.parseArray(operation.getUserInfo(), UserInfo.class);
|
||||
if (null != userInfos) {
|
||||
for (UserInfo userInfo : userInfos) {
|
||||
userInfo.setState(state);
|
||||
}
|
||||
operation.setUserInfo(JSON.toJSONString(userInfos));
|
||||
}
|
||||
operationMapper.update(operation);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
package cn.fateverse.workflow.process.listener;
|
||||
|
||||
import cn.fateverse.admin.dubbo.DubboDeptService;
|
||||
import cn.fateverse.admin.dubbo.DubboUserService;
|
||||
import cn.fateverse.admin.entity.Dept;
|
||||
import cn.fateverse.admin.entity.User;
|
||||
import cn.fateverse.admin.vo.DeptVo;
|
||||
import cn.fateverse.admin.vo.UserVo;
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.workflow.constant.ProcessConstant;
|
||||
import cn.fateverse.workflow.entity.HistoricalOperation;
|
||||
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
|
||||
import cn.fateverse.workflow.entity.bpmn.Properties;
|
||||
import cn.fateverse.workflow.entity.bpmn.RoleInfo;
|
||||
import cn.fateverse.workflow.entity.bpmn.UserInfo;
|
||||
import cn.fateverse.workflow.enums.AssigneeTypeEnums;
|
||||
import cn.fateverse.workflow.enums.ModeEnums;
|
||||
import cn.fateverse.workflow.enums.OperationStateEnums;
|
||||
import cn.fateverse.workflow.mapper.HistoricalOperationMapper;
|
||||
import cn.fateverse.workflow.process.ProcessServiceUtils;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.flowable.engine.RepositoryService;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.ExecutionListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 会签监听器
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/29
|
||||
*/
|
||||
@Component
|
||||
public class CounterSignListener implements ExecutionListener {
|
||||
|
||||
private final RepositoryService repositoryService;
|
||||
|
||||
private final HistoricalOperationMapper operationMapper;
|
||||
|
||||
@DubboReference
|
||||
private DubboUserService userService;
|
||||
|
||||
@DubboReference
|
||||
private DubboDeptService deptService;
|
||||
|
||||
public CounterSignListener(RepositoryService repositoryService,
|
||||
HistoricalOperationMapper operationMapper) {
|
||||
this.repositoryService = repositoryService;
|
||||
this.operationMapper = operationMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(DelegateExecution execution) {
|
||||
String value = ProcessServiceUtils.getBpmnValue(repositoryService, execution.getProcessDefinitionId(), ProcessConstant.BPMN_FORM_PROCESS);
|
||||
//获取到节点的数据信息
|
||||
String currentActivityId = execution.getCurrentActivityId();
|
||||
//获取到流程节点的数据信息
|
||||
Optional<ProcessNode> nodeOptional = JSON.parseArray(value, ProcessNode.class).stream().filter(node -> currentActivityId.equals(node.getId())).findAny();
|
||||
if (nodeOptional.isEmpty()) {
|
||||
throw new CustomException("数据异常!");
|
||||
}
|
||||
ProcessNode processNode = nodeOptional.get();
|
||||
//获取到流程参数数据
|
||||
Map<String, Object> variables = execution.getVariables();
|
||||
//创建审批人用户列表
|
||||
List<String> assignedUserList = new ArrayList<>();
|
||||
//创建当前需要使用审批人列表的assigneeList,配合MultiInstanceLoopCharacteristics实现会签功能
|
||||
String variableKey = currentActivityId + ProcessConstant.ASSIGNEE_LIST;
|
||||
//获取到当前节点的配置信息
|
||||
Properties props = processNode.getProps();
|
||||
//获取到当前节点的审批类型枚举
|
||||
AssigneeTypeEnums assignedType = props.getAssignedType();
|
||||
switch (assignedType) {
|
||||
case ASSIGN_USER:
|
||||
//用户自己选择审批人
|
||||
List<UserInfo> assignedUser = props.getAssignedUser();
|
||||
for (UserInfo userInfo : assignedUser) {
|
||||
assignedUserList.add(userInfo.getId());
|
||||
}
|
||||
break;
|
||||
//用户自己审批
|
||||
case SELF:
|
||||
String userId = variables.get(ProcessConstant.START_USER_ID).toString();
|
||||
assignedUserList.add(userId);
|
||||
break;
|
||||
//用户自选
|
||||
case SELF_SELECT:
|
||||
setSelfSelect(processNode.getId(), assignedUserList, variables);
|
||||
break;
|
||||
//角色
|
||||
case ROLE:
|
||||
setRoleUser(assignedUserList, props);
|
||||
break;
|
||||
//连续多级主管
|
||||
case LEADER_TOP:
|
||||
setLeaderUserTop(assignedUserList, props, variables);
|
||||
break;
|
||||
//主管
|
||||
case LEADER:
|
||||
setLeaderUser(assignedUserList, props, variables);
|
||||
break;
|
||||
//表单内联系人
|
||||
case FORM_USER:
|
||||
break;
|
||||
default:
|
||||
throw new CustomException("数据异常!");
|
||||
}
|
||||
//当没有查询到审批人的时候,进行的业务处理
|
||||
Map<String, Object> nobody = props.getNobody();
|
||||
boolean hasAssignedUser = true;
|
||||
OperationStateEnums assigneeEnums = null;
|
||||
if (assignedUserList.isEmpty()) {
|
||||
switch (nobody.get("handler").toString()) {
|
||||
case "TO_PASS":
|
||||
//todo 直接通过
|
||||
assigneeEnums = OperationStateEnums.AUTO_PASS;
|
||||
assignedUserList.add(OperationStateEnums.AUTO_PASS.getState());
|
||||
execution.setVariable(OperationStateEnums.AUTO_PASS.getState(), Boolean.TRUE);
|
||||
hasAssignedUser = false;
|
||||
break;
|
||||
case "TO_REFUSE":
|
||||
//todo 直接拒绝
|
||||
assigneeEnums = OperationStateEnums.AUTO_REFUSE;
|
||||
assignedUserList.add(OperationStateEnums.AUTO_REFUSE.getState());
|
||||
execution.setVariable(OperationStateEnums.AUTO_REFUSE.getState(), Boolean.TRUE);
|
||||
hasAssignedUser = false;
|
||||
break;
|
||||
case "TO_ADMIN":
|
||||
//交给admin审批
|
||||
//todo 需要去查询所有为admin角色的用户
|
||||
List<UserVo> userList = userService.searchUserListByRoleIds(Collections.singletonList(1L));
|
||||
userList = checkResult(userList, "获取到用户数据为空!");
|
||||
userList.forEach(user -> assignedUserList.add(user.getUserId().toString()));
|
||||
break;
|
||||
case "TO_USER":
|
||||
//指定到用户
|
||||
Object userObj = nobody.get("assignedUser");
|
||||
if (!Objects.isNull(userObj)) {
|
||||
List<UserInfo> userInfoList = JSON.parseArray(JSON.toJSONString(userObj), UserInfo.class);
|
||||
if (userInfoList.size() > 0) {
|
||||
for (UserInfo userInfo : userInfoList) {
|
||||
assignedUserList.add(userInfo.getId());
|
||||
}
|
||||
} else {
|
||||
execution.setVariable(OperationStateEnums.AUTO_PASS.getState(), Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new CustomException("数据异常!");
|
||||
}
|
||||
}
|
||||
List<Long> userIds = assignedUserList.stream().map(Long::valueOf).collect(Collectors.toList());
|
||||
List<UserVo> userList = userService.searchUserListByUserIds(userIds);
|
||||
List<UserInfo> userInfoList = userList.stream().map(UserInfo::toUserInfo).collect(Collectors.toList());
|
||||
String approvalMode = ModeEnums.NORMAL.getTypeName();
|
||||
if (userInfoList.size() > 1) {
|
||||
approvalMode = props.getMode();
|
||||
}
|
||||
HistoricalOperation operation = HistoricalOperation.toHistoricalOperation(execution, userInfoList, processNode.getName(), approvalMode);
|
||||
if (!hasAssignedUser) {
|
||||
operation.setUserInfo(null);
|
||||
operation.setState(assigneeEnums);
|
||||
}
|
||||
int count = operationMapper.selectCountByProcessInstanceIdAndNodeId(execution.getProcessInstanceId(), processNode.getId());
|
||||
if (count <= 0) {
|
||||
operationMapper.insert(operation);
|
||||
}
|
||||
//将参数设置回流程数据中
|
||||
execution.setVariable(variableKey, assignedUserList, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户自选模式
|
||||
*
|
||||
* @param assignedUserList 审批人
|
||||
* @param nodeId 节点id
|
||||
* @param variables 节点数据
|
||||
*/
|
||||
private void setSelfSelect(String nodeId, List<String> assignedUserList, Map<String, Object> variables) {
|
||||
JSONObject optionalUser = (JSONObject) variables.get(ProcessConstant.OPTIONAL_USER);
|
||||
List<UserInfo> userInfos = optionalUser.getList(nodeId,UserInfo.class);
|
||||
userInfos.forEach(user -> assignedUserList.add(user.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 连续多级主管
|
||||
*
|
||||
* @param assignedUserList 审批人
|
||||
* @param props 配置数据
|
||||
* @param variables 节点数据
|
||||
*/
|
||||
private void setLeaderUserTop(List<String> assignedUserList, Properties props, Map<String, Object> variables) {
|
||||
Map<String, Object> leaderTop = props.getLeaderTop();
|
||||
String endCondition = leaderTop.get("endCondition").toString();
|
||||
Dept dept = getDept(variables);
|
||||
List<Long> parentIds = Arrays.stream(dept.getAncestors().split(",")).map(Long::valueOf).collect(Collectors.toList());
|
||||
List<DeptVo> deptVoList;
|
||||
switch (endCondition) {
|
||||
case "TOP"://直接到最上层
|
||||
deptVoList = deptService.searchDeptByDeptId(parentIds.subList(1, parentIds.size()));
|
||||
break;
|
||||
case "LEAVE"://到指定级别
|
||||
int level = Integer.parseInt(leaderTop.get("level").toString());
|
||||
List<Long> leaderDeptIds;
|
||||
int length = parentIds.size() - 1;
|
||||
if (level > length) {
|
||||
leaderDeptIds = parentIds.subList(1, parentIds.size());
|
||||
} else {
|
||||
leaderDeptIds = parentIds.subList(parentIds.size() - level, parentIds.size());
|
||||
}
|
||||
deptVoList = deptService.searchDeptByDeptId(leaderDeptIds);
|
||||
break;
|
||||
default:
|
||||
throw new CustomException("当前选项还未实现!");
|
||||
}
|
||||
deptVoList = checkResult(deptVoList, "获取到部门数据为空!");
|
||||
deptVoList.forEach(deptVo -> assignedUserList.add(deptVo.getLeaderId().toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据领导选择用户
|
||||
*
|
||||
* @param assignedUserList 审批人
|
||||
* @param props 配置数据
|
||||
* @param variables 节点数据
|
||||
*/
|
||||
private void setLeaderUser(List<String> assignedUserList, Properties props, Map<String, Object> variables) {
|
||||
int level = Integer.parseInt(props.getLeader().get("level").toString());
|
||||
Dept dept = getDept(variables);
|
||||
if (1 == level) {
|
||||
String leaderId = dept.getLeaderId().toString();
|
||||
assignedUserList.add(leaderId);
|
||||
} else {
|
||||
List<Long> parentIds = Arrays.stream(dept.getAncestors().split(",")).map(Long::valueOf).collect(Collectors.toList());
|
||||
Long leaderDeptId;
|
||||
int length = parentIds.size() - 1;
|
||||
if (level > length) {
|
||||
leaderDeptId = parentIds.get(1);
|
||||
} else {
|
||||
leaderDeptId = parentIds.get(parentIds.size() - level);
|
||||
}
|
||||
List<DeptVo> deptVoList = deptService.searchDeptByDeptId(Collections.singletonList(leaderDeptId));
|
||||
deptVoList = checkResult(deptVoList, "获取到部门数据为空!");
|
||||
DeptVo deptVo = deptVoList.get(0);
|
||||
assignedUserList.add(deptVo.getLeaderId().toString());
|
||||
}
|
||||
}
|
||||
|
||||
private <T> List<T> checkResult(List<T> result, String msg) {
|
||||
if (null == result || result.isEmpty()) {
|
||||
throw new CustomException(msg);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色选择审批人
|
||||
*
|
||||
* @param assignedUserList 审批人
|
||||
* @param props 配置数据
|
||||
*/
|
||||
private void setRoleUser(List<String> assignedUserList, Properties props) {
|
||||
List<Long> roleIds = props.getRoleList().stream().map(RoleInfo::getRoleId).collect(Collectors.toList());
|
||||
//rpc远程通过角色id获取到当前角色下的所有用户数据
|
||||
if (roleIds.isEmpty()){
|
||||
throw new CustomException("系统错误,请稍后再试");
|
||||
}
|
||||
List<UserVo> userList = userService.searchUserListByRoleIds(roleIds);
|
||||
if (null == userList || userList.isEmpty()) {
|
||||
throw new CustomException("系统错误,请稍后再试");
|
||||
}
|
||||
//todo 将userInfo加入到流程数据中
|
||||
for (UserVo user : userList) {
|
||||
assignedUserList.add(user.getUserId().toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param variables 节点数据
|
||||
* @return 返回部门
|
||||
*/
|
||||
public Dept getDept(Map<String, Object> variables) {
|
||||
User user = (User) variables.get(ProcessConstant.START_USER_INFO);
|
||||
return user.getDept();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package cn.fateverse.workflow.process.listener;
|
||||
|
||||
import cn.fateverse.admin.entity.User;
|
||||
import cn.fateverse.workflow.constant.ProcessConstant;
|
||||
import cn.fateverse.workflow.entity.HistoricalOperation;
|
||||
import cn.fateverse.workflow.entity.bpmn.UserInfo;
|
||||
import cn.fateverse.workflow.enums.OperationStateEnums;
|
||||
import cn.fateverse.workflow.mapper.HistoricalOperationMapper;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.ExecutionListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 事件创建监听器
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-02-24
|
||||
*/
|
||||
@Component
|
||||
public class CreateStartEventListener implements ExecutionListener {
|
||||
|
||||
private final HistoricalOperationMapper operationMapper;
|
||||
|
||||
public CreateStartEventListener(HistoricalOperationMapper operationMapper) {
|
||||
this.operationMapper = operationMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(DelegateExecution execution) {
|
||||
User user = (User) execution.getVariables().get(ProcessConstant.START_USER_INFO);
|
||||
UserInfo userInfo = UserInfo.toUserInfo(user);
|
||||
userInfo.setState(OperationStateEnums.AGREE);
|
||||
HistoricalOperation operation = HistoricalOperation.toHistoricalOperation(execution, userInfo);
|
||||
operation.setState(OperationStateEnums.CREATE);
|
||||
operationMapper.insert(operation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package cn.fateverse.workflow.process.listener;
|
||||
|
||||
import cn.fateverse.workflow.constant.ProcessConstant;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.fateverse.common.core.constant.DateConstants;
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.workflow.entity.HistoricalOperation;
|
||||
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
|
||||
import cn.fateverse.workflow.entity.bpmn.Properties;
|
||||
import cn.fateverse.workflow.enums.OperationStateEnums;
|
||||
import cn.fateverse.workflow.mapper.HistoricalOperationMapper;
|
||||
import cn.fateverse.workflow.process.ProcessServiceUtils;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.bpmn.model.IntermediateCatchEvent;
|
||||
import org.flowable.bpmn.model.TimerEventDefinition;
|
||||
import org.flowable.engine.RepositoryService;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.ExecutionListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 延时等待时间监听器
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-21
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class IntermediateCatchEventListener implements ExecutionListener {
|
||||
|
||||
|
||||
private final RepositoryService repositoryService;
|
||||
|
||||
private final HistoricalOperationMapper operationMapper;
|
||||
|
||||
public IntermediateCatchEventListener(RepositoryService repositoryService, HistoricalOperationMapper operationMapper) {
|
||||
this.repositoryService = repositoryService;
|
||||
this.operationMapper = operationMapper;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void notify(DelegateExecution execution) {
|
||||
String value = ProcessServiceUtils.getBpmnValue(repositoryService, execution.getProcessDefinitionId(), ProcessConstant.BPMN_FORM_PROCESS);
|
||||
//获取到节点的数据信息
|
||||
String currentActivityId = execution.getCurrentActivityId();
|
||||
//获取到流程节点的数据信息
|
||||
Optional<ProcessNode> nodeOptional = JSON.parseArray(value, ProcessNode.class).stream().filter(node -> currentActivityId.equals(node.getId())).findAny();
|
||||
if (nodeOptional.isEmpty()) {
|
||||
throw new CustomException("数据异常!");
|
||||
}
|
||||
ProcessNode processNode = nodeOptional.get();
|
||||
switch (execution.getEventName()) {
|
||||
case ExecutionListener.EVENTNAME_START:
|
||||
FlowElement currentFlowElement = execution.getCurrentFlowElement();
|
||||
if (currentFlowElement instanceof IntermediateCatchEvent){
|
||||
IntermediateCatchEvent intermediateCatchEvent = (IntermediateCatchEvent) currentFlowElement;
|
||||
TimerEventDefinition timerEventDefinition = (TimerEventDefinition)intermediateCatchEvent.getEventDefinitions().get(0);
|
||||
Properties props = processNode.getProps();
|
||||
if (props.getType().equals("AUTO")) {
|
||||
Date date = new Date();
|
||||
String dateToStr = DateUtil.format(date,DateConstants.YYYY_MM_DD);
|
||||
dateToStr = dateToStr + " " + props.getDateTime();
|
||||
Date targetDate = DateUtil.parseDateTime(dateToStr);
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(targetDate);
|
||||
if (targetDate.getTime() < date.getTime()){
|
||||
log.info("超过当前时间,需要移交到明天");
|
||||
calendar.add(Calendar.DATE,1);
|
||||
}
|
||||
String timeDate = DateUtil.format(calendar.getTime(),"yyyy-MM-dd'T'HH:mm:ss");
|
||||
log.info("时间为:"+timeDate);
|
||||
timerEventDefinition.setTimeDate(timeDate);
|
||||
|
||||
}
|
||||
//设置好时间事件
|
||||
intermediateCatchEvent.setEventDefinitions(Collections.singletonList(timerEventDefinition));
|
||||
execution.setCurrentFlowElement(currentFlowElement);
|
||||
}
|
||||
HistoricalOperation operation = HistoricalOperation.toHistoricalOperation(execution, null, processNode.getName(), processNode.getType().getType());
|
||||
int count = operationMapper.selectCountByProcessInstanceIdAndNodeId(execution.getProcessInstanceId(), processNode.getId());
|
||||
if (count <= 0) {
|
||||
operationMapper.insert(operation);
|
||||
}
|
||||
break;
|
||||
case ExecutionListener.EVENTNAME_END:
|
||||
operationMapper.updateStatusByProcessInstanceIdAndNodeId(execution.getProcessInstanceId(),processNode.getId(), OperationStateEnums.AGREE);
|
||||
log.info("延时器结束!");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cn.fateverse.workflow.process.listener;
|
||||
|
||||
import cn.fateverse.workflow.constant.ProcessConstant;
|
||||
import cn.fateverse.workflow.mapper.UserInstanceMapper;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.ExecutionListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 流程结束监听器
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/29
|
||||
*/
|
||||
@Component
|
||||
public class ProcessListener implements ExecutionListener {
|
||||
//用户流程mapper
|
||||
private final UserInstanceMapper userInstanceMapper;
|
||||
|
||||
public ProcessListener(UserInstanceMapper userInstanceMapper) {
|
||||
this.userInstanceMapper = userInstanceMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(DelegateExecution execution) {
|
||||
//将流程状态设置为已结束
|
||||
execution.setVariable(ProcessConstant.PROCESS_STATUS, ProcessConstant.BUSINESS_STATUS_ENDED);
|
||||
userInstanceMapper.updateState(execution.getProcessInstanceId(), ProcessConstant.BUSINESS_STATUS_ENDED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package cn.fateverse.workflow.process.listener;
|
||||
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.workflow.constant.ProcessConstant;
|
||||
import cn.fateverse.workflow.entity.HistoricalOperation;
|
||||
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
|
||||
import cn.fateverse.workflow.entity.bpmn.Properties;
|
||||
import cn.fateverse.workflow.entity.bpmn.UserInfo;
|
||||
import cn.fateverse.workflow.enums.OperationStateEnums;
|
||||
import cn.fateverse.workflow.enums.OperationEnums;
|
||||
import cn.fateverse.workflow.mapper.HistoricalOperationMapper;
|
||||
import cn.fateverse.workflow.mapper.ProcessMapper;
|
||||
import cn.fateverse.workflow.process.ProcessServiceUtils;
|
||||
import cn.fateverse.workflow.process.TriggerService;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.RepositoryService;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.JavaDelegate;
|
||||
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* ServiceTask服务监听类
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023/1/2
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ServiceListener implements JavaDelegate {
|
||||
|
||||
private final ProcessMapper processMapper;
|
||||
|
||||
private final TriggerService triggerService;
|
||||
|
||||
private final RepositoryService repositoryService;
|
||||
|
||||
private final HistoricalOperationMapper operationMapper;
|
||||
|
||||
|
||||
public ServiceListener(ProcessMapper processMapper,
|
||||
RepositoryService repositoryService,
|
||||
HistoricalOperationMapper operationMapper,
|
||||
TriggerService triggerService) {
|
||||
this.processMapper = processMapper;
|
||||
this.repositoryService = repositoryService;
|
||||
this.operationMapper = operationMapper;
|
||||
this.triggerService = triggerService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(DelegateExecution execution) {
|
||||
//获取到流程数据,并找到当前节点的数据信息
|
||||
String value = ProcessServiceUtils.getBpmnValue(repositoryService, execution.getProcessDefinitionId(), ProcessConstant.BPMN_FORM_PROCESS);
|
||||
Optional<ProcessNode> processNodeOptional = JSON.parseArray(value, ProcessNode.class).stream().filter(node -> node.getId().equals(execution.getCurrentActivityId())).findFirst();
|
||||
if (processNodeOptional.isEmpty()) {
|
||||
throw new CustomException("数据异常!");
|
||||
}
|
||||
ProcessNode processNode = processNodeOptional.get();
|
||||
switch (processNode.getType()) {
|
||||
case CC:
|
||||
sendCC(execution, processNode);
|
||||
break;
|
||||
case TRIGGER:
|
||||
triggerEvent(execution, processNode);
|
||||
break;
|
||||
default:
|
||||
throw new CustomException("数据异常!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理触发事件
|
||||
*
|
||||
* @param execution 执行器
|
||||
* @param processNode 流程节点信息
|
||||
*/
|
||||
private void triggerEvent(DelegateExecution execution, ProcessNode processNode) {
|
||||
HistoricalOperation operation = HistoricalOperation.toHistoricalOperation(execution, new ArrayList<>());
|
||||
Properties props = processNode.getProps();
|
||||
operation.setOperation(props.getType().equals("WEBHOOK") ? OperationEnums.TRIGGER_WEBHOOK : OperationEnums.TRIGGER_EMAIL);
|
||||
operation.setNodeId(processNode.getId());
|
||||
operation.setOperationName(processNode.getName());
|
||||
operation.setState(OperationStateEnums.RUNNING);
|
||||
switch (props.getType()) {
|
||||
case "WEBHOOK":
|
||||
//当使用脚本的时候则表示需要发起同步请求,此时的请求会影响整个流程
|
||||
if (props.getHttp().getHandlerByScript()) {
|
||||
triggerService.sendHttpRequest(processNode, operation,execution);
|
||||
saveOperation(operation);
|
||||
} else {
|
||||
triggerService.asyncSendHttpRequest(processNode, operation,execution);
|
||||
saveOperation(operation);
|
||||
}
|
||||
break;
|
||||
case "EMAIL":
|
||||
triggerService.asyncSendEmail(processNode, operation);
|
||||
saveOperation(operation);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void saveOperation(HistoricalOperation operation) {
|
||||
try {
|
||||
operationMapper.insert(operation);
|
||||
} catch (Exception e) {
|
||||
log.info("异步事务已经处理完成!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起抄送
|
||||
*
|
||||
* @param execution 执行器信息
|
||||
* @param processNode 流程节点信息
|
||||
*/
|
||||
private void sendCC(DelegateExecution execution, ProcessNode processNode) {
|
||||
Properties props = processNode.getProps();
|
||||
List<UserInfo> assignedUser = props.getAssignedUser();
|
||||
if (props.getShouldAdd()) {
|
||||
Map<String, Object> variables = execution.getVariables();
|
||||
JSONObject optionalUser = (JSONObject) variables.get(ProcessConstant.OPTIONAL_USER);
|
||||
if (null != optionalUser) {
|
||||
List<UserInfo> userInfos = optionalUser.getList(execution.getCurrentActivityId(), UserInfo.class);
|
||||
assignedUser.addAll(userInfos);
|
||||
}
|
||||
}
|
||||
//对审批用户进行去重操作
|
||||
assignedUser = assignedUser.stream().collect(Collectors.collectingAndThen(
|
||||
Collectors.toCollection(() -> new TreeSet<>(
|
||||
Comparator.comparing(UserInfo::getId))), ArrayList::new
|
||||
));
|
||||
List<TaskEntityImpl> taskList = new ArrayList<>(assignedUser.size());
|
||||
assignedUser.forEach(userInfo -> {
|
||||
String taskId = UUID.randomUUID().toString();
|
||||
TaskEntityImpl task = new TaskEntityImpl();
|
||||
task.setId(taskId);
|
||||
task.setProcessDefinitionId(execution.getProcessDefinitionId());
|
||||
task.setTaskDefinitionKey("cc-" + taskId);
|
||||
task.setProcessInstanceId(execution.getProcessInstanceId());
|
||||
task.setExecutionId(execution.getId());
|
||||
task.setName(processNode.getName());
|
||||
task.setAssignee(userInfo.getId());
|
||||
task.setPriority(50);
|
||||
taskList.add(task);
|
||||
userInfo.setState(OperationStateEnums.AGREE);
|
||||
});
|
||||
processMapper.batchInsertUserTaskInHistorical(taskList);
|
||||
HistoricalOperation operation = HistoricalOperation.toHistoricalOperation(execution, assignedUser);
|
||||
operation.setOperation(OperationEnums.CC);
|
||||
operation.setTaskId(UUID.randomUUID().toString());
|
||||
operation.setOperationName(processNode.getName());
|
||||
operationMapper.insert(operation);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package cn.fateverse.workflow.process.listener;
|
||||
|
||||
import cn.fateverse.workflow.constant.ProcessConstant;
|
||||
import cn.fateverse.workflow.enums.OperationStateEnums;
|
||||
import cn.fateverse.workflow.mapper.UserInstanceMapper;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.engine.delegate.TaskListener;
|
||||
import org.flowable.task.service.delegate.DelegateTask;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* task任务创建监听器
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/29
|
||||
*/
|
||||
@Component
|
||||
public class TaskCreatedListener implements TaskListener {
|
||||
|
||||
private final TaskService taskService;
|
||||
|
||||
private final RuntimeService runtimeService;
|
||||
|
||||
private final UserInstanceMapper userInstanceMapper;
|
||||
|
||||
public TaskCreatedListener(TaskService taskService,
|
||||
RuntimeService runtimeService, UserInstanceMapper userInstanceMapper) {
|
||||
this.taskService = taskService;
|
||||
this.runtimeService = runtimeService;
|
||||
this.userInstanceMapper = userInstanceMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(DelegateTask delegateTask) {
|
||||
//判断当前的task是否是自动通过
|
||||
if (OperationStateEnums.AUTO_REFUSE.getState().equals(delegateTask.getAssignee())) {
|
||||
Object autoRefuse = delegateTask.getVariable(OperationStateEnums.AUTO_REFUSE.getState());
|
||||
if (null == autoRefuse) {
|
||||
taskService.complete(delegateTask.getId());
|
||||
} else {
|
||||
changVariables(delegateTask, ProcessConstant.BUSINESS_STATUS_REJECT);
|
||||
runtimeService.deleteProcessInstance(delegateTask.getProcessInstanceId(), "审批人为空,自动驳回");
|
||||
userInstanceMapper.updateState(delegateTask.getProcessInstanceId(), ProcessConstant.BUSINESS_STATUS_REJECT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变运行装填
|
||||
*
|
||||
* @param delegateTask 执行器任务信息
|
||||
* @param state 需要修改的状态
|
||||
*/
|
||||
private void changVariables(DelegateTask delegateTask, String state) {
|
||||
//获取流程数据,并设置为拒绝状态
|
||||
Map<String, Object> variables = delegateTask.getTransientVariables();
|
||||
variables.put(ProcessConstant.PROCESS_STATUS, state);
|
||||
//将表单数据设置到流程中
|
||||
runtimeService.setVariables(delegateTask.getProcessInstanceId(), variables);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package cn.fateverse.workflow.service;
|
||||
|
||||
import cn.fateverse.admin.dubbo.DubboUserService;
|
||||
import cn.fateverse.admin.entity.User;
|
||||
import cn.fateverse.admin.vo.UserVo;
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.common.email.entity.SendEmailInfo;
|
||||
import cn.fateverse.common.email.service.EmailService;
|
||||
import cn.fateverse.workflow.constant.ProcessConstant;
|
||||
import cn.fateverse.workflow.entity.bpmn.SettingsInfo;
|
||||
import cn.fateverse.notice.dto.NoticeDto;
|
||||
import cn.fateverse.notice.dubbo.DubboNoticeService;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 消息通知服务
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-22
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class NotifyService {
|
||||
|
||||
@DubboReference
|
||||
private DubboUserService userService;
|
||||
|
||||
@DubboReference
|
||||
private DubboNoticeService noticeService;
|
||||
|
||||
private final EmailService emailService;
|
||||
|
||||
public NotifyService(EmailService emailService) {
|
||||
this.emailService = emailService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知任务
|
||||
*
|
||||
* @param deploymentName 部署的名称
|
||||
* @param list 任务通知list
|
||||
* @param settingsInfo 设置信息
|
||||
*/
|
||||
public void notifyTaskList(String deploymentName, List<Task> list, SettingsInfo settingsInfo) {
|
||||
JSONObject notify = settingsInfo.getNotify();
|
||||
List<Long> userIds = list.stream().map(task -> Long.valueOf(task.getAssignee())).collect(Collectors.toList());
|
||||
Map<Long, UserVo> userMap = new HashMap<>();
|
||||
if (!userIds.isEmpty()) {
|
||||
userMap = userService.searchUserListByUserIds(userIds).stream().collect(Collectors.toMap(UserVo::getUserId, Function.identity()));
|
||||
}
|
||||
Map<Long, UserVo> finalUserMap = userMap;
|
||||
list.forEach(task -> {
|
||||
JSONArray types = notify.getJSONArray("types");
|
||||
for (Object type : types) {
|
||||
switch (type.toString()) {
|
||||
case "APP":
|
||||
// 应用内通知
|
||||
NoticeDto notice = new NoticeDto();
|
||||
notice.setNoticeTitle(notify.getString("title"));
|
||||
//noticeService.syncSend();
|
||||
break;
|
||||
case "EMAIL":
|
||||
// 邮件通知
|
||||
sendEmail(deploymentName, notify.getString("title"), task, finalUserMap.get(Long.valueOf(task.getAssignee())));
|
||||
break;
|
||||
case "SMS":
|
||||
// 短信通知
|
||||
break;
|
||||
case "WX":
|
||||
// 微信通知
|
||||
break;
|
||||
case "DING":
|
||||
// 钉钉通知
|
||||
break;
|
||||
default:
|
||||
throw new CustomException("通知方式还未实现!");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮件
|
||||
*
|
||||
* @param deploymentName 部署的名称
|
||||
* @param title 通知的title
|
||||
* @param task 任务
|
||||
* @param user 用户信息
|
||||
*/
|
||||
private void sendEmail(String deploymentName, String title, Task task, UserVo user) {
|
||||
if (!user.checkEmail()) {
|
||||
return;
|
||||
}
|
||||
User startUser = (User) task.getProcessVariables().get(ProcessConstant.START_USER_INFO);
|
||||
SendEmailInfo emailInfo = new SendEmailInfo();
|
||||
if ("消息通知标题".equals(title)) {
|
||||
emailInfo.setSubject("您有新的审批任务");
|
||||
}
|
||||
emailInfo.setContent(startUser.getNickName() + " 提交 [" + deploymentName + "] 需要您审批,请立即处理");
|
||||
emailInfo.setSenders(Collections.singletonList(user.getEmail()));
|
||||
emailService.asyncSendMail(emailInfo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package cn.fateverse.workflow.service;
|
||||
|
||||
import cn.fateverse.common.core.result.page.TableDataInfo;
|
||||
import cn.fateverse.workflow.entity.dto.ProcessDto;
|
||||
import cn.fateverse.workflow.entity.query.ProcessQuery;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessDefinitionHistoryVo;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessDefinitionInfoVo;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessDefinitionVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/23
|
||||
*/
|
||||
public interface ProcessDefinitionService {
|
||||
|
||||
/**
|
||||
* 获取到流程部署的list
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @return 结果
|
||||
*/
|
||||
TableDataInfo<ProcessDefinitionVo> searchList(ProcessQuery query);
|
||||
|
||||
/**
|
||||
* 查询流程部署的历史版本
|
||||
*
|
||||
* @param processDefinitionKey 流程部署关键词
|
||||
* @return 结果
|
||||
*/
|
||||
List<ProcessDefinitionHistoryVo> searchHistoryList(String processDefinitionKey);
|
||||
|
||||
/**
|
||||
* 通过流程部署id获取到流程详细数据(表单,流程)
|
||||
*
|
||||
* @param deploymentId 流程部署id
|
||||
* @return 结果
|
||||
*/
|
||||
ProcessDefinitionInfoVo searchByDeploymentId(String deploymentId);
|
||||
|
||||
/**
|
||||
* 通过processDefinitionKey 获取到当前流程的数据信息
|
||||
*
|
||||
* @param processDefinitionKey 流程部署关键词
|
||||
* @return 结果
|
||||
*/
|
||||
ProcessDefinitionInfoVo searchInitiateInfoByKey(String processDefinitionKey);
|
||||
|
||||
/**
|
||||
* 挂起流程部署
|
||||
*
|
||||
* @param processDefinitionId 流程部署id
|
||||
*/
|
||||
void suspend(String processDefinitionId);
|
||||
|
||||
/**
|
||||
* 激活流程部署
|
||||
*
|
||||
* @param processDefinitionId 流程部署id
|
||||
*/
|
||||
void activate(String processDefinitionId);
|
||||
|
||||
/**
|
||||
* 删除流程
|
||||
*
|
||||
* @param deploymentId 流程部署id
|
||||
*/
|
||||
void delete(String deploymentId);
|
||||
|
||||
/**
|
||||
* 添加流程
|
||||
*
|
||||
* @param processDto 流程数据信息
|
||||
*/
|
||||
void add(ProcessDto processDto);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package cn.fateverse.workflow.service;
|
||||
|
||||
import cn.fateverse.workflow.entity.bpmn.Attachment;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023/1/10
|
||||
*/
|
||||
public interface ProcessFileService {
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param file 文件对象
|
||||
* @throws Exception
|
||||
*/
|
||||
Attachment uploadFile(MultipartFile file) throws Exception;
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param fileId 文件id
|
||||
*/
|
||||
void deleteFile(Long fileId);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package cn.fateverse.workflow.service;
|
||||
|
||||
import cn.fateverse.common.core.result.page.TableDataInfo;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessInstanceDetailVo;
|
||||
import cn.fateverse.workflow.entity.dto.ProcessInstanceDto;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessInstanceVo;
|
||||
|
||||
|
||||
/**
|
||||
* 流程实例服务
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/24
|
||||
*/
|
||||
public interface ProcessInstanceService {
|
||||
/**
|
||||
* 发起流程实例
|
||||
*
|
||||
* @param processInstanceDto 流程dto
|
||||
*/
|
||||
void start(ProcessInstanceDto processInstanceDto);
|
||||
|
||||
/**
|
||||
* 重新启动流
|
||||
*
|
||||
* @param processInstanceDto 流程dto
|
||||
*/
|
||||
void restart(ProcessInstanceDto processInstanceDto);
|
||||
|
||||
/**
|
||||
* 查询当前启动的实例
|
||||
*
|
||||
* @param state 流程状态
|
||||
* @return 列表数据
|
||||
*/
|
||||
TableDataInfo<ProcessInstanceVo> searchList(String state);
|
||||
|
||||
/**
|
||||
* 查询自己的流程
|
||||
*
|
||||
* @param state 流程状态
|
||||
* @param deploymentName 流程名称
|
||||
* @return 列表数据
|
||||
*/
|
||||
TableDataInfo<ProcessInstanceVo> searchSelfList(String state, String deploymentName);
|
||||
|
||||
/**
|
||||
* 查询关于我的列表
|
||||
*
|
||||
* @param state 流程状态
|
||||
* @param deploymentName 流程名称
|
||||
* @return 列表数据
|
||||
*/
|
||||
TableDataInfo<ProcessInstanceVo> searchAboutMeList(String state, String deploymentName);
|
||||
|
||||
/**
|
||||
* 获取到自己的流程详细信息
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @return 流程详情
|
||||
*/
|
||||
ProcessInstanceDetailVo searchSelfInfo(String processInstanceId);
|
||||
|
||||
/**
|
||||
* 重新启动时获取到流程数据信息
|
||||
*
|
||||
* @param instanceId 用户实例id
|
||||
* @return 流程详情
|
||||
*/
|
||||
ProcessInstanceDetailVo searchReInfo(String instanceId);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package cn.fateverse.workflow.service;
|
||||
|
||||
import cn.fateverse.common.core.entity.Option;
|
||||
import cn.fateverse.workflow.entity.dto.ProcessListenerDto;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessListenerVo;
|
||||
import cn.fateverse.workflow.entity.query.ProcessListenerQuery;
|
||||
import cn.fateverse.common.core.result.page.TableDataInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统内置监听器 Service
|
||||
*
|
||||
* @author clay
|
||||
* @date 2023-03-27
|
||||
*/
|
||||
public interface ProcessListenerService {
|
||||
|
||||
/**
|
||||
* 查询系统内置监听器
|
||||
*
|
||||
* @param id 系统内置监听器Id
|
||||
* @return 系统内置监听器
|
||||
*/
|
||||
ProcessListenerVo searchById(String id);
|
||||
|
||||
/**
|
||||
* 查询系统内置监听器列表
|
||||
*
|
||||
* @param query 系统内置监听器
|
||||
* @return 系统内置监听器集合
|
||||
*/
|
||||
TableDataInfo<ProcessListenerVo> searchList(ProcessListenerQuery query);
|
||||
|
||||
|
||||
/**
|
||||
* 查询系统内置监听器option
|
||||
*
|
||||
* @param type 监听器类型
|
||||
* @return 选项列表
|
||||
*/
|
||||
List<Option> searchOptionList(String type);
|
||||
|
||||
/**
|
||||
* 导出系统内置监听器列表
|
||||
*
|
||||
* @param query query 系统内置监听器
|
||||
* @return 系统内置监听器集合
|
||||
*/
|
||||
List<ProcessListenerVo> exportList(ProcessListenerQuery query);
|
||||
|
||||
/**
|
||||
* 新增系统内置监听器
|
||||
*
|
||||
* @param processListener 系统内置监听器
|
||||
* @return 结果
|
||||
*/
|
||||
int save(ProcessListenerDto processListener);
|
||||
|
||||
/**
|
||||
* 修改系统内置监听器
|
||||
*
|
||||
* @param processListener 系统内置监听器
|
||||
* @return 结果
|
||||
*/
|
||||
int edit(ProcessListenerDto processListener);
|
||||
|
||||
/**
|
||||
* 删除系统内置监听器
|
||||
*
|
||||
* @param id 需要删除的系统内置监听器Id
|
||||
* @return 结果
|
||||
*/
|
||||
int removeById(String id);
|
||||
|
||||
/**
|
||||
* 批量删除系统内置监听器
|
||||
*
|
||||
* @param idList 需要删除的系统内置监听器Id 集合
|
||||
* @return 结果
|
||||
*/
|
||||
int removeBatch(List<String> idList);
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
package cn.fateverse.workflow.service;
|
||||
|
||||
import cn.fateverse.admin.dubbo.DubboUserService;
|
||||
import cn.fateverse.admin.entity.Dept;
|
||||
import cn.fateverse.admin.entity.User;
|
||||
import cn.fateverse.admin.vo.UserVo;
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.workflow.constant.ProcessConstant;
|
||||
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
|
||||
import cn.fateverse.workflow.entity.bpmn.Properties;
|
||||
import cn.fateverse.workflow.entity.bpmn.RoleInfo;
|
||||
import cn.fateverse.workflow.entity.bpmn.UserInfo;
|
||||
import cn.fateverse.workflow.entity.vo.HistoricalOperationVo;
|
||||
import cn.fateverse.workflow.enums.OperationStateEnums;
|
||||
import cn.fateverse.workflow.enums.AssigneeTypeEnums;
|
||||
import cn.fateverse.workflow.enums.ProcessNodeEnum;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 流程节点操作服务
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-02-26
|
||||
*/
|
||||
@Component
|
||||
public class ProcessNodeService {
|
||||
|
||||
private final ThreadPoolExecutor executor;
|
||||
|
||||
@DubboReference
|
||||
private DubboUserService userService;
|
||||
/**
|
||||
* 用户id缓存
|
||||
*/
|
||||
private final Map<String, List<Long>> cacheUserIdMap;
|
||||
//角色用户缓存
|
||||
private final Map<Long, List<UserVo>> cacheRoleCacheUserMap;
|
||||
//部门用户缓存
|
||||
private final Map<Long, UserInfo> cacheDeptCacheUserMap;
|
||||
|
||||
//用户缓存信息
|
||||
private final Map<Long, UserInfo> cacheUserMap;
|
||||
|
||||
|
||||
public ProcessNodeService() {
|
||||
executor = new ThreadPoolExecutor(2,
|
||||
4,
|
||||
60,
|
||||
TimeUnit.SECONDS, new LinkedBlockingDeque<>(128),
|
||||
new ThreadFactoryBuilder().setNameFormat("process_%d").build(),
|
||||
new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
cacheUserIdMap = new ConcurrentHashMap<>();
|
||||
cacheRoleCacheUserMap = new ConcurrentHashMap<>();
|
||||
cacheDeptCacheUserMap = new ConcurrentHashMap<>();
|
||||
cacheUserMap = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取到流程节点数据信息
|
||||
*
|
||||
* @param processStr 流程数据字符串
|
||||
* @return 流程节点数据
|
||||
*/
|
||||
public List<ProcessNode> getProcessNodes(String processStr, User user, List<HistoricalOperationVo> operationList,
|
||||
JSONObject optionalUser, Boolean isStart) {
|
||||
Date startTime = new Date();
|
||||
Map<String, List<UserInfo>> operationInfoMap = operationList.stream().filter(operation -> null != operation.getUserInfo()).collect(Collectors.toMap(HistoricalOperationVo::getNodeId, HistoricalOperationVo::getUserInfo));
|
||||
ProcessNodeWrapper wrapper = new ProcessNodeWrapper(operationInfoMap, optionalUser, user, isStart);
|
||||
//todo 获取到用户信息.将root节点的信息进更改,后期为对所有的节点数据进行处理
|
||||
List<ProcessNode> processNodeList = JSON.parseArray(processStr, ProcessNode.class);
|
||||
CountDownLatch countDownLatch = new CountDownLatch(processNodeList.size());
|
||||
for (ProcessNode processNode : processNodeList) {
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
doInitProcessNode(processNode, wrapper);
|
||||
} finally {
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
try {
|
||||
countDownLatch.await();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
clearCache();
|
||||
System.out.println("用时 :" + (new Date().getTime() - startTime.getTime()));
|
||||
return wrapper.result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
|
||||
/**
|
||||
* 清除缓存,当空间大于16时进行清除
|
||||
*/
|
||||
private void clearCache() {
|
||||
if (cacheRoleCacheUserMap.size() > 16) {
|
||||
cacheRoleCacheUserMap.clear();
|
||||
}
|
||||
if (cacheUserIdMap.size() > 16) {
|
||||
cacheUserIdMap.clear();
|
||||
}
|
||||
if (cacheUserMap.size() > 16) {
|
||||
cacheUserMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色查询
|
||||
*
|
||||
* @param roleIds 角色id列表
|
||||
* @return 用户信息
|
||||
*/
|
||||
private List<UserInfo> searchUserListByRoleIds(List<Long> roleIds) {
|
||||
List<UserVo> result = new ArrayList<>();
|
||||
List<Long> roleIdsNew = new ArrayList<>();
|
||||
roleIds.forEach(roleId -> {
|
||||
if (cacheRoleCacheUserMap.containsKey(roleId)) {
|
||||
result.addAll(cacheRoleCacheUserMap.get(roleId));
|
||||
} else {
|
||||
roleIdsNew.add(roleId);
|
||||
}
|
||||
});
|
||||
if (!roleIdsNew.isEmpty()) {
|
||||
List<UserVo> userList = userService.searchUserListByRoleIds(roleIdsNew);
|
||||
Map<Long, List<UserVo>> userRole = userList.stream().collect(Collectors.groupingBy(UserVo::getRoleId));
|
||||
cacheRoleCacheUserMap.putAll(userRole);
|
||||
result.addAll(userList);
|
||||
}
|
||||
return result.stream().map(userVo ->
|
||||
UserInfo.toUserInfo(userVo, OperationStateEnums.UNACTIVATED)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门查询数据信息
|
||||
*
|
||||
* @param deptIds 部门id列表
|
||||
* @return 用户信息
|
||||
*/
|
||||
private List<UserInfo> searchUserByDeptIds(List<Long> deptIds) {
|
||||
List<UserInfo> result = new ArrayList<>();
|
||||
List<Long> deptIdsNew = new ArrayList<>();
|
||||
deptIds.forEach(deptId -> {
|
||||
if (cacheDeptCacheUserMap.containsKey(deptId)) {
|
||||
result.add(cacheDeptCacheUserMap.get(deptId));
|
||||
} else {
|
||||
deptIdsNew.add(deptId);
|
||||
}
|
||||
});
|
||||
if (!deptIdsNew.isEmpty()) {
|
||||
List<UserVo> userList = userService.searchUserByDeptIds(deptIdsNew);
|
||||
userList.forEach(userVo -> {
|
||||
UserInfo value = UserInfo.toUserInfo(userVo, OperationStateEnums.UNACTIVATED);
|
||||
result.add(value);
|
||||
cacheDeptCacheUserMap.put(userVo.getLeaderDeptId(), value);
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpc查询用户数据
|
||||
*
|
||||
* @param userIds
|
||||
* @return 用户信息列表
|
||||
*/
|
||||
private List<UserInfo> searchUserListByUserIds(List<Long> userIds) {
|
||||
List<UserInfo> result = new ArrayList<>();
|
||||
List<Long> userIdsNew = new ArrayList<>();
|
||||
userIds.forEach(userId -> {
|
||||
if (cacheUserMap.containsKey(userId)) {
|
||||
result.add(cacheUserMap.get(userId));
|
||||
} else {
|
||||
userIdsNew.add(userId);
|
||||
}
|
||||
});
|
||||
if (!userIdsNew.isEmpty()) {
|
||||
List<UserVo> userList = userService.searchUserListByUserIds(userIdsNew);
|
||||
userList.forEach(userVo -> {
|
||||
UserInfo value = UserInfo.toUserInfo(userVo, OperationStateEnums.UNACTIVATED);
|
||||
result.add(value);
|
||||
cacheUserMap.put(userVo.getUserId(), value);
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置返回集合
|
||||
*
|
||||
* @param processNode
|
||||
*/
|
||||
private void setResult(ProcessNode processNode, ProcessNodeWrapper wrapper) {
|
||||
synchronized (wrapper.result) {
|
||||
wrapper.result.add(processNode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对流程节点进筛选处理
|
||||
*
|
||||
* @param processNode
|
||||
*/
|
||||
private void doInitProcessNode(ProcessNode processNode, ProcessNodeWrapper wrapper) {
|
||||
if (processNode.getId().equals(ProcessConstant.ROOT_NODE)) {
|
||||
processNodeSelf(processNode, wrapper);
|
||||
return;
|
||||
}
|
||||
List<UserInfo> operationInfoList = wrapper.userOperationInfoMap.get(processNode.getId());
|
||||
//为空则表示未被激活,激活后的都会在有一条操作记录
|
||||
Properties props = processNode.getProps();
|
||||
if (null == operationInfoList || operationInfoList.isEmpty()) {
|
||||
if (ProcessNodeEnum.APPROVAL.equals(processNode.getType())) {
|
||||
//未获取到用户id则可能是分支节点,获取是未激活的节点(未激活的节点则会有 抄送节点(已选择好人 或者)和审核节点)
|
||||
AssigneeTypeEnums assignedType = props.getAssignedType();
|
||||
if (!wrapper.isStart) {
|
||||
props.setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
|
||||
}
|
||||
switch (assignedType) {
|
||||
case SELF:
|
||||
processNodeSelf(processNode, wrapper);
|
||||
break;
|
||||
case SELF_SELECT:
|
||||
processNodeSelfSelect(processNode, wrapper);
|
||||
break;
|
||||
case ROLE:
|
||||
processNodeRole(processNode, wrapper);
|
||||
break;
|
||||
case LEADER:
|
||||
processNodeLeader(processNode, wrapper);
|
||||
break;
|
||||
case LEADER_TOP:
|
||||
processNodeLeaderTop(processNode, wrapper);
|
||||
break;
|
||||
case ASSIGN_USER:
|
||||
default:
|
||||
setResult(processNode, wrapper);
|
||||
break;
|
||||
}
|
||||
} else if (ProcessNodeEnum.CC.equals(processNode.getType())) {
|
||||
processNodeCC(processNode, wrapper);
|
||||
} else {
|
||||
setResult(processNode, wrapper);
|
||||
}
|
||||
} else {
|
||||
props.setAssignedUser(operationInfoList);
|
||||
if (!wrapper.isStart) {
|
||||
props.setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
|
||||
}
|
||||
setResult(processNode, wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
private void processNodeCC(ProcessNode processNode, ProcessNodeWrapper wrapper) {
|
||||
List<UserInfo> userList = wrapper.optionalUser.getList(processNode.getId(), UserInfo.class);
|
||||
if (null != userList && !userList.isEmpty()) {
|
||||
Properties props = processNode.getProps();
|
||||
props.setAssignedUser(userList);
|
||||
if (!wrapper.isStart) {
|
||||
props.setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
|
||||
} else {
|
||||
processNode.getProps().setAssignedType(null);
|
||||
}
|
||||
}
|
||||
setResult(processNode, wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户自选
|
||||
*
|
||||
* @param processNode
|
||||
*/
|
||||
private void processNodeSelfSelect(ProcessNode processNode, ProcessNodeWrapper wrapper) {
|
||||
List<UserInfo> userList = wrapper.optionalUser.getList(processNode.getId(), UserInfo.class);
|
||||
if (null != userList && !userList.isEmpty()) {
|
||||
Properties props = processNode.getProps();
|
||||
props.setAssignedUser(userList);
|
||||
if (!wrapper.isStart) {
|
||||
processNode.getProps().setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
|
||||
} else {
|
||||
processNode.getProps().setAssignedType(AssigneeTypeEnums.SELF_SELECT);
|
||||
}
|
||||
}
|
||||
setResult(processNode, wrapper);
|
||||
}
|
||||
|
||||
private void processNodeLeaderTop(ProcessNode processNode, ProcessNodeWrapper wrapper) {
|
||||
Properties props = processNode.getProps();
|
||||
List<UserInfo> assignedUser;
|
||||
Map<String, Object> leaderTop = props.getLeaderTop();
|
||||
String endCondition = leaderTop.get("endCondition").toString();
|
||||
Dept dept = wrapper.user.getDept();
|
||||
List<Long> parentIds = Arrays.stream(dept.getAncestors().split(",")).map(Long::valueOf).collect(Collectors.toList());
|
||||
switch (endCondition) {
|
||||
case "TOP"://直接到最上层
|
||||
assignedUser = searchUserByDeptIds(parentIds.subList(1, parentIds.size()));
|
||||
break;
|
||||
case "LEAVE"://到指定级别
|
||||
int level = Integer.parseInt(leaderTop.get("level").toString());
|
||||
List<Long> leaderDeptIds = null;
|
||||
int length = parentIds.size();
|
||||
if (level > length) {
|
||||
leaderDeptIds = parentIds.subList(1, parentIds.size());
|
||||
} else {
|
||||
leaderDeptIds = parentIds.subList(parentIds.size() - level, parentIds.size());
|
||||
}
|
||||
assignedUser = searchUserByDeptIds(leaderDeptIds);
|
||||
break;
|
||||
default:
|
||||
throw new CustomException("当前选项还未实现!");
|
||||
}
|
||||
processNode.getProps().setAssignedUser(assignedUser);
|
||||
processNode.getProps().setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
|
||||
setResult(processNode, wrapper);
|
||||
}
|
||||
|
||||
private void processNodeLeader(ProcessNode processNode, ProcessNodeWrapper wrapper) {
|
||||
List<UserInfo> assignedUser;
|
||||
Properties props = processNode.getProps();
|
||||
int level = Integer.parseInt(props.getLeader().get("level").toString());
|
||||
Dept dept = wrapper.user.getDept();
|
||||
if (1 == level) {
|
||||
assignedUser = searchUserListByUserIds(Collections.singletonList(dept.getDeptId()));
|
||||
} else {
|
||||
List<Long> parentIds = Arrays.stream(dept.getAncestors().split(",")).map(Long::valueOf).collect(Collectors.toList());
|
||||
Long leaderDeptId = null;
|
||||
int length = parentIds.size() - 1;
|
||||
if (level > length) {
|
||||
leaderDeptId = parentIds.get(1);
|
||||
} else {
|
||||
leaderDeptId = parentIds.get(parentIds.size() - level);
|
||||
}
|
||||
assignedUser = searchUserByDeptIds(Collections.singletonList(leaderDeptId));
|
||||
}
|
||||
props.setAssignedUser(assignedUser);
|
||||
props.setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
|
||||
setResult(processNode, wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 流程节点的用户为自己
|
||||
*
|
||||
* @param processNode 流程节点
|
||||
*/
|
||||
private void processNodeSelf(ProcessNode processNode, ProcessNodeWrapper wrapper) {
|
||||
Properties props = processNode.getProps();
|
||||
List<UserInfo> assignedUser;
|
||||
assignedUser = new ArrayList<>();
|
||||
UserInfo userInfo = UserInfo.toUserInfo(wrapper.user, OperationStateEnums.UNACTIVATED);
|
||||
assignedUser.add(userInfo);
|
||||
props.setAssignedUser(assignedUser);
|
||||
props.setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
|
||||
setResult(processNode, wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 流程节点选择的为角色
|
||||
*
|
||||
* @param processNode 流程节点
|
||||
*/
|
||||
private void processNodeRole(ProcessNode processNode, ProcessNodeWrapper wrapper) {
|
||||
Properties props = processNode.getProps();
|
||||
List<UserInfo> assignedUser;
|
||||
List<Long> roleIds = props.getRoleList().stream().map(RoleInfo::getRoleId).collect(Collectors.toList());
|
||||
//rpc远程通过角色id获取到当前角色下的所有用户数据
|
||||
assignedUser = searchUserListByRoleIds(roleIds);
|
||||
props.setAssignedUser(assignedUser);
|
||||
props.setAssignedType(AssigneeTypeEnums.ASSIGN_USER);
|
||||
setResult(processNode, wrapper);
|
||||
}
|
||||
|
||||
|
||||
private static class ProcessNodeWrapper {
|
||||
|
||||
public ProcessNodeWrapper(Map<String, List<UserInfo>> userOperationInfoMap, JSONObject optionalUser, User user, Boolean isStart) {
|
||||
this.userOperationInfoMap = userOperationInfoMap;
|
||||
this.optionalUser = optionalUser;
|
||||
this.user = user;
|
||||
this.isStart = isStart;
|
||||
}
|
||||
|
||||
//操作历史缓存
|
||||
private final Map<String, List<UserInfo>> userOperationInfoMap;
|
||||
//自选用户信息
|
||||
private final JSONObject optionalUser;
|
||||
private final List<ProcessNode> result = new ArrayList<>();
|
||||
private final User user;
|
||||
private final Boolean isStart;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
package cn.fateverse.workflow.service;
|
||||
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.workflow.entity.HistoricalOperation;
|
||||
import cn.fateverse.workflow.entity.bo.AnalysisProcessBo;
|
||||
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
|
||||
import cn.fateverse.workflow.enums.OperationStateEnums;
|
||||
import cn.fateverse.workflow.enums.ProcessNodeEnum;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.bpmn.model.SequenceFlow;
|
||||
import org.flowable.engine.runtime.ActivityInstance;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 流程引擎服务
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2023-03-20
|
||||
*/
|
||||
public class ProcessService {
|
||||
|
||||
//流程节点id的映射集合
|
||||
private Map<String, ProcessNode> processNodeIdMap;
|
||||
//流程节点父级id映射集合
|
||||
private Map<String, ProcessNode> processNodeParentIdMap;
|
||||
//已经通过的节点信息
|
||||
private Set<String> agreeNodeSet;
|
||||
//运行中的节点信息
|
||||
private Set<String> runningNodeSet;
|
||||
//需要改变的节点信息
|
||||
private Set<String> changeNodeSet;
|
||||
//离开网关的节点信息
|
||||
private Set<String> leaveMergeNodeSet;
|
||||
//进入网关的节点信息
|
||||
private Set<String> enterMergeNodeSet;
|
||||
//需要回滚的网关信息
|
||||
private Set<String> rollbackNodeSet;
|
||||
//节点经过的路径
|
||||
private Set<String> nodePathSet;
|
||||
|
||||
private Boolean findTargetNode = false;
|
||||
|
||||
public void clear() {
|
||||
this.processNodeIdMap = null;
|
||||
this.processNodeParentIdMap = null;
|
||||
this.agreeNodeSet = null;
|
||||
this.runningNodeSet = null;
|
||||
this.leaveMergeNodeSet = null;
|
||||
this.enterMergeNodeSet = null;
|
||||
this.changeNodeSet = null;
|
||||
this.rollbackNodeSet = null;
|
||||
this.findTargetNode = false;
|
||||
this.nodePathSet = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取到可以到达的节点
|
||||
*
|
||||
* @param operationList 操作历史节点
|
||||
* @param processStr 流程json数据
|
||||
* @param nodeId 节点id
|
||||
* @return 可以到达的节点list
|
||||
*/
|
||||
public List<ProcessNode> analysisEndOption(List<HistoricalOperation> operationList, String processStr, String nodeId) {
|
||||
AnalysisProcessBo processData = initAnalysisProcessData(operationList, processStr);
|
||||
//需要返回的两三天
|
||||
List<ProcessNode> result = new ArrayList<>();
|
||||
//获取到当前nodeId对应的node数据
|
||||
processNodeIdMap = processData.getProcessNodeIdMap();
|
||||
processNodeParentIdMap = processData.getProcessNodeParentIdMap();
|
||||
nodePathSet = new HashSet<>();
|
||||
agreeNodeSet = processData.getHiOperationNodeSet();
|
||||
ProcessNode processNode = processNodeIdMap.get(nodeId);
|
||||
//开始分析
|
||||
doAnalysisEndOption(result, processNode);
|
||||
clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析节点流程
|
||||
*
|
||||
* @param result 返回list
|
||||
* @param oldNode 上一个节点信息
|
||||
*/
|
||||
private void doAnalysisEndOption(List<ProcessNode> result, ProcessNode oldNode) {
|
||||
//获取到新的节点
|
||||
ProcessNode processNode = processNodeIdMap.get(oldNode.getParentId());
|
||||
//如果为空则直接返回,或者已经走过了直接返回
|
||||
if (null == processNode || nodePathSet.contains(processNode.getId())) {
|
||||
return;
|
||||
}
|
||||
nodePathSet.add(processNode.getId());
|
||||
//判断当前节点是否条件分支和并行分支
|
||||
if ((processNode.getType() == ProcessNodeEnum.CONDITIONS && oldNode.getType() == ProcessNodeEnum.EMPTY)
|
||||
|| (processNode.getType() == ProcessNodeEnum.CONCURRENTS && oldNode.getType() == ProcessNodeEnum.MERGE)) {
|
||||
//是则需要处理当前分支下面的节点信息
|
||||
doGetBranchApproval(processNode, result, false);
|
||||
}
|
||||
//如果是用户,则直接放入到返回节点列表
|
||||
if (processNode.getType() == ProcessNodeEnum.APPROVAL) {
|
||||
result.add(processNode);
|
||||
}
|
||||
//递归调用当前函数
|
||||
doAnalysisEndOption(result, processNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到分支节点下的数据信息
|
||||
*
|
||||
* @param processNode 当前的节点
|
||||
* @param result 返回结果
|
||||
*/
|
||||
private void doGetBranchApproval(ProcessNode processNode, List<ProcessNode> result, Boolean check) {
|
||||
if ((null == processNode || nodePathSet.contains(processNode.getId())) && check) {
|
||||
return;
|
||||
}
|
||||
nodePathSet.add(processNode.getId());
|
||||
//如果是条件分支或者并行分支,则需要每一个分支进行递归处理
|
||||
if (processNode.getType() == ProcessNodeEnum.CONCURRENTS || processNode.getType() == ProcessNodeEnum.CONDITIONS) {
|
||||
for (ProcessNode branch : processNode.getBranchs()) {
|
||||
ProcessNode branchSun = processNodeParentIdMap.get(branch.getId());
|
||||
doGetBranchApproval(branchSun, result, true);
|
||||
}
|
||||
if (nodePathSet.contains(processNode.getId())) {
|
||||
return;
|
||||
} else {
|
||||
processNode = processNodeParentIdMap.get(processNode.getId());
|
||||
}
|
||||
//return;
|
||||
//如果是审批节点,则需要加入到返回结果集中
|
||||
} else if (processNode.getType() == ProcessNodeEnum.APPROVAL) {
|
||||
if (agreeNodeSet.contains(processNode.getId())) {
|
||||
result.add(processNode);
|
||||
}
|
||||
}
|
||||
//继续下一个节点
|
||||
ProcessNode sun = processNodeParentIdMap.get(processNode.getId());
|
||||
//递归调用
|
||||
doGetBranchApproval(sun, result, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析节点中运行中的节点信息,用作回退时的回滚记录
|
||||
*
|
||||
* @param operationList 操作历史记录
|
||||
* @param processStr 流程数据Str
|
||||
* @param nodeId 当前节点id
|
||||
* @param targetNodeId 退回目标节点id
|
||||
* @return 分析完成后的结果信息
|
||||
*/
|
||||
public AnalysisProcessBo analysisProcessNodeAndRunningNode(List<HistoricalOperation> operationList, String processStr, String nodeId,
|
||||
String targetNodeId, List<ActivityInstance> activityInstanceList, Collection<FlowElement> flowElements) {
|
||||
if (null == activityInstanceList || activityInstanceList.isEmpty()) {
|
||||
throw new CustomException("数据异常!");
|
||||
}
|
||||
AnalysisProcessBo processData = initAnalysisProcessData(operationList, processStr);
|
||||
//获取到所有的
|
||||
agreeNodeSet = processData.getHiOperationNodeSet();
|
||||
//处理所有的流程实例
|
||||
Map<String, List<ActivityInstance>> activityInstanceMap = activityInstanceList.stream().collect(Collectors.groupingBy(ActivityInstance::getActivityId));
|
||||
//获取到所有的连线
|
||||
List<SequenceFlow> sequenceFlows = flowElements.stream().filter(element -> element instanceof SequenceFlow).map(element -> (SequenceFlow) element).collect(Collectors.toList());
|
||||
//将连线通过源接地那id进行映射
|
||||
Map<String, List<SequenceFlow>> sequenceFlowSourceRefMap = sequenceFlows.stream().collect(Collectors.groupingBy(SequenceFlow::getSourceRef));
|
||||
//初始化所有运行状态的节点
|
||||
runningNodeSet = operationList.stream()
|
||||
.filter(operation -> operation.getState().equals(OperationStateEnums.RUNNING) && operation.getMark().equals(0))
|
||||
.map(HistoricalOperation::getNodeId).collect(Collectors.toSet());
|
||||
//id与流程节点的映射关系
|
||||
processNodeIdMap = processData.getProcessNodeIdMap();
|
||||
//parentId和流程节点的映射关系
|
||||
processNodeParentIdMap = processData.getProcessNodeParentIdMap();
|
||||
//需要改变的节点
|
||||
changeNodeSet = new HashSet<>();
|
||||
//需要回滚的节点
|
||||
rollbackNodeSet = new HashSet<>();
|
||||
//进入网关的节点信息 (网关关闭节点)
|
||||
leaveMergeNodeSet = new HashSet<>();
|
||||
//退出网关的节点信息 (网关关闭节点)
|
||||
enterMergeNodeSet = new HashSet<>();
|
||||
//将当然节点加入到回滚节点列表中
|
||||
rollbackNodeSet.add(nodeId);
|
||||
//将目标节点加入到需要改变的节点列表中
|
||||
changeNodeSet.add(targetNodeId);
|
||||
//分析需要删除的操作历史和当前正在运行的实例
|
||||
Set<String> activityIdSet = new HashSet<>();
|
||||
analysisActivityId(activityInstanceMap, sequenceFlowSourceRefMap, activityIdSet, targetNodeId);
|
||||
//筛选出㤇改变和回滚的接地那列表
|
||||
doAnalysisProcessNodeAndRunning(sequenceFlowSourceRefMap, targetNodeId);
|
||||
//获取到当前节点
|
||||
ProcessNode processNode = processNodeIdMap.get(nodeId);
|
||||
//分析正在运行的接地那流程,筛选出需要进出网关的情况
|
||||
doAnalysisProcessNodeAndGateway(processNode, targetNodeId);
|
||||
//将数据打包返回
|
||||
AnalysisProcessBo result = AnalysisProcessBo.builder()
|
||||
.changeNodeSet(changeNodeSet)
|
||||
.rollbackNodeSet(rollbackNodeSet)
|
||||
.leaveMergeNodeSet(leaveMergeNodeSet)
|
||||
.enterMergeNodeSet(enterMergeNodeSet)
|
||||
.activityIdSet(activityIdSet)
|
||||
.build();
|
||||
clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到从退回节点到需要退回节点的所有节点状态,统计需要修改的节点,和需要回滚的节点信息
|
||||
* (正向寻找)
|
||||
*
|
||||
* @param sequenceFlowSourceRefMap 连线映射
|
||||
* @param targetNodeId 目标节点
|
||||
*/
|
||||
private void doAnalysisProcessNodeAndRunning(Map<String, List<SequenceFlow>> sequenceFlowSourceRefMap, String targetNodeId) {
|
||||
//获取到连线
|
||||
List<SequenceFlow> sequenceFlows = sequenceFlowSourceRefMap.get(targetNodeId);
|
||||
if (null == sequenceFlows) {
|
||||
return;
|
||||
}
|
||||
//遍历连线
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
//获取到当前线条目标节点
|
||||
ProcessNode processNode = processNodeIdMap.get(sequenceFlow.getTargetRef());
|
||||
if (null == processNode) {
|
||||
continue;
|
||||
}
|
||||
//判断目标接地那是否是审批节点,抄送节点,延时等待节点,触发器
|
||||
if (processNode.getType() == ProcessNodeEnum.APPROVAL || processNode.getType() == ProcessNodeEnum.CC
|
||||
|| processNode.getType() == ProcessNodeEnum.DELAY || processNode.getType() == ProcessNodeEnum.TRIGGER) {
|
||||
//判断当前节点是否在运行集合或者同意集合中
|
||||
if (!(runningNodeSet.contains(processNode.getId()) || agreeNodeSet.contains(processNode.getId()))) {
|
||||
continue;
|
||||
}
|
||||
//加入到可以到达的集合中
|
||||
processNodeAdded(processNode);
|
||||
}
|
||||
//进行递归
|
||||
doAnalysisProcessNodeAndRunning(sequenceFlowSourceRefMap, processNode.getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void analysisActivityId(Map<String, List<ActivityInstance>> activityInstanceMap,
|
||||
Map<String, List<SequenceFlow>> sequenceFlowSourceRefMap,
|
||||
Set<String> activityIdSet, String activityId) {
|
||||
//获取连线
|
||||
List<SequenceFlow> sequenceFlows = sequenceFlowSourceRefMap.get(activityId);
|
||||
if (null == sequenceFlows) {
|
||||
return;
|
||||
}
|
||||
//遍历连线
|
||||
for (SequenceFlow sequenceFlow : sequenceFlows) {
|
||||
//获取到当前连线的目标节点
|
||||
List<ActivityInstance> activityInstanceList = activityInstanceMap.get(sequenceFlow.getTargetRef());
|
||||
if (null == activityInstanceList || activityInstanceList.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
//不为空则需要加入到集合中
|
||||
for (ActivityInstance activityInstance : activityInstanceList) {
|
||||
activityIdSet.add(activityInstance.getId());
|
||||
}
|
||||
//获取到连线的实例
|
||||
List<ActivityInstance> sequenceInstanceList = activityInstanceMap.get(sequenceFlow.getId());
|
||||
//不为空加入到集合中
|
||||
if (null != sequenceInstanceList && !sequenceInstanceList.isEmpty()) {
|
||||
for (ActivityInstance sequenceInstance : sequenceInstanceList) {
|
||||
activityIdSet.add(sequenceInstance.getId());
|
||||
}
|
||||
}
|
||||
//进行递归
|
||||
analysisActivityId(activityInstanceMap, sequenceFlowSourceRefMap, activityIdSet, activityInstanceList.get(0).getActivityId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到并行网关出入情况 (逆向寻找,从当前节点逆推到目标节点)
|
||||
*
|
||||
* @param oldNode 历史节点
|
||||
* @param targetNodeId 目标节点id
|
||||
*/
|
||||
private void doAnalysisProcessNodeAndGateway(ProcessNode oldNode, String targetNodeId) {
|
||||
ProcessNode processNode = processNodeIdMap.get(oldNode.getParentId());
|
||||
//如果为空则直接返回
|
||||
if (null == processNode) {
|
||||
return;
|
||||
}
|
||||
//如果当前获取到的接地那等于目标节点,则说明找到,打上找到了的标记进行返回
|
||||
if (processNode.getId().equals(targetNodeId)) {
|
||||
findTargetNode = true;
|
||||
return;
|
||||
}
|
||||
//判断当前节点是否条件分支和并行分支, 进入网关 从 MERGE 走向 CONCURRENTS 则说明是从网关外部进入,则说明需要进入网关
|
||||
if (processNode.getType() == ProcessNodeEnum.CONCURRENTS && oldNode.getType() == ProcessNodeEnum.MERGE) {
|
||||
//处理分支下面的数据信息
|
||||
for (ProcessNode branch : processNode.getBranchs()) {
|
||||
if (!findTargetNode) {
|
||||
ProcessNode branchSun = processNodeParentIdMap.get(branch.getId());
|
||||
doAnalysisProcessAndGatewayBranch(branchSun, targetNodeId);
|
||||
}
|
||||
}
|
||||
//
|
||||
setEnterMergeNodeSet(processNode.getId());
|
||||
}
|
||||
if (findTargetNode) {
|
||||
return;
|
||||
}
|
||||
//判断当前节点是否条件分支和并行分支 从 CONCURRENT 到 CONCURRENTS 则说明是出网关
|
||||
if (processNode.getType() == ProcessNodeEnum.CONCURRENTS && oldNode.getType() == ProcessNodeEnum.CONCURRENT) {
|
||||
setLeaveMergeNodeSet(processNode.getId());
|
||||
}
|
||||
doAnalysisProcessNodeAndGateway(processNode, targetNodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到 并行网关分支的进入情况
|
||||
*
|
||||
* @param processNode 流程节点数据
|
||||
* @param targetNodeId 目标id
|
||||
*/
|
||||
private void doAnalysisProcessAndGatewayBranch(ProcessNode processNode, String targetNodeId) {
|
||||
//结果是否为空,为空则跳出递归
|
||||
if (null == processNode) {
|
||||
return;
|
||||
}
|
||||
if (processNode.getId().equals(targetNodeId)) {
|
||||
findTargetNode = true;
|
||||
return;
|
||||
}
|
||||
//判断当前节点是否存在于agree 或 这running中,如果不存在,则表示未激活
|
||||
if (!(agreeNodeSet.contains(processNode.getId()) || runningNodeSet.contains(processNode.getId()))
|
||||
&& (processNode.getType() == ProcessNodeEnum.CC || processNode.getType() == ProcessNodeEnum.TRIGGER
|
||||
|| processNode.getType() == ProcessNodeEnum.APPROVAL)) {
|
||||
return;
|
||||
}
|
||||
//如果是条件分支或者并行分支,则需要每一个分支进行递归处理 分支中有网关节点,则说明是进入网关
|
||||
if (processNode.getType() == ProcessNodeEnum.CONCURRENTS || processNode.getType() == ProcessNodeEnum.CONDITIONS) {
|
||||
for (ProcessNode branch : processNode.getBranchs()) {
|
||||
if (!findTargetNode) {
|
||||
ProcessNode branchSun = processNodeParentIdMap.get(branch.getId());
|
||||
doAnalysisProcessAndGatewayBranch(branchSun, targetNodeId);
|
||||
}
|
||||
}
|
||||
if (processNode.getType() == ProcessNodeEnum.CONCURRENTS) {
|
||||
setEnterMergeNodeSet(processNode.getId());
|
||||
}
|
||||
//如果是审批节点,则需要加入到返回结果集中
|
||||
}
|
||||
if (findTargetNode) {
|
||||
return;
|
||||
}
|
||||
//继续下一个节点
|
||||
ProcessNode sun = processNodeParentIdMap.get(processNode.getId());
|
||||
//递归调用
|
||||
doAnalysisProcessAndGatewayBranch(sun, targetNodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加出网关的分支节点
|
||||
*
|
||||
* @param nodeId 节点id
|
||||
*/
|
||||
private void setLeaveMergeNodeSet(String nodeId) {
|
||||
ProcessNode mergeNode = processNodeParentIdMap.get(nodeId);
|
||||
leaveMergeNodeSet.add(mergeNode.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加进去分支网关的节点
|
||||
*
|
||||
* @param nodeId 节点id
|
||||
*/
|
||||
private void setEnterMergeNodeSet(String nodeId) {
|
||||
ProcessNode mergeNode = processNodeParentIdMap.get(nodeId);
|
||||
enterMergeNodeSet.add(mergeNode.getId());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加流程节点信息
|
||||
*
|
||||
* @param processNode 流程节点
|
||||
*/
|
||||
private void processNodeAdded(ProcessNode processNode) {
|
||||
//在agree中
|
||||
if (agreeNodeSet.contains(processNode.getId())) {
|
||||
changeNodeSet.add(processNode.getId());
|
||||
//在running中
|
||||
} else if (runningNodeSet.contains(processNode.getId())) {
|
||||
rollbackNodeSet.add(processNode.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化流程bo的数据
|
||||
*
|
||||
* @param operationList 操作历史
|
||||
* @param processStr 流程节点数据Str
|
||||
* @return 流程分析业务数据
|
||||
*/
|
||||
private AnalysisProcessBo initAnalysisProcessData(List<HistoricalOperation> operationList, String processStr) {
|
||||
//获取到所有的
|
||||
Set<String> hiOperationNodeSet = operationList.stream()
|
||||
.filter(operation -> (operation.getState().equals(OperationStateEnums.AGREE) || operation.getState().equals(OperationStateEnums.AUTO_PASS)) && operation.getMark().equals(0))
|
||||
.map(HistoricalOperation::getNodeId).collect(Collectors.toSet());
|
||||
//分支id映射关系
|
||||
Map<String, ProcessNode> branchNodeIdMap = new HashMap<>();
|
||||
//解析流程JSON
|
||||
List<ProcessNode> processNodes = JSON.parseArray(processStr, ProcessNode.class);
|
||||
//parentId和流程节点的映射关系
|
||||
Map<String, ProcessNode> processNodeParentIdMap = new HashMap<>(processNodes.size());
|
||||
//id与流程节点的映射关系
|
||||
Map<String, ProcessNode> processNodeIdMap = processNodes.stream()
|
||||
.peek(processNode -> {
|
||||
if (processNode.getType() == ProcessNodeEnum.CONCURRENTS || processNode.getType() == ProcessNodeEnum.CONDITIONS) {
|
||||
Map<String, ProcessNode> nodeMap = processNode.getBranchs().stream().collect(Collectors.toMap(ProcessNode::getId, Function.identity()));
|
||||
branchNodeIdMap.putAll(nodeMap);
|
||||
}
|
||||
processNodeParentIdMap.put(processNode.getParentId(), processNode);
|
||||
})
|
||||
.collect(Collectors.toMap(ProcessNode::getId, Function.identity()));
|
||||
//将分支节点的映射关系放入到id映射关系内
|
||||
processNodeIdMap.putAll(branchNodeIdMap);
|
||||
return AnalysisProcessBo.builder()
|
||||
.hiOperationNodeSet(hiOperationNodeSet)
|
||||
.processNodeParentIdMap(processNodeParentIdMap)
|
||||
.processNodeIdMap(processNodeIdMap)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package cn.fateverse.workflow.service;
|
||||
|
||||
import cn.fateverse.common.core.result.page.TableDataInfo;
|
||||
import cn.fateverse.workflow.entity.bpmn.CommentInfo;
|
||||
import cn.fateverse.workflow.entity.dto.TaskDto;
|
||||
import cn.fateverse.workflow.entity.vo.task.TaskResultVo;
|
||||
import cn.fateverse.workflow.entity.vo.task.TaskVo;
|
||||
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/24
|
||||
*/
|
||||
public interface ProcessTaskService {
|
||||
|
||||
/**
|
||||
* 查询我的代办任务
|
||||
*
|
||||
* @return 任务列表
|
||||
*/
|
||||
TableDataInfo<TaskVo> searchTaskList();
|
||||
|
||||
/**
|
||||
* 获取任务详情
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @return 任务返回对象
|
||||
*/
|
||||
TaskResultVo searchTaskByTaskId(String taskId);
|
||||
|
||||
/**
|
||||
* 完成任务
|
||||
*
|
||||
* @param taskDto 任务数据
|
||||
*/
|
||||
void completeTask(TaskDto taskDto);
|
||||
|
||||
/**
|
||||
* 拒绝任务
|
||||
*
|
||||
* @param taskDto 任务数据
|
||||
*/
|
||||
void refuseTask(TaskDto taskDto);
|
||||
|
||||
/**
|
||||
* 委派
|
||||
*
|
||||
* @param taskDto 任务数据
|
||||
*/
|
||||
void delegateTas(TaskDto taskDto);
|
||||
|
||||
/**
|
||||
* 委派人完成
|
||||
*
|
||||
* @param taskDto 任务数据
|
||||
*/
|
||||
void resolveCompleteTask(TaskDto taskDto);
|
||||
|
||||
/**
|
||||
* 撤销
|
||||
*
|
||||
* @param taskDto 任务数据
|
||||
*/
|
||||
void revokeTask(TaskDto taskDto);
|
||||
|
||||
/**
|
||||
* 移交转办
|
||||
*
|
||||
* @param taskDto 任务数据
|
||||
*/
|
||||
void assigneeTask(TaskDto taskDto);
|
||||
|
||||
/**
|
||||
* 退回
|
||||
*
|
||||
* @param taskDto 任务数据
|
||||
*/
|
||||
void rollbackTask(TaskDto taskDto);
|
||||
|
||||
/**
|
||||
* 加签
|
||||
* @param taskDto 任务数据
|
||||
*/
|
||||
void addMultiTask(TaskDto taskDto);
|
||||
|
||||
/**
|
||||
* 查看签上的人
|
||||
*
|
||||
* @param taskId 任务id
|
||||
*/
|
||||
void searchMultiUsersInfo(String taskId);
|
||||
|
||||
/**
|
||||
* 减签
|
||||
* @param taskId 任务id
|
||||
*/
|
||||
void deleteMultiTask(String taskId);
|
||||
|
||||
/**
|
||||
* 添加评论信息
|
||||
*
|
||||
* @param commentInfo 任务数据
|
||||
*/
|
||||
void addCommentTask(CommentInfo commentInfo);
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
package cn.fateverse.workflow.service.impl;
|
||||
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.common.core.result.page.TableDataInfo;
|
||||
import cn.fateverse.common.mybatis.utils.PageUtils;
|
||||
import cn.fateverse.workflow.constant.ProcessConstant;
|
||||
import cn.fateverse.workflow.entity.FormPerm;
|
||||
import cn.fateverse.workflow.entity.ProcessBusiness;
|
||||
import cn.fateverse.workflow.entity.ProcessData;
|
||||
import cn.fateverse.workflow.entity.ProcessListener;
|
||||
import cn.fateverse.workflow.entity.bpmn.FormItem;
|
||||
import cn.fateverse.workflow.entity.bpmn.ListenerInfo;
|
||||
import cn.fateverse.workflow.entity.bpmn.ListenerItem;
|
||||
import cn.fateverse.workflow.entity.dto.ProcessDto;
|
||||
import cn.fateverse.workflow.entity.query.ProcessQuery;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessDefinitionHistoryVo;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessDefinitionInfoVo;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessDefinitionVo;
|
||||
import cn.fateverse.workflow.enums.ProcessNodeEnum;
|
||||
import cn.fateverse.workflow.mapper.FormPermMapper;
|
||||
import cn.fateverse.workflow.mapper.ProcessBusinessMapper;
|
||||
import cn.fateverse.workflow.mapper.ProcessDataMapper;
|
||||
import cn.fateverse.workflow.mapper.ProcessListenerMapper;
|
||||
import cn.fateverse.workflow.service.ProcessDefinitionService;
|
||||
import cn.fateverse.workflow.process.BpmnParseUtil;
|
||||
import cn.fateverse.workflow.process.FormItemParseUtil;
|
||||
import cn.fateverse.workflow.service.ProcessNodeService;
|
||||
import cn.fateverse.common.security.utils.SecurityUtils;
|
||||
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.engine.RepositoryService;
|
||||
import org.flowable.engine.repository.Deployment;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/23
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ProcessDefinitionServiceImpl implements ProcessDefinitionService {
|
||||
|
||||
private final RepositoryService repositoryService;
|
||||
|
||||
private final ProcessBusinessMapper processBusinessMapper;
|
||||
|
||||
private final ProcessDataMapper processDataMapper;
|
||||
|
||||
private final FormPermMapper formPermMapper;
|
||||
|
||||
private final ProcessNodeService processNodeService;
|
||||
|
||||
private final ProcessListenerMapper listenerMapper;
|
||||
|
||||
|
||||
public ProcessDefinitionServiceImpl(RepositoryService repositoryService,
|
||||
ProcessBusinessMapper processBusinessMapper,
|
||||
ProcessDataMapper processDataMapper,
|
||||
FormPermMapper formPermMapper,
|
||||
ProcessNodeService processNodeService, ProcessListenerMapper listenerMapper) {
|
||||
this.repositoryService = repositoryService;
|
||||
this.processBusinessMapper = processBusinessMapper;
|
||||
this.processDataMapper = processDataMapper;
|
||||
this.formPermMapper = formPermMapper;
|
||||
this.processNodeService = processNodeService;
|
||||
this.listenerMapper = listenerMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<ProcessDefinitionVo> searchList(ProcessQuery query) {
|
||||
PageUtils.startPage();
|
||||
List<ProcessDefinitionVo> list = processBusinessMapper.selectWorkflowBusinessSimple(query);
|
||||
return PageUtils.getDataTable(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProcessDefinitionHistoryVo> searchHistoryList(String processDefinitionKey) {
|
||||
return processDataMapper.selectByDeploymentKey(processDefinitionKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessDefinitionInfoVo searchByDeploymentId(String deploymentId) {
|
||||
//获取流程数据
|
||||
ProcessData processData = processDataMapper.selectByDeploymentId(deploymentId);
|
||||
//获取流程业务数据
|
||||
ProcessBusiness processBusiness = processBusinessMapper.selectByDeploymentId(deploymentId);
|
||||
if (Objects.isNull(processBusiness)) {
|
||||
throw new CustomException("当前流程id有无,获取数据失败!");
|
||||
}
|
||||
//组装数据
|
||||
ProcessDefinitionInfoVo definitionInfoVo = ProcessDefinitionInfoVo.builder()
|
||||
.processDefinitionKey(processBusiness.getProcessDefinitionKey())
|
||||
.deploymentName(processBusiness.getDeploymentName())
|
||||
.build();
|
||||
definitionInfoVo.setLogo(JSON.parse(processData.getLogo()));
|
||||
definitionInfoVo.setSettings(JSON.parse(processData.getSettings()));
|
||||
definitionInfoVo.setProcess(JSON.parseArray(processData.getProcessStr(), ProcessNode.class));
|
||||
definitionInfoVo.setFormItems(JSON.parseArray(processData.getFormItemStr(), FormItem.class));
|
||||
return definitionInfoVo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessDefinitionInfoVo searchInitiateInfoByKey(String processDefinitionKey) {
|
||||
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
|
||||
.processDefinitionKey(processDefinitionKey).latestVersion().singleResult();
|
||||
if (processDefinition.isSuspended()) {
|
||||
throw new CustomException("当前流程已停用");
|
||||
}
|
||||
//获取流程数据
|
||||
ProcessBusiness processBusiness = processBusinessMapper.selectByDeploymentKey(processDefinitionKey);
|
||||
//获取流程业务数据
|
||||
ProcessData processData = processDataMapper.selectByDeploymentId(processBusiness.getDeploymentId());
|
||||
//获取权限列表
|
||||
Map<String, FormPerm> formPermMap = formPermMapper.selectListByFormKeyAndDeploymentId(ProcessConstant.ROOT_NODE, processData.getDeploymentId());
|
||||
//进行权限判断
|
||||
List<FormItem> formItems = FormItemParseUtil.checkFromItemPermsNew(JSON.parseArray(processData.getFormItemStr(), FormItem.class), formPermMap, null);
|
||||
ProcessDefinitionInfoVo definitionInfoVo = ProcessDefinitionInfoVo.builder()
|
||||
.processDefinitionKey(processBusiness.getProcessDefinitionKey())
|
||||
.deploymentName(processBusiness.getDeploymentName())
|
||||
.build();
|
||||
definitionInfoVo.setLogo(JSON.parse(processData.getLogo()));
|
||||
definitionInfoVo.setSettings(JSON.parse(processData.getSettings()));
|
||||
List<ProcessNode> process = processNodeService.getProcessNodes(processData.getProcessStr(), SecurityUtils.getLoginUser().getUser(), new ArrayList<>(), new JSONObject(), true);
|
||||
definitionInfoVo.setProcess(process);
|
||||
definitionInfoVo.setFormItems(formItems);
|
||||
return definitionInfoVo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suspend(String processDefinitionId) {
|
||||
repositoryService.suspendProcessDefinitionById(processDefinitionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate(String processDefinitionId) {
|
||||
repositoryService.activateProcessDefinitionById(processDefinitionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String deploymentId) {
|
||||
repositoryService.deleteDeployment(deploymentId, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
|
||||
public void add(ProcessDto processDto) {
|
||||
//获取到node节点数据
|
||||
List<ProcessNode> processNodeList = processDto.getProcess();
|
||||
Set<String> listenerIdSet = new HashSet<>();
|
||||
processNodeList.stream().filter(node ->
|
||||
node.getType() == ProcessNodeEnum.APPROVAL
|
||||
|| node.getType() == ProcessNodeEnum.TRIGGER
|
||||
|| node.getType() == ProcessNodeEnum.CC
|
||||
|| node.getType() == ProcessNodeEnum.DELAY).forEach(node -> {
|
||||
ListenerInfo listener = node.getProps().getListener();
|
||||
if (listener.getState()) {
|
||||
listener.getList().stream().filter(ListenerItem::getIsSys).forEach(item -> {
|
||||
listenerIdSet.add(item.getListenerValue());
|
||||
});
|
||||
}
|
||||
});
|
||||
List<ProcessListener> processListeners = null;
|
||||
if (!listenerIdSet.isEmpty()) {
|
||||
processListeners = listenerMapper.selectIds(new ArrayList<>(listenerIdSet));
|
||||
}
|
||||
//开始解析节点 todo 还需要解析处每一个节点所需要的动态字段数据,并且也需要入库存放与一张新表中,或者在表单权限中进行存储
|
||||
BpmnParseUtil util = new BpmnParseUtil(processDto, processListeners);
|
||||
BpmnModel bpmnModel = util.run();
|
||||
//查询当前key在系统中是否有过部署记录
|
||||
ProcessBusiness processBusiness = processBusinessMapper.selectByDeploymentKey(processDto.getProcessDefinitionKey());
|
||||
//当系统中key有部署记录是,deploymentName直接使用系统中的,不允许做其他更改
|
||||
String deploymentName;
|
||||
if (null == processBusiness) {
|
||||
deploymentName = processDto.getDeploymentName();
|
||||
} else {
|
||||
deploymentName = processBusiness.getDeploymentName();
|
||||
}
|
||||
//部署流程
|
||||
Deployment deployment = repositoryService.createDeployment()
|
||||
.addBpmnModel(deploymentName + ".bpmn", bpmnModel)
|
||||
.name(deploymentName)
|
||||
.deploy();
|
||||
//设置流程数据
|
||||
processDto.setDeploymentId(deployment.getId());
|
||||
//当前key在系统中有过部署则更新数据,没有则新增数据
|
||||
if (null == processBusiness) {
|
||||
processBusinessMapper.insert(processDto);
|
||||
} else {
|
||||
processDto.setBusinessId(processBusiness.getBusinessId());
|
||||
processBusinessMapper.update(processDto);
|
||||
}
|
||||
//构建当前部署流程的附带数据
|
||||
ProcessData processData = ProcessData.builder()
|
||||
.deploymentId(deployment.getId())
|
||||
.deploymentName(processDto.getDeploymentName())
|
||||
.formItemStr(JSON.toJSONString(processDto.getFormItems()))
|
||||
.processStr(JSON.toJSONString(processNodeList))
|
||||
.logo(JSON.toJSONString(processDto.getLogo()))
|
||||
.settings(JSON.toJSONString(processDto.getSettings()))
|
||||
.remark(processDto.getRemark())
|
||||
.build();
|
||||
//新增附带数据
|
||||
processDataMapper.insert(processData);
|
||||
//获取到当前流程中对应节点的表单权限数据
|
||||
List<FormPerm> formPerms = util.getFormPerms();
|
||||
//判断是否为空,不为空则进行入库
|
||||
if (!formPerms.isEmpty()) {
|
||||
formPerms = formPerms.stream().peek(fromPerm -> fromPerm.setDeploymentId(deployment.getId())).collect(Collectors.toList());
|
||||
formPermMapper.batchInsert(formPerms);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package cn.fateverse.workflow.service.impl;
|
||||
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.common.file.service.impl.FastDFSStoreService;
|
||||
import cn.fateverse.workflow.entity.ProcessFile;
|
||||
import cn.fateverse.workflow.entity.bpmn.Attachment;
|
||||
import cn.fateverse.workflow.mapper.ProcessFileMapper;
|
||||
import cn.fateverse.workflow.service.ProcessFileService;
|
||||
import cn.fateverse.common.file.entity.FileInfo;
|
||||
import cn.fateverse.common.file.service.FileStoreService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023/1/10
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ProcessFileServiceImpl implements ProcessFileService {
|
||||
|
||||
private final FileStoreService fileStoreService;
|
||||
|
||||
private final ProcessFileMapper processFileMapper;
|
||||
|
||||
private final ThreadPoolTaskExecutor executor;
|
||||
|
||||
|
||||
public ProcessFileServiceImpl(FastDFSStoreService fileStoreService,
|
||||
ProcessFileMapper processFileMapper,
|
||||
ThreadPoolTaskExecutor taskExecuteExecutor) {
|
||||
this.fileStoreService = fileStoreService;
|
||||
this.processFileMapper = processFileMapper;
|
||||
this.executor = taskExecuteExecutor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
|
||||
public Attachment uploadFile(MultipartFile file) throws Exception {
|
||||
//调用oss上传文件
|
||||
FileInfo upload = fileStoreService.upload(file);
|
||||
ProcessFile processFile = new ProcessFile();
|
||||
BeanUtils.copyProperties(upload, processFile);
|
||||
//将文件信息落地到本地数据库,方便进行管理
|
||||
processFileMapper.insert(processFile);
|
||||
//返回前端需要的数据
|
||||
return Attachment.builder()
|
||||
.id(processFile.getId())
|
||||
.name(processFile.getOriginalFilename())
|
||||
.size(processFile.getSize())
|
||||
.isImage(processFile.getIsImage())
|
||||
.url(processFile.getUrl())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
|
||||
public void deleteFile(Long fileId) {
|
||||
//从本地数据库中查询出流程文件对象
|
||||
ProcessFile processFile = processFileMapper.selectById(fileId);
|
||||
if (Objects.isNull(processFile)) {
|
||||
throw new CustomException("文件不存在");
|
||||
}
|
||||
//本地删除文件信息
|
||||
processFileMapper.delete(fileId);
|
||||
executor.execute(() -> {
|
||||
//在oss端删除文件
|
||||
fileStoreService.delete(processFile.getUri());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,439 @@
|
||||
package cn.fateverse.workflow.service.impl;
|
||||
|
||||
import cn.fateverse.admin.entity.User;
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.common.core.result.page.TableDataInfo;
|
||||
import cn.fateverse.workflow.constant.ProcessConstant;
|
||||
import cn.fateverse.workflow.entity.FormPerm;
|
||||
import cn.fateverse.workflow.entity.HistoricalOperation;
|
||||
import cn.fateverse.workflow.entity.ProcessData;
|
||||
import cn.fateverse.workflow.entity.UserInstance;
|
||||
import cn.fateverse.workflow.mapper.*;
|
||||
import cn.fateverse.workflow.entity.bpmn.FormItem;
|
||||
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
|
||||
import cn.fateverse.workflow.entity.bpmn.UserInfo;
|
||||
import cn.fateverse.workflow.entity.dto.ProcessInstanceDto;
|
||||
import cn.fateverse.workflow.entity.vo.FormItemData;
|
||||
import cn.fateverse.workflow.entity.vo.HistoricalOperationVo;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessInstanceDetailVo;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessInstanceVo;
|
||||
import cn.fateverse.workflow.enums.FormItemEnum;
|
||||
import cn.fateverse.workflow.enums.FormPermEnum;
|
||||
import cn.fateverse.workflow.process.ProcessServiceUtils;
|
||||
import cn.fateverse.workflow.service.ProcessInstanceService;
|
||||
import cn.fateverse.workflow.service.ProcessNodeService;
|
||||
import cn.fateverse.common.mybatis.utils.PageUtils;
|
||||
import cn.fateverse.common.security.utils.SecurityUtils;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.HistoryService;
|
||||
import org.flowable.engine.RepositoryService;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntityImpl;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/12/24
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ProcessInstanceServiceImpl implements ProcessInstanceService {
|
||||
|
||||
//任务服务
|
||||
private final TaskService taskService;
|
||||
//运行中的服务
|
||||
private final RuntimeService runtimeService;
|
||||
//资源服务
|
||||
private final RepositoryService repositoryService;
|
||||
//历史服务
|
||||
private final HistoryService historyService;
|
||||
//表单item数数据mapper
|
||||
private final FormItemDataMapper formItemDataMapper;
|
||||
//自定义历史操作
|
||||
private final HistoricalOperationMapper operationMapper;
|
||||
//流程数据
|
||||
private final ProcessDataMapper processDataMapper;
|
||||
//表单权限mapper
|
||||
private final FormPermMapper formPermMapper;
|
||||
//用户流程mapper
|
||||
private final UserInstanceMapper userInstanceMapper;
|
||||
//流程节点操作服务
|
||||
private final ProcessNodeService processNodeService;
|
||||
|
||||
public ProcessInstanceServiceImpl(TaskService taskService,
|
||||
RuntimeService runtimeService,
|
||||
HistoryService historyService,
|
||||
RepositoryService repositoryService,
|
||||
FormItemDataMapper formItemDataMapper,
|
||||
HistoricalOperationMapper operationMapper,
|
||||
ProcessDataMapper processDataMapper,
|
||||
FormPermMapper formPermMapper,
|
||||
UserInstanceMapper userInstanceMapper,
|
||||
ProcessNodeService processNodeService) {
|
||||
this.taskService = taskService;
|
||||
this.runtimeService = runtimeService;
|
||||
this.repositoryService = repositoryService;
|
||||
this.historyService = historyService;
|
||||
this.formItemDataMapper = formItemDataMapper;
|
||||
this.processDataMapper = processDataMapper;
|
||||
this.operationMapper = operationMapper;
|
||||
this.formPermMapper = formPermMapper;
|
||||
this.userInstanceMapper = userInstanceMapper;
|
||||
this.processNodeService = processNodeService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
|
||||
public void start(ProcessInstanceDto processInstanceDto) {
|
||||
ProcessInstance processInstance = getProcessInstance(processInstanceDto, processInstanceDto.getProcessDefinitionId());
|
||||
//创建用户实例
|
||||
UserInstance userInstance = UserInstance.builder()
|
||||
.instanceId(processInstance.getProcessInstanceId())
|
||||
.processInstanceId(processInstance.getProcessInstanceId())
|
||||
.deploymentName(processInstance.getProcessDefinitionName())
|
||||
.state(ProcessConstant.BUSINESS_STATUS_DEAL)
|
||||
.build();
|
||||
userInstance.setCreateBy(userInstance.getInitiator());
|
||||
userInstance.setUserId(SecurityUtils.getUserId().toString());
|
||||
userInstanceMapper.insert(userInstance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restart(ProcessInstanceDto processInstanceDto) {
|
||||
UserInstance userInstance = userInstanceMapper.selectByInstanceId(processInstanceDto.getProcessDefinitionId());
|
||||
if (null == userInstance || userInstance.getState().equals(ProcessConstant.BUSINESS_STATUS_DEAL)
|
||||
|| !userInstance.getUserId().equals(SecurityUtils.getUserId().toString())) {
|
||||
throw new CustomException("当前节点状态不允许!");
|
||||
}
|
||||
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
|
||||
.processInstanceId(userInstance.getProcessInstanceId())
|
||||
.singleResult();
|
||||
ProcessInstance processInstance = getProcessInstance(processInstanceDto, historicProcessInstance.getProcessDefinitionId());
|
||||
userInstance.setProcessInstanceId(processInstance.getProcessInstanceId());
|
||||
userInstance.setState(ProcessConstant.BUSINESS_STATUS_DEAL);
|
||||
userInstanceMapper.update(userInstance);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ProcessInstance getProcessInstance(ProcessInstanceDto processInstanceDto, String processDefinitionId) {
|
||||
//获取到当前key对应的流程对象
|
||||
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
|
||||
.processDefinitionId(processDefinitionId)
|
||||
.singleResult();
|
||||
//todo 流程实例启动时需要的参数
|
||||
Map<String, Object> variables = new HashMap<>(16);
|
||||
//表单参数
|
||||
variables.put(ProcessConstant.FORM_VAR, processInstanceDto.getFormData());
|
||||
//用户自选参数
|
||||
if (null != processInstanceDto.getOptionalUser() && !processInstanceDto.getOptionalUser().isEmpty()) {
|
||||
variables.put(ProcessConstant.OPTIONAL_USER, processInstanceDto.getOptionalUser());
|
||||
}
|
||||
//用户信息
|
||||
variables.put(ProcessConstant.START_USER_INFO, Objects.requireNonNull(SecurityUtils.getLoginUser()).getUser());
|
||||
//流程状态
|
||||
variables.put(ProcessConstant.PROCESS_STATUS, ProcessConstant.BUSINESS_STATUS_DEAL);
|
||||
//将用户信息传入,用于后边判断发起人是谁的分支判断条件
|
||||
variables.put(ProcessConstant.START_USER_ID, SecurityUtils.getUserId());
|
||||
Map<String, Object> fromValue = JSONObject.parseObject(processInstanceDto.getFormData(), new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
//插入表单数据项
|
||||
variables.putAll(fromValue);
|
||||
//启动流程对象
|
||||
ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
|
||||
.processDefinitionId(processDefinitionId)
|
||||
.variables(variables)
|
||||
.name(processDefinition.getName())
|
||||
.start();
|
||||
String processInstanceId = processInstance.getId();
|
||||
//对表单数据进行解析
|
||||
//入库表单数据
|
||||
FormItemData data = FormItemData.builder()
|
||||
.isStart(Boolean.TRUE)
|
||||
.isPresent(Boolean.TRUE)
|
||||
.processInstanceId(processInstanceId)
|
||||
.formData(processInstanceDto.getFormData())
|
||||
.createBy(SecurityUtils.getUsername())
|
||||
.build();
|
||||
//将自选用户添加到历史数据中
|
||||
if (null != processInstanceDto.getOptionalUser()) {
|
||||
data.setOptionalUser(JSON.toJSONString(processInstanceDto.getOptionalUser()));
|
||||
}
|
||||
formItemDataMapper.insert(data);
|
||||
return processInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* todo 需要检查分页是否生效
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<ProcessInstanceVo> searchList(String state) {
|
||||
PageUtils.startPage();
|
||||
List<UserInstance> userInstanceList = userInstanceMapper.selectByUserId(null, state, null);
|
||||
long total = new PageInfo<>(userInstanceList).getTotal();
|
||||
return processingUserInstanceList(userInstanceList, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<ProcessInstanceVo> searchSelfList(String state, String deploymentName) {
|
||||
PageUtils.startPage();
|
||||
List<UserInstance> userInstanceList = userInstanceMapper.selectByUserId(SecurityUtils.getUserId().toString(), state, deploymentName);
|
||||
long total = new PageInfo<>(userInstanceList).getTotal();
|
||||
return processingUserInstanceList(userInstanceList, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<ProcessInstanceVo> searchAboutMeList(String state, String deploymentName) {
|
||||
PageUtils.startPage();
|
||||
List<UserInstance> userInstanceList = userInstanceMapper.selectProcessInstanceListByUserIdAndState(SecurityUtils.getUserId(), state, deploymentName);
|
||||
long total = new PageInfo<>(userInstanceList).getTotal();
|
||||
return processingUserInstanceList(userInstanceList, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessInstanceDetailVo searchSelfInfo(String processInstanceId) {
|
||||
//查询出当前实例对应的表单数据
|
||||
FormItemData formItemData = formItemDataMapper.selectByProcessInstanceId(processInstanceId, true);
|
||||
JSONObject formData = JSON.parseObject(formItemData.getFormData());
|
||||
//获取到流程实例对昂
|
||||
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
|
||||
.processInstanceId(processInstanceId)
|
||||
.includeProcessVariables()
|
||||
.singleResult();
|
||||
//获取到bpmn附带的数据
|
||||
Map<String, String> attribute = ProcessServiceUtils.getBpmnValueAttribute(repositoryService, processInstance.getProcessDefinitionId());
|
||||
//获取到流程用户数据
|
||||
Map<String, Object> variables = processInstance.getProcessVariables();
|
||||
//获取用户对象
|
||||
User user = (User) variables.get(ProcessConstant.START_USER_INFO);
|
||||
//获取到操作历史
|
||||
List<HistoricalOperationVo> operationList = ProcessServiceUtils.getHistoricalOperations(processInstanceId, operationMapper);
|
||||
//获取到流程数据
|
||||
List<ProcessNode> process = processNodeService.getProcessNodes(attribute.get(ProcessConstant.BPMN_FORM_PROCESS), user, operationList,
|
||||
(JSONObject) variables.get(ProcessConstant.OPTIONAL_USER), false);
|
||||
//流程中所有的节点信息
|
||||
Set<String> processIdSet = process.stream().map(ProcessNode::getId).collect(Collectors.toSet());
|
||||
//表单数据转换
|
||||
List<FormItem> formItems = JSON.parseArray(attribute.get(ProcessConstant.BPMN_FORM_ITEMS), FormItem.class);
|
||||
//将用户数据返回
|
||||
UserInfo userInfo = UserInfo.toUserInfo(user);
|
||||
//初始化基础对象
|
||||
ProcessInstanceDetailVo detailVo = ProcessInstanceDetailVo.builder()
|
||||
.formData(formData)
|
||||
.process(process)
|
||||
.userInfo(userInfo)
|
||||
.operationList(operationList)
|
||||
.runningList(new ArrayList<>())
|
||||
.noTakeList(new ArrayList<>())
|
||||
.refuseList(new ArrayList<>())
|
||||
.passList(new ArrayList<>())
|
||||
.endList(new ArrayList<>())
|
||||
.build();
|
||||
//获取到正在运行的节点
|
||||
Set<String> runningList = new HashSet<>();
|
||||
//获取到已经运行结束的节点
|
||||
Set<String> endList = new HashSet<>();
|
||||
//获取到拒绝的节点
|
||||
Set<String> refuseList = new HashSet<>();
|
||||
//直接通过的节点
|
||||
Set<String> passList = new HashSet<>();
|
||||
operationList.forEach(operation -> {
|
||||
switch (operation.getState()) {
|
||||
case RUNNING:
|
||||
runningList.add(operation.getNodeId());
|
||||
processIdSet.remove(operation.getNodeId());
|
||||
break;
|
||||
case AGREE:
|
||||
case CREATE:
|
||||
case AUTO_PASS:
|
||||
endList.add(operation.getNodeId());
|
||||
processIdSet.remove(operation.getNodeId());
|
||||
break;
|
||||
case REFUSE:
|
||||
refuseList.add(operation.getNodeId());
|
||||
processIdSet.remove(operation.getNodeId());
|
||||
break;
|
||||
case PASS:
|
||||
passList.add(operation.getNodeId());
|
||||
processIdSet.remove(operation.getNodeId());
|
||||
break;
|
||||
}
|
||||
});
|
||||
List<FormItem> result;
|
||||
if (ProcessConstant.BUSINESS_STATUS_DEAL.equals(variables.get(ProcessConstant.PROCESS_STATUS))) {
|
||||
//获取到当前的运行节点
|
||||
List<Task> taskList = taskService.createTaskQuery()
|
||||
.processInstanceId(processInstanceId)
|
||||
.list();
|
||||
//拿到当前运行的formKey,用于后边进行表单权限合并
|
||||
List<String> formKeys = taskList.stream().map(Task::getFormKey).collect(Collectors.toList());
|
||||
Set<String> formPermSet = null;
|
||||
if (!formKeys.isEmpty()) {
|
||||
//获取到当前运行节点的表单权限数据
|
||||
List<FormPerm> formPerms = formPermMapper.selectListByFormKeyListAndDeploymentId(formKeys, processInstance.getDeploymentId());
|
||||
formPermSet = formPerms.stream().filter(formPerm -> null != formPerm.getPerm() && formPerm.getPerm().equals(FormPermEnum.READ)).map(FormPerm::getId).collect(Collectors.toSet());
|
||||
//筛选出可读的所有表单id
|
||||
//初始化表单权限
|
||||
}
|
||||
result = initFormItems(formItems, formPermSet);
|
||||
} else {
|
||||
result = initFormItems(formItems, null);
|
||||
}
|
||||
//将剩下的参数填入到表单当中
|
||||
detailVo.setRunningList(new ArrayList<>(runningList));
|
||||
detailVo.setEndList(new ArrayList<>(endList));
|
||||
detailVo.setNoTakeList(new ArrayList<>(processIdSet));
|
||||
detailVo.setRefuseList(new ArrayList<>(refuseList));
|
||||
detailVo.setPassList(new ArrayList<>(passList));
|
||||
detailVo.setFormItems(result);
|
||||
return detailVo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessInstanceDetailVo searchReInfo(String instanceId) {
|
||||
UserInstance userInstance = userInstanceMapper.selectByInstanceId(instanceId);
|
||||
if (null == userInstance || userInstance.getState().equals(ProcessConstant.BUSINESS_STATUS_DEAL)
|
||||
|| !userInstance.getUserId().equals(SecurityUtils.getUserId().toString())) {
|
||||
throw new CustomException("当前节点状态不允许!");
|
||||
}
|
||||
HistoricProcessInstanceEntityImpl historicProcessInstance = (HistoricProcessInstanceEntityImpl) historyService.createHistoricProcessInstanceQuery()
|
||||
.processInstanceId(userInstance.getProcessInstanceId())
|
||||
.singleResult();
|
||||
//表单数据
|
||||
FormItemData formItemData = formItemDataMapper.selectByProcessInstanceId(historicProcessInstance.getProcessInstanceId(), Boolean.TRUE);
|
||||
JSONObject formData = JSON.parseObject(formItemData.getFormData());
|
||||
//表单项数据
|
||||
ProcessData processData = processDataMapper.selectByProcessDefinitionKey(historicProcessInstance.getProcessDefinitionKey());
|
||||
List<FormItem> formItems = JSON.parseArray(processData.getFormItemStr(), FormItem.class);
|
||||
//流程数据
|
||||
User user = Objects.requireNonNull(SecurityUtils.getLoginUser()).getUser();
|
||||
//流程数据
|
||||
List<ProcessNode> process = processNodeService.getProcessNodes(processData.getProcessStr(), user, new ArrayList<>(),
|
||||
new JSONObject(), false);
|
||||
//流程节点上的自选数据
|
||||
JSONObject optionalUser = JSONObject.parseObject(formItemData.getOptionalUser());
|
||||
return ProcessInstanceDetailVo.builder()
|
||||
.process(process)
|
||||
.formData(formData)
|
||||
.formItems(formItems)
|
||||
.optionalUser(optionalUser)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化表单数据
|
||||
*
|
||||
* @param formItems 表单项数据
|
||||
* @param formPermSet 表单权限
|
||||
* @return 处理后的表单数据
|
||||
*/
|
||||
private List<FormItem> initFormItems(List<FormItem> formItems, Set<String> formPermSet) {
|
||||
//初始化接受对象
|
||||
List<FormItem> result = new ArrayList<>();
|
||||
//对当前的表单项进行判断
|
||||
for (FormItem formItem : formItems) {
|
||||
//判断是否是分栏布局独享
|
||||
if (FormItemEnum.SpanLayout.getName().equals(formItem.getName())) {
|
||||
JSONObject props = formItem.getProps();
|
||||
List<FormItem> items = props.getList("items", FormItem.class);
|
||||
props.put("items", initFormItems(items, formPermSet));
|
||||
}
|
||||
//进行全新校验,如果set集合中没有参数,则认为当前表单为可读,
|
||||
if (null != formPermSet && !formPermSet.isEmpty()) {
|
||||
//如果当前表单中存在则设置可读,如果没有,则直接丢弃当前表单,不传递给前端
|
||||
if (formPermSet.contains(formItem.getId())) {
|
||||
formItem.setPerm(FormPermEnum.READ.getType());
|
||||
result.add(formItem);
|
||||
}
|
||||
} else {
|
||||
formItem.setPerm(FormPermEnum.READ.getType());
|
||||
result.add(formItem);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将HistoricProcessInstance组装成需要返回的数据
|
||||
*
|
||||
* @param userInstanceList 用户实例
|
||||
* @param count 总数
|
||||
* @return view需要的渲染数据
|
||||
*/
|
||||
private TableDataInfo<ProcessInstanceVo> processingUserInstanceList(List<UserInstance> userInstanceList, long count) {
|
||||
//如果为空,则直接返回空列表
|
||||
if (null == userInstanceList || userInstanceList.isEmpty()) {
|
||||
return PageUtils.getDataTable(new ArrayList<>());
|
||||
}
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
//获取到流程中的流程实例id
|
||||
List<String> processInstanceListIds = userInstanceList.stream().map(UserInstance::getProcessInstanceId).collect(Collectors.toList());
|
||||
List<HistoricalOperation> operationAllList;
|
||||
if (processInstanceListIds.isEmpty()) {
|
||||
operationAllList = new ArrayList<>();
|
||||
} else {
|
||||
//获取数据库中正在运行中的状态
|
||||
operationAllList = operationMapper.selectRunningByProcessInstanceId(processInstanceListIds);
|
||||
}
|
||||
//对获取到的历史记录进行映射
|
||||
Map<String, List<HistoricalOperation>> operationMap = operationAllList.stream().collect(Collectors.groupingBy(HistoricalOperation::getProcessInstanceId));
|
||||
//开始流程Vo的映射
|
||||
List<ProcessInstanceVo> processInstanceVoList = userInstanceList.stream().map(instance -> {
|
||||
//获取到历史节点中正在运行的节点
|
||||
List<HistoricalOperation> operationList = operationMap.get(instance.getProcessInstanceId());
|
||||
ProcessInstanceVo processInstanceVo = ProcessInstanceVo.builder()
|
||||
.taskId(instance.getInstanceId())
|
||||
.deploymentName(instance.getDeploymentName())
|
||||
.processInstanceId(instance.getProcessInstanceId())
|
||||
.state(instance.getState())
|
||||
.initiator(instance.getInitiator())
|
||||
.submitTime(instance.getSubmitTime())
|
||||
.endTime(instance.getFinishTime())
|
||||
.isSelf(instance.getUserId().equals(userId.toString()))
|
||||
.build();
|
||||
//如果获取到当前运行中的节点不为空,则需要修改审批人和当前任务节点
|
||||
if (null != operationList && !operationList.isEmpty() && instance.getState().equals(ProcessConstant.BUSINESS_STATUS_DEAL)) {
|
||||
List<String> taskNameList = new ArrayList<>();
|
||||
List<String> approveNameList = new ArrayList<>();
|
||||
operationList.forEach(operation -> {
|
||||
taskNameList.add(operation.getOperationName());
|
||||
List<UserInfo> userInfos = JSON.parseArray(operation.getUserInfo(), UserInfo.class);
|
||||
if (null != userInfos && !userInfos.isEmpty()) {
|
||||
approveNameList.addAll(userInfos.stream().map(UserInfo::getName).collect(Collectors.toList()));
|
||||
}
|
||||
});
|
||||
processInstanceVo.setTaskName(taskNameList.toString());
|
||||
processInstanceVo.setApproveName(approveNameList.toString());
|
||||
} else if (instance.getState().equals(ProcessConstant.BUSINESS_STATUS_REVOCATION)) {
|
||||
processInstanceVo.setApproveName(null);
|
||||
processInstanceVo.setTaskName("撤销");
|
||||
} else if (instance.getState().equals(ProcessConstant.BUSINESS_STATUS_REJECT)) {
|
||||
processInstanceVo.setApproveName(null);
|
||||
processInstanceVo.setTaskName("驳回");
|
||||
} else if (instance.getState().equals(ProcessConstant.BUSINESS_STATUS_ENDED)) {
|
||||
processInstanceVo.setApproveName(null);
|
||||
processInstanceVo.setTaskName("完成");
|
||||
}
|
||||
return processInstanceVo;
|
||||
}).collect(Collectors.toList());
|
||||
//返回分页信息
|
||||
return PageUtils.convertDataTable(processInstanceVoList, count);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package cn.fateverse.workflow.service.impl;
|
||||
|
||||
import cn.fateverse.common.core.entity.Option;
|
||||
import cn.fateverse.workflow.entity.ProcessListener;
|
||||
import cn.fateverse.workflow.entity.dto.ProcessListenerDto;
|
||||
import cn.fateverse.workflow.entity.vo.ProcessListenerVo;
|
||||
import cn.fateverse.workflow.entity.query.ProcessListenerQuery;
|
||||
import cn.fateverse.workflow.mapper.ProcessListenerMapper;
|
||||
import cn.fateverse.workflow.service.ProcessListenerService;
|
||||
import cn.fateverse.common.core.result.page.TableDataInfo;
|
||||
import cn.fateverse.common.mybatis.utils.PageUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 系统内置监听器 Controller
|
||||
*
|
||||
* @author clay
|
||||
* @date 2023-03-27
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ProcessListenerServiceImpl implements ProcessListenerService {
|
||||
|
||||
private final ProcessListenerMapper processListenerMapper;
|
||||
|
||||
public ProcessListenerServiceImpl(ProcessListenerMapper processListenerMapper) {
|
||||
this.processListenerMapper = processListenerMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessListenerVo searchById(String id){
|
||||
ProcessListener processListener = processListenerMapper.selectById(id);
|
||||
return ProcessListenerVo.toProcessListenerVo(processListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<ProcessListenerVo> searchList(ProcessListenerQuery query){
|
||||
PageUtils.startPage();
|
||||
List<ProcessListener> list = processListenerMapper.selectList(query);
|
||||
return PageUtils.convertDataTable(list, ProcessListenerVo::toProcessListenerVo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Option> searchOptionList(String type) {
|
||||
ProcessListenerQuery query = new ProcessListenerQuery();
|
||||
query.setListenerType(type);
|
||||
List<ProcessListener> list = processListenerMapper.selectList(query);
|
||||
return list.stream().map(ProcessListener::toOption).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProcessListenerVo> exportList(ProcessListenerQuery query){
|
||||
List<ProcessListener> list = processListenerMapper.selectList(query);
|
||||
return list.stream().map(ProcessListenerVo::toProcessListenerVo)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
|
||||
public int save(ProcessListenerDto processListener){
|
||||
String id = UUID.randomUUID().toString();
|
||||
ProcessListener info = ProcessListenerDto.toProcessListener(processListener);
|
||||
info.setId(id);
|
||||
return processListenerMapper.insert(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
|
||||
public int edit(ProcessListenerDto processListener){
|
||||
ProcessListener info = ProcessListenerDto.toProcessListener(processListener);
|
||||
return processListenerMapper.update(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
|
||||
public int removeById(String id){
|
||||
return processListenerMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
|
||||
public int removeBatch(List<String> idList){
|
||||
return processListenerMapper.deleteBatchByIdList(idList);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,632 @@
|
||||
package cn.fateverse.workflow.service.impl;
|
||||
|
||||
import cn.fateverse.workflow.constant.ProcessConstant;
|
||||
import cn.fateverse.workflow.entity.bpmn.*;
|
||||
import cn.fateverse.workflow.mapper.*;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.fateverse.admin.entity.User;
|
||||
import cn.fateverse.common.core.entity.Option;
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.common.core.result.page.TableDataInfo;
|
||||
import cn.fateverse.common.core.utils.ObjectUtils;
|
||||
import cn.fateverse.workflow.entity.FormPerm;
|
||||
import cn.fateverse.workflow.entity.HistoricalOperation;
|
||||
import cn.fateverse.workflow.entity.bo.AnalysisProcessBo;
|
||||
import cn.fateverse.workflow.entity.bo.VariablesBo;
|
||||
import cn.fateverse.workflow.entity.dto.TaskDto;
|
||||
import cn.fateverse.workflow.entity.vo.FormItemData;
|
||||
import cn.fateverse.workflow.entity.vo.HistoricalOperationVo;
|
||||
import cn.fateverse.workflow.entity.vo.task.TaskResultVo;
|
||||
import cn.fateverse.workflow.entity.vo.task.TaskVo;
|
||||
import cn.fateverse.workflow.enums.ModeEnums;
|
||||
import cn.fateverse.workflow.enums.OperationStateEnums;
|
||||
import cn.fateverse.workflow.enums.OperationEnums;
|
||||
import cn.fateverse.workflow.process.ProcessServiceUtils;
|
||||
import cn.fateverse.workflow.process.cmd.RollbackUserTaskCmd;
|
||||
import cn.fateverse.workflow.service.ProcessService;
|
||||
import cn.fateverse.workflow.service.ProcessTaskService;
|
||||
import cn.fateverse.workflow.process.FormItemParseUtil;
|
||||
import cn.fateverse.common.mybatis.utils.PageUtils;
|
||||
import cn.fateverse.common.core.utils.TableSupport;
|
||||
import cn.fateverse.common.security.utils.SecurityUtils;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.bpmn.model.Process;
|
||||
import org.flowable.editor.language.json.converter.util.CollectionUtils;
|
||||
import org.flowable.engine.*;
|
||||
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
|
||||
import org.flowable.engine.runtime.ActivityInstance;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 流程任务流转实现
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/12/24
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ProcessTaskServiceImpl implements ProcessTaskService {
|
||||
|
||||
//task任务服务
|
||||
private final TaskService taskService;
|
||||
//运行时服务
|
||||
private final RuntimeService runtimeService;
|
||||
//用户流程实例
|
||||
private final UserInstanceMapper userInstanceMapper;
|
||||
//资源服务
|
||||
private final RepositoryService repositoryService;
|
||||
//表单权限
|
||||
private final FormPermMapper formPermMapper;
|
||||
//表单数据
|
||||
private final FormItemDataMapper formItemDataMapper;
|
||||
//流程辅助
|
||||
private final ProcessMapper processMapper;
|
||||
//历史操作
|
||||
private final HistoricalOperationMapper operationMapper;
|
||||
//运行中的实例
|
||||
private final RunFlowableActinstMapper runFlowableActinstMapper;
|
||||
//历史记录
|
||||
private final HisFlowableActinstMapper hisFlowableActinstMapper;
|
||||
//流程引擎管理服务
|
||||
private final ManagementService managementService;
|
||||
|
||||
|
||||
public ProcessTaskServiceImpl(TaskService taskService,
|
||||
RuntimeService runtimeService,
|
||||
UserInstanceMapper userInstanceMapper,
|
||||
RepositoryService repositoryService,
|
||||
FormPermMapper formPermMapper,
|
||||
FormItemDataMapper formItemDataMapper,
|
||||
ProcessMapper processMapper,
|
||||
HistoricalOperationMapper operationMapper,
|
||||
RunFlowableActinstMapper runFlowableActinstMapper,
|
||||
HisFlowableActinstMapper hisFlowableActinstMapper,
|
||||
ManagementService managementService) {
|
||||
this.taskService = taskService;
|
||||
this.runtimeService = runtimeService;
|
||||
this.userInstanceMapper = userInstanceMapper;
|
||||
this.repositoryService = repositoryService;
|
||||
this.formPermMapper = formPermMapper;
|
||||
this.formItemDataMapper = formItemDataMapper;
|
||||
this.processMapper = processMapper;
|
||||
this.operationMapper = operationMapper;
|
||||
this.runFlowableActinstMapper = runFlowableActinstMapper;
|
||||
this.hisFlowableActinstMapper = hisFlowableActinstMapper;
|
||||
this.managementService = managementService;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public TableDataInfo<TaskVo> searchTaskList() {
|
||||
//获取分页信息
|
||||
int pageSize = TableSupport.buildPageRequest().getPageSize();
|
||||
Integer startSize = PageUtils.getStartSize();
|
||||
//查询代办任务数据
|
||||
String userId = SecurityUtils.getUserId().toString();
|
||||
List<Task> taskList = taskService.createTaskQuery().taskAssignee(userId)
|
||||
.includeProcessVariables()
|
||||
.orderByTaskCreateTime().desc()
|
||||
.listPage(startSize, pageSize);
|
||||
if (null == taskList || taskList.isEmpty()) {
|
||||
return PageUtils.convertDataTable(null, 0L);
|
||||
}
|
||||
long count = taskService.createTaskQuery().taskAssignee(userId).count();
|
||||
//将 processInstanceId 抽离,然后进入到list获取到 ProcessInstanceImpl对象获取到创建时间和审批人等相关的数据
|
||||
List<String> processInstanceIdList = taskList.stream().map(Task::getProcessInstanceId).distinct().collect(Collectors.toList());
|
||||
Map<String, ExecutionEntityImpl> processInstanceMap = processMapper.selectProcessInstanceMapByList(processInstanceIdList);
|
||||
List<TaskVo> taskVoList = taskList.stream().map(task -> {
|
||||
TaskVo taskVo = toTaskVo(task);
|
||||
ExecutionEntityImpl executionEntity = processInstanceMap.get(task.getProcessInstanceId());
|
||||
if (null != executionEntity) {
|
||||
taskVo.setProcessName(executionEntity.getName());
|
||||
taskVo.setCreatedDate(executionEntity.getStartTime());
|
||||
}
|
||||
return taskVo;
|
||||
}).collect(Collectors.toList());
|
||||
return PageUtils.convertDataTable(taskVoList, count);
|
||||
|
||||
}
|
||||
|
||||
private TaskVo toTaskVo(Task task) {
|
||||
Map<String, Object> variables = task.getProcessVariables();
|
||||
User user = (User) task.getProcessVariables().get(ProcessConstant.START_USER_INFO);
|
||||
String processStatus = variables.get(ProcessConstant.PROCESS_STATUS).toString();
|
||||
return TaskVo.builder()
|
||||
.taskId(task.getId())
|
||||
.taskName(task.getName())
|
||||
.initiatorName(user.getNickName())
|
||||
.arriveDate(task.getCreateTime())
|
||||
.state(processStatus)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskResultVo searchTaskByTaskId(String taskId) {
|
||||
//获取到task任务
|
||||
Task task = getTaskByTaskId(taskId, Boolean.TRUE);//查询出当前实例对应的表单数据
|
||||
List<HistoricalOperation> historicalOperationList = operationMapper.selectListByProcessInstanceId(task.getProcessInstanceId(), null);
|
||||
//筛选出没有呗mark的操作历史
|
||||
List<HistoricalOperation> operationNoMarkList = historicalOperationList.stream().filter(operation -> operation.getMark().equals(0)).collect(Collectors.toList());
|
||||
//将所有的操作历史转换为vo
|
||||
List<HistoricalOperationVo> operationList = ProcessServiceUtils.getHistoricalOperationVoList(historicalOperationList);
|
||||
//获取到变淡数据
|
||||
FormItemData formItemData = formItemDataMapper.selectByProcessInstanceId(task.getProcessInstanceId(), true);
|
||||
//获取到流程文件
|
||||
Process mainProcess = repositoryService.getBpmnModel(task.getProcessDefinitionId()).getMainProcess();
|
||||
//获取到bpmn中附带的参数
|
||||
Map<String, String> bpmnAttribute = ProcessServiceUtils.getBpmnValueAttribute(mainProcess);
|
||||
//获取到表单json
|
||||
String formItemsStr = bpmnAttribute.get(ProcessConstant.BPMN_FORM_ITEMS);
|
||||
//解析
|
||||
List<FormItem> formItems = JSON.parseArray(formItemsStr, FormItem.class);
|
||||
//获取到当前task对应的表单权限
|
||||
Map<String, FormPerm> fromPermMap = formPermMapper.selectListByFormKeyAndDeploymentId(task.getFormKey(), null);
|
||||
//解析表单数据
|
||||
JSONObject formData = JSON.parseObject(formItemData.getFormData());
|
||||
//将权限和表单数据进行整合,筛选掉需要影藏的表单项,并将表单权限放入到props中
|
||||
List<FormItem> itemPerms = FormItemParseUtil.checkFromItemPermsNew(formItems, fromPermMap, formData, true);
|
||||
String taskDefinitionKey = task.getTaskDefinitionKey();
|
||||
//获取到所有的bpmn节点对象
|
||||
String processStr = bpmnAttribute.get(ProcessConstant.BPMN_FORM_PROCESS);
|
||||
//获取到可以退回的节点数据
|
||||
List<ProcessNode> processNodeList = new ProcessService().analysisEndOption(operationNoMarkList, processStr, taskDefinitionKey);
|
||||
//转换成为option list
|
||||
List<Option> userTaskOption = processNodeList.stream().map(processNode -> Option.builder()
|
||||
.value(processNode.getId())
|
||||
.label(processNode.getName())
|
||||
.build()).collect(Collectors.toList());
|
||||
//获取到当前流程的用户信息
|
||||
User user = (User) task.getProcessVariables().get(ProcessConstant.START_USER_INFO);
|
||||
UserInfo userInfo = UserInfo.toUserInfo(user);
|
||||
return TaskResultVo.builder()
|
||||
.userTaskOption(userTaskOption)
|
||||
.formItems(itemPerms)
|
||||
.formData(formData)
|
||||
.operationList(operationList)
|
||||
.userInfo(userInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void completeTask(TaskDto taskDto) {
|
||||
//获取到task任务
|
||||
Task task = getTaskByTaskId(taskDto.getTaskId(), Boolean.TRUE);
|
||||
//对表单数据进行处理
|
||||
VariablesBo variablesBo = screeningFromPerm(task, taskDto.getFormData());
|
||||
//添加评论信息
|
||||
addComment(taskDto.getComment(), task);
|
||||
HistoricalOperation operation = operationMapper.selectByProcessInstanceIdAndNodeId(task.getProcessInstanceId(), task.getTaskDefinitionKey());
|
||||
List<UserInfo> infoList = JSON.parseArray(operation.getUserInfo(), UserInfo.class);
|
||||
String userId = SecurityUtils.getUserId().toString();
|
||||
if (ModeEnums.AND.getTypeName().equals(operation.getApprovalMode())
|
||||
|| ModeEnums.NEXT.getTypeName().equals(operation.getApprovalMode())) {
|
||||
boolean flag = true;
|
||||
for (UserInfo userInfo : infoList) {
|
||||
if (userInfo.getId().equals(userId)) {
|
||||
userInfo.setState(OperationStateEnums.AGREE);
|
||||
} else {
|
||||
flag = userInfo.getState().equals(OperationStateEnums.AGREE);
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
operation.setFinishTime(new Date());
|
||||
operation.setState(OperationStateEnums.AGREE);
|
||||
}
|
||||
} else if (ModeEnums.OR.getTypeName().equals(operation.getApprovalMode())
|
||||
|| ModeEnums.NORMAL.getTypeName().equals(operation.getApprovalMode())) {
|
||||
for (UserInfo userInfo : infoList) {
|
||||
if (userInfo.getId().equals(userId)) {
|
||||
userInfo.setState(OperationStateEnums.AGREE);
|
||||
} else {
|
||||
userInfo.setState(OperationStateEnums.PASS);
|
||||
}
|
||||
}
|
||||
operation.setFinishTime(new Date());
|
||||
operation.setState(OperationStateEnums.AGREE);
|
||||
}
|
||||
operation.setUserInfo(JSON.toJSONString(infoList));
|
||||
operationMapper.update(operation);
|
||||
//完成task
|
||||
taskService.complete(taskDto.getTaskId(), variablesBo.getVariables());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void refuseTask(TaskDto taskDto) {
|
||||
//获取到task任务
|
||||
Task task = getTaskByTaskId(taskDto.getTaskId(), Boolean.TRUE);
|
||||
Date finishTime = new Date();
|
||||
String userId = SecurityUtils.getUserId().toString();
|
||||
//获取到所有正在处理的节点信息
|
||||
List<HistoricalOperation> operationList = operationMapper.selectByProcessInstanceIdAndNotNodeId(task.getProcessInstanceId(), ProcessConstant.PROCESS_RUNNING);
|
||||
//遍历所有节点
|
||||
for (HistoricalOperation operation : operationList) {
|
||||
List<UserInfo> infoList = JSON.parseArray(operation.getUserInfo(), UserInfo.class);
|
||||
//如果与当前运行的节点相同,则进行拒绝的操作
|
||||
if (operation.getNodeId().equals(task.getTaskDefinitionKey())) {
|
||||
//遍历所有的用户
|
||||
for (UserInfo userInfo : infoList) {
|
||||
//找到对应的用户
|
||||
if (userInfo.getId().equals(userId)) {
|
||||
userInfo.setState(OperationStateEnums.REFUSE);
|
||||
} else {
|
||||
//其他设置为pass
|
||||
if (userInfo.getState().equals(OperationStateEnums.RUNNING)) {
|
||||
userInfo.setState(OperationStateEnums.PASS);
|
||||
}
|
||||
}
|
||||
}
|
||||
taskAddComment(taskDto, operation);
|
||||
//更新当前历史操作的信息
|
||||
operation.setState(OperationStateEnums.REFUSE);
|
||||
} else {
|
||||
if (null != infoList && !infoList.isEmpty()) {
|
||||
//不是当前节点则将所有的用户操作状态设置为pass
|
||||
for (UserInfo userInfo : infoList) {
|
||||
if (userInfo.getState().equals(OperationStateEnums.RUNNING)) {
|
||||
userInfo.setState(OperationStateEnums.PASS);
|
||||
}
|
||||
}
|
||||
}
|
||||
//节点也设置为pass
|
||||
operation.setState(OperationStateEnums.PASS);
|
||||
}
|
||||
operation.setUserInfo(JSON.toJSONString(infoList));
|
||||
operation.setFinishTime(finishTime);
|
||||
}
|
||||
//批量更新节点信息
|
||||
operationMapper.batchUpdate(operationList);
|
||||
//获取流程数据,并设置为拒绝状态
|
||||
Map<String, Object> variables = task.getProcessVariables();
|
||||
variables.put(ProcessConstant.PROCESS_STATUS, ProcessConstant.BUSINESS_STATUS_REJECT);
|
||||
//将表单数据设置到流程中
|
||||
runtimeService.setVariables(task.getProcessInstanceId(), variables);
|
||||
//拒绝流程
|
||||
runtimeService.deleteProcessInstance(task.getProcessInstanceId(), "拒绝");
|
||||
userInstanceMapper.updateState(task.getProcessInstanceId(), ProcessConstant.BUSINESS_STATUS_REJECT);
|
||||
}
|
||||
|
||||
private void taskAddComment(TaskDto taskDto, HistoricalOperation operation) {
|
||||
CommentInfo comment = taskDto.getComment();
|
||||
String context = comment.getContext();
|
||||
List<Attachment> attachments = comment.getAttachments();
|
||||
if (!StrUtil.isEmpty(context) || !ObjectUtils.isEmpty(attachments)) {
|
||||
operation.setMessage(JSON.toJSONString(comment));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void delegateTas(TaskDto taskDto) {
|
||||
//获取到task任务
|
||||
Task task = getTaskByTaskId(taskDto.getTaskId(), Boolean.TRUE);
|
||||
//对表单数据进行处理
|
||||
VariablesBo variablesBo = screeningFromPerm(task, taskDto.getFormData());
|
||||
if (variablesBo.getFlag()) {
|
||||
runtimeService.setVariables(task.getProcessInstanceId(), variablesBo.getVariables());
|
||||
}
|
||||
//添加评论信息
|
||||
addComment(taskDto.getComment(), task);
|
||||
//设置好委托人
|
||||
UserInfo delegateUserInfo = taskDto.getDelegateUserInfo();
|
||||
taskService.delegateTask(task.getId(), delegateUserInfo.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void resolveCompleteTask(TaskDto taskDto) {
|
||||
//获取到task任务
|
||||
Task task = getTaskByTaskId(taskDto.getTaskId(), Boolean.TRUE);
|
||||
//对表单数据进行处理
|
||||
VariablesBo variablesBo = screeningFromPerm(task, taskDto.getFormData());
|
||||
//添加评论信息
|
||||
addComment(taskDto.getComment(), task);
|
||||
//委托人同意
|
||||
taskService.resolveTask(task.getId(), variablesBo.getVariables());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void revokeTask(TaskDto taskDto) {
|
||||
//自己的才能够撤销
|
||||
//1. 判断当前流程是否是自己发起的
|
||||
Task task = getTaskByTaskId(taskDto.getTaskId(), Boolean.TRUE);
|
||||
VariablesBo variablesBo = screeningFromPerm(task, taskDto.getFormData());
|
||||
Map<String, Object> variables = variablesBo.getVariables();
|
||||
User user = (User) variables.get(ProcessConstant.START_USER_INFO);
|
||||
if (!user.getUserId().equals(SecurityUtils.getUserId())) {
|
||||
throw new CustomException("您不是发起人,无权撤销当前流程!");
|
||||
}
|
||||
//将流程状态设置为撤销
|
||||
variables.put(ProcessConstant.PROCESS_STATUS, ProcessConstant.BUSINESS_STATUS_REVOCATION);
|
||||
runtimeService.setVariables(task.getProcessInstanceId(), variables);
|
||||
//2. 进行撤销操作
|
||||
addComment(taskDto.getComment(), task);
|
||||
runtimeService.deleteProcessInstance(task.getProcessInstanceId(), "撤销");
|
||||
userInstanceMapper.updateState(task.getProcessInstanceId(), ProcessConstant.BUSINESS_STATUS_REVOCATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void assigneeTask(TaskDto taskDto) {
|
||||
//获取到task任务
|
||||
Task task = getTaskByTaskId(taskDto.getTaskId(), Boolean.TRUE);
|
||||
//对表单数据进行处理
|
||||
VariablesBo variablesBo = screeningFromPerm(task, taskDto.getFormData());
|
||||
if (variablesBo.getFlag()) {
|
||||
runtimeService.setVariables(task.getProcessInstanceId(), variablesBo.getVariables());
|
||||
}
|
||||
//添加评论信息
|
||||
addComment(taskDto.getComment(), task);
|
||||
//转办
|
||||
taskService.setAssignee(taskDto.getTaskId(), taskDto.getTransferUserInfo().getId());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void rollbackTask(TaskDto taskDto) {
|
||||
//获取到task任务
|
||||
Task task = getTaskByTaskId(taskDto.getTaskId(), Boolean.TRUE);
|
||||
//获取到流程数据
|
||||
Process process = repositoryService.getBpmnModel(task.getProcessDefinitionId()).getProcesses().get(0);
|
||||
String processStr = process.getAttributes().get(ProcessConstant.BPMN_FORM_PROCESS).get(0).getValue();
|
||||
//获取到操作历史
|
||||
List<HistoricalOperation> operationList = operationMapper.selectListByProcessInstanceId(task.getProcessInstanceId(), 0);
|
||||
|
||||
//获取到流程需要删除的数据信息
|
||||
List<ActivityInstance> activityInstanceList = runtimeService.createActivityInstanceQuery()
|
||||
.processInstanceId(task.getProcessInstanceId())
|
||||
.list();
|
||||
//解析需要改变和回滚的节点id
|
||||
AnalysisProcessBo processBo = new ProcessService().analysisProcessNodeAndRunningNode(operationList, processStr, task.getTaskDefinitionKey(), taskDto.getRollBackId(), activityInstanceList, process.getFlowElements());
|
||||
//需要改变的节点
|
||||
Set<String> changeNodeSet = processBo.getChangeNodeSet();
|
||||
//需要回滚的节点
|
||||
Set<String> rollbackNodeSet = processBo.getRollbackNodeSet();
|
||||
//初始化需要改变的历史操作记录
|
||||
List<HistoricalOperation> changeOperations = new ArrayList<>(rollbackNodeSet.size() + changeNodeSet.size());
|
||||
//将流程节点根据改变还是回滚进行数据清洗
|
||||
for (HistoricalOperation operation : operationList) {
|
||||
//当前节点还应特殊处理
|
||||
List<UserInfo> userInfos = JSON.parseArray(operation.getUserInfo(), UserInfo.class);
|
||||
if (operation.getNodeId().equals(task.getTaskDefinitionKey())) {
|
||||
log.info("此处对当前节点进行特殊处理");
|
||||
for (UserInfo userInfo : userInfos) {
|
||||
if (userInfo.getState().equals(OperationStateEnums.RUNNING)) {
|
||||
if (userInfo.getId().equals(SecurityUtils.getUserId().toString())) {
|
||||
userInfo.setState(OperationStateEnums.ROLLBACK);
|
||||
} else {
|
||||
userInfo.setState(OperationStateEnums.PASS);
|
||||
}
|
||||
}
|
||||
}
|
||||
operation.setUserInfo(JSON.toJSONString(userInfos));
|
||||
operation.setMark(1);
|
||||
operation.setState(OperationStateEnums.ROLLBACK);
|
||||
changeOperations.add(operation);
|
||||
} else if (rollbackNodeSet.contains(operation.getNodeId())) {
|
||||
for (UserInfo userInfo : userInfos) {
|
||||
if (userInfo.getState().equals(OperationStateEnums.RUNNING)) {
|
||||
userInfo.setState(OperationStateEnums.PASS);
|
||||
}
|
||||
}
|
||||
operation.setUserInfo(JSON.toJSONString(userInfos));
|
||||
operation.setMark(1);
|
||||
operation.setState(OperationStateEnums.PASS);
|
||||
changeOperations.add(operation);
|
||||
//需要改变状态的节点和回退目标节点
|
||||
} else if (changeNodeSet.contains(operation.getNodeId())) {
|
||||
operation.setMark(1);
|
||||
changeOperations.add(operation);
|
||||
}
|
||||
}
|
||||
//批量的处理需要更新的操作历史
|
||||
if (!changeOperations.isEmpty()) {
|
||||
operationMapper.batchUpdate(changeOperations);
|
||||
}
|
||||
//对表单数据进行处理
|
||||
addComment(taskDto.getComment(), task);
|
||||
//todo 签字信息后续添加
|
||||
addSignInfo(taskDto.getSignInfo(), task);
|
||||
//删除历史节点的信息
|
||||
deleteActivity(processBo);
|
||||
//回滚执行
|
||||
managementService.executeCommand(new RollbackUserTaskCmd(runtimeService, task, taskDto, processBo, process));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除历史记录
|
||||
*
|
||||
* @param processBo
|
||||
*/
|
||||
private void deleteActivity(AnalysisProcessBo processBo) {
|
||||
List<String> runActivityIds = new ArrayList<>(processBo.getActivityIdSet());
|
||||
if (CollectionUtils.isNotEmpty(runActivityIds)) {
|
||||
runFlowableActinstMapper.deleteRunActinstsByIds(runActivityIds);
|
||||
hisFlowableActinstMapper.deleteByIds(runActivityIds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void addMultiTask(TaskDto taskDto) {
|
||||
//获取到task任务
|
||||
Task task = getTaskByTaskId(taskDto.getTaskId(), Boolean.TRUE);
|
||||
Map<String, Object> variables = task.getProcessVariables();
|
||||
addComment(taskDto.getComment(), task);
|
||||
//进行加签操作
|
||||
variables.put(ProcessConstant.ASSIGNEE_NAME, taskDto.getMultiAddUserInfo().getId());
|
||||
runtimeService.addMultiInstanceExecution(task.getTaskDefinitionKey(), task.getProcessInstanceId(), variables);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void searchMultiUsersInfo(String taskId) {
|
||||
//1. 获取到任务信息
|
||||
Task task = getTaskByTaskId(taskId, Boolean.FALSE);
|
||||
List<Task> list = taskService.createTaskQuery()
|
||||
.processInstanceId(task.getProcessInstanceId())
|
||||
.taskDefinitionKey(task.getTaskDefinitionKey()).list();
|
||||
//2. 获取当前节点所有的task任务
|
||||
|
||||
//3. 通过用户ids获取到对应的用户信息
|
||||
|
||||
//4. 组装好数据返回
|
||||
System.out.println("输出加签的用户");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteMultiTask(String taskId) {
|
||||
//获取当前的task 任务
|
||||
Task task = getTaskByTaskId(taskId, Boolean.FALSE);
|
||||
//删除这个执行任务
|
||||
runtimeService.deleteMultiInstanceExecution(task.getExecutionId(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void addCommentTask(CommentInfo CommentInfo) {
|
||||
//获取当前的task 任务
|
||||
Task task = getTaskByTaskId(CommentInfo.getTaskId(), Boolean.FALSE);
|
||||
//添加评论
|
||||
addComment(CommentInfo, task);
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤表单数据
|
||||
*
|
||||
* @param task
|
||||
* @param formStr
|
||||
* @return
|
||||
*/
|
||||
private VariablesBo screeningFromPerm(Task task, String formStr) {
|
||||
Map<String, Object> variables = task.getProcessVariables();
|
||||
//获取当当前task的表单权限,将权限为 E 的写入到旧的表单中,向数据库插入一条新的数据,并将新插入的这一条数据设为当前数据
|
||||
Map<String, FormPerm> fromPermMap = formPermMapper.selectListByFormKeyAndDeploymentId(task.getFormKey(), null);
|
||||
if (!fromPermMap.isEmpty()) {
|
||||
JSONObject formData = JSON.parseObject(formStr);
|
||||
FormItemData formItemData = formItemDataMapper.selectByProcessInstanceId(task.getProcessInstanceId(), true);
|
||||
JSONObject formDataOld = JSON.parseObject(formItemData.getFormData());
|
||||
JSONObject formDataNew = FormItemParseUtil.checkFromDataPrem(formData, formDataOld, fromPermMap);
|
||||
formItemDataMapper.updatePresentStart(task.getProcessInstanceId());
|
||||
//入库表单数据
|
||||
FormItemData data = FormItemData.builder()
|
||||
.isPresent(Boolean.TRUE)
|
||||
.processInstanceId(task.getProcessInstanceId())
|
||||
.formData(formDataNew.toJSONString())
|
||||
.createBy(SecurityUtils.getUsername())
|
||||
.build();
|
||||
formItemDataMapper.insert(data);
|
||||
//todo 获取到当前节点是否有参数传入
|
||||
//todo 流程实例启动时需要的参数
|
||||
//开始判断走审核通过的流程,此处还应该有评论数据入库
|
||||
variables.putAll(formDataNew);
|
||||
return VariablesBo.ok(variables);
|
||||
}
|
||||
return VariablesBo.no(variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到task 对象
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @param variable 是否需要参数
|
||||
* @return task任务对象
|
||||
*/
|
||||
private Task getTaskByTaskId(String taskId, Boolean variable) {
|
||||
if (variable) {
|
||||
return taskService.createTaskQuery()
|
||||
.includeProcessVariables()
|
||||
.taskId(taskId)
|
||||
.singleResult();
|
||||
} else {
|
||||
return taskService.createTaskQuery()
|
||||
.taskId(taskId)
|
||||
.singleResult();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加签字信息
|
||||
*
|
||||
* @param info 签字信息
|
||||
* @param task task任务对象
|
||||
*/
|
||||
private void addSignInfo(String info, Task task) {
|
||||
if (!StrUtil.isEmpty(info)) {
|
||||
taskService.addComment(task.getId(), task.getProcessInstanceId(), ProcessConstant.COMMENT_SIGN, info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作过程中填写的数据
|
||||
*
|
||||
* @param comment 评论对象
|
||||
* @param task task任务对象
|
||||
*/
|
||||
private void addComment(CommentInfo comment, Task task) {
|
||||
addComment(comment, task, OperationStateEnums.COMMENT, OperationEnums.COMMENT, "参与评论");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加评论,后期可能会进行更改,只是添加评论,附加就加到评论信息内
|
||||
* todo 注释信息需要单独创建一张表存储
|
||||
*
|
||||
* @param comment 评论对象
|
||||
* @param task task任务对象
|
||||
* @param start 评论类型
|
||||
* @param operationName 评论名称
|
||||
*/
|
||||
private void addComment(CommentInfo comment, Task task, OperationStateEnums start, OperationEnums operation, String operationName) {
|
||||
String taskId = UUID.randomUUID().toString();
|
||||
addComment(comment, taskId, task.getProcessInstanceId(), start, operation, operationName);
|
||||
}
|
||||
|
||||
|
||||
private void addComment(CommentInfo comment, String taskId,
|
||||
String processInstanceId,
|
||||
OperationStateEnums start,
|
||||
OperationEnums operationType,
|
||||
String operationName) {
|
||||
//判断评论是否为空
|
||||
if (!ObjectUtils.isEmpty(comment)) {
|
||||
//判断评论内容是否为空
|
||||
String context = comment.getContext();
|
||||
List<Attachment> attachments = comment.getAttachments();
|
||||
if (!StrUtil.isEmpty(context) || !ObjectUtils.isEmpty(attachments)) {
|
||||
Date date = new Date();
|
||||
comment.setTaskId(taskId);
|
||||
User user = Objects.requireNonNull(SecurityUtils.getLoginUser()).getUser();
|
||||
UserInfo userInfo = UserInfo.toUserInfo(user);
|
||||
String message = JSON.toJSONString(comment);
|
||||
HistoricalOperation operation = HistoricalOperation.builder()
|
||||
.taskId(taskId)
|
||||
.processInstanceId(processInstanceId)
|
||||
.message(message)
|
||||
.operationName(operationName)
|
||||
.operation(operationType)
|
||||
.nodeId(taskId)
|
||||
.state(start)
|
||||
.finishTime(date)
|
||||
.startTime(date.getTime())
|
||||
.userInfo(JSON.toJSONString(Collections.singletonList(userInfo)))
|
||||
.build();
|
||||
operationMapper.insert(operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user