feat : 模块抽离 + 自定义查分表模块基本完成

This commit is contained in:
clay
2024-04-10 14:31:08 +08:00
parent da94c4fa32
commit cde8e3a928
377 changed files with 3249 additions and 136 deletions

142
workflow/pom.xml Normal file
View 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>

View File

@@ -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("工作流模块启动成功");
}
}

View File

@@ -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";
}

View File

@@ -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不能为空");
}
}
}

View File

@@ -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();
}
}

View File

@@ -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("流程启动成功");
}
}

View File

@@ -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();
}
}

View File

@@ -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("提交成功!");
}
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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());
});
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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