微服务版后端初始化

This commit is contained in:
yaoyn
2025-02-08 17:51:37 +08:00
parent 54af6be188
commit da009a7cc4
1897 changed files with 429541 additions and 81 deletions

View File

@ -0,0 +1,16 @@
# 基础镜像
FROM nexus.gdyditc.com:8082/openjdk:11-arm64
# author
MAINTAINER xjrsoft
# 挂载目录
VOLUME /home/xjrsoft
# 创建目录
RUN mkdir -p /home/xjrsoft
# 指定路径
WORKDIR /home/xjrsoft
# 复制jar文件到路径
RUN ls
COPY ./target/xjrsoft-service-workflow.jar /home/xjrsoft/xjrsoft-service-workflow.jar
# 启动认证服务
ENTRYPOINT ["java","-Dfile.encoding=utf-8","--add-opens","java.base/java.lang.reflect=ALL-UNNAMED","-jar","xjrsoft-service-workflow.jar"]

View File

@ -0,0 +1,119 @@
<?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>xjrsoft-service</artifactId>
<groupId>com.xjrsoft</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>xjrsoft-service-workflow</artifactId>
<properties>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.xjrsoft</groupId>
<artifactId>xjrsoft-service-workflow-api</artifactId>
<version>${xjrsoft.framework.version}</version>
</dependency>
<dependency>
<groupId>com.xjrsoft</groupId>
<artifactId>xjrsoft-common-core</artifactId>
<version>${xjrsoft.framework.version}</version>
</dependency>
<dependency>
<groupId>com.xjrsoft</groupId>
<artifactId>xjrsoft-common-mybatis</artifactId>
<version>${xjrsoft.framework.version}</version>
</dependency>
<dependency>
<groupId>com.xjrsoft</groupId>
<artifactId>xjrsoft-service-form-api</artifactId>
<version>${xjrsoft.framework.version}</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.xjrsoft</groupId>-->
<!-- <artifactId>xjrsoft-service-organization-api</artifactId>-->
<!-- <version>${xjrsoft.framework.version}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>com.xjrsoft</groupId>-->
<!-- <artifactId>xjrsoft-service-system-api</artifactId>-->
<!-- <version>${xjrsoft.framework.version}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.xjrsoft</groupId>
<artifactId>xjrsoft-service-app-api</artifactId>
<version>${xjrsoft.framework.version}</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.xjrsoft</groupId>-->
<!-- <artifactId>xjrsoft-service-magicapi-api</artifactId>-->
<!-- <version>${xjrsoft.framework.version}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.ssssssss</groupId>-->
<!-- <artifactId>magic-api-spring-boot-starter</artifactId>-->
<!-- </dependency>-->
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,23 @@
package com.xjrsoft.workflow;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
/**
* @Author: tzx
* @Date: 2023/10/18 10:38
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.xjrsoft")
@MapperScan(value = "com.xjrsoft.**.mapper")
@ComponentScan(value = "com.xjrsoft")
public class WorkflowApplication {
public static void main(String[] args) {
SpringApplication.run(WorkflowApplication.class, args);
}
}

View File

@ -0,0 +1,42 @@
package com.xjrsoft.workflow.client;
import cn.hutool.core.bean.BeanUtil;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.workflow.vo.HistoryProcessInstanceVo;
import io.swagger.v3.oas.annotations.Hidden;
import lombok.AllArgsConstructor;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.history.HistoricProcessInstance;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @Author: tzx
* @Date: 2023/10/28 20:08
*/
@Hidden
@RestController
@AllArgsConstructor
public class HistoryClient implements IHistoryClient {
private final HistoryService historyService;
@Override
@GetMapping(GlobalConstant.CLIENT_API_PRE + GlobalConstant.MODULE_WORKFLOW_NAME + "/getProcessInstanceListFeign")
public List<HistoryProcessInstanceVo> getProcessInstanceListFeign(Set<String> processIds) {
List<HistoricProcessInstance> processInstanceList;
if (processIds.size() == 0) {
processInstanceList = new ArrayList<>();
} else {
processInstanceList = historyService.createHistoricProcessInstanceQuery().processInstanceIds(processIds).list();
}
return BeanUtil.copyToList(processInstanceList, HistoryProcessInstanceVo.class);
}
}

View File

@ -0,0 +1,52 @@
package com.xjrsoft.workflow.client;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.common.core.enums.WorkflowIsRecycleType;
import com.xjrsoft.common.core.enums.YesOrNoEnum;
import com.xjrsoft.workflow.constant.WorkflowConstant;
import com.xjrsoft.workflow.vo.TaskInstanceVo;
import io.swagger.v3.oas.annotations.Hidden;
import lombok.AllArgsConstructor;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.task.Task;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: tzx
* @Date: 2023/10/28 20:24
*/
@Hidden
@RestController
@AllArgsConstructor
public class TaskClient implements ITaskClient {
private final TaskService taskService;
@Override
@GetMapping(GlobalConstant.CLIENT_API_PRE + GlobalConstant.MODULE_WORKFLOW_NAME + "/getActiveTaskInstanceListFeign")
public List<TaskInstanceVo> getActiveTaskInstanceListFeign(String[] processIdArrays) {
List<Task> taskList;
if (processIdArrays.length > 0) {
taskList = taskService.createTaskQuery()
.active()
.processInstanceIdIn(processIdArrays)
.taskVariableValueEquals(WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode())
.processVariableValueEquals(WorkflowConstant.PROCESS_ISRECYCLE_FLAG_KEY, WorkflowIsRecycleType.NO.getCode())
.taskVariableValueLike(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StringPool.PERCENT + StpUtil.getLoginIdAsString() + StringPool.PERCENT)
.list();
} else {
taskList = new ArrayList<>();
}
return BeanUtil.copyToList(taskList, TaskInstanceVo.class);
}
}

View File

@ -0,0 +1,28 @@
package com.xjrsoft.workflow.client;
import com.xjrsoft.workflow.dto.PendingTaskDto;
import com.xjrsoft.workflow.service.IWorkflowExecuteService;
import com.xjrsoft.workflow.vo.HistoryProcessInstanceVo;
import com.xjrsoft.workflow.vo.PendingTaskVo;
import io.swagger.v3.oas.annotations.Hidden;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author yjw
* @createDate 2024-07-10
*/
@Hidden
@RestController
@AllArgsConstructor
public class WorkflowExecuteClient implements IWorkflowExecuteClient{
private final IWorkflowExecuteService workflowExecuteService;
@Override
public List<PendingTaskVo> getPendingListFeign(PendingTaskDto pendingTaskDto) {
return workflowExecuteService.pending(pendingTaskDto);
}
}

View File

@ -0,0 +1,47 @@
package com.xjrsoft.workflow.client;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.workflow.entity.WorkflowFormRelation;
import com.xjrsoft.workflow.service.IWorkflowFormRelationService;
import io.swagger.v3.oas.annotations.Hidden;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @Author: tzx
* @Date: 2023/10/19 16:18
*/
@Hidden
@RestController
@AllArgsConstructor
public class WorkflowFormRelationClient implements IWorkflowFormRelationClient {
private final IWorkflowFormRelationService workflowFormRelationService;
/**
* 表单id 获取工作流关联
* @param formId
* @param formKeyValue
* @return
*/
@GetMapping(GlobalConstant.CLIENT_API_PRE + GlobalConstant.MODULE_WORKFLOW_NAME + "/getWorkflowFormRelationFeign")
@Override
public List<WorkflowFormRelation> getWorkflowFormRelationFeign(Long formId, String formKeyValue) {
return workflowFormRelationService.list(Wrappers.lambdaQuery(WorkflowFormRelation.class)
.eq(WorkflowFormRelation::getFormId, formId).eq(WorkflowFormRelation::getFormKeyValue, formKeyValue));
}
@Override
@GetMapping(GlobalConstant.CLIENT_API_PRE + GlobalConstant.MODULE_WORKFLOW_NAME + "/getWorkflowFormRelationBatchFeign")
public List<WorkflowFormRelation> getWorkflowFormRelationBatchFeign(Long formId, List<Object> formKeyValues) {
return workflowFormRelationService.list(Wrappers.lambdaQuery(WorkflowFormRelation.class).eq(WorkflowFormRelation::getFormId, formId).in(WorkflowFormRelation::getFormKeyValue, formKeyValues).select(WorkflowFormRelation::getProcessId, WorkflowFormRelation::getFormKeyValue));
}
}

View File

@ -0,0 +1,28 @@
package com.xjrsoft.workflow.client;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.workflow.entity.WorkflowSchema;
import com.xjrsoft.workflow.service.IWorkflowSchemaService;
import io.swagger.v3.oas.annotations.Hidden;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author: tzx
* @Date: 2023/10/28 19:59
*/
@Hidden
@RestController
@AllArgsConstructor
public class WorkflowSchemaClient implements IWorkflowSchemaClient {
private final IWorkflowSchemaService workflowSchemaService;
@Override
@GetMapping(GlobalConstant.CLIENT_API_PRE + GlobalConstant.MODULE_WORKFLOW_NAME + "/getWorkflowSchemaByFormIdFeign")
public WorkflowSchema getWorkflowSchemaByFormIdFeign(Long formId) {
return workflowSchemaService.getOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getFormId, formId).select(WorkflowSchema::getId));
}
}

View File

@ -0,0 +1,19 @@
package com.xjrsoft.workflow.controller;
import com.xjrsoft.common.core.constant.GlobalConstant;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* <p>
* 前端控制器
* </p>
*
* @author tzx
* @since 2023-02-10
*/
@Controller
@RequestMapping(GlobalConstant.WORKFLOW_MODULE_PREFIX + "/workflowApproveRecord")
public class WorkflowApproveRecordController {
}

View File

@ -0,0 +1,19 @@
package com.xjrsoft.workflow.controller;
import com.xjrsoft.common.core.constant.GlobalConstant;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* <p>
* 流程传阅信息表 前端控制器
* </p>
*
* @author tzx
* @since 2023-02-09
*/
@Controller
@RequestMapping(GlobalConstant.WORKFLOW_MODULE_PREFIX + "/workflowCirculated")
public class WorkflowCirculatedController {
}

View File

@ -0,0 +1,63 @@
package com.xjrsoft.workflow.controller;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.common.core.domain.result.R;
import com.xjrsoft.workflow.dto.AddDelegateDto;
import com.xjrsoft.workflow.dto.DelegatePageDto;
import com.xjrsoft.workflow.dto.UpdateDelegateDto;
import com.xjrsoft.workflow.service.IWorkflowDelegateService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
/**
* <p>
* 流程委托 前端控制器
* </p>
*
* @author tzx
* @since 2022-11-05
*/
@RestController
@RequestMapping(GlobalConstant.WORKFLOW_MODULE_PREFIX + "/delegate")
@Tag(name = GlobalConstant.WORKFLOW_MODULE_PREFIX + "/delegate", description = "流程委托接口")
@AllArgsConstructor
public class WorkflowDelegateController {
private final IWorkflowDelegateService workflowDelegateService;
@GetMapping("/page")
@Operation(summary = "流程委托(分页)")
public R page(@Valid DelegatePageDto dto) {
return R.ok(workflowDelegateService.page(dto));
}
@PostMapping
@Operation(summary = "新增流程委托")
public R add(@Valid @RequestBody AddDelegateDto dto){
return R.ok(workflowDelegateService.add(dto));
}
@PutMapping
@Operation(summary = "修改流程委托")
public R update(@Valid @RequestBody UpdateDelegateDto dto){
return R.ok(workflowDelegateService.update(dto));
}
@DeleteMapping
@Operation(summary = "删除流程委托")
public R delete(@Valid @RequestBody List<Long> ids){
return R.ok(workflowDelegateService.delete(ids));
}
@GetMapping("/info")
@Operation(summary = "获取流程委托信息")
public R info(@RequestParam Long id){
return R.ok(workflowDelegateService.getById(id));
}
}

View File

@ -0,0 +1,338 @@
package com.xjrsoft.workflow.controller;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.common.core.domain.result.R;
import com.xjrsoft.workflow.dto.*;
import com.xjrsoft.workflow.service.IWorkflowExecuteService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
/**
* 工作流操作接口
*
* @Author: tzx
* @Date: 2022/9/8 14:15
*/
@RestController
@RequestMapping(GlobalConstant.WORKFLOW_MODULE_PREFIX + "/execute")
@Tag(name = GlobalConstant.WORKFLOW_MODULE_PREFIX + "/execute", description = "工作流操作接口")
@AllArgsConstructor
public class WorkflowExecuteController {
private final IWorkflowExecuteService workflowExecuteService;
@PostMapping("/deploy")
@Operation(summary = "部署流程")
public R deploy(@Valid @RequestBody DeployDto dto) {
return R.ok(workflowExecuteService.deploy(dto));
}
@GetMapping("/preview")
@Operation(summary = "预览流程")
public R preview(@RequestParam Long schemaId) {
return R.ok(workflowExecuteService.preview(schemaId));
}
@GetMapping("/start-process-info")
@Operation(summary = "发起流程所需要的信息")
public R startProcessInfo(@RequestParam Long schemaId) {
return R.ok(workflowExecuteService.getStartProcessInfo(schemaId));
}
@GetMapping("/approve-process-info")
@Operation(summary = "审批流程所需要的信息")
public R approveProcessInfo(@RequestParam String taskId) {
return R.ok(workflowExecuteService.getApproveProcessInfo(taskId));
}
@GetMapping("/view-process-info")
@Operation(summary = "查看流程所需要的信息processId获取")
public R approveProcessInfoByProcessId(@RequestParam String processId) {
return R.ok(workflowExecuteService.getApproveProcessInfoByProcessId(processId));
}
@GetMapping("/process/all-record")
@Operation(summary = "查看流程所需要的所有流转信息processId获取包括父子流程")
public R allRecordInfoByProcessId(@RequestParam String processId,@RequestParam(required = false,defaultValue = "1") Integer onlySelf) {
return R.ok(workflowExecuteService.getAllRecordInfoByProcessId(processId,onlySelf));
}
@GetMapping("/recycle-process-info")
@Operation(summary = "获取回收站的数据流程数据")
public R recycleProcessInfo(@RequestParam String processId) {
return R.ok(workflowExecuteService.getRecycleProcessInfo(processId));
}
@PostMapping("/new-launch")
@Operation(summary = "重构后发起流程")
public R newLaunch(@Valid @RequestBody LaunchDto dto) {
return R.ok(workflowExecuteService.newLaunch(dto));
}
@PostMapping("/relaunch")
@Operation(summary = "重新发起")
public R reLaunch(@Valid @RequestBody ReLaunchDto dto) {
return R.ok(workflowExecuteService.reLaunch(dto));
}
@GetMapping("/pending")
@Operation(summary = "待办任务")
public R pending(@Valid PendingTaskPageDto dto) {
return R.ok(workflowExecuteService.pending(dto));
}
@GetMapping("/task-info")
@Operation(summary = "查询任务详情")
public R taskInfo(@RequestParam String taskId) {
return R.ok(workflowExecuteService.getTaskInfo(taskId));
}
@GetMapping("/process-info")
@Operation(summary = "查询流程详情")
public R processInfo(@RequestParam String processId) {
return R.ok(workflowExecuteService.getProcessInfo(processId));
}
@GetMapping("/circulated-task-info")
@Operation(summary = "查询传阅任务详情")
public R circulatedTaskInfo(@RequestParam String taskId) {
return R.ok(workflowExecuteService.getCirculatedTaskInfo(taskId));
}
@GetMapping("/process/finished-task")
@Operation(summary = "查询当前流程已完成的任务")
public R finishedTask(@RequestParam String processInstanceId){
return R.ok(workflowExecuteService.getFinishedTask(processInstanceId));
}
@GetMapping("/process/record")
@Operation(summary = "查询当前流程所有流转记录")
public R processRecord(@RequestParam String processInstanceId){
return R.ok(workflowExecuteService.getProcessRecord(processInstanceId));
}
@PostMapping("/new-approve")
@Operation(summary = "重构后发审批")
public R newApprove(@Valid @RequestBody ApproveDto dto) {
return R.ok(workflowExecuteService.newApprove(dto));
}
@GetMapping("/approve/multi-info")
@Operation(summary = "批量审批获取流程信息")
public R approveMultiInfo(@Valid ApproveMultiInfoDto dto) {
return R.ok(workflowExecuteService.approveMultiInfo(dto));
}
@PostMapping("/approve/multi")
@Operation(summary = "批量审批")
public R approveMulti(@Valid @RequestBody ApproveMultiDto dto) {
return R.ok(workflowExecuteService.approveMulti(dto));
}
@PostMapping("/getNextTaskMaybeArrival/{taskId}")
@Operation(summary = "获取下一步可能到达的任务节点,包括节点可选人")
@SneakyThrows
public R getNextTaskMaybeArrival(@PathVariable String taskId,@RequestBody JSONObject parameter){
Set<HashMap<String, Object>> result = workflowExecuteService.getNextUserTaskNodeMaybeArrival(taskId, parameter);
return R.ok(result);
}
@PostMapping("/set-approve")
@Operation(summary = "指定下一节点审批人(覆盖)")
public R setApproveUser(@Valid @RequestBody ApproveUserDto dto) {
return R.ok(workflowExecuteService.setApproveUser(dto));
}
@PostMapping("/set-approve-multi")
@Operation(summary = "指定下一节点审批人(批量覆盖)")
public R setApproveUserMulti(@Valid @RequestBody ApproveUserMultiDto dto) {
return R.ok(workflowExecuteService.setApproveUserMulti(dto));
}
@GetMapping("/relation-task/page")
@Operation(summary = "查询流程模板关联的任务分页")
public R relationTaskPage(@Valid RelationTaskPageDto dto) {
return R.ok(workflowExecuteService.getRelationTaskPage(dto));
}
@GetMapping("/relation-task/info")
@Operation(summary = "查询流程模板关联的任务 详情")
public R relationTaskPage(@Valid RelationTaskInfoDto dto) {
return R.ok(workflowExecuteService.getRelationTaskInfo(dto));
}
@GetMapping("/circulated/page")
@Operation(summary = "查询我的传阅")
public R circulatedTaskPage(@Valid CirculatedTaskPageDto dto) {
return R.ok(workflowExecuteService.getCirculatedTaskPage(dto));
}
@GetMapping("/finished/page")
@Operation(summary = "查询我的已办任务分页")
public R finishedTaskPage(@Valid FinishedTaskPageDto dto) {
return R.ok(workflowExecuteService.getFinishedTaskPage(dto));
}
@GetMapping("/my-process/page")
@Operation(summary = "查询我的流程")
public R myProcessPage(@Valid MyProcessPageDto dto) {
return R.ok(workflowExecuteService.getMyProcessPage(dto));
}
@PostMapping("/my-process/move-recycle")
@Operation(summary = "我的流程 移入回收站")
public R moveRecycle(@Valid @RequestBody MoveRecycleDto dto) {
return R.ok(workflowExecuteService.moveRecycle(dto));
}
@GetMapping("/my-process/recycle/page")
@Operation(summary = "回收站列表")
public R recycleProcessPage(@Valid RecycleProcessPageDto dto) {
return R.ok(workflowExecuteService.getRecycleProcessPage(dto));
}
@DeleteMapping("/my-process/recycle")
@Operation(summary = "回收站 删除")
public R recycleDelete(@Valid @RequestBody RecycleDeleteDto dto) {
return R.ok(workflowExecuteService.recycleDelete(dto));
}
@PostMapping("/my-process/recycle/restart")
@Operation(summary = "回收站 重新发起")
public R restart(@Valid @RequestBody RestartDto dto) {
return R.ok(workflowExecuteService.restart(dto));
}
@GetMapping("/my-task/history-task")
@Operation(summary = "我的流程 已经完成的任务(不包括 外部流程 子流程 会签流程)")
public R historyTask(@RequestParam String schemaId, @RequestParam String processInstanceId) {
return R.ok(workflowExecuteService.getHistoryTask(schemaId, processInstanceId));
}
@PostMapping("/my-task/withdraw")
@Operation(summary = "我的流程 撤回")
public R withdraw(@Valid @RequestBody WithdrawDto dto) {
return R.ok(workflowExecuteService.withdraw(dto));
}
@PostMapping("/draft")
@Operation(summary = "保存草稿")
public R addDraft(@Valid @RequestBody SaveDraftDto dto) throws JsonProcessingException {
return R.ok(workflowExecuteService.saveDraft(dto));
}
@PutMapping("/draft")
@Operation(summary = "修改草稿")
public R updateDraft(@Valid @RequestBody UpdateDraftDto dto) {
return R.ok(workflowExecuteService.updateDraft(dto));
}
@GetMapping("/draft/page")
@Operation(summary = "查询草稿列表分页")
public R draftPage(@Valid DraftPageDto dto) {
return R.ok(workflowExecuteService.draftPage(dto));
}
@GetMapping("/draft/info")
@Operation(summary = "查询草稿详情")
public R draftInfo(@RequestParam Long id) {
return R.ok(workflowExecuteService.draftInfo(id));
}
@DeleteMapping("/draft")
@Operation(summary = "删除草稿")
public R deleteDraft(@RequestBody List<Long> ids) {
return R.ok(workflowExecuteService.deleteDraft(ids));
}
@GetMapping("/process-monitor/page")
@Operation(summary = "流程监控分页列表")
public R processMonitorPage(@Valid MonitorPageDto dto) {
return R.ok(workflowExecuteService.getProcessMonitorPage(dto));
}
@DeleteMapping("/process-monitor/delete")
@Operation(summary = "删除流程(流程监控使用)")
public R deleteProcessMonitor(@RequestBody DeleteMonitorDto dto) {
return R.ok(workflowExecuteService.deleteProcessMonitor(dto));
}
@PostMapping("/set-assignee")
@Operation(summary = "指派审核人(给任务添加审批人)")
public R setAssignee(@Valid @RequestBody SetAssigneeDto dto) {
return R.ok(workflowExecuteService.setAssignee(dto));
}
@PostMapping("/set-suspended")
@Operation(summary = "将流程挂起")
public R setSuspended(@Valid @RequestBody SetSuspendedDto dto) {
return R.ok(workflowExecuteService.setSuspended(dto));
}
@GetMapping("/approve-user")
@Operation(summary = "获取节点审批人(获取下一节点审批人也是这个接口)")
public R getAssignee(@Valid GetAssigneeDto dto){
return R.ok(workflowExecuteService.getAssignee(dto));
}
@PostMapping("/set-sign")
@Operation(summary = "加签/减签")
public R addOrSubSign(@Valid @RequestBody AddOrSubSignDto dto) {
return R.ok(workflowExecuteService.addOrSubSign(dto));
}
@GetMapping("/reject-node")
@Operation(summary = "获取可以驳回的节点")
public R rejectNode(@Valid RejectNodeDto dto){
return R.ok(workflowExecuteService.getRejectNode(dto));
}
@GetMapping("/withdraw-node")
@Operation(summary = "获取可以撤回的节点")
public R withdrawNode(@Valid WithdrawNodeDto dto){
return R.ok(workflowExecuteService.getProcessTaskCanWithdraw(dto));
}
@PostMapping("/transfer")
@Operation(summary = "转办")
public R transfer(@Valid @RequestBody TransferDto dto){
return R.ok(workflowExecuteService.transfer(dto));
}
@GetMapping("/process/form-finished-task")
@Operation(summary = "根据formId 查询流程图 以及 当前流程已完成的任务")
public R formFinishedTask(FormFinishedTaskDto dto){
return R.ok(workflowExecuteService.getFormFinishedTask(dto));
}
@GetMapping("/count")
@Operation(summary = "合计")
public R count() {
return R.ok(workflowExecuteService.getCount());
}
}

View File

@ -0,0 +1,19 @@
package com.xjrsoft.workflow.controller;
import com.xjrsoft.common.core.constant.GlobalConstant;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* <p>
* 前端控制器
* </p>
*
* @author tzx
* @since 2022-10-13
*/
@Controller
@RequestMapping(GlobalConstant.WORKFLOW_MODULE_PREFIX + "/workflowExtra")
public class WorkflowExtraController {
}

View File

@ -0,0 +1,18 @@
package com.xjrsoft.workflow.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* <p>
* 工作流 流程 与 表单关联表 前端控制器
* </p>
*
* @author tzx
* @since 2023-03-27
*/
@Controller
@RequestMapping("/workflow/workflowFormRelation")
public class WorkflowFormRelationController {
}

View File

@ -0,0 +1,61 @@
package com.xjrsoft.workflow.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.common.core.domain.result.R;
import com.xjrsoft.workflow.entity.WorkflowRecord;
import com.xjrsoft.workflow.service.IWorkflowRecordService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.history.HistoricProcessInstance;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
/**
* <p>
* 流程记录前端控制器
* </p>
*
* @author tzx
* @since 2022-10-13
*/
@RestController
@RequestMapping(GlobalConstant.WORKFLOW_MODULE_PREFIX + "/record")
@Tag(name = GlobalConstant.WORKFLOW_MODULE_PREFIX + "/record", description = "流程记录接口")
@AllArgsConstructor
public class WorkflowRecordController {
private final IWorkflowRecordService workflowRecordService;
private final HistoryService historyService;
/**
* 数据库存储xml字段的后缀
*/
private static final String DB_FIELD_XML_PREFIX = "xml";
@GetMapping("/list")
@Operation(summary = "查询整个流程的流程记录")
public R list(@RequestParam String processInstanceId){
List<HistoricProcessInstance> subProcess = historyService.createHistoricProcessInstanceQuery()
.superProcessInstanceId(processInstanceId).list();
List<String> subProcessIds = subProcess.stream().map(HistoricProcessInstance::getId).collect(Collectors.toList());
subProcessIds.add(processInstanceId);
LambdaQueryWrapper<WorkflowRecord> queryWrapper = Wrappers.lambdaQuery(WorkflowRecord.class).in(WorkflowRecord::getProcessId, subProcessIds).orderByDesc(WorkflowRecord::getId);
return R.ok(workflowRecordService.list(queryWrapper));
}
}

View File

@ -0,0 +1,23 @@
package com.xjrsoft.workflow.controller;
import com.xjrsoft.common.core.constant.GlobalConstant;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 流程模板权限 前端控制器
* </p>
*
* @author zwq
* @since 2022-07-04
*/
@RestController
@RequestMapping(GlobalConstant.WORKFLOW_MODULE_PREFIX + "/schema-auth")
@Tag(name = GlobalConstant.WORKFLOW_MODULE_PREFIX + "/schema-auth", description = "流程模板权限")
@AllArgsConstructor
public class WorkflowSchemaAuthController {
}

View File

@ -0,0 +1,144 @@
package com.xjrsoft.workflow.controller;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.common.core.domain.result.R;
import com.xjrsoft.common.core.enums.EnabledMark;
import com.xjrsoft.workflow.dto.*;
import com.xjrsoft.workflow.entity.WorkflowSchema;
import com.xjrsoft.workflow.model.WorkflowSchemaConfig;
import com.xjrsoft.workflow.service.IWorkflowSchemaService;
import com.xjrsoft.workflow.vo.WorkflowSchemaInfoVo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.Valid;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* <p>
* 流程模板表 前端控制器
* </p>
*
* @author zwq
* @since 2022-07-04
*/
@RestController
@RequestMapping(GlobalConstant.WORKFLOW_MODULE_PREFIX + "/schema")
@Tag(name = GlobalConstant.WORKFLOW_MODULE_PREFIX + "/schema", description = "流程模板")
@AllArgsConstructor
public class WorkflowSchemaController {
private final IWorkflowSchemaService workflowSchemaService;
@GetMapping("/page")
@Operation(summary = "流程列表(分页)")
public R page(@Valid WorkflowSchemaPageDto dto) {
return R.ok(workflowSchemaService.getPage(dto));
}
@PostMapping
@Operation(summary = "新增流程模板")
public R add(@Valid @RequestBody AddWorkflowSchemaDto dto) {
return R.ok(workflowSchemaService.add(dto));
}
@PostMapping("/draft")
@Operation(summary = "新增流程模板草稿")
public R draft(@Valid @RequestBody AddWorkflowSchemaDraftDto dto) {
return R.ok(workflowSchemaService.addDraft(dto));
}
@GetMapping("/draft")
@Operation(summary = "流程模板草稿")
public R draftPage(@Valid WorkflowSchemaDraftPageDto dto) {
return R.ok(workflowSchemaService.getDraftPage(dto));
}
@PutMapping
@Operation(summary = "修改流程模板")
public R update(@Valid @RequestBody UpdateWorkflowSchemaDto dto) {
return R.ok(workflowSchemaService.update(dto));
}
@GetMapping("/info")
@Operation(summary = "获取流程详细信息")
public R info(@RequestParam Long id) {
WorkflowSchema workflowSchema = workflowSchemaService.getById(id);
if (workflowSchema == null) {
return R.error("找不到此模板信息!");
}
return R.ok(BeanUtil.toBean(workflowSchema, WorkflowSchemaInfoVo.class));
}
@GetMapping("/multi/info")
@Operation(summary = "获取批量流程详细信息")
public R multiInfo(@RequestParam String ids) {
List<String> idList = Arrays.stream(ids.split(StringPool.COMMA)).collect(Collectors.toList());
List<WorkflowSchema> workflowSchemas = workflowSchemaService.listByIds(idList);
return R.ok(BeanUtil.copyToList(workflowSchemas, WorkflowSchemaInfoVo.class));
}
@DeleteMapping
@Operation(summary = "删除模板(可批量)")
public R delete(@RequestBody List<Long> ids) {
return R.ok(workflowSchemaService.delete(ids));
}
@PutMapping("/enabled")
@Operation(summary = "流程启用")
public R enabled(@Valid @RequestBody EnabledDto dto) {
WorkflowSchema schema = new WorkflowSchema();
schema.setId(dto.getSchemaId());
schema.setEnabledMark(EnabledMark.ENABLED.getCode());
return R.ok(workflowSchemaService.updateById(schema));
}
@PutMapping("/disabled")
@Operation(summary = "流程禁用")
public R disabled(@Valid @RequestBody EnabledDto dto) {
WorkflowSchema schema = new WorkflowSchema();
schema.setId(dto.getSchemaId());
schema.setEnabledMark(EnabledMark.DISABLED.getCode());
return R.ok(workflowSchemaService.updateById(schema));
}
@GetMapping("/export")
@Operation(summary = "导出流程")
@SneakyThrows
public R export(@RequestParam Long id) {
WorkflowSchema workflowSchema = workflowSchemaService.getById(id);
if (workflowSchema == null) {
return R.error("找不到此模板信息!");
}
WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);
workflowSchemaConfig.getProcessConfig().setXmlContent(workflowSchema.getXmlContent());
return R.ok(JSONUtil.toJsonStr(workflowSchemaConfig));
}
@PostMapping("/import")
@Operation(summary = "导入流程")
@SneakyThrows
public R importSchema(@RequestParam(value = "file") MultipartFile multipartFile) {
return R.ok(workflowSchemaService.importSchema(multipartFile));
}
}

View File

@ -0,0 +1,75 @@
package com.xjrsoft.workflow.controller;
import cn.hutool.core.bean.BeanUtil;
import com.github.yulichang.toolkit.MPJWrappers;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.common.core.domain.result.R;
import com.xjrsoft.common.core.uitls.VoToColumnUtil;
import com.xjrsoft.organization.entity.User;
import com.xjrsoft.workflow.dto.HistoryChangeDto;
import com.xjrsoft.workflow.entity.WorkflowSchemaHistory;
import com.xjrsoft.workflow.service.IWorkflowSchemaHistoryService;
import com.xjrsoft.workflow.vo.SchemaHistoryListVo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
/**
* <p>
* 流程模板历史记录表 前端控制器
* </p>
*
* @author tzx
* @since 2022-11-16
*/
@RestController
@RequestMapping(GlobalConstant.WORKFLOW_MODULE_PREFIX + "/schema-history")
@Tag(name = GlobalConstant.WORKFLOW_MODULE_PREFIX + "/schema-history", description = "流程模板历史记录接口")
@AllArgsConstructor
public class WorkflowSchemaHistoryController {
private final IWorkflowSchemaHistoryService workflowSchemaHistoryService;
@GetMapping("/list")
@Operation(summary = "流程列表(分页)")
public R list(@RequestParam Long schemaId) {
List<SchemaHistoryListVo> schemaHistoryListVos = workflowSchemaHistoryService.selectJoinList(SchemaHistoryListVo.class,
MPJWrappers.<WorkflowSchemaHistory>lambdaJoin()
.eq(WorkflowSchemaHistory::getSchemaId, schemaId)
.select(WorkflowSchemaHistory::getId)
.select(WorkflowSchemaHistory.class, x -> VoToColumnUtil.fieldsToColumns(SchemaHistoryListVo.class).contains(x.getProperty()))
.selectAs(User::getName, SchemaHistoryListVo::getCreateUserName)
.leftJoin(User.class, User::getId, WorkflowSchemaHistory::getCreateUserId)
.orderByAsc(WorkflowSchemaHistory::getVersion)
);
List<SchemaHistoryListVo> listVos = BeanUtil.copyToList(schemaHistoryListVos, SchemaHistoryListVo.class);
return R.ok(listVos);
}
@PutMapping("/set-current")
@Operation(summary = "更新到此版本")
public R change(@Valid @RequestBody HistoryChangeDto dto) {
return R.ok(workflowSchemaHistoryService.change(dto));
}
@PutMapping("/processChanges")
@Operation(summary = "新流程变更")
public R processChanges(@Valid @RequestBody HistoryChangeDto dto) {
return R.ok(workflowSchemaHistoryService.change(dto));
}
@GetMapping("/preview")
@Operation(summary = "历史记录的xml")
public R preview(@RequestParam String historyId){
WorkflowSchemaHistory history = workflowSchemaHistoryService.getById(historyId);
return R.ok(history.getXmlContent());
}
}

View File

@ -0,0 +1,96 @@
package com.xjrsoft.workflow.controller;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.yulichang.toolkit.MPJWrappers;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.common.core.domain.page.PageOutput;
import com.xjrsoft.common.core.domain.result.R;
import com.xjrsoft.common.core.uitls.VoToColumnUtil;
import com.xjrsoft.common.mybatis.utils.ConventPage;
import com.xjrsoft.system.entity.Menu;
import com.xjrsoft.workflow.dto.AddSpecialMenuDto;
import com.xjrsoft.workflow.dto.SpecialMenuPageDto;
import com.xjrsoft.workflow.dto.UpdateSpecialMenuDto;
import com.xjrsoft.workflow.entity.WorkflowSpecialMenu;
import com.xjrsoft.workflow.service.IWorkflowSpecialMenuService;
import com.xjrsoft.workflow.vo.SpecialMenuPageVo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
/**
* <p>
* 专项菜单表 前端控制器
* </p>
*
* @author tzx
* @since 2023-03-20
*/
@RestController
@RequestMapping(GlobalConstant.WORKFLOW_MODULE_PREFIX + "/special-menu")
@Tag(name = GlobalConstant.WORKFLOW_MODULE_PREFIX + "/special-menu", description = "流程专项菜单接口")
@AllArgsConstructor
public class WorkflowSpecialMenuController {
private final IWorkflowSpecialMenuService specialMenuService;
@GetMapping("/page")
@Operation(summary = "专项菜单(分页)")
public R page(SpecialMenuPageDto dto){
//因为多表关联 会有多个表都使用了id字段 所以必须专门指定主表的Id
IPage<SpecialMenuPageVo> page = specialMenuService.selectJoinListPage(ConventPage.getPage(dto), SpecialMenuPageVo.class,
MPJWrappers.<WorkflowSpecialMenu>lambdaJoin()
.orderByDesc(WorkflowSpecialMenu::getId)
.like(StrUtil.isNotBlank(dto.getKeyword()), SpecialMenuPageVo::getName, dto.getKeyword())
.or()
.like(StrUtil.isNotBlank(dto.getKeyword()), SpecialMenuPageVo::getCode, dto.getKeyword())
.or()
.like(StrUtil.isNotBlank(dto.getKeyword()), SpecialMenuPageVo::getMenuCode, dto.getKeyword())
.or()
.like(StrUtil.isNotBlank(dto.getKeyword()), SpecialMenuPageVo::getMenuName, dto.getKeyword())
.select(WorkflowSpecialMenu::getId)
.select(WorkflowSpecialMenu.class, x -> VoToColumnUtil.fieldsToColumns(SpecialMenuPageVo.class).contains(x.getProperty()))
.selectAs(Menu::getName, SpecialMenuPageVo::getMenuName)
.selectAs(Menu::getCode, SpecialMenuPageVo::getMenuCode)
.leftJoin(Menu.class, Menu::getId, WorkflowSpecialMenu::getMenuId));
PageOutput<SpecialMenuPageVo> pageOutput = ConventPage.getPageOutput(page);
return R.ok(pageOutput);
}
@GetMapping("/info")
@Operation(summary = "查询单个详情")
public R info(@RequestParam Long id) {
return R.ok(specialMenuService.info(id));
}
@PostMapping
@Operation(summary = "新增专项菜单")
public R add(@Valid @RequestBody AddSpecialMenuDto dto) {
return R.ok(specialMenuService.add(dto));
}
@PutMapping
@Operation(summary = "修改专项菜单")
public R update(@Valid @RequestBody UpdateSpecialMenuDto dto) {
return R.ok(specialMenuService.update(dto));
}
@DeleteMapping
@Operation(summary = "删除专项菜单(可批量)")
public R delete(@RequestBody List<Long> ids) {
return R.ok(specialMenuService.delete(ids));
}
@GetMapping("/pending")
@Operation(summary = "待办任务")
public R pending(Map<String,Object> param) {
return R.ok(specialMenuService.pending(param));
}
}

View File

@ -0,0 +1,481 @@
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
* under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. Camunda licenses this file to you under the Apache License,
* Version 2.0; you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.camunda.bpm.engine.history;
import org.camunda.bpm.engine.ProcessEngineException;
import org.camunda.bpm.engine.query.Query;
import org.camunda.bpm.engine.task.Task;
import java.io.Serializable;
import java.util.Date;
/**
* Allows programmatic querying for {@link HistoricTaskInstance}s.
*
* @author Tom Baeyens
*/
public interface HistoricTaskInstanceQuery extends Query<HistoricTaskInstanceQuery, HistoricTaskInstance> {
/** Only select historic task instances for the given task id. */
HistoricTaskInstanceQuery taskId(String taskId);
/** Only select historic task instances for the given process instance. */
HistoricTaskInstanceQuery processInstanceId(String processInstanceId);
/** Only select historic tasks for the given process instance business key */
HistoricTaskInstanceQuery processInstanceBusinessKey(String processInstanceBusinessKey);
/**
* Only select historic tasks for any of the given the given process instance business keys.
*/
HistoricTaskInstanceQuery processInstanceBusinessKeyIn(String... processInstanceBusinessKeys);
/** Only select historic tasks matching the given process instance business key.
* The syntax is that of SQL: for example usage: nameLike(%camunda%)*/
HistoricTaskInstanceQuery processInstanceBusinessKeyLike(String processInstanceBusinessKey);
/** Only select historic task instances for the given execution. */
HistoricTaskInstanceQuery executionId(String executionId);
/** Only select historic task instances which have one of the given activity instance ids. **/
HistoricTaskInstanceQuery activityInstanceIdIn(String... activityInstanceIds);
/** Only select historic task instances for the given process definition. */
HistoricTaskInstanceQuery processDefinitionId(String processDefinitionId);
/**
* Only select historic task instances which are part of a (historic) process instance
* which has the given process definition key.
*/
HistoricTaskInstanceQuery processDefinitionKey(String processDefinitionKey);
/**
* Only select historic task instances which are part of a (historic) process instance
* which has the given definition name.
*/
HistoricTaskInstanceQuery processDefinitionName(String processDefinitionName);
/** Only select historic task instances for the given case definition. */
HistoricTaskInstanceQuery caseDefinitionId(String caseDefinitionId);
/**
* Only select historic task instances which are part of a case instance
* which has the given case definition key.
*/
HistoricTaskInstanceQuery caseDefinitionKey(String caseDefinitionKey);
/**
* Only select historic task instances which are part of a (historic) case instance
* which has the given case definition name.
*/
HistoricTaskInstanceQuery caseDefinitionName(String caseDefinitionName);
/**
* Only select historic task instances for the given case instance.
*/
HistoricTaskInstanceQuery caseInstanceId(String caseInstanceId);
/**
* Only select historic task instances for the given case execution id.
*/
HistoricTaskInstanceQuery caseExecutionId(String caseExecutionId);
/**
* Only select historic task instances with the given task name.
* This is the last name given to the task.
* The query will match the names of historic task instances in a case-insensitive way.
*/
HistoricTaskInstanceQuery taskName(String taskName);
/**
* Only select historic task instances with a task name like the given value.
* This is the last name given to the task.
* The syntax that should be used is the same as in SQL, eg. %activiti%.
* The query will match the names of historic task instances in a case-insensitive way.
*/
HistoricTaskInstanceQuery taskNameLike(String taskNameLike);
/**
* Only select historic task instances with the given task description.
* This is the last description given to the task.
* The query will match the descriptions of historic task instances in a case-insensitive way.
*/
HistoricTaskInstanceQuery taskDescription(String taskDescription);
/**
* Only select historic task instances with a task description like the given value.
* This is the last description given to the task.
* The syntax that should be used is the same as in SQL, eg. %activiti%.
* The query will match the descriptions of historice task instances in a case-insensitive way.
*/
HistoricTaskInstanceQuery taskDescriptionLike(String taskDescriptionLike);
/**
* Only select historic task instances with the given task definition key.
* @see Task#getTaskDefinitionKey()
*/
HistoricTaskInstanceQuery taskDefinitionKey(String taskDefinitionKey);
/**
* Only select historic task instances with one of the given task definition keys.
* @see Task#getTaskDefinitionKey()
*/
HistoricTaskInstanceQuery taskDefinitionKeyIn(String... taskDefinitionKeys);
/** Only select historic task instances with the given task delete reason. */
HistoricTaskInstanceQuery taskDeleteReason(String taskDeleteReason);
/**
* Only select historic task instances with a task description like the given value.
* The syntax that should be used is the same as in SQL, eg. %activiti%.
*/
HistoricTaskInstanceQuery taskDeleteReasonLike(String taskDeleteReasonLike);
/** Only select historic task instances with an assignee. */
HistoricTaskInstanceQuery taskAssigned();
/** Only select historic task instances without an assignee. */
HistoricTaskInstanceQuery taskUnassigned();
/**
* Only select historic task instances which were last taskAssigned to the given assignee.
*/
HistoricTaskInstanceQuery taskAssignee(String taskAssignee);
/**
* Only select historic task instances which were last taskAssigned to an assignee like
* the given value.
* The syntax that should be used is the same as in SQL, eg. %activiti%.
*/
HistoricTaskInstanceQuery taskAssigneeLike(String taskAssigneeLike);
/**
* Only select historic task instances which have the given owner.
*/
HistoricTaskInstanceQuery taskOwner(String taskOwner);
/**
* Only select historic task instances which have an owner like the one specified.
* The syntax that should be used is the same as in SQL, eg. %activiti%.
*/
HistoricTaskInstanceQuery taskOwnerLike(String taskOwnerLike);
/**
* Only select historic task instances with the given priority.
*/
HistoricTaskInstanceQuery taskPriority(Integer taskPriority);
/**
* Only select historic task instances which are finished.
*/
HistoricTaskInstanceQuery finished();
/**
* Only select historic task instances which aren't finished yet.
*/
HistoricTaskInstanceQuery unfinished();
/**
* Only select historic task instances which are part of a process
* instance which is already finished.
*/
HistoricTaskInstanceQuery processFinished();
/**
* Only select historic task instances which are part of a process
* instance which is not finished yet.
*/
HistoricTaskInstanceQuery processUnfinished();
/**
* Only select historic task instances which have mapping
* with Historic identity links based on user id
*
* @since 7.5
*/
HistoricTaskInstanceQuery taskInvolvedUser(String involvedUser);
/**
* Only select historic task instances which have mapping
* with Historic identity links based on group id
*
* @since 7.5
*/
HistoricTaskInstanceQuery taskInvolvedGroup(String involvedGroup);
/**
* Only select historic task instances which have mapping
* with Historic identity links with the condition of user being a candidate
*
* @since 7.5
*/
HistoricTaskInstanceQuery taskHadCandidateUser(String candidateUser);
/**
* Only select historic task instances which have mapping
* with Historic identity links with the condition of group being a candidate
*
* @since 7.5
*/
HistoricTaskInstanceQuery taskHadCandidateGroup(String candidateGroup);
/** Only select historic task instances which have a candidate group */
HistoricTaskInstanceQuery withCandidateGroups();
/** Only select historic task instances which have no candidate group */
HistoricTaskInstanceQuery withoutCandidateGroups();
/**
* The query will match the names of task and process variables in a case-insensitive way.
*/
HistoricTaskInstanceQuery matchVariableNamesIgnoreCase();
/**
* The query will match the values of task and process variables in a case-insensitive way.
*/
HistoricTaskInstanceQuery matchVariableValuesIgnoreCase();
/**
* Only select historic task instances which have a local task variable with the
* given name set to the given value. Make sure history-level is configured
* >= AUDIT when this feature is used.
*/
HistoricTaskInstanceQuery taskVariableValueEquals(String variableName, Object variableValue);
/**
* Only select historic task instances which have a local task variable with the
* given name set to the given value. Make sure history-level is configured
* >= AUDIT when this feature is used.
*/
HistoricTaskInstanceQuery taskVariableValueLike(String variableName, Object variableValue);
/** Only select subtasks of the given parent task */
HistoricTaskInstanceQuery taskParentTaskId(String parentTaskId);
/**
* Only select historic task instances which are part of a process instance which have a variable
* with the given name set to the given value. The last variable value in the variable updates
* ({@link HistoricDetail}) will be used, so make sure history-level is configured
* >= AUDIT when this feature is used.
*/
HistoricTaskInstanceQuery processVariableValueEquals(String variableName, Object variableValue);
/**
* Only select historic task instances which have a variable with the given name, but
* with a different value than the passed value.
* Byte-arrays and {@link Serializable} objects (which are not primitive type wrappers)
* are not supported.
*/
HistoricTaskInstanceQuery processVariableValueNotEquals(String variableName, Object variableValue);
/**
* Only select historic task instances which are part of a process that have a variable
* with the given name and matching the given value.
* The syntax is that of SQL: for example usage: valueLike(%value%)
* */
HistoricTaskInstanceQuery processVariableValueLike(String variableName, Object variableValue);
/**
* Only select historic task instances which are part of a process that have a variable
* with the given name and not matching the given value.
* The syntax is that of SQL: for example usage: valueNotLike(%value%)
* */
HistoricTaskInstanceQuery processVariableValueNotLike(String variableName, Object variableValue);
/**
* Only select historic task instances which are part of a process that have a variable
* with the given name and a value greater than the given one.
*/
HistoricTaskInstanceQuery processVariableValueGreaterThan(String variableName, Object variableValue);
/**
* Only select historic task instances which are part of a process that have a variable
* with the given name and a value greater than or equal to the given one.
*/
HistoricTaskInstanceQuery processVariableValueGreaterThanOrEquals(String variableName, Object variableValue);
/**
* Only select historic task instances which are part of a process that have a variable
* with the given name and a value less than the given one.
*/
HistoricTaskInstanceQuery processVariableValueLessThan(String variableName, Object variableValue);
/**
* Only select historic task instances which are part of a process that have a variable
* with the given name and a value less than or equal to the given one.
*/
HistoricTaskInstanceQuery processVariableValueLessThanOrEquals(String variableName, Object variableValue);
/**
* Only select select historic task instances with the given due date.
*/
HistoricTaskInstanceQuery taskDueDate(Date dueDate);
/**
* Only select select historic task instances which have a due date before the given date.
*/
HistoricTaskInstanceQuery taskDueBefore(Date dueDate);
/**
* Only select select historic task instances which have a due date after the given date.
*/
HistoricTaskInstanceQuery taskDueAfter(Date dueDate);
/**
* Only select select historic task instances that have no due date.
*/
HistoricTaskInstanceQuery withoutTaskDueDate();
/**
* Only select select historic task instances with the given follow-up date.
*/
HistoricTaskInstanceQuery taskFollowUpDate(Date followUpDate);
/**
* Only select select historic task instances which have a follow-up date before the given date.
*/
HistoricTaskInstanceQuery taskFollowUpBefore(Date followUpDate);
/**
* Only select select historic task instances which have a follow-up date after the given date.
*/
HistoricTaskInstanceQuery taskFollowUpAfter(Date followUpDate);
/** Only select historic task instances with one of the given tenant ids. */
HistoricTaskInstanceQuery tenantIdIn(String... tenantIds);
/** Only selects historic task instances that have no tenant id. */
HistoricTaskInstanceQuery withoutTenantId();
/**
* Only select tasks where end time is after given date
*/
HistoricTaskInstanceQuery finishedAfter(Date date);
/**
* Only select tasks where end time is before given date
*/
HistoricTaskInstanceQuery finishedBefore(Date date);
/**
* Only select tasks where started after given date
*/
HistoricTaskInstanceQuery startedAfter(Date date);
/**
* Only select tasks where started before given date
*/
HistoricTaskInstanceQuery startedBefore(Date date);
/**
* Order by tenant id (needs to be followed by {@link #asc()} or {@link #desc()}).
* Note that the ordering of historic task instances without tenant id is database-specific.
*/
HistoricTaskInstanceQuery orderByTenantId();
/** Order by task id (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByTaskId();
/**
* Order by the historic activity instance id this task was used in
* (needs to be followed by {@link #asc()} or {@link #desc()}).
*/
HistoricTaskInstanceQuery orderByHistoricActivityInstanceId();
/** Order by process definition id (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByProcessDefinitionId();
/** Order by process instance id (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByProcessInstanceId();
/** Order by execution id (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByExecutionId();
/** Order by duration (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByHistoricTaskInstanceDuration();
/** Order by end time (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByHistoricTaskInstanceEndTime();
/** Order by start time (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByHistoricActivityInstanceStartTime();
/** Order by task name (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByTaskName();
/** Order by task description (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByTaskDescription();
/** Order by task assignee (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByTaskAssignee();
/** Order by task owner (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByTaskOwner();
/** Order by task due date (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByTaskDueDate();
/** Order by task follow-up date (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByTaskFollowUpDate();
/** Order by task delete reason (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByDeleteReason();
/** Order by task definition key (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByTaskDefinitionKey();
/** Order by task priority key (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByTaskPriority();
/** Order by case definition id (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByCaseDefinitionId();
/** Order by case instance id (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByCaseInstanceId();
/** Order by case execution id (needs to be followed by {@link #asc()} or {@link #desc()}). */
HistoricTaskInstanceQuery orderByCaseExecutionId();
/**
* <p>After calling or(), a chain of several filter criteria could follow. Each filter criterion that follows or()
* will be linked together with an OR expression until the OR query is terminated. To terminate the OR query right
* after the last filter criterion was applied, {@link #endOr()} must be invoked.</p>
*
* @return an object of the type {@link HistoricTaskInstanceQuery} on which an arbitrary amount of filter criteria could be applied.
* The several filter criteria will be linked together by an OR expression.
*
* @throws ProcessEngineException when or() has been invoked directly after or() or after or() and trailing filter
* criteria. To prevent throwing this exception, {@link #endOr()} must be invoked after a chain of filter criteria to
* mark the end of the OR query.
* */
HistoricTaskInstanceQuery or();
/**
* <p>endOr() terminates an OR query on which an arbitrary amount of filter criteria were applied. To terminate the
* OR query which has been started by invoking {@link #or()}, endOr() must be invoked. Filter criteria which are
* applied after calling endOr() are linked together by an AND expression.</p>
*
* @return an object of the type {@link HistoricTaskInstanceQuery} on which an arbitrary amount of filter criteria could be applied.
* The filter criteria will be linked together by an AND expression.
*
* @throws ProcessEngineException when endOr() has been invoked before {@link #or()} was invoked. To prevent throwing
* this exception, {@link #or()} must be invoked first.
* */
HistoricTaskInstanceQuery endOr();
}

View File

@ -0,0 +1,33 @@
package com.xjrsoft.workflow.listener;
import lombok.AllArgsConstructor;
import org.camunda.bpm.engine.impl.bpmn.parser.BpmnParseListener;
import org.camunda.bpm.engine.impl.cfg.AbstractProcessEnginePlugin;
import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: tzx
* @Date: 2022/10/11 14:27
*/
@Component
@AllArgsConstructor
public class CamundaGlobalPlugin extends AbstractProcessEnginePlugin {
private final NewListener newListener;
@Override
public void preInit(ProcessEngineConfigurationImpl processEngineConfiguration) {
List<BpmnParseListener> listeners = processEngineConfiguration.getCustomPreBPMNParseListeners();
if(listeners == null){
listeners = new ArrayList<>();
processEngineConfiguration.setCustomPreBPMNParseListeners(listeners);
}
// listeners.add(new CamundaGlobalListener());
listeners.add(newListener);
}
}

View File

@ -0,0 +1,286 @@
package com.xjrsoft.workflow.listener;
import lombok.AllArgsConstructor;
import org.camunda.bpm.engine.delegate.ExecutionListener;
import org.camunda.bpm.engine.delegate.TaskListener;
import org.camunda.bpm.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.camunda.bpm.engine.impl.bpmn.parser.AbstractBpmnParseListener;
import org.camunda.bpm.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.camunda.bpm.engine.impl.pvm.process.ActivityImpl;
import org.camunda.bpm.engine.impl.pvm.process.ScopeImpl;
import org.camunda.bpm.engine.impl.pvm.process.TransitionImpl;
import org.camunda.bpm.engine.impl.task.TaskDefinition;
import org.camunda.bpm.engine.impl.util.xml.Element;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @Author: tzx
* @Date: 2022/12/7 16:24
*/
@Component
@AllArgsConstructor
public class NewListener extends AbstractBpmnParseListener {
private final ExecutionDelegate EXECUTION_LISTENER;
private final TaskDelegate TASK_LISTENER;
// public final static ExecutionListener EXECUTION_LISTENER = new ExecutionDelegate();
// public final static TaskListener TASK_LISTENER = new TaskDelegate();
protected void addEndEventListener(ScopeImpl activity) {
activity.addListener(ExecutionListener.EVENTNAME_END, EXECUTION_LISTENER);
}
protected void addStartEventListener(ScopeImpl activity) {
activity.addListener(ExecutionListener.EVENTNAME_START, EXECUTION_LISTENER);
}
protected void addTakeEventListener(TransitionImpl transition) {
transition.addListener(ExecutionListener.EVENTNAME_TAKE, EXECUTION_LISTENER);
}
protected void addTransitionStartEventListener(TransitionImpl transition) {
transition.addListener(ExecutionListener.EVENTNAME_START, EXECUTION_LISTENER);
}
protected void addTransitionEndEventListener(TransitionImpl transition) {
transition.addListener(ExecutionListener.EVENTNAME_END, EXECUTION_LISTENER);
}
protected void addTaskAssignmentListeners(TaskDefinition taskDefinition) {
taskDefinition.addTaskListener(TaskListener.EVENTNAME_ASSIGNMENT, TASK_LISTENER);
}
protected void addTaskCreateListeners(TaskDefinition taskDefinition) {
taskDefinition.addTaskListener(TaskListener.EVENTNAME_CREATE, TASK_LISTENER);
}
protected void addTaskCompleteListeners(TaskDefinition taskDefinition) {
taskDefinition.addTaskListener(TaskListener.EVENTNAME_COMPLETE, TASK_LISTENER);
}
protected void addTaskUpdateListeners(TaskDefinition taskDefinition) {
taskDefinition.addTaskListener(TaskListener.EVENTNAME_UPDATE, TASK_LISTENER);
}
protected void addTaskDeleteListeners(TaskDefinition taskDefinition) {
taskDefinition.addTaskListener(TaskListener.EVENTNAME_DELETE, TASK_LISTENER);
}
// BpmnParseListener implementation
// /
@Override
public void parseProcess(Element processElement, ProcessDefinitionEntity processDefinition) {
addStartEventListener(processDefinition);
addEndEventListener(processDefinition);
}
@Override
public void parseStartEvent(Element startEventElement, ScopeImpl scope, ActivityImpl startEventActivity) {
addStartEventListener(startEventActivity);
addEndEventListener(startEventActivity);
}
@Override
public void parseExclusiveGateway(Element exclusiveGwElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseInclusiveGateway(Element inclusiveGwElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseParallelGateway(Element parallelGwElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseScriptTask(Element scriptTaskElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseServiceTask(Element serviceTaskElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseBusinessRuleTask(Element businessRuleTaskElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseTask(Element taskElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseManualTask(Element manualTaskElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseUserTask(Element userTaskElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
UserTaskActivityBehavior activityBehavior = (UserTaskActivityBehavior) activity.getActivityBehavior();
TaskDefinition taskDefinition = activityBehavior.getTaskDefinition();
addTaskCreateListeners(taskDefinition);
addTaskAssignmentListeners(taskDefinition);
addTaskCompleteListeners(taskDefinition);
addTaskUpdateListeners(taskDefinition);
addTaskDeleteListeners(taskDefinition);
}
@Override
public void parseEndEvent(Element endEventElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseBoundaryTimerEventDefinition(Element timerEventDefinition, boolean interrupting, ActivityImpl timerActivity) {
// start and end event listener are set by parseBoundaryEvent()
}
@Override
public void parseBoundaryErrorEventDefinition(Element errorEventDefinition, boolean interrupting, ActivityImpl activity, ActivityImpl nestedErrorEventActivity) {
// start and end event listener are set by parseBoundaryEvent()
}
@Override
public void parseSubProcess(Element subProcessElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseCallActivity(Element callActivityElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseSequenceFlow(Element sequenceFlowElement, ScopeImpl scopeElement, TransitionImpl transition) {
addTakeEventListener(transition);
addTransitionStartEventListener(transition);
addTransitionEndEventListener(transition);
}
@Override
public void parseSendTask(Element sendTaskElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseMultiInstanceLoopCharacteristics(Element activityElement, Element multiInstanceLoopCharacteristicsElement, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseRootElement(Element rootElement, List<ProcessDefinitionEntity> processDefinitions) {
}
@Override
public void parseIntermediateTimerEventDefinition(Element timerEventDefinition, ActivityImpl timerActivity) {
// start and end event listener are set by parseIntermediateCatchEvent()
}
@Override
public void parseReceiveTask(Element receiveTaskElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseIntermediateSignalCatchEventDefinition(Element signalEventDefinition, ActivityImpl signalActivity) {
// start and end event listener are set by parseIntermediateCatchEvent()
}
@Override
public void parseBoundarySignalEventDefinition(Element signalEventDefinition, boolean interrupting, ActivityImpl signalActivity) {
// start and end event listener are set by parseBoundaryEvent()
}
@Override
public void parseEventBasedGateway(Element eventBasedGwElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseTransaction(Element transactionElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseCompensateEventDefinition(Element compensateEventDefinition, ActivityImpl compensationActivity) {
}
@Override
public void parseIntermediateThrowEvent(Element intermediateEventElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseIntermediateCatchEvent(Element intermediateEventElement, ScopeImpl scope, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseBoundaryEvent(Element boundaryEventElement, ScopeImpl scopeElement, ActivityImpl activity) {
addStartEventListener(activity);
addEndEventListener(activity);
}
@Override
public void parseIntermediateMessageCatchEventDefinition(Element messageEventDefinition, ActivityImpl nestedActivity) {
}
@Override
public void parseBoundaryMessageEventDefinition(Element element, boolean interrupting, ActivityImpl messageActivity) {
}
@Override
public void parseBoundaryEscalationEventDefinition(Element escalationEventDefinition, boolean interrupting, ActivityImpl boundaryEventActivity) {
}
@Override
public void parseBoundaryConditionalEventDefinition(Element element, boolean interrupting, ActivityImpl conditionalActivity) {
}
@Override
public void parseIntermediateConditionalEventDefinition(Element conditionalEventDefinition, ActivityImpl conditionalActivity) {
}
public void parseConditionalStartEventForEventSubprocess(Element element, ActivityImpl conditionalActivity, boolean interrupting) {
}
}

View File

@ -0,0 +1,540 @@
package com.xjrsoft.workflow.listener;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.common.core.enums.*;
import com.xjrsoft.common.redis.service.RedisUtil;
import com.xjrsoft.organization.client.IUserClient;
import com.xjrsoft.organization.entity.User;
import com.xjrsoft.organization.entity.UserRoleRelation;
import com.xjrsoft.workflow.constant.WorkflowConstant;
import com.xjrsoft.workflow.entity.*;
import com.xjrsoft.workflow.model.NoticePolicyParam;
import com.xjrsoft.workflow.model.TimeoutRemidConfig;
import com.xjrsoft.workflow.model.UserTaskConfig;
import com.xjrsoft.workflow.model.WorkflowSchemaConfig;
import com.xjrsoft.workflow.service.*;
import com.xjrsoft.workflow.utils.WorkFlowUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.delegate.DelegateTask;
import org.camunda.bpm.engine.delegate.TaskListener;
import org.camunda.bpm.engine.task.Task;
import org.springframework.stereotype.Component;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* 任务监听器
*
* @Author: tzx
* @Date: 2022/12/7 16:28
*/
@Component
@Slf4j
@AllArgsConstructor
public class TaskDelegate implements TaskListener {
private final RedisUtil redisUtil;
private final IWorkflowDelegateService workflowDelegateService;
private final IWorkflowCirculatedService circulatedService;
private final IUserClient userClient;
private Map<String,ScriptEngine> scriptEngineMap;
// private final IWorkflowSchemaService workflowSchemaService;
@Override
public void notify(DelegateTask delegateTask) {
//判断事件类型 如果是创建 (如果是单元测试代码 需要注释此代码)
if (delegateTask.getEventName().equals(EVENTNAME_CREATE)) {
log.info("[用户任务监听器] 事件: {}", delegateTask.getEventName());
List<String> variableNames = ListUtil.toList(
WorkflowConstant.PROCESS_SCHEMA_ID_KEY,
WorkflowConstant.PROCESS_SCHEMA_NAME_KEY,
WorkflowConstant.PROCESS_START_USER_ID_KEY,
WorkflowConstant.PROCESS_START_USER_NAME_KEY,
WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY,
WorkflowConstant.PROCESS_NAME);
RuntimeService runtimeService = delegateTask.getProcessEngine().getRuntimeService();
Map<String, Object> variableMaps = runtimeService.getVariables(delegateTask.getProcessInstanceId(), variableNames);
IWorkflowSchemaService workflowSchemaService = SpringUtil.getBean(IWorkflowSchemaService.class);
WorkflowSchema workflowSchema = workflowSchemaService.getOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, MapUtil.getLong(variableMaps, WorkflowConstant.PROCESS_SCHEMA_ID_KEY)));
// WorkflowSchema workflowSchema = redisUtil.get(WorkflowConstant.SCHEMA_CACHE_PREFIX + MapUtil.getStr(variableMaps, WorkflowConstant.PROCESS_SCHEMA_ID_KEY), WorkflowSchema.class);
if (workflowSchema == null) {
workflowSchema = workflowSchemaService.getById(MapUtil.getLong(variableMaps, WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
}
WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);
Optional<Map<String, Object>> userTaskConfigOp = workflowSchemaConfig.getChildNodeConfig().stream().filter(c -> c.containsValue(delegateTask.getTaskDefinitionKey())).findFirst();
if (!userTaskConfigOp.isPresent()) {
return;
}
//将map 转为 java类
UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userTaskConfigOp.get());
//设置超时提醒
setTimeoutRemid(delegateTask, workflowSchemaConfig, userTaskConfig);
//多实例任务 会在 流程线 执行监听器 就设置了人员List 会自动生成审批人变量
initApproveUser(delegateTask, workflowSchemaConfig, userTaskConfig, variableMaps);
//设置自定义任务名称规则
setProcessExtra(delegateTask, variableMaps, workflowSchema, workflowSchemaConfig, userTaskConfig);
}
//如果是审批事件 判断当前任务 是否有按钮事件
if(delegateTask.getEventName().equals(EVENTNAME_COMPLETE)){
if(redisUtil.containsKey(WorkflowConstant.BUTTON_EVENT_CACHE_PRE + delegateTask.getId())){
Map<String, Object> map = redisUtil.get(WorkflowConstant.BUTTON_EVENT_CACHE_PRE + delegateTask.getId(), new TypeReference<Map<String, Object>>() {
});
Integer language = MapUtil.get(map, "language", Integer.class);
String content = MapUtil.get(map, "content", String.class);
String scriptvar = "execution";
ScriptEngine scriptEngine;
if (ObjectUtil.isNotEmpty(language) && StrUtil.isNotBlank(content)){
if(language == 0){
String JAVA_SCRIPT = "js";
if(scriptEngineMap.containsKey(JAVA_SCRIPT)){
scriptEngine = scriptEngineMap.get(JAVA_SCRIPT);
}
else {
scriptEngine = new ScriptEngineManager().getEngineByName(JAVA_SCRIPT);
scriptEngineMap.put(JAVA_SCRIPT,scriptEngine);
}
}
else{
String GROOVY = "groovy";
if(scriptEngineMap.containsKey(GROOVY)){
scriptEngine = scriptEngineMap.get(GROOVY);
}
else {
scriptEngine = new ScriptEngineManager().getEngineByName(GROOVY);
scriptEngineMap.put(GROOVY,scriptEngine);
}
}
// 方式一,默认设置变量
scriptEngine.put(scriptvar, delegateTask);
try {
redisUtil.delete(WorkflowConstant.BUTTON_EVENT_CACHE_PRE + delegateTask.getId());
scriptEngine.eval(content);
}
catch (Exception e){
e.printStackTrace();
}
}
else {
redisUtil.delete(WorkflowConstant.BUTTON_EVENT_CACHE_PRE + delegateTask.getId());
}
}
}
}
/**
* 设置用户传阅人
*
* @param delegateTask
* @param variableMaps
* @param workflowSchemaConfig
* @param userTaskConfig
*/
private void initCirculatedUser(DelegateTask delegateTask, Map<String, Object> variableMaps, WorkflowSchemaConfig workflowSchemaConfig, UserTaskConfig userTaskConfig, Long id) {
if (userTaskConfig.getCirculateConfigs() == null) {
return;
}
//获取到用户节点的传阅人
List<Long> nextUserTaskCirculated = WorkFlowUtil.getUserIdsByMemberConfig(userTaskConfig.getCirculateConfigs(), workflowSchemaConfig.getChildNodeConfig(),delegateTask.getProcessInstanceId());
//多实例流程不记录传阅人消息
if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() == WorkflowMultiInstanceType.NONE.getCode()) {
List<WorkflowCirculated> circulatedList = new ArrayList<>();
for (Long userId : nextUserTaskCirculated) {
WorkflowCirculated workflowCirculated = new WorkflowCirculated();
workflowCirculated.setProcessId(delegateTask.getProcessInstanceId());
workflowCirculated.setCreateTime(DateUtil.toLocalDateTime(delegateTask.getCreateTime()));
workflowCirculated.setTaskId(delegateTask.getId());
workflowCirculated.setCurrentProgress(userTaskConfig.getCurrentProgress());
workflowCirculated.setTaskName(delegateTask.getName());
workflowCirculated.setIsRead(YesOrNoEnum.NO.getCode());
workflowCirculated.setProcessName(MapUtil.getStr(variableMaps, WorkflowConstant.PROCESS_NAME));
workflowCirculated.setSchemaId(MapUtil.getLong(variableMaps, WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
workflowCirculated.setCirculatedUserId(userId);
workflowCirculated.setStartUserId(MapUtil.getLong(variableMaps, WorkflowConstant.PROCESS_START_USER_ID_KEY));
workflowCirculated.setSerialNumber(MapUtil.getLong(variableMaps, WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY));
circulatedList.add(workflowCirculated);
}
NoticePolicyParam param = new NoticePolicyParam();
param.setNoticeUserIds(nextUserTaskCirculated);
param.setTaskId(delegateTask.getId());
param.setTaskName(delegateTask.getName());
param.setProcessId(delegateTask.getProcessInstanceId());
param.setTaskName(delegateTask.getName());
param.setSchemaId(MapUtil.get(variableMaps, WorkflowConstant.PROCESS_SCHEMA_ID_KEY, Long.class));
param.setSchemaName(MapUtil.get(variableMaps, WorkflowConstant.PROCESS_SCHEMA_NAME_KEY, String.class));
param.setStartUserName(MapUtil.get(variableMaps, WorkflowConstant.PROCESS_START_USER_NAME_KEY, String.class));
param.setNoticePolicyConfigs(userTaskConfig.getNoticePolicyConfigs());
WorkFlowUtil.sendCirculatedNoticePolicy(param,delegateTask.getName());
circulatedService.saveBatch(circulatedList);
}
//当前传阅人[所有传阅人名称]
String circulatedName = WorkFlowUtil.getAllUserNamesByIds(nextUserTaskCirculated);
//如果当前传阅人不为空,就给上面那条准备审批信息中间加上传阅人信息
if (StrUtil.isNotBlank(circulatedName)) {
IWorkflowRecordService workflowRecordService = SpringUtil.getBean(IWorkflowRecordService.class);
WorkflowRecord record = new WorkflowRecord();
record.setId(id);
record.setCirculateMessage("当前传阅人【" + circulatedName + "");
workflowRecordService.updateById(record);
}
// addProcessRecord(delegateTask, MapUtil.getLong(variableMaps, WorkflowConstant.PROCESS_SCHEMA_ID_KEY), "当前传阅人【"+circulatedName+"】");
}
/**
* 设置审批人
* 然后 判断是否自动通过
* 新增 流程记录
*
* @param delegateTask
* @param workflowSchemaConfig
* @param userTaskConfig
* @return 是否指定审批人
*/
private void initApproveUser(DelegateTask delegateTask, WorkflowSchemaConfig workflowSchemaConfig, UserTaskConfig userTaskConfig, Map<String, Object> variableMaps) {
TaskService taskService = delegateTask.getProcessEngine().getTaskService();
taskService.setVariableLocal(delegateTask.getId(), WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode());
Long schemaId = MapUtil.get(variableMaps, WorkflowConstant.PROCESS_SCHEMA_ID_KEY, Long.class);
List<Long> approveUserIds = new ArrayList<>();
// 如果是单实例
if (userTaskConfig.getCountersignConfig().getMultipleInstancesType() == WorkflowMultiInstanceType.NONE.getCode()) {
//获取到所有审批人
approveUserIds = WorkFlowUtil.getUserIdsByMemberConfig(userTaskConfig.getApproverConfigs(), workflowSchemaConfig.getChildNodeConfig(),delegateTask.getProcessInstanceId());
//如果节点设置了指定审批人 就不再走无对应处理人逻辑
if (userTaskConfig.getIsPrevChooseNext() == WorkflowIsPrevChooseNextType.PREV.getCode()) {
taskService.setVariableLocal(delegateTask.getId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StrUtil.join(StringPool.COMMA, approveUserIds));
return;
}
//如果全局设置了指定审批人 就不再走无对应处理人逻辑
if (workflowSchemaConfig.getProcessConfig().getIsPrevChooseNext() == WorkflowIsPrevChooseNextType.PREV.getCode()) {
taskService.setVariableLocal(delegateTask.getId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StrUtil.join(StringPool.COMMA, approveUserIds));
return;
}
//无对应处理人
if (userTaskConfig.getApproverConfigs().size() == 0 || approveUserIds.size() == 0) {
//如果节点配置了 无对应处理人 为admin 默认覆盖 全局设置
if (userTaskConfig.getNoHandler() == WorkflowNoHandlerType.ADMIN.getCode()) {
List<UserRoleRelation> userRoleRelations = redisUtil.get(GlobalConstant.USER_ROLE_RELATION_CACHE_KEY, new TypeReference<List<UserRoleRelation>>() {
});
//获取超级管理员成员
List<Long> adminUserIds = userRoleRelations.stream().filter(x -> x.getRoleId().equals(GlobalConstant.SUPER_ADMIN_ROLE_ID)).map(UserRoleRelation::getUserId).collect(Collectors.toList());
//根据用户id获取对应的用户名并进行拼接输出张三、李四这种
approveUserIds = adminUserIds;
taskService.setVariableLocal(delegateTask.getId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StrUtil.join(StringPool.COMMA, adminUserIds));
}
} else {
//根据所选审批人 找到审批人 新增到 所选审批人中。
setUserDelegate(delegateTask, schemaId, approveUserIds);
taskService.setVariableLocal(delegateTask.getId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StrUtil.join(StringPool.COMMA, approveUserIds));
}
}
//多实例 不可能没有处理人 因为多实例如果不设置处理人 不会生成task 但是会需要判断是否自动同意
else {
//如果有这个key 就是代表 会签节点有设置过审批人id
if(redisUtil.containsKey(WorkflowConstant.MULTI_ASSIGNEE_CACHE_PREFIX + delegateTask.getExecutionId())){
String approveId = redisUtil.get(WorkflowConstant.MULTI_ASSIGNEE_CACHE_PREFIX + delegateTask.getExecutionId());
approveUserIds.add(Convert.toLong(approveId));
taskService.setVariableLocal(delegateTask.getId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, approveId);
redisUtil.delete(delegateTask.getExecutionId());
}
}
String approveName = WorkFlowUtil.getAllUserNamesByIds(approveUserIds);
Long recordId = addProcessRecord(delegateTask, schemaId, "" + approveName + "" + "准备审批");
//设置用户传阅人
initCirculatedUser(delegateTask, variableMaps, workflowSchemaConfig, userTaskConfig, recordId);
//默认所有任务不需要指定审批人 如果需要指定审批人 会在 launch 和 approve 接口 处理。
}
/**
* 根据所选审批人 找到委托人 新增到 所选审批人中。
*
* @param schemaId
* @param approveUserIds
*/
private void setUserDelegate(DelegateTask delegateTask, Long schemaId, List<Long> approveUserIds) {
//查询下一个节点审批人 委托 数据 将被委托人加入到 审批人数据中
LambdaQueryWrapper<WorkflowDelegate> wrapper = Wrappers.lambdaQuery(WorkflowDelegate.class)
.in(WorkflowDelegate::getUserId, approveUserIds)
.lt(WorkflowDelegate::getStartTime, DateUtil.parse(DateUtil.now()).toJdkDate())
.gt(WorkflowDelegate::getEndTime, DateUtil.parse(DateUtil.now()).toJdkDate())
.like(WorkflowDelegate::getSchemaIds, schemaId)
.select(WorkflowDelegate::getDelegateUserIds);
List<WorkflowDelegate> workflowDelegates = workflowDelegateService.list(wrapper);
if (workflowDelegates.size() > 0) {
List<String> allDelegateStr = workflowDelegates.stream().map(WorkflowDelegate::getDelegateUserIds).collect(Collectors.toList());
List<Long> allDelegateUserIds = Arrays.stream(StrUtil.join(StringPool.COMMA, allDelegateStr).split(StringPool.COMMA)).map(Convert::toLong).collect(Collectors.toList());
approveUserIds.addAll(allDelegateUserIds);
//新增流程发起流程记录
//获取当前用户的信息
User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
//根据用户id获取对应的用户名并进行拼接输出
String allDelegateUserNames = WorkFlowUtil.getAllUserNamesByIds(allDelegateUserIds);
//[操作人名称]通过[流程委托]委托[被委托人名称]作为审批人
String message = "" + user.getName() + "】通过【流程委托】委托【" + allDelegateUserNames + "】作为审批人";
addProcessRecord(delegateTask, schemaId, message);
}
}
/**
* 是否自动同意
*
* @param delegateTask
*/
private void setAutoAgree(DelegateTask delegateTask, WorkflowSchemaConfig workflowSchemaConfig, Long schemaId, List<Long> approveUserIds) {
List<Integer> autoAgreeRule = workflowSchemaConfig.getProcessConfig().getAutoAgreeRule();
TaskService taskService = delegateTask.getProcessEngine().getTaskService();
//如果没有配置过 默认跳过
if (autoAgreeRule != null && autoAgreeRule.size() > 0) {
User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
//新增流程发起流程记录
String opinion = StringPool.EMPTY;
//候选审批人包含流程任务发起人
if (autoAgreeRule.contains(WorkflowAutoAgreeType.APPROVED_INCLUDE_INITIATOR.getCode())) {
//模板id
Object startUserId = delegateTask.getVariable(WorkflowConstant.PROCESS_START_USER_ID_KEY);
if (approveUserIds.contains(Convert.toLong(startUserId))) {
taskService.createComment(delegateTask.getId(), delegateTask.getProcessInstanceId(), "候选审批人包含流程任务发起人---自动同意");
taskService.complete(delegateTask.getId());
opinion = "候选审批人包含流程任务发起人";
}
}
//候选审批人包含上一节点审批人
if (autoAgreeRule.contains(WorkflowAutoAgreeType.APPROVED_INCLUDE_PREV.getCode())) {
//当前操作人 就是上一结点审批人
if (approveUserIds.contains(StpUtil.getLoginIdAsLong())) {
taskService.createComment(delegateTask.getId(), delegateTask.getProcessInstanceId(), "候选审批人包含上一节点审批人---自动同意");
taskService.complete(delegateTask.getId());
opinion = "候选审批人包含上一节点审批人";
}
}
//候选审批人在此流程中审批过
if (autoAgreeRule.contains(WorkflowAutoAgreeType.APPROVED_IN_PROCESS.getCode())) {
//模板id
Object startUserId = delegateTask.getVariable(WorkflowConstant.PROCESS_START_USER_ID_KEY);
if (approveUserIds.contains(Convert.toLong(startUserId))) {
taskService.createComment(delegateTask.getId(), delegateTask.getProcessInstanceId(), "候选审批人在此流程中审批过---自动同意");
taskService.complete(delegateTask.getId());
} else {
if (redisUtil.containsKey(WorkflowConstant.TASK_ASSIGNEE_RECORD_VAR_KEY)) {
Map<String, List<Long>> taskAssigneeRecordMap = redisUtil.get(WorkflowConstant.TASK_ASSIGNEE_RECORD_VAR_KEY, new TypeReference<Map<String, List<Long>>>() {
});
for (String taskId : taskAssigneeRecordMap.keySet()) {
List<Long> userIds = taskAssigneeRecordMap.get(taskId);
if (userIds.retainAll(approveUserIds)) {
taskService.createComment(delegateTask.getId(), delegateTask.getProcessInstanceId(), "候选审批人在此流程中审批过---自动同意");
taskService.complete(delegateTask.getId());
}
}
}
}
opinion = "候选审批人在此流程中审批过";
}
//缓存 自动通过 记录
WorkFlowUtil.cacheTaskAssigneeRecord(delegateTask.getProcessInstanceId(), delegateTask.getId(), StpUtil.getLoginIdAsLong());
List<Task> list = taskService.createTaskQuery().processInstanceId(delegateTask.getProcessInstanceId()).list();
//如果找不到任务了 默认流程已经结束
if (list.size() > 0) {
String allNextTaskName = StrUtil.join(StringPool.SPACE, list.parallelStream().map(t -> "" + t.getName() + "").collect(Collectors.toList()));
String message = "" + user.getName() + "】【自动同意】 审批, 审批意见为:“【" + opinion + "】”,由【" + delegateTask.getName() + "】流转到 " + allNextTaskName;
addProcessRecord(delegateTask, schemaId, message);
} else {
String message = "" + user.getName() + "】【自动同意】 审批, 审批意见为:“【" + opinion + "】”,由【" + delegateTask.getName() + "】流转到 结束节点";
addProcessRecord(delegateTask, schemaId, message);
}
}
}
/**
* 添加流程记录
*/
private Long addProcessRecord(DelegateTask delegateTask, Long schemaId, String message) {
IWorkflowRecordService workflowRecordService = SpringUtil.getBean(IWorkflowRecordService.class);
//新增流程发起流程记录
WorkflowRecord record = new WorkflowRecord();
record.setNodeId(delegateTask.getId());
record.setNodeName(delegateTask.getName());
record.setNodeType(WorkflowConstant.START_EVENT_TYPE_NAME);
record.setProcessId(delegateTask.getProcessInstanceId());
record.setSchemaId(schemaId);
record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
record.setRecordTime(LocalDateTime.now());
record.setMessage(message);
workflowRecordService.save(record);
return record.getId();
}
/**
* 设置根据命名规则 设置任务名字
*
* @param delegateTask
*/
private void setProcessExtra(DelegateTask delegateTask, Map<String, Object> variableMaps, WorkflowSchema workflowSchema, WorkflowSchemaConfig workflowSchemaConfig, UserTaskConfig userTaskConfig) {
WorkflowExtra extra = new WorkflowExtra();
extra.setProcessId(delegateTask.getProcessInstanceId());
extra.setTaskId(delegateTask.getId());
extra.setTaskKey(delegateTask.getTaskDefinitionKey());
extra.setStartUserId(MapUtil.getLong(variableMaps, WorkflowConstant.PROCESS_START_USER_ID_KEY));
extra.setStartUserName(MapUtil.getStr(variableMaps, WorkflowConstant.PROCESS_START_USER_NAME_KEY));
extra.setProcessName(MapUtil.getStr(variableMaps, WorkflowConstant.PROCESS_NAME));
extra.setSchemaId(MapUtil.getLong(variableMaps, WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
extra.setSchemaName(MapUtil.getStr(variableMaps, WorkflowConstant.PROCESS_SCHEMA_NAME_KEY));
extra.setSerialNumber(MapUtil.getLong(variableMaps, WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY));
extra.setStartTime(LocalDateTime.now());
extra.setLaunchTime(DateUtil.toLocalDateTime(delegateTask.getCreateTime()));
extra.setCurrentProgress(userTaskConfig.getCurrentProgress());
TaskService taskService = delegateTask.getProcessEngine().getTaskService();
Object variable = taskService.getVariable(delegateTask.getId(), WorkflowConstant.TASK_ASSIGNEE_VAR_KEY);
extra.setApproveUserIds(Convert.toStr(variable));
extra.setTaskName(delegateTask.getName());
IWorkflowExtraService workflowExtraService = SpringUtil.getBean(IWorkflowExtraService.class);
workflowExtraService.save(extra);
}
/**
* 设置超时提醒定时任务
*/
private void setTimeoutRemid(DelegateTask delegateTask, WorkflowSchemaConfig
workflowSchemaConfig, UserTaskConfig userTaskConfig) {
if (workflowSchemaConfig.getProcessConfig().getTimeoutRemidConfig() == null) return;
TimeoutRemidConfig timeoutRemidConfig = workflowSchemaConfig.getProcessConfig().getTimeoutRemidConfig();
if (timeoutRemidConfig.getEnabled()) {
//获取到所有需要提示的人员
List<Long> userIdsByMemberConfig = WorkFlowUtil.getUserIdsByMemberConfig(timeoutRemidConfig.getPushMemberConfigs(), workflowSchemaConfig.getChildNodeConfig(),delegateTask.getProcessInstanceId());
List<Long> approveUserIds = WorkFlowUtil.getUserIdsByMemberConfig(userTaskConfig.getApproverConfigs(), workflowSchemaConfig.getChildNodeConfig(), delegateTask.getProcessInstanceId());
userIdsByMemberConfig.addAll(approveUserIds);
List<Integer> noticePolicyConfig = userTaskConfig.getNoticePolicyConfigs();
if (noticePolicyConfig.size() == 0) {
return;
}
String noticePolicyConfigString = StrUtil.join(StringPool.UNDERSCORE, noticePolicyConfig);
RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
//推送次数设定为几次 就设置几个key值 然后计算过期时间
for (Integer i = 0; i < timeoutRemidConfig.getPushHits(); i++) {
// 公式为 (超时时间 + (间隔 * 第几次提醒)) * 3600秒
// 第一次提醒 直接使用超时时间 所以 i == 0
int expire = (timeoutRemidConfig.getHour() + (timeoutRemidConfig.getInterval() * i)) * 3600;
// 格式为 timeout:{taskId}:{hit}:{noticePolicyConfigString}
// taskId为任务id hit为次数 noticePolicyConfigString为通知策略使用下划线隔开
// 存储数据为所有需要推送的人员
// expire 为过期时间
redisUtil.set(WorkflowConstant.TIMEOUT_REMID_KEY + delegateTask.getId() + StringPool.COLON + (i + 1) + StringPool.COLON + noticePolicyConfigString, userIdsByMemberConfig, expire);
}
}
}
}

View File

@ -0,0 +1,208 @@
package com.xjrsoft.workflow.listener;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xjrsoft.common.core.enums.WorkflowNoticePolicyType;
import com.xjrsoft.common.core.enums.YesOrNoEnum;
import com.xjrsoft.system.client.IMessageClient;
import com.xjrsoft.system.model.MessageNoticePolicyParam;
import com.xjrsoft.workflow.constant.WorkflowConstant;
import com.xjrsoft.workflow.entity.WorkflowSchema;
import com.xjrsoft.workflow.model.MemberConfig;
import com.xjrsoft.workflow.model.NoticePolicyParam;
import com.xjrsoft.workflow.model.UserTaskConfig;
import com.xjrsoft.workflow.model.WorkflowSchemaConfig;
import com.xjrsoft.workflow.service.IWorkflowSchemaService;
import com.xjrsoft.workflow.utils.WorkFlowUtil;
import org.camunda.bpm.engine.ProcessEngines;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.task.Task;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/**
* 超时提醒 监听器
* 监听redis key 过期
*
* @Author: tzx
* @Date: 2022/10/26 14:46
*/
@Component
public class TaskTimeoutListener extends KeyExpirationEventMessageListener {
public TaskTimeoutListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Override
public void onMessage(Message message, byte[] pattern) {
String expiredKey = message.toString();
TaskService taskService = ProcessEngines.getDefaultProcessEngine().getTaskService();
//如果是指定审批人 key 失效
if (expiredKey.contains(WorkflowConstant.TASK_IS_APPOINT_APPROVE)) {
String[] split = expiredKey.split(StringPool.UNDERSCORE);
String taskId = split[0];
//获取到审批人 发送消息
Object approvedIds = taskService.getVariableLocal(taskId, WorkflowConstant.TASK_ASSIGNEE_VAR_KEY);
List<String> variableNames = ListUtil.toList(
WorkflowConstant.PROCESS_SCHEMA_ID_KEY,
WorkflowConstant.PROCESS_SCHEMA_NAME_KEY,
WorkflowConstant.PROCESS_START_USER_NAME_KEY);
Map<String, Object> variables = taskService.getVariables(taskId, variableNames);
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
IWorkflowSchemaService workflowSchemaService = SpringUtil.getBean(IWorkflowSchemaService.class);
WorkflowSchema workflowSchema = workflowSchemaService.getOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, schemaId).select(WorkflowSchema::getJsonContent));
WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);
Map<String, Object> userTaskConfigMap = workflowSchemaConfig.getChildNodeConfig().stream().filter(x -> x.containsValue(task.getTaskDefinitionKey())).findFirst().orElse(new HashMap<>());
UserTaskConfig userTaskConfig = Convert.convert(UserTaskConfig.class, userTaskConfigMap);
List<String> approveIds = ListUtil.toList(Convert.toStr(approvedIds).split(StringPool.COMMA));
NoticePolicyParam param = new NoticePolicyParam();
param.setNoticeUserIds(Convert.toList(Long.class, approveIds));
param.setTaskId(task.getId());
param.setTaskName(task.getName());
param.setProcessId(task.getProcessInstanceId());
param.setTaskName(task.getName());
param.setSchemaId(MapUtil.get(variables, WorkflowConstant.PROCESS_SCHEMA_ID_KEY, Long.class));
param.setSchemaName(MapUtil.get(variables, WorkflowConstant.PROCESS_SCHEMA_NAME_KEY, String.class));
param.setStartUserName(MapUtil.get(variables, WorkflowConstant.PROCESS_START_USER_NAME_KEY, String.class));
param.setNoticePolicyConfigs(userTaskConfig.getNoticePolicyConfigs());
//发送消息
WorkFlowUtil.sendApproveNoticePolicy(param,task.getName());
//到期未指定的任务 将变量设置为不需要指定
taskService.setVariableLocal(task.getId(), WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode());
return;
}
//如果key 不包含此前缀 不再执行此逻辑
if (!expiredKey.contains(WorkflowConstant.TIMEOUT_REMID_KEY)) {
return;
}
// 将过期key使用冒号 切割
String[] expiredKeyArr = expiredKey.split(StringPool.COLON);
//获取到任务id
String taskId = expiredKeyArr[1];
//获取到次数
// String hit = expiredKeyArr[2];
//获取到通知策略
String noticePolicyConfigString = expiredKeyArr[3];
String[] noticePolicyTypeList = noticePolicyConfigString.split(StringPool.UNDERSCORE);
//根据taskid 获取发起人 以及 模板名称
Map<String, Object> variables = taskService.getVariables(taskId, ListUtil.toList(WorkflowConstant.PROCESS_START_USER_NAME_KEY, WorkflowConstant.PROCESS_SCHEMA_NAME_KEY));
String starter = StrUtil.toString(variables.get(WorkflowConstant.PROCESS_START_USER_NAME_KEY));
String schemaName = StrUtil.toString(variables.get(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY));
//获取到需要提醒的人员(过期key 无法获取到value 需要数据库取审批人)
Object assignee = taskService.getVariableLocal(taskId, WorkflowConstant.TASK_ASSIGNEE_VAR_KEY);
List<String> approveIds = ListUtil.toList(Convert.toStr(assignee).split(StringPool.COMMA));
List<Long> ids = Convert.toList(Long.class, approveIds);
//获取任务信息
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
IWorkflowSchemaService workflowSchemaService = SpringUtil.getBean(IWorkflowSchemaService.class);
WorkflowSchema workflowSchema = workflowSchemaService.getOne(Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getId, schemaId).select(WorkflowSchema::getJsonContent));
WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);
//找到当前用户任务节点的配置信息
List<MemberConfig> pushMemberConfigs = workflowSchemaConfig.getProcessConfig().getTimeoutRemidConfig().getPushMemberConfigs();
if (pushMemberConfigs.size() > 0) {
List<Long> userIdsByMemberConfig = WorkFlowUtil.getUserIdsByMemberConfig(pushMemberConfigs, workflowSchemaConfig.getChildNodeConfig(), task.getProcessInstanceId());
ids.addAll(userIdsByMemberConfig);
}
List<Long> allPushUserIds = ids.stream().distinct().collect(Collectors.toList());
//遍历所有设定的通知策略
// 暂时这样写 后续可以改成 策略模式 或者 工厂模式
//TODO sendMessageUtil 改成与 oss 一样的工厂
MessageNoticePolicyParam param = new MessageNoticePolicyParam();
param.setNoticeUserIds(Convert.toList(Long.class, allPushUserIds));
param.setTaskId(task.getId());
param.setTaskName(task.getName());
param.setProcessId(task.getProcessInstanceId());
param.setTaskName(task.getName());
param.setSchemaId(schemaId);
param.setSchemaName(schemaName);
param.setStartUserName(starter);
IMessageClient messageClient = SpringUtil.getBean(IMessageClient.class);
for (String type : noticePolicyTypeList) {
//如果包含系统消息推送
if (Convert.toInt(type) == WorkflowNoticePolicyType.SYSTEM.getCode()) {
CompletableFuture.runAsync(() -> {
messageClient.sendWorkflowTimeoutMessageFeign(param);
});
}
//短信发送
if (Convert.toInt(type) == WorkflowNoticePolicyType.SMS.getCode()) {
//TODO 短信发送代码
CompletableFuture.runAsync(() -> {
messageClient.sendWorkflowTimeoutMessageFeign(param);
});
}
//企业微信
if (Convert.toInt(type) == WorkflowNoticePolicyType.WECHAT.getCode()) {
//TODO 企业微信发送代码
}
//钉钉
if (Convert.toInt(type) == WorkflowNoticePolicyType.DING.getCode()) {
//TODO 钉钉发送代码
}
//邮箱
if (Convert.toInt(type) == WorkflowNoticePolicyType.EMAIL.getCode()) {
//TODO 邮箱发送
CompletableFuture.runAsync(() -> {
messageClient.sendWorkflowTimeoutMessageFeign(param);
});
}
}
}
}

View File

@ -0,0 +1,50 @@
package com.xjrsoft.workflow.runner;
import cn.hutool.json.JSONUtil;
import com.xjrsoft.common.redis.service.RedisUtil;
import com.xjrsoft.workflow.entity.WorkflowSchema;
import com.xjrsoft.workflow.model.WorkflowSchemaConfig;
import com.xjrsoft.workflow.service.IWorkflowSchemaService;
import com.xjrsoft.workflow.utils.WorkFlowUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* @Description 加载数据权限 缓存
* @Author: tzx
* @Date: 2023/2/28 14:49
*/
@Component
@AllArgsConstructor
@Slf4j
public class WorkflowRunner implements CommandLineRunner {
private IWorkflowSchemaService workflowSchemaService;
@Override
public void run(String... args) {
loadEventCache();
}
@Async
void loadEventCache() {
log.info("ITC-FRAMEWORK: 加载所有工作流模板开始与结束事件缓存开始");
List<WorkflowSchema> list = workflowSchemaService.list();
for (WorkflowSchema workflowSchema : list) {
//获取到整个流程模板的配置
WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);
CompletableFuture.runAsync(() -> {
//先删除,后新增
WorkFlowUtil.removeNodeListener(workflowSchema.getDeploymentId());
WorkFlowUtil.cacheNodeListener(workflowSchema.getDeploymentId(), workflowSchemaConfig.getChildNodeConfig());
});
}
log.info("ITC-FRAMEWORK: 加载所有工作流模板开始与结束事件缓存结束");
}
}

View File

@ -0,0 +1,183 @@
package com.xjrsoft.workflow.service;
import cn.hutool.db.Entity;
import cn.hutool.db.Session;
import com.xjrsoft.common.core.domain.page.PageOutput;
import com.xjrsoft.common.generate.model.QueryConfig;
import com.xjrsoft.desktop.dto.*;
import com.xjrsoft.form.dto.*;
import com.xjrsoft.form.vo.DeskFormReleaseVo;
import com.xjrsoft.form.vo.DeskTableInfoVo;
import org.apache.commons.lang3.tuple.Triple;
import java.util.List;
import java.util.Map;
/**
* @Author: tzx
* @Date: 2022/5/11 14:57
*/
public interface IFormExecuteService {
/**
* 获取自定义表单不分页列表数据
* @param dto
* @return
*/
List<Entity> list(FormExecuteListDto dto);
/**
* App 获取自定义表单不分页列表数据
* @param dto
* @return
*/
List<Entity> appList(AppFormExecuteListDto dto);
/**
* 获取自定义表单分页列表数据
* @param dto
* @return
*/
PageOutput<Entity> page(FormExecutePageDto dto);
/**
* App 获取自定义表单分页列表数据
* @param dto
* @return
*/
PageOutput<Entity> appPage(AppFormExecutePageDto dto);
/**
* 获取自定义表单 表单数据
* @return
*/
Object info(FormExecuteInfoDto dto);
/**
* App 获取自定义表单 表单数据
* @return
*/
Object appInfo(AppFormExecuteInfoDto dto);
/**
* 自定义表单 新增
* @param dto
* @return
*/
Boolean add(FormExecuteAddOrUpdateDto dto);
/**
* App 自定义表单 新增
* @param dto
* @return
*/
Boolean appAdd(AppFormExecuteAddOrUpdateDto dto);
/**
* 自定义表单 修改
* @param dto
* @return
*/
Boolean update(FormExecuteAddOrUpdateDto dto);
/**
* app 自定义表单 修改
* @param dto
* @return
*/
Boolean appUpdate(AppFormExecuteAddOrUpdateDto dto);
/**
* 自定义表单 删除
* @param dto
* @return
*/
Boolean delete(FormExecuteDeleteDto dto);
/**
* app 自定义表单 删除
* @param dto
* @return
*/
Boolean appDelete(AppFormExecuteDeleteDto dto);
/**
* 工作流调用 自定义表单 新增
* @param dto
* @return
*/
Triple<Session, Long, Long> workflowAdd(FormExecuteWorkflowAddDto dto);
/**
* 工作流调用 自定义表单 修改
* @param dto
* @return
*/
Triple<Session, Long, Long> workflowUpdate(FormExecuteWorkflowUpdateDto dto);
/**
* 工作流调用 自定义表单 新增或者修改
* @param dto
* @return
*/
Triple<Session, Boolean, Long> workflowAddOrUpdate(FormExecuteWorkflowUpdateDto dto);
/**
* 获取自定义表单 表单数据
* @return
*/
Object workFlowInfo(FormExecuteWorkflowInfoDto dto);
/**
* 批量新增主表数据
* @return
*/
Boolean saveMainBatch(Long formTemplateId, List<Map<String, Object>> dataList);
/**
* 桌面设计调用新增
* @param dto
* @return
*/
boolean complexAdd(AddDeskComplexDto dto);
/**
* 桌面设计调用编辑
* @param dto
* @return
*/
boolean complexUpdate(UpdateDeskComplexDto dto);
/**
* 桌面设计调用删除
* @param dto
* @return
*/
boolean complexDelete(DeleteDeskComplexDto dto);
/**
* 桌面设计调用删除
* @param dto
* @return
*/
Object complexInfo(DeskComplexInfoDto dto);
/**
* 桌面设计调用自定义表单获取发布id
* @param formId
* @return
*/
List<DeskFormReleaseVo> getReleaseInfo(Long formId);
List<QueryConfig> complexQuery(ComplexQueryDto dto);
/**
* 桌面设计-复杂列表页-获取列表数据 分页
* @param dto
* @return
*/
PageOutput<Entity> complexPage(ComplexPageDto dto);
DeskTableInfoVo getTableInfo(Long formId);
}

View File

@ -0,0 +1,16 @@
package com.xjrsoft.workflow.service;
import com.github.yulichang.base.MPJBaseService;
import com.xjrsoft.workflow.entity.WorkflowApproveRecord;
/**
* <p>
* 服务类
* </p>
*
* @author tzx
* @since 2023-02-10
*/
public interface IWorkflowApproveRecordService extends MPJBaseService<WorkflowApproveRecord> {
}

View File

@ -0,0 +1,16 @@
package com.xjrsoft.workflow.service;
import com.github.yulichang.base.MPJBaseService;
import com.xjrsoft.workflow.entity.WorkflowCirculated;
/**
* <p>
* 流程传阅信息表 服务类
* </p>
*
* @author tzx
* @since 2023-02-09
*/
public interface IWorkflowCirculatedService extends MPJBaseService<WorkflowCirculated> {
}

View File

@ -0,0 +1,54 @@
package com.xjrsoft.workflow.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xjrsoft.common.core.domain.page.PageOutput;
import com.xjrsoft.workflow.dto.AddDelegateDto;
import com.xjrsoft.workflow.dto.DelegatePageDto;
import com.xjrsoft.workflow.dto.UpdateDelegateDto;
import com.xjrsoft.workflow.entity.WorkflowDelegate;
import com.xjrsoft.workflow.vo.DelegatePageVo;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 流程委托 服务类
* </p>
*
* @author tzx
* @since 2022-11-05
*/
@Service
public interface IWorkflowDelegateService extends IService<WorkflowDelegate> {
/**
* 分页查询
* @param dto
* @return
*/
PageOutput<DelegatePageVo> page(DelegatePageDto dto);
/**
* 新增
* @param dto
* @return
*/
boolean add(AddDelegateDto dto);
/**
* 修改
* @param dto
* @return
*/
boolean update(UpdateDelegateDto dto);
/**
* 删除
* @param ids
* @return
*/
boolean delete(List<Long> ids);
}

View File

@ -0,0 +1,413 @@
package com.xjrsoft.workflow.service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xjrsoft.common.core.domain.page.PageOutput;
import com.xjrsoft.workflow.dto.*;
import com.xjrsoft.workflow.vo.*;
import org.camunda.bpm.model.bpmn.instance.SequenceFlow;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @Author: tzx
* @Date: 2022/9/8 14:22
*/
public interface IWorkflowExecuteService {
/**
* 部署
*
* @param dto
* @return
*/
boolean deploy(DeployDto dto);
/**
* 部署
*
* @param schemaId
* @return
*/
String preview(Long schemaId);
/**
* 发起流程前 所需要获取的流程信息数据
* @param schemaId
* @return
*/
StartProcessInfoVo getStartProcessInfo(Long schemaId);
/**
* 审批流程前 需要获取的流程信息数据
* @param taskId
* @return
*/
UserTaskInfoVo getApproveProcessInfo(String taskId);
/**
* 审批流程前 需要获取的流程信息数据
* @param processId
* @return
*/
UserTaskInfoVo getApproveProcessInfoByProcessId(String processId);
/**
* 获取当前流程所有的流转记录
* @param processId
* @return
*/
AllRecordListVo getAllRecordInfoByProcessId(String processId,Integer onlySelf);
/**
* 审批流程所需要的信息 需要获取的流程信息数据
* @param processId
* @return
*/
RecycleProcessInfoVo getRecycleProcessInfo(String processId);
/**
* 发起流程
*
* @param dto
* @return
*/
List<LaunchAndApproveVo> newLaunch(LaunchDto dto);
/**
* 发起流程
*
* @param dto
* @return
*/
List<LaunchAndApproveVo> reLaunch(ReLaunchDto dto);
/**
* 当前人员待处理
*
* @param dto
* @return
*/
PageOutput<PendingTaskVo> pending(PendingTaskPageDto dto);
/**
* 当前人员待处理
*
* @param dto
* @return
*/
List<PendingTaskVo> pending(PendingTaskDto dto);
/**
* 审批
*
* @param dto
* @return
*/
List<LaunchAndApproveVo> newApprove(ApproveDto dto);
List<ApproveMultiVo> approveMulti(ApproveMultiDto dto);
ApproveMultiInfoVo approveMultiInfo(ApproveMultiInfoDto dto);
/**
* 获取当前用户任务的下一节点审批人
*
* @param dto
* @return
*/
boolean setApproveUser(ApproveUserDto dto);
/**
* 获取当前用户任务的下一节点审批人
*
* @param dto
* @return
*/
boolean setApproveUserMulti(ApproveUserMultiDto dto);
Set<HashMap<String, Object>> getNextUserTaskNodeMaybeArrival(String taskId, JSONObject paramsMap) throws Exception;
JSONArray getUserInfoForNextUserTaskNodeMaybeArrival(Set<Long> assigns)throws Exception;
Set<HashMap<String, Object>> getNextUserTaskNodeMaybeArrival(SequenceFlow flow, Map<String,Object> variableMap) throws Exception;
Boolean checkFlowRuleEl(String el, Map<String,Object> variableMap)throws Exception;
/**
* 获取关联任务
*
* @param dto
* @return
*/
PageOutput<RelationTaskPageVo> getRelationTaskPage(RelationTaskPageDto dto);
/**
* 查询关联任务详情
*
* @param dto
* @return
*/
RelationTaskInfoVo getRelationTaskInfo(RelationTaskInfoDto dto);
/**
* 查询任务详情
*
* @param taskId
* @return
*/
TaskInfoVo getTaskInfo(String taskId);
/**
* 获取流程详情信息
*
* @param processId
* @return
*/
ProcessInfoVo getProcessInfo(String processId);
/**
* 查询任务详情
*
* @param taskId
* @return
*/
TaskInfoVo getCirculatedTaskInfo(String taskId);
/**
* 查询流程已完成任务
* @param processInstanceId 流程id
* @return
*/
FinishedTaskVo getFinishedTask(String processInstanceId);
/**
* 根据formId 查询流程图 以及 当前流程已完成的任务
* @param dto
* @return
*/
FormFinishedTaskVo getFormFinishedTask(FormFinishedTaskDto dto);
/**
* 查询流程流转记录
* @param processInstanceId 流程id
* @return
*/
List<ProcessRecordListVo> getProcessRecord(String processInstanceId);
/**
* 获取传阅分页列表
*
* @param dto
* @return
*/
PageOutput<CirculatedTaskPageVo> getCirculatedTaskPage(CirculatedTaskPageDto dto);
/**
* 获取已办分页列表
*
* @param dto
* @return
*/
PageOutput<FinishedTaskPageVo> getFinishedTaskPage(FinishedTaskPageDto dto);
/**
* 获取我所有发起的流程
*
* @param dto
* @return
*/
PageOutput<MyProcessPageVo> getMyProcessPage(MyProcessPageDto dto);
/**
* 流程移入回收站
*
* @param dto
* @return
*/
boolean moveRecycle(MoveRecycleDto dto);
/**
* 流程移入回收站
*
* @param dto
* @return
*/
PageOutput<RecycleProcessPageVo> getRecycleProcessPage(RecycleProcessPageDto dto);
/**
* 回收站流程 重新发起
*
* @param dto
* @return
*/
RestartVo restart(RestartDto dto);
/**
* 回收站删除
*
* @param dto
* @return
*/
Boolean recycleDelete(RecycleDeleteDto dto);
/**
* 获取当前流程已经审批过的节点
* @param schemaId 流程实例id
* @param processInstanceId 流程实例id
* @return
*/
List<HistoryTaskVo> getHistoryTask(String schemaId, String processInstanceId);
/**
* 撤回
*
* @param dto
* @return
*/
boolean withdraw(WithdrawDto dto);
/**
* 保存流程草稿
*
* @param dto
* @return
*/
boolean saveDraft(SaveDraftDto dto);
/**
* 修改流程草稿
*
* @param dto
* @return
*/
boolean updateDraft(UpdateDraftDto dto);
/**
* 保存流程草稿
*
* @param dto
* @return
*/
PageOutput<DraftPageVo> draftPage(DraftPageDto dto);
/**
* 删除草稿
*
* @param ids
* @return
*/
boolean deleteDraft(List<Long> ids);
/**
* 查询草稿详情
*
* @param id
* @return
*/
DraftInfoVo draftInfo(Long id);
/**
* 查询流程监控
*
* @param dto
* @return
*/
PageOutput<MonitorPageVo> getProcessMonitorPage(MonitorPageDto dto);
/**
* 删除流程监控
*
* @param dto
* @return
*/
boolean deleteProcessMonitor(DeleteMonitorDto dto);
/**
* 设置审批人
*
* @param dto
* @return
*/
boolean setAssignee(SetAssigneeDto dto);
/**
* 流程挂起
*
* @param dto
* @return
*/
boolean setSuspended(SetSuspendedDto dto);
/**
* 获取节点审批人
*
* @param dto
* @return
*/
List<GetAssigneeVo> getAssignee(GetAssigneeDto dto);
/**
* 从流程未结束的任务获取流程当前审批人(还未审批的人)
* @param dto
* @return 按taskDefKey-taskList-taskAssigneeList组织数据
*/
Map<String,List<GetTaskAssigneeVo>> getCurrentAssignee(GetCurrentAssigneeDto dto);
/**
* 加签/减签
*
* @param dto
* @return
*/
boolean addOrSubSign(AddOrSubSignDto dto);
/**
* 获取到可以驳回的节点
* @param dto
* @return
*/
List<RejectNodeVo> getRejectNode(RejectNodeDto dto);
/**
* 获取可以撤回的节点
* @param dto
* @return
*/
List<WithdrawNodeVo> getProcessTaskCanWithdraw(WithdrawNodeDto dto);
Boolean transfer(TransferDto dto);
/**
* 获取总和
*
* @param
* @return
*/
GetCountVo getCount();
Boolean dealTimeoutTask(Integer handleType,String taskId);
}

View File

@ -0,0 +1,16 @@
package com.xjrsoft.workflow.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xjrsoft.workflow.entity.WorkflowExtra;
/**
* <p>
* 服务类
* </p>
*
* @author tzx
* @since 2022-10-11
*/
public interface IWorkflowExtraService extends IService<WorkflowExtra> {
}

View File

@ -0,0 +1,16 @@
package com.xjrsoft.workflow.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xjrsoft.workflow.entity.WorkflowFormRelation;
/**
* <p>
* 工作流 流程 与 表单关联表 服务类
* </p>
*
* @author tzx
* @since 2023-03-27
*/
public interface IWorkflowFormRelationService extends IService<WorkflowFormRelation> {
}

View File

@ -0,0 +1,16 @@
package com.xjrsoft.workflow.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xjrsoft.workflow.entity.WorkflowRecord;
/**
* <p>
* 工作流 流转记录信息 服务类
* </p>
*
* @author tzx
* @since 2022-11-10
*/
public interface IWorkflowRecordService extends IService<WorkflowRecord> {
}

View File

@ -0,0 +1,16 @@
package com.xjrsoft.workflow.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xjrsoft.workflow.entity.WorkflowSchemaAuth;
/**
* <p>
* 流程模板权限 服务类
* </p>
*
* @author tzx
* @since 2022-07-04
*/
public interface IWorkflowSchemaAuthService extends IService<WorkflowSchemaAuth> {
}

View File

@ -0,0 +1,34 @@
package com.xjrsoft.workflow.service;
import com.github.yulichang.base.MPJBaseService;
import com.xjrsoft.workflow.dto.HistoryChangeDto;
import com.xjrsoft.workflow.entity.WorkflowSchemaHistory;
import com.xjrsoft.workflow.vo.ProcessChangeVo;
import java.util.List;
/**
* <p>
* 流程模板历史记录表 服务类
* </p>
*
* @author tzx
* @since 2022-11-16
*/
public interface IWorkflowSchemaHistoryService extends MPJBaseService<WorkflowSchemaHistory> {
/**
* 设置为当前版本
* @param dto
* @return
*/
List<ProcessChangeVo> change(HistoryChangeDto dto);
/**
* 流程变更
* @param dto
* @return
*/
List<ProcessChangeVo> processChanges(HistoryChangeDto dto);
}

View File

@ -0,0 +1,76 @@
package com.xjrsoft.workflow.service;
import com.github.yulichang.base.MPJBaseService;
import com.xjrsoft.common.core.domain.page.PageOutput;
import com.xjrsoft.workflow.dto.*;
import com.xjrsoft.workflow.entity.WorkflowSchema;
import com.xjrsoft.workflow.vo.WorkflowSchemaDraftPageVo;
import com.xjrsoft.workflow.vo.WorkflowSchemaPageVo;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* <p>
* 流程模板表 服务类
* </p>
*
* @author tzx
* @since 2022-07-04
*/
public interface IWorkflowSchemaService extends MPJBaseService<WorkflowSchema> {
/**
* 新增流程模板草稿
* @param dto
* @return
*/
PageOutput<WorkflowSchemaPageVo> getPage(WorkflowSchemaPageDto dto);
/**
* 新增流程模板设计
* @param dto
* @return
*/
boolean add(AddWorkflowSchemaDto dto);
/**
* 新增流程模板草稿
* @param dto
* @return
*/
boolean addDraft(AddWorkflowSchemaDraftDto dto);
/**
* 新增流程模板草稿
* @param dto
* @return
*/
PageOutput<WorkflowSchemaDraftPageVo> getDraftPage(WorkflowSchemaDraftPageDto dto);
/**
* 修改流程模板设计
* @param dto
* @return
*/
boolean update(UpdateWorkflowSchemaDto dto);
/**
* 删除流程模板设计
* @param ids
* @return
*/
boolean delete(List<Long> ids);
/**
* 删除流程模板设计
* @param multipartFile
* @return
*/
boolean importSchema(MultipartFile multipartFile);
}

View File

@ -0,0 +1,56 @@
package com.xjrsoft.workflow.service;
import com.github.yulichang.base.MPJBaseService;
import com.xjrsoft.workflow.dto.AddSpecialMenuDto;
import com.xjrsoft.workflow.dto.UpdateSpecialMenuDto;
import com.xjrsoft.workflow.entity.WorkflowSpecialMenu;
import com.xjrsoft.workflow.vo.SpecialMenuInfoVo;
import java.util.List;
import java.util.Map;
/**
* <p>
* 专项菜单表 服务类
* </p>
*
* @author tzx
* @since 2023-03-20
*/
public interface IWorkflowSpecialMenuService extends MPJBaseService<WorkflowSpecialMenu> {
/**
* 新增专项菜单
* @param dto
* @return
*/
boolean add(AddSpecialMenuDto dto);
/**
* 修改专项菜单
* @param dto
* @return
*/
boolean update(UpdateSpecialMenuDto dto);
/**
* 删除专项菜单
* @param ids
* @return
*/
boolean delete(List<Long> ids);
/**
* 获取详情
* @param id
* @return
*/
SpecialMenuInfoVo info(Long id);
/**
* 专项菜单查询列表
* @param param
* @return
*/
List<Map<String,Object>> pending(Map<String,Object> param);
}

View File

@ -0,0 +1,20 @@
package com.xjrsoft.workflow.service.impl;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.xjrsoft.workflow.entity.WorkflowApproveRecord;
import com.xjrsoft.workflow.mapper.WorkflowApproveRecordMapper;
import com.xjrsoft.workflow.service.IWorkflowApproveRecordService;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author tzx
* @since 2023-02-10
*/
@Service
public class WorkflowApproveRecordServiceImpl extends MPJBaseServiceImpl<WorkflowApproveRecordMapper, WorkflowApproveRecord> implements IWorkflowApproveRecordService {
}

View File

@ -0,0 +1,20 @@
package com.xjrsoft.workflow.service.impl;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.xjrsoft.workflow.entity.WorkflowCirculated;
import com.xjrsoft.workflow.mapper.WorkflowCirculatedMapper;
import com.xjrsoft.workflow.service.IWorkflowCirculatedService;
import org.springframework.stereotype.Service;
/**
* <p>
* 流程传阅信息表 服务实现类
* </p>
*
* @author tzx
* @since 2023-02-09
*/
@Service
public class WorkflowCirculatedServiceImpl extends MPJBaseServiceImpl<WorkflowCirculatedMapper, WorkflowCirculated> implements IWorkflowCirculatedService {
}

View File

@ -0,0 +1,109 @@
package com.xjrsoft.workflow.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.common.core.domain.page.PageOutput;
import com.xjrsoft.common.mybatis.utils.ConventPage;
import com.xjrsoft.common.redis.service.RedisUtil;
import com.xjrsoft.organization.entity.User;
import com.xjrsoft.workflow.dto.AddDelegateDto;
import com.xjrsoft.workflow.dto.DelegatePageDto;
import com.xjrsoft.workflow.dto.UpdateDelegateDto;
import com.xjrsoft.workflow.entity.WorkflowDelegate;
import com.xjrsoft.workflow.mapper.WorkflowDelegateMapper;
import com.xjrsoft.workflow.service.IWorkflowDelegateService;
import com.xjrsoft.workflow.vo.DelegatePageVo;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* <p>
* 流程委托 服务实现类
* </p>
*
* @author tzx
* @since 2022-11-05
*/
@Service
@AllArgsConstructor
public class WorkflowDelegateServiceImpl extends ServiceImpl<WorkflowDelegateMapper, WorkflowDelegate> implements IWorkflowDelegateService {
private final RedisUtil redisUtil;
@Override
public PageOutput<DelegatePageVo> page(DelegatePageDto dto) {
//用户只能看到自己的流程委托信息
LambdaQueryWrapper<WorkflowDelegate> eq = Wrappers.lambdaQuery(WorkflowDelegate.class)
.eq(WorkflowDelegate::getUserId,StpUtil.getLoginIdAsLong())
.like(StrUtil.isNotBlank(dto.getKeyword()), WorkflowDelegate::getRemark, dto.getKeyword())
.or(StrUtil.isNotBlank(dto.getKeyword()))
.like(StrUtil.isNotBlank(dto.getKeyword()), WorkflowDelegate::getDelegateUserNames, dto.getKeyword());
IPage<WorkflowDelegate> page = page(ConventPage.getPage(dto), eq);
PageOutput<DelegatePageVo> pageOutput = ConventPage.getPageOutput(page, DelegatePageVo.class);
User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
for (DelegatePageVo delegatePageVo : pageOutput.getList()) {
delegatePageVo.setDelegator(user.getName());
}
return pageOutput;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean add(AddDelegateDto dto) {
WorkflowDelegate delegate = BeanUtil.toBean(dto, WorkflowDelegate.class);
delegate.setUserId(StpUtil.getLoginIdAsLong());
List<User> allUser = redisUtil.get(GlobalConstant.USER_CACHE_KEY, new TypeReference<List<User>>() {
});
String allUserIdStr = StrUtil.join(StringPool.COMMA, dto.getDelegateUserIds());
List<Long> ids = Arrays.stream(allUserIdStr.split(StringPool.COMMA)).map(Convert::toLong).collect(Collectors.toList());
List<String> names = allUser.stream().filter(x -> ids.contains(x.getId())).map(User::getName).collect(Collectors.toList());
delegate.setDelegateUserNames(StrUtil.join(StringPool.COMMA,names));
return save(delegate);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean update(UpdateDelegateDto dto) {
WorkflowDelegate delegate = BeanUtil.toBean(dto, WorkflowDelegate.class);
delegate.setUserId(StpUtil.getLoginIdAsLong());
List<User> allUser = redisUtil.get(GlobalConstant.USER_CACHE_KEY, new TypeReference<List<User>>() {
});
String allUserIdStr = StrUtil.join(StringPool.COMMA, dto.getDelegateUserIds());
List<Long> ids = Arrays.stream(allUserIdStr.split(StringPool.COMMA)).map(Convert::toLong).collect(Collectors.toList());
List<String> names = allUser.stream().filter(x -> ids.contains(x.getId())).map(User::getName).collect(Collectors.toList());
delegate.setDelegateUserNames(StrUtil.join(StringPool.COMMA,names));
return updateById(delegate);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean delete(List<Long> ids){
return removeBatchByIds(ids);
}
}

View File

@ -0,0 +1,20 @@
package com.xjrsoft.workflow.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xjrsoft.workflow.entity.WorkflowExtra;
import com.xjrsoft.workflow.mapper.WorkflowExtraMapper;
import com.xjrsoft.workflow.service.IWorkflowExtraService;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author tzx
* @since 2022-10-11
*/
@Service
public class WorkflowExtraServiceImpl extends ServiceImpl<WorkflowExtraMapper, WorkflowExtra> implements IWorkflowExtraService {
}

View File

@ -0,0 +1,20 @@
package com.xjrsoft.workflow.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xjrsoft.workflow.entity.WorkflowFormRelation;
import com.xjrsoft.workflow.mapper.WorkflowFormRelationMapper;
import com.xjrsoft.workflow.service.IWorkflowFormRelationService;
import org.springframework.stereotype.Service;
/**
* <p>
* 工作流 流程 与 表单关联表 服务实现类
* </p>
*
* @author tzx
* @since 2023-03-27
*/
@Service
public class WorkflowFormRelationServiceImpl extends ServiceImpl<WorkflowFormRelationMapper, WorkflowFormRelation> implements IWorkflowFormRelationService {
}

View File

@ -0,0 +1,20 @@
package com.xjrsoft.workflow.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xjrsoft.workflow.entity.WorkflowRecord;
import com.xjrsoft.workflow.mapper.WorkflowRecordMapper;
import com.xjrsoft.workflow.service.IWorkflowRecordService;
import org.springframework.stereotype.Service;
/**
* <p>
* 工作流 流转记录信息 服务实现类
* </p>
*
* @author tzx
* @since 2022-11-10
*/
@Service
public class WorkflowRecordServiceImpl extends ServiceImpl<WorkflowRecordMapper, WorkflowRecord> implements IWorkflowRecordService {
}

View File

@ -0,0 +1,22 @@
package com.xjrsoft.workflow.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xjrsoft.workflow.entity.WorkflowSchemaAuth;
import com.xjrsoft.workflow.mapper.WorkflowSchemaAuthMapper;
import com.xjrsoft.workflow.service.IWorkflowSchemaAuthService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
/**
* <p>
* 流程模板权限 服务实现类
* </p>
*
* @author tzx
* @since 2022-07-04
*/
@Service
@AllArgsConstructor
public class WorkflowSchemaAuthServiceImpl extends ServiceImpl<WorkflowSchemaAuthMapper, WorkflowSchemaAuth> implements IWorkflowSchemaAuthService {
}

View File

@ -0,0 +1,226 @@
package com.xjrsoft.workflow.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.common.core.enums.EnabledMark;
import com.xjrsoft.common.core.enums.WorkflowMultiInstanceType;
import com.xjrsoft.common.core.exception.MyException;
import com.xjrsoft.organization.entity.User;
import com.xjrsoft.workflow.constant.WorkflowConstant;
import com.xjrsoft.workflow.dto.HistoryChangeDto;
import com.xjrsoft.workflow.entity.WorkflowRecord;
import com.xjrsoft.workflow.entity.WorkflowSchema;
import com.xjrsoft.workflow.entity.WorkflowSchemaHistory;
import com.xjrsoft.workflow.mapper.WorkflowSchemaHistoryMapper;
import com.xjrsoft.workflow.mapper.WorkflowSchemaMapper;
import com.xjrsoft.workflow.model.WorkflowSchemaConfig;
import com.xjrsoft.workflow.service.IWorkflowRecordService;
import com.xjrsoft.workflow.service.IWorkflowSchemaHistoryService;
import com.xjrsoft.workflow.utils.WorkFlowUtil;
import com.xjrsoft.workflow.vo.ProcessChangeVo;
import lombok.AllArgsConstructor;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.history.HistoricVariableInstance;
import org.camunda.bpm.engine.impl.persistence.entity.HistoricVariableInstanceEntity;
import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity;
import org.camunda.bpm.engine.migration.MigrationPlan;
import org.camunda.bpm.engine.migration.MigrationPlanExecutionBuilder;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.spring.SpringProcessEngineConfiguration;
import org.camunda.bpm.engine.task.Task;
import org.camunda.commons.utils.IoUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/**
* <p>
* 流程模板历史记录表 服务实现类
* </p>
*
* @author tzx
* @since 2022-11-16
*/
@Service
@AllArgsConstructor
public class WorkflowSchemaHistoryServiceImpl extends MPJBaseServiceImpl<WorkflowSchemaHistoryMapper, WorkflowSchemaHistory> implements IWorkflowSchemaHistoryService {
private final WorkflowSchemaHistoryMapper workflowSchemaHistoryMapper;
private final RepositoryService repositoryService;
private final WorkflowSchemaMapper workflowSchemaMapper;
private final HistoryService historyService;
private final RuntimeService runtimeService;
private final TaskService taskService;
private final SpringProcessEngineConfiguration processEngineConfiguration;
@Override
@Transactional(rollbackFor = Exception.class)
public List<ProcessChangeVo> change(HistoryChangeDto dto) {
WorkflowSchema workflowSchema = workflowSchemaMapper.selectById(dto.getSchemaId());
WorkflowSchemaHistory history = getById(dto.getId());
WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(history.getJsonContent(), WorkflowSchemaConfig.class);
WorkflowSchema newWorkflowSchema = BeanUtil.toBean(workflowSchemaConfig.getProcessConfig(), WorkflowSchema.class);
newWorkflowSchema.setId(workflowSchema.getId());
newWorkflowSchema.setXmlContent(history.getXmlContent());
newWorkflowSchema.setJsonContent(history.getJsonContent());
//找到历史记录当前活动流程
// WorkflowSchemaHistory activeHistory = getOne(Wrappers.lambdaQuery(WorkflowSchemaHistory.class).eq(WorkflowSchemaHistory::getSchemaId, dto.getSchemaId()).eq(WorkflowSchemaHistory::getActivityFlag, YesOrNoEnum.YES.getCode()));
//找到所有非当前所选历史记录的所有数据
List<WorkflowSchemaHistory> list = list(Wrappers.lambdaQuery(WorkflowSchemaHistory.class).eq(WorkflowSchemaHistory::getSchemaId, dto.getSchemaId()).ne(WorkflowSchemaHistory::getId, dto.getId()).select(WorkflowSchemaHistory::getDefinitionId,WorkflowSchemaHistory::getDefinitionKey));
//将所有历史记录设置为 非活动版本
WorkflowSchemaHistory paramObj = new WorkflowSchemaHistory();
paramObj.setActivityFlag(EnabledMark.DISABLED.getCode());
LambdaQueryWrapper<WorkflowSchemaHistory> wrapper = Wrappers.lambdaQuery(WorkflowSchemaHistory.class).eq(WorkflowSchemaHistory::getSchemaId, dto.getSchemaId());
workflowSchemaHistoryMapper.update(paramObj, wrapper);
//将当前所选history 设置为活动版本
wrapper.eq(WorkflowSchemaHistory::getId, dto.getId());
paramObj.setActivityFlag(EnabledMark.ENABLED.getCode());
workflowSchemaHistoryMapper.update(paramObj, wrapper);
try {
for (WorkflowSchemaHistory workflowSchemaHistory : list) {
MigrationPlan migrationPlan = runtimeService
.createMigrationPlan(workflowSchemaHistory.getDefinitionId(), history.getDefinitionId())
.mapEqualActivities()
.updateEventTriggers()
.build();
//找到当前流程模板的正在运行的所有流程
List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().processDefinitionId(workflowSchemaHistory.getDefinitionId()).active().list();
if (processInstances.size() > 0) {
List<String> processInstanceIds = processInstances.stream().map(ProcessInstance::getProcessInstanceId).collect(Collectors.toList());
MigrationPlanExecutionBuilder migrationPlanExecutionBuilder = runtimeService.newMigration(migrationPlan).processInstanceIds(processInstanceIds)
.skipCustomListeners()
.skipIoMappings();
migrationPlanExecutionBuilder.execute();
}
}
}
catch (Exception e){
throw new MyException("当前流程图过于复杂,并且流程历史记录的模板,有正在进行的流程,不适用于迁移,请等待进行的流程完结!");
}
List<ProcessInstance> newProcesses = runtimeService.createProcessInstanceQuery().processDefinitionId(history.getDefinitionId()).list();
String[] processIds = newProcesses.stream().map(ProcessInstance::getProcessInstanceId).toArray(String[]::new);
List<HistoricVariableInstance> variableInstanceList = historyService.createHistoricVariableInstanceQuery().processInstanceIdIn(processIds).variableName(WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY).list();
List<Task> taskList = taskService.createTaskQuery().processInstanceIdIn(processIds).list();
List<ProcessChangeVo> voList = new ArrayList<>(newProcesses.size());
for (ProcessInstance newProcess : newProcesses) {
ProcessChangeVo vo = new ProcessChangeVo();
vo.setName(newWorkflowSchema.getName());
HistoricVariableInstance historicVariableInstance = variableInstanceList.stream().filter(x -> x.getProcessInstanceId().equals(newProcess.getProcessInstanceId())).findFirst().orElse(new HistoricVariableInstanceEntity());
vo.setSerailNumber(Convert.toInt(historicVariableInstance.getValue()));
vo.setStatus(1);
Task task = taskList.stream().filter(x -> x.getProcessInstanceId().equals(newProcess.getProcessInstanceId())).findFirst().orElse(new TaskEntity());
//获取当前用户的信息
User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
Long schemaId = Convert.toLong(taskService.getVariable(task.getId(), WorkflowConstant.PROCESS_SCHEMA_ID_KEY));
IWorkflowRecordService workflowRecordService = SpringUtil.getBean(IWorkflowRecordService.class);
//新增流程发起流程记录
WorkflowRecord record = new WorkflowRecord();
record.setNodeId(task.getId());
record.setNodeName(task.getName());
record.setNodeType(WorkflowConstant.USER_TASK_TYPE_NAME);
record.setProcessId(task.getProcessInstanceId());
record.setSchemaId(schemaId);
record.setNodeMultiType(WorkflowMultiInstanceType.NONE.getCode());
record.setRecordTime(LocalDateTime.now());
//如果当前任务的节点被删除 默认将此任务完成
if (workflowSchemaConfig.getChildNodeConfig().stream().noneMatch(x -> x.get(WorkflowConstant.NODE_CONFIG_ID_KEY).equals(task.getTaskDefinitionKey()))) {
taskService.complete(task.getId());
List<Task> nextTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
List<String> nextTaskNameList = nextTaskList.stream().map(Task::getName).collect(Collectors.toList());
vo.setDetail("流程已经变更到" + StrUtil.join(StringPool.COMMA, nextTaskNameList) + "节点");
//[操作人名称]对流程进行了变更,流程当前节点为[节点名称]。
record.setMessage("" + user.getName() + "】对流程进行了变更,流程当前节点为【" + StrUtil.join("", nextTaskNameList) + "");
workflowRecordService.save(record);
} else {
vo.setDetail("流程已经变更到" + task.getName() + "节点");
//[操作人名称]对流程进行了变更,流程当前节点为[节点名称]。
record.setMessage("" + user.getName() + "】对流程进行了变更,流程当前节点为【" + task.getName() + "");
workflowRecordService.save(record);
}
voList.add(vo);
}
//更新的流程重新部署
Deployment deploy = repositoryService.createDeployment()
.addInputStream(newWorkflowSchema.getName() + StringPool.DOT + WorkflowConstant.WORKFLOW_SUFFIX, IoUtil.stringAsInputStream(newWorkflowSchema.getXmlContent())).name(newWorkflowSchema.getName())
.deploy();
newWorkflowSchema.setDeploymentId(deploy.getId());
workflowSchemaMapper.updateById(newWorkflowSchema);
//缓存节点监听器数据
CompletableFuture.runAsync(() -> {
WorkFlowUtil.removeNodeListener(workflowSchema.getDeploymentId());
WorkFlowUtil.cacheNodeListener(deploy.getId(), workflowSchemaConfig.getChildNodeConfig());
});
return voList;
}
@Override
public List<ProcessChangeVo> processChanges(HistoryChangeDto dto) {
return null;
}
}

View File

@ -0,0 +1,502 @@
package com.xjrsoft.workflow.service.impl;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.toolkit.MPJWrappers;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.common.core.domain.page.PageOutput;
import com.xjrsoft.common.core.enums.EnabledMark;
import com.xjrsoft.common.core.enums.YesOrNoEnum;
import com.xjrsoft.common.core.exception.MyException;
import com.xjrsoft.common.core.uitls.VoToColumnUtil;
import com.xjrsoft.common.mybatis.utils.ConventPage;
import com.xjrsoft.organization.entity.Post;
import com.xjrsoft.system.entity.DictionaryDetail;
import com.xjrsoft.workflow.constant.WorkflowConstant;
import com.xjrsoft.workflow.dto.*;
import com.xjrsoft.workflow.entity.WorkflowSchema;
import com.xjrsoft.workflow.entity.WorkflowSchemaAuth;
import com.xjrsoft.workflow.entity.WorkflowSchemaDraft;
import com.xjrsoft.workflow.entity.WorkflowSchemaHistory;
import com.xjrsoft.workflow.mapper.WorkflowSchemaDraftMapper;
import com.xjrsoft.workflow.mapper.WorkflowSchemaHistoryMapper;
import com.xjrsoft.workflow.mapper.WorkflowSchemaMapper;
import com.xjrsoft.workflow.model.AuthConfig;
import com.xjrsoft.workflow.model.FormInitConfig;
import com.xjrsoft.workflow.model.MemberConfig;
import com.xjrsoft.workflow.model.WorkflowSchemaConfig;
import com.xjrsoft.workflow.service.IWorkflowSchemaAuthService;
import com.xjrsoft.workflow.service.IWorkflowSchemaService;
import com.xjrsoft.workflow.utils.WorkFlowUtil;
import com.xjrsoft.workflow.vo.WorkflowSchemaDraftPageVo;
import com.xjrsoft.workflow.vo.WorkflowSchemaPageVo;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.spring.SpringProcessEngineConfiguration;
import org.camunda.commons.utils.IoUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/**
* <p>
* 流程模板表 服务实现类
* </p>
*
* @author tzx
* @since 2022-07-04
*/
@Service
@AllArgsConstructor
public class WorkflowSchemaServiceImpl extends MPJBaseServiceImpl<WorkflowSchemaMapper, WorkflowSchema> implements IWorkflowSchemaService {
private final RepositoryService repositoryService;
private final SpringProcessEngineConfiguration processEngineConfiguration;
private final WorkflowSchemaDraftMapper workflowSchemaDraftMapper;
private final WorkflowSchemaHistoryMapper workflowSchemaHistoryMapper;
private final TaskService taskService;
private final IWorkflowSchemaAuthService workflowSchemaAuthService;
@Override
public PageOutput<WorkflowSchemaPageVo> getPage(WorkflowSchemaPageDto dto) {
SaSession tokenSession = StpUtil.getTokenSession();
List<Long> roleIds = tokenSession.get(GlobalConstant.LOGIN_USER_ROLE_ID_KEY, new ArrayList<>());
Post post = tokenSession.get(GlobalConstant.LOGIN_USER_POST_INFO_KEY, new Post());
List<Long> allSchemaId = new ArrayList<>();
//是否需要管控权限
if (dto.getIsAuth()) {
LambdaQueryWrapper<WorkflowSchemaAuth> query = Wrappers.lambdaQuery(WorkflowSchemaAuth.class)
.eq(WorkflowSchemaAuth::getObjType, -1)
.or(x -> x.eq(WorkflowSchemaAuth::getObjType, 2).in(WorkflowSchemaAuth::getObjId, post.getId()))
.or(x -> x.eq(WorkflowSchemaAuth::getObjType, 1).in(WorkflowSchemaAuth::getObjId, roleIds))
.or(x -> x.eq(WorkflowSchemaAuth::getObjType, 0).in(WorkflowSchemaAuth::getObjId, StpUtil.getLoginIdAsLong())
);
List<WorkflowSchemaAuth> authList = workflowSchemaAuthService.list(query);
allSchemaId = authList.stream().map(WorkflowSchemaAuth::getSchemaId).collect(Collectors.toList());
if (CollectionUtil.isEmpty(allSchemaId)) {
//如果权限为空 返回空数组
PageOutput<WorkflowSchemaPageVo> pageOutput = new PageOutput<>();
pageOutput.setList(new ArrayList<>());
pageOutput.setCurrentPage(0);
pageOutput.setTotalPage(0);
pageOutput.setPageSize(0);
return pageOutput;
}
}
//因为多表关联 会有多个表都使用了id字段 所以必须专门指定主表的Id
IPage<WorkflowSchemaPageVo> page = selectJoinListPage(ConventPage.getPage(dto), WorkflowSchemaPageVo.class,
MPJWrappers.<WorkflowSchema>lambdaJoin()
.disableSubLogicDel()
.orderByDesc(WorkflowSchema::getId)
.eq(ObjectUtil.isNotNull(dto.getEnabledMark()), WorkflowSchema::getEnabledMark, dto.getEnabledMark())
.like(StrUtil.isNotBlank(dto.getKeyword()), WorkflowSchema::getName, dto.getKeyword())
.like(StrUtil.isNotBlank(dto.getName()), WorkflowSchema::getName, dto.getName())
.like(StrUtil.isNotBlank(dto.getCode()), WorkflowSchema::getCode, dto.getCode())
.eq(ObjectUtil.isNotNull(dto.getCategory()), WorkflowSchema::getCategory, dto.getCategory())
.in(dto.getIsAuth() && CollectionUtil.isNotEmpty(allSchemaId), WorkflowSchema::getId, allSchemaId)
.select(WorkflowSchema::getId)
.select(WorkflowSchema.class, x -> VoToColumnUtil.fieldsToColumns(WorkflowSchemaPageVo.class).contains(x.getProperty()))
.selectAs(DictionaryDetail::getName, WorkflowSchemaPageVo::getCategoryName)
.leftJoin(DictionaryDetail.class, DictionaryDetail::getId, WorkflowSchema::getCategory));
return ConventPage.getPageOutput(page);
}
@Override
@Transactional(rollbackFor = Exception.class)
@SneakyThrows
public boolean add(AddWorkflowSchemaDto dto) {
LambdaQueryWrapper<WorkflowSchema> countQuery = Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getName, dto.getProcessConfig().getName()).or().eq(WorkflowSchema::getCode, dto.getProcessConfig().getCode());
if (count(countQuery) > 0) {
throw new MyException("当前模板名称和模板编号重复!");
}
WorkflowSchemaConfig workflowSchemaConfig = BeanUtil.toBean(dto, WorkflowSchemaConfig.class);
WorkflowSchema workflowSchema = BeanUtil.toBean(dto.getProcessConfig(), WorkflowSchema.class);
//表单发起流程
FormInitConfig formInitConfig = workflowSchemaConfig.getProcessConfig().getFormInitConfig();
if (formInitConfig.getEnabled()) {
LambdaQueryWrapper<WorkflowSchema> select = Wrappers.lambdaQuery(WorkflowSchema.class).eq(WorkflowSchema::getFormId, formInitConfig.getFormId()).select(WorkflowSchema::getId);
if (count(select) > 0) {
throw new MyException("当前表单已经绑定过流程,请重新选择!");
}
workflowSchema.setFormId(formInitConfig.getFormId());
}
Deployment deploy = repositoryService.createDeployment()
.addInputStream(workflowSchemaConfig.getProcessConfig().getName() + StringPool.DOT + WorkflowConstant.WORKFLOW_SUFFIX, IoUtil.stringAsInputStream(workflowSchemaConfig.getProcessConfig().getXmlContent())).name(workflowSchemaConfig.getProcessConfig().getName()).deploy();
//存储流程部署id
workflowSchema.setDeploymentId(deploy.getId());
//获取流程定义id
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
//存储流程定义key
workflowSchema.setDefinitionKey(processDefinition.getKey());
//存储流程定义id
workflowSchema.setDefinitionId(processDefinition.getId());
//把配置json存储 (清除掉xml)
workflowSchemaConfig.getProcessConfig().setXmlContent("");
ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
mapper.registerModule(simpleModule);
String jsonContent = mapper.writeValueAsString(workflowSchemaConfig);
workflowSchema.setJsonContent(jsonContent);
save(workflowSchema);
AuthConfig authConfig = workflowSchemaConfig.getProcessConfig().getAuthConfig();
//如果是指定人员
if (authConfig.getAuthType() == 1) {
List<WorkflowSchemaAuth> authList = new ArrayList<>();
List<MemberConfig> authMemberConfigs = authConfig.getAuthMemberConfigs();
for (MemberConfig authMemberConfig : authMemberConfigs) {
WorkflowSchemaAuth auth = new WorkflowSchemaAuth();
auth.setSchemaId(workflowSchema.getId());
auth.setObjId(Long.parseLong(authMemberConfig.getId()));
auth.setObjName(authMemberConfig.getName());
auth.setObjType(authMemberConfig.getMemberType());
authList.add(auth);
}
workflowSchemaAuthService.saveBatch(authList);
}
//如果所有人
else {
WorkflowSchemaAuth auth = new WorkflowSchemaAuth();
auth.setSchemaId(workflowSchema.getId());
auth.setObjType(-1);
workflowSchemaAuthService.save(auth);
}
//将新增的数据 记录模板历史记录
WorkflowSchemaHistory history = new WorkflowSchemaHistory();
history.setSchemaId(workflowSchema.getId());
history.setVersion(1);
history.setActivityFlag(EnabledMark.ENABLED.getCode());
history.setXmlContent(workflowSchema.getXmlContent());
history.setJsonContent(workflowSchema.getJsonContent());
history.setDefinitionKey(workflowSchema.getDefinitionKey());
history.setDeploymentId(workflowSchema.getDeploymentId());
history.setDefinitionId(workflowSchema.getDefinitionId());
//缓存节点监听器数据
CompletableFuture.runAsync(() -> {
WorkFlowUtil.cacheNodeListener(deploy.getId(), workflowSchemaConfig.getChildNodeConfig());
});
return workflowSchemaHistoryMapper.insert(history) > 0;
}
@Override
public boolean addDraft(AddWorkflowSchemaDraftDto dto) {
WorkflowSchemaConfig workflowSchemaConfig = BeanUtil.toBean(dto, WorkflowSchemaConfig.class);
WorkflowSchemaDraft workflowSchemaDraft = BeanUtil.toBean(dto.getProcessConfig(), WorkflowSchemaDraft.class);
//把配置json存储
workflowSchemaDraft.setJsonContent(JSONUtil.toJsonStr(workflowSchemaConfig));
return workflowSchemaDraftMapper.insert(workflowSchemaDraft) > 0;
}
@Override
public PageOutput<WorkflowSchemaDraftPageVo> getDraftPage(WorkflowSchemaDraftPageDto dto) {
LambdaQueryWrapper<WorkflowSchemaDraft> queryWrapper = Wrappers.lambdaQuery(WorkflowSchemaDraft.class)
.like(StrUtil.isNotBlank(dto.getKeyword()), WorkflowSchemaDraft::getName, dto.getKeyword())
.between(ObjectUtil.isNotNull(dto.getStartTime()) && ObjectUtil.isNotNull(dto.getEndTime()), WorkflowSchemaDraft::getCreateDate, dto.getStartTime(), dto.getEndTime())
.select(WorkflowSchemaDraft.class, x -> VoToColumnUtil.fieldsToColumns(WorkflowSchemaPageVo.class).contains(x.getProperty()));
IPage<WorkflowSchemaDraft> workflowSchemaDraftPage = workflowSchemaDraftMapper.selectPage(ConventPage.getPage(dto), queryWrapper);
return ConventPage.getPageOutput(workflowSchemaDraftPage, WorkflowSchemaDraftPageVo.class);
}
@Override
@Transactional(rollbackFor = Exception.class)
@SneakyThrows
public boolean update(UpdateWorkflowSchemaDto dto) {
LambdaQueryWrapper<WorkflowSchema> countQuery = Wrappers.lambdaQuery(WorkflowSchema.class).ne(WorkflowSchema::getId, dto.getId()).and(x -> x.eq(WorkflowSchema::getName, dto.getProcessConfig().getName()).or().eq(WorkflowSchema::getCode, dto.getProcessConfig().getCode()));
if (count(countQuery) > 0) {
throw new MyException("当前模板名称和模板编号重复!");
}
WorkflowSchema workflowSchema = getById(dto.getId());
if (workflowSchema == null) {
throw new MyException("找不到此模板!");
}
workflowSchema.setName(dto.getProcessConfig().getName());
workflowSchema.setCode(dto.getProcessConfig().getCode());
workflowSchema.setRemark(dto.getProcessConfig().getRemark());
workflowSchema.setXmlContent(dto.getProcessConfig().getXmlContent());
workflowSchema.setWorkflowChat(dto.getProcessConfig().getWorkflowChat());
workflowSchema.setCategory(Long.valueOf(dto.getProcessConfig().getCategory()));
WorkflowSchemaConfig workflowSchemaConfig = BeanUtil.toBean(dto, WorkflowSchemaConfig.class);
//表单发起流程
FormInitConfig formInitConfig = workflowSchemaConfig.getProcessConfig().getFormInitConfig();
if (formInitConfig.getEnabled()) {
LambdaQueryWrapper<WorkflowSchema> select = Wrappers.lambdaQuery(WorkflowSchema.class).ne(WorkflowSchema::getId, dto.getId()).eq(WorkflowSchema::getFormId, formInitConfig.getFormId()).select(WorkflowSchema::getId);
if (count(select) > 0) {
throw new MyException("当前表单已经绑定过流程,请重新选择!");
}
workflowSchema.setFormId(formInitConfig.getFormId());
} else {
workflowSchema.setFormId(0L);
}
// ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(workflowSchema.getDeploymentId()).singleResult();
//
//
// //如果需要级联删除 把第二个参数设置为true
// //级联删除 会删除所有关联数据 包括 历史记录
// repositoryService.deleteProcessDefinition(processDefinition.getId());
//
// //如果需要级联删除 把第二个参数设置为true
// //级联删除 会删除所有关联数据 包括 历史记录
// repositoryService.deleteDeployment(workflowSchema.getDeploymentId());
//因为流程图会被缓存 如果修改了流程图 缓存会没变。 需要清理本地缓存
// processEngineConfiguration.getDeploymentCache().removeDeployment(workflowSchema.getDeploymentId());
//更新的流程重新部署
Deployment deploy = repositoryService.createDeployment()
.addInputStream(dto.getProcessConfig().getName() + StringPool.DOT + WorkflowConstant.WORKFLOW_SUFFIX, IoUtil.stringAsInputStream(dto.getProcessConfig().getXmlContent())).name(dto.getProcessConfig().getName()).deploy();
//获取流程定义id
ProcessDefinition newProcessDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
//存储流程定义key
workflowSchema.setDefinitionKey(newProcessDefinition.getKey());
//存储流程定义id
workflowSchema.setDefinitionId(newProcessDefinition.getId());
workflowSchema.setDeploymentId(deploy.getId());
AuthConfig authConfig = workflowSchemaConfig.getProcessConfig().getAuthConfig();
//如果是指定人员
if (authConfig.getAuthType() == 1) {
workflowSchemaAuthService.remove(Wrappers.lambdaQuery(WorkflowSchemaAuth.class).eq(WorkflowSchemaAuth::getSchemaId, workflowSchema.getId()));
List<WorkflowSchemaAuth> authList = new ArrayList<>();
List<MemberConfig> authMemberConfigs = authConfig.getAuthMemberConfigs();
for (MemberConfig authMemberConfig : authMemberConfigs) {
WorkflowSchemaAuth auth = new WorkflowSchemaAuth();
auth.setSchemaId(workflowSchema.getId());
auth.setObjId(Long.parseLong(authMemberConfig.getId()));
auth.setObjName(authMemberConfig.getName());
auth.setObjType(authMemberConfig.getMemberType());
authList.add(auth);
}
workflowSchemaAuthService.saveBatch(authList);
}
//如果所有人
else {
workflowSchemaAuthService.remove(Wrappers.lambdaQuery(WorkflowSchemaAuth.class).eq(WorkflowSchemaAuth::getSchemaId, workflowSchema.getId()));
WorkflowSchemaAuth auth = new WorkflowSchemaAuth();
auth.setSchemaId(workflowSchema.getId());
auth.setObjType(-1);
workflowSchemaAuthService.save(auth);
}
//找到上一个版本号 只需要版本号 以及 id
LambdaQueryWrapper<WorkflowSchemaHistory> queryWrapper = Wrappers.lambdaQuery(WorkflowSchemaHistory.class)
.eq(WorkflowSchemaHistory::getSchemaId, workflowSchema.getId())
.eq(WorkflowSchemaHistory::getActivityFlag, YesOrNoEnum.YES.getCode())
.select(WorkflowSchemaHistory::getVersion, WorkflowSchemaHistory::getId)
.orderByDesc(WorkflowSchemaHistory::getVersion);
WorkflowSchemaHistory oldHistory = workflowSchemaHistoryMapper.selectOne(queryWrapper);
Integer version = oldHistory.getVersion();
//把配置json存储 (清除掉xml)
workflowSchemaConfig.getProcessConfig().setXmlContent("");
ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
mapper.registerModule(simpleModule);
String jsonContent = mapper.writeValueAsString(workflowSchemaConfig);
workflowSchema.setJsonContent(jsonContent);
//将修改后的数据 记录模板历史记录
WorkflowSchemaHistory history = new WorkflowSchemaHistory();
history.setSchemaId(workflowSchema.getId());
history.setVersion(version + 1);
history.setActivityFlag(EnabledMark.ENABLED.getCode());
history.setXmlContent(workflowSchema.getXmlContent());
history.setJsonContent(workflowSchema.getJsonContent());
history.setWorkflowChat(workflowSchema.getWorkflowChat());
history.setDefinitionId(workflowSchema.getDefinitionId());
history.setDefinitionKey(workflowSchema.getDefinitionKey());
workflowSchemaHistoryMapper.insert(history);
//将上一个版本的 历史数据 flag 改为 非活动版本
oldHistory.setActivityFlag(EnabledMark.DISABLED.getCode());
workflowSchemaHistoryMapper.updateById(oldHistory);
//缓存节点监听器数据
CompletableFuture.runAsync(() -> {
WorkFlowUtil.cacheNodeListener(deploy.getId(), workflowSchemaConfig.getChildNodeConfig());
});
return updateById(workflowSchema);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean delete(List<Long> ids) {
for (Long id : ids) {
long count = taskService.createTaskQuery().processVariableValueEquals(WorkflowConstant.PROCESS_SCHEMA_ID_KEY, id).count();
if (count > 0) {
throw new MyException("有流程正在执行 无法删除!");
}
}
QueryWrapper<WorkflowSchema> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.in(WorkflowSchema::getId, ids)
.select(WorkflowSchema::getDeploymentId, WorkflowSchema::getDefinitionId);
List<WorkflowSchema> workflowSchemas = list(queryWrapper);
//获取到所有流程定义id
List<String> definitionIds = workflowSchemas.stream().map(WorkflowSchema::getDefinitionId).collect(Collectors.toList());
//获取到所有deployIds
List<String> deploymentIds = workflowSchemas.stream().map(WorkflowSchema::getDeploymentId).collect(Collectors.toList());
try {
repositoryService.deleteProcessDefinitions().byIds(StrUtil.join(",", definitionIds)).delete();
}catch (Exception e){
if (e.getMessage().contains("since there exists")) {
throw new MyException("有流程正在执行 无法删除!");//避免有外部任务正在执行的流程删除时,提示引擎删除错误。
}
}
for (String deploymentId : deploymentIds) {
repositoryService.deleteDeployment(deploymentId);
}
//缓存节点监听器数据
CompletableFuture.runAsync(() -> {
for (String deploymentId : deploymentIds) {
WorkFlowUtil.removeNodeListener(deploymentId);
}
});
return removeBatchByIds(ids);
}
@Override
@Transactional(rollbackFor = Exception.class)
@SneakyThrows
public boolean importSchema(MultipartFile multipartFile) {
//用流读取文件
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(multipartFile.getInputStream()));
String line;
StringBuilder content = new StringBuilder();
// 读取想定文件
while ((line = bufferedReader.readLine()) != null) {
content.append(line);
}
WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(content.toString(), WorkflowSchemaConfig.class);
WorkflowSchema workflowSchema = BeanUtil.toBean(workflowSchemaConfig.getProcessConfig(), WorkflowSchema.class);
Deployment deploy = repositoryService.createDeployment()
.addInputStream(workflowSchemaConfig.getProcessConfig().getName() + StringPool.DOT + WorkflowConstant.WORKFLOW_SUFFIX, IoUtil.stringAsInputStream(workflowSchemaConfig.getProcessConfig().getXmlContent())).name(workflowSchemaConfig.getProcessConfig().getName()).deploy();
//存储流程部署id
workflowSchema.setDeploymentId(deploy.getId());
//获取流程定义id
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
//存储流程定义key
workflowSchema.setDefinitionKey(processDefinition.getKey());
//存储流程定义id
workflowSchema.setDefinitionId(processDefinition.getId());
//把配置json存储 (清除掉xml)
workflowSchemaConfig.getProcessConfig().setXmlContent("");
workflowSchema.setJsonContent(JSONUtil.toJsonStr(workflowSchemaConfig));
save(workflowSchema);
//将新增的数据 记录模板历史记录
WorkflowSchemaHistory history = new WorkflowSchemaHistory();
history.setSchemaId(workflowSchema.getId());
history.setVersion(1);
history.setActivityFlag(EnabledMark.ENABLED.getCode());
history.setXmlContent(workflowSchema.getXmlContent());
history.setJsonContent(workflowSchema.getJsonContent());
//缓存节点监听器数据
CompletableFuture.runAsync(() -> {
WorkFlowUtil.cacheNodeListener(deploy.getId(), workflowSchemaConfig.getChildNodeConfig());
});
return workflowSchemaHistoryMapper.insert(history) > 0;
}
}

View File

@ -0,0 +1,199 @@
package com.xjrsoft.workflow.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.common.core.enums.MenuType;
import com.xjrsoft.common.core.enums.WorkflowIsRecycleType;
import com.xjrsoft.common.core.enums.YesOrNoEnum;
import com.xjrsoft.common.core.exception.MyException;
import com.xjrsoft.organization.entity.User;
import com.xjrsoft.system.client.IMenuClient;
import com.xjrsoft.system.entity.Menu;
import com.xjrsoft.system.vo.MenuVo;
import com.xjrsoft.workflow.constant.WorkflowConstant;
import com.xjrsoft.workflow.dto.AddSpecialMenuDto;
import com.xjrsoft.workflow.dto.UpdateSpecialMenuDto;
import com.xjrsoft.workflow.entity.WorkflowSpecialMenu;
import com.xjrsoft.workflow.mapper.WorkflowSpecialMenuMapper;
import com.xjrsoft.workflow.model.SpecialMenuQueryConfig;
import com.xjrsoft.workflow.service.IWorkflowSpecialMenuService;
import com.xjrsoft.workflow.vo.SpecialMenuInfoVo;
import lombok.AllArgsConstructor;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.task.TaskQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* <p>
* 专项菜单表 服务实现类
* </p>
*
* @author tzx
* @since 2023-03-20
*/
@Service
@AllArgsConstructor
public class WorkflowSpecialMenuServiceImpl extends MPJBaseServiceImpl<WorkflowSpecialMenuMapper, WorkflowSpecialMenu> implements IWorkflowSpecialMenuService {
private final IMenuClient menuClient;
private final TaskService taskService;
@Override
@Transactional(rollbackFor = Exception.class)
public boolean add(AddSpecialMenuDto dto) {
WorkflowSpecialMenu workflowSpecialMenu = BeanUtil.toBean(dto, WorkflowSpecialMenu.class);
save(workflowSpecialMenu);
Menu menu = BeanUtil.toBean(dto.getMenuInfo(), Menu.class);
menu.setTitle(dto.getMenuInfo().getName());
menu.setMenuType(MenuType.FUNCTION.getCode());
menu.setDisplay(YesOrNoEnum.YES.getCode());
menu.setAllowDelete(YesOrNoEnum.YES.getCode());
menu.setAllowModify(YesOrNoEnum.YES.getCode());
menu.setOutLink(YesOrNoEnum.NO.getCode());
menu.setKeepAlive(YesOrNoEnum.NO.getCode());
menu.setSortCode(dto.getMenuInfo().getSortCode());
//固定路径 与前端必须匹配
menu.setPath("/special-menu/" + workflowSpecialMenu.getId());
menuClient.insertMenuFeign(menu);
WorkflowSpecialMenu updateSpecialMenu = new WorkflowSpecialMenu();
updateSpecialMenu.setId(workflowSpecialMenu.getId());
updateSpecialMenu.setMenuId(menu.getId());
updateById(updateSpecialMenu);
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean update(UpdateSpecialMenuDto dto) {
WorkflowSpecialMenu workflowSpecialMenu = BeanUtil.toBean(dto, WorkflowSpecialMenu.class);
updateById(workflowSpecialMenu);
Menu menu = BeanUtil.toBean(dto.getMenuInfo(), Menu.class);
menuClient.updateMenuFeign(menu);
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean delete(List<Long> ids) {
List<WorkflowSpecialMenu> list = list(Wrappers.lambdaQuery(WorkflowSpecialMenu.class).in(WorkflowSpecialMenu::getId, ids).select(WorkflowSpecialMenu::getMenuId));
List<Long> allMenuIds = list.stream().map(WorkflowSpecialMenu::getMenuId).collect(Collectors.toList());
removeBatchByIds(ids);
menuClient.deleteMenuBatchFeign(allMenuIds);
return true;
}
@Override
public SpecialMenuInfoVo info(Long id) {
WorkflowSpecialMenu specialMenu = getById(id);
SpecialMenuInfoVo specialMenuInfoVo = BeanUtil.toBean(specialMenu, SpecialMenuInfoVo.class);
Menu menu = menuClient.getMenuByIdFeign(specialMenu.getMenuId());
specialMenuInfoVo.setMenuInfo(BeanUtil.toBean(menu, MenuVo.class));
return specialMenuInfoVo;
}
@Override
public List<Map<String, Object>> pending(Map<String, Object> param) {
Long specialMenuId = MapUtil.get(param, WorkflowConstant.SPECIAL_MENU_ID_PARAM_NAME, Long.class);
WorkflowSpecialMenu specialMenu = getById(specialMenuId);
//最先判断当前操作人 是否有权限使用 此专项菜单。
if (StrUtil.isBlank(specialMenu.getSchemaAuthUserId())) {
throw new MyException("当前登陆人无操作权限!");
}
if (StrUtil.isBlank(specialMenu.getSchemaId())) {
throw new MyException("当前专项菜单没有绑定模板!");
}
String[] userIds = specialMenu.getSchemaAuthUserId().split(StringPool.COMMA);
if (Arrays.stream(userIds).noneMatch(u -> u.equals(StpUtil.getLoginIdAsString()))) {
throw new MyException("当前登陆人无操作权限!");
}
//判断是多模板 还是单模板
String schemaIdsStr = specialMenu.getSchemaId();
String[] schemaIds = schemaIdsStr.split(StringPool.COMMA);
List<SpecialMenuQueryConfig> specialMenuQueryConfigs = JSONUtil.toBean(specialMenu.getQueryConfig(), new TypeReference<List<SpecialMenuQueryConfig>>() {
}, true);
User user = StpUtil.getTokenSession().get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());
TaskQuery taskQuery = taskService.createTaskQuery()
.active()
.taskVariableValueEquals(WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode())
.processVariableValueEquals(WorkflowConstant.PROCESS_ISRECYCLE_FLAG_KEY, WorkflowIsRecycleType.NO.getCode())
.taskVariableValueLike(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StringPool.PERCENT + user.getId() + StringPool.PERCENT);
//获取所有查询条件
for (SpecialMenuQueryConfig specialMenuQueryConfig : specialMenuQueryConfigs) {
if (specialMenuQueryConfig.getFieldName().equals(WorkflowConstant.SPECIAL_MENU_START_TIME_PARAM_NAME)) {
Date starTime = MapUtil.get(param, WorkflowConstant.SPECIAL_MENU_START_TIME_PARAM_NAME, Date.class);
taskQuery.taskCreatedAfter(starTime);
}
if (specialMenuQueryConfig.getFieldName().equals(WorkflowConstant.SPECIAL_MENU_END_TIME_PARAM_NAME)) {
Date endTime = MapUtil.get(param, WorkflowConstant.SPECIAL_MENU_END_TIME_PARAM_NAME, Date.class);
taskQuery.taskCreatedBefore(endTime);
}
if (specialMenuQueryConfig.getFieldName().equals(WorkflowConstant.SPECIAL_MENU_SERIAL_NUMBER_PARAM_NAME)) {
Integer serialNumber = MapUtil.get(param, WorkflowConstant.SPECIAL_MENU_SERIAL_NUMBER_PARAM_NAME, Integer.class);
taskQuery.processVariableValueEquals(WorkflowConstant.PROCESS_SERIAL_NUMBER_KEY, serialNumber);
}
if (specialMenuQueryConfig.getFieldName().equals(WorkflowConstant.SPECIAL_MENU_SCHEMA_NAME_PARAM_NAME)) {
String schemaName = MapUtil.get(param, WorkflowConstant.SPECIAL_MENU_SCHEMA_NAME_PARAM_NAME, String.class);
taskQuery.processVariableValueLike(WorkflowConstant.PROCESS_SCHEMA_NAME_KEY, StringPool.PERCENT + schemaName + StringPool.PERCENT);
}
if (specialMenuQueryConfig.getFieldName().equals(WorkflowConstant.SPECIAL_MENU_ORIGINATOR_PARAM_NAME)) {
String originator = MapUtil.get(param, WorkflowConstant.SPECIAL_MENU_ORIGINATOR_PARAM_NAME, String.class);
taskQuery.or()
.processVariableValueEquals(WorkflowConstant.PROCESS_START_USER_NAME_KEY, originator)
.processVariableValueEquals(WorkflowConstant.PROCESS_START_USER_ID_KEY,originator)
.endOr();
}
if (specialMenuQueryConfig.getFieldName().equals(WorkflowConstant.SPECIAL_MENU_TASK_NAME_PARAM_NAME)) {
String taskName = MapUtil.get(param, WorkflowConstant.SPECIAL_MENU_TASK_NAME_PARAM_NAME, String.class);
taskQuery.taskNameLike(StringPool.PERCENT + taskName + StringPool.PERCENT);
}
}
return null;
}
}

View File

@ -0,0 +1,401 @@
package com.xjrsoft.workflow.utils;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Entity;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.xjrsoft.common.core.constant.GlobalConstant;
import com.xjrsoft.common.core.constant.StringPool;
import com.xjrsoft.common.core.domain.generator.ComponentConfig;
import com.xjrsoft.common.mybatis.model.Department;
import com.xjrsoft.common.mybatis.model.User;
import com.xjrsoft.common.mybatis.utils.LocalDateTimeUtil;
import com.xjrsoft.common.redis.service.RedisUtil;
import com.xjrsoft.form.entity.FormDesignConfig;
import com.xjrsoft.generate.constant.ComponentTypeConstant;
import com.xjrsoft.generate.utils.GeneratorUtil;
import com.xjrsoft.magicapi.client.IMagicApiClient;
import com.xjrsoft.system.client.IAreaClient;
import com.xjrsoft.system.entity.Area;
import com.xjrsoft.system.entity.DictionaryDetail;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.ssssssss.magicapi.modules.db.model.PageResult;
import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author Zexy
*/
@Slf4j
public final class FormDataTransUtil {
private FormDataTransUtil(){}
private static final RedisUtil redisUtil;
private static final IMagicApiClient magcApiClient;
private static final IAreaClient areaClient;
static {
redisUtil = SpringUtil.getBean(RedisUtil.class);
magcApiClient = SpringUtil.getBean(IMagicApiClient.class);
areaClient = SpringUtil.getBean(IAreaClient.class);
}
public static void transData(List<Entity> dataList, FormDesignConfig formDesignConfig) {
if (CollectionUtils.isEmpty(dataList)) {
return;
}
List<DictionaryDetail> detailList = null;
Map<String, Object> userMap = null;
Map<String, Object> deptMap = null;
// List<Map<String, Object>> postList = null;
Map<String, Object> areaMap = null;
Map<String, Map<String, Object>> apiDataMap = new HashMap<>();
List<ComponentConfig> componentConfigList = GeneratorUtil.getFormComponentListWithMain(formDesignConfig.getFormJson().getList());
Map<String, Map<String, Object>> fieldValuesMap = new HashMap<>(componentConfigList.size());
Map<String, Map<String, String>> multiFieldMap = new HashMap<>();
Map<String, String> dateFormatMap = new HashMap<>(4);
for (ComponentConfig componentConfig : componentConfigList) {
String type = componentConfig.getType();
String bindField = componentConfig.getBindField();
Map<String, Object> options = componentConfig.getOptions();
Integer infoType = MapUtils.getInteger(options, "infoType");
boolean isCascade = StrUtil.equalsIgnoreCase(ComponentTypeConstant.CASCADE, type);
if (StrUtil.equalsIgnoreCase(ComponentTypeConstant.CHECKBOX, type)
|| StrUtil.equalsIgnoreCase(ComponentTypeConstant.SELECT, type)
|| StrUtil.equalsIgnoreCase(ComponentTypeConstant.ASSOCIATE_SELECT, type)
|| StrUtil.equalsIgnoreCase(ComponentTypeConstant.ASSOCIATE_POPUP, type)
|| StrUtil.equalsIgnoreCase(ComponentTypeConstant.MULTIPLE_POPUP, type)
|| StrUtil.equalsIgnoreCase(ComponentTypeConstant.RADIO, type)
|| isCascade) {
String datasourceType = MapUtils.getString(options, "datasourceType");
if (StrUtil.equalsIgnoreCase(datasourceType, "dic")) {
if (detailList == null) {
detailList = redisUtil.get(GlobalConstant.DIC_DETAIL_CACHE_KEY, new TypeReference<List<DictionaryDetail>>() {});
}
Map<String, Object> detailMap = detailList.stream().filter(x -> StrUtil.equalsIgnoreCase(x.getItemId().toString(), MapUtils.getString(options, "itemId"))).collect(Collectors.toMap(DictionaryDetail::getValue, DictionaryDetail::getName, (e1, e2) -> e1));
fieldValuesMap.put(bindField, detailMap);
} else if (StrUtil.equalsIgnoreCase(datasourceType, "api") || isCascade) {
String apiId = MapUtils.getString(MapUtils.getMap(options, "apiConfig"), "apiId");
if (apiDataMap.get(apiId) == null) {
Object resultApiData = magcApiClient.executeApiFeign(apiId);
List<Map<String, Object>> apiDataList = null;
if (resultApiData.getClass().getName().contains("PageResult")) {
Map<String, Object> map = Convert.toMap(String.class, Object.class, resultApiData);
apiDataList = (List<Map<String, Object>>) map.get("list");
} else if (resultApiData instanceof List) {
apiDataList = (List<Map<String, Object>>) resultApiData;
}
if (isCascade) {
apiDataList = treeToList(apiDataList);
}
if (apiDataList != null ) {
apiDataMap.put(apiId, apiDataList.stream().filter(x -> ObjectUtil.isNotNull(x.get("value"))).collect(Collectors.toMap(data -> data.get("value").toString(), data -> data.get("label"), (e1, e2) -> e1)));
}
}
fieldValuesMap.put(bindField, apiDataMap.get(apiId));
}
if (StrUtil.equalsIgnoreCase(ComponentTypeConstant.MULTIPLE_POPUP, type)
|| StrUtil.equalsIgnoreCase(ComponentTypeConstant.CHECKBOX, type)
|| isCascade) {
Map<String, String> multiSettingMap = new HashMap<>(4);
multiSettingMap.put("separator", MapUtils.getString(options, "separator", StringPool.COMMA));
multiSettingMap.put("showFormat", MapUtils.getString(options, "showFormat"));
multiFieldMap.put(bindField, multiSettingMap);
}
} else if (StrUtil.equalsIgnoreCase(ComponentTypeConstant.AREA, type)) {
Set<Long> areaValueList = new LinkedHashSet<>();
for (Map<String, Object> data : dataList) {
String values = MapUtils.getString(data, bindField);
if (StrUtil.isEmpty(values)) {
continue;
}
String[] valueArray = values.split(StringPool.COMMA);
areaValueList.addAll(Arrays.stream(valueArray).map(NumberUtils::toLong).collect(Collectors.toSet()));
}
if (CollectionUtils.isNotEmpty(areaValueList)) {
List<Area> areaList = areaClient.getAreaListFeign(new ArrayList<>(areaValueList));
areaMap = areaList.stream().collect(Collectors.toMap(area -> area.getId().toString(), Area::getName, (e1, e2) -> e1));
} else {
areaMap = new HashMap<>(0);
}
fieldValuesMap.put(bindField, areaMap);
multiFieldMap.put(bindField, null);
}else if (StrUtil.equalsIgnoreCase(ComponentTypeConstant.ORGANIZATION, type)
|| (StrUtil.equalsIgnoreCase(ComponentTypeConstant.INFO, type) && infoType.equals(1))) {
if (deptMap == null) {
List<Department> departmentList = redisUtil.get(GlobalConstant.DEP_CACHE_KEY, new TypeReference<List<Department>>() {});
deptMap = departmentList.stream().collect(Collectors.toMap(department -> department.getId().toString(), Department::getName, (e1, e2) -> e1));
}
fieldValuesMap.put(bindField, deptMap);
} else if (StrUtil.equalsIgnoreCase(ComponentTypeConstant.USER, type)
|| (StrUtil.equalsIgnoreCase(ComponentTypeConstant.INFO, type) && infoType.equals(0))) {
if (userMap == null) {
List<User> userList = redisUtil.get(GlobalConstant.USER_CACHE_KEY, new TypeReference<List<User>>() {});
userMap = userList.stream().collect(Collectors.toMap(user -> user.getId().toString(), User::getName, (e1, e2) -> e1));
}
if (infoType == null) {
multiFieldMap.put(bindField, null);
}
fieldValuesMap.put(bindField, userMap);
} else if (StrUtil.equalsIgnoreCase(ComponentTypeConstant.DATE, type)) {
dateFormatMap.put(bindField, LocalDateTimeUtil.convertFontFormat(MapUtils.getString(options, "format")));
} else if (StrUtil.equalsIgnoreCase(ComponentTypeConstant.DATE_RANGE, type)) {
String format = LocalDateTimeUtil.convertFontFormat(MapUtils.getString(options, "format"));
dateFormatMap.put(componentConfig.getBindStartTime(), format);
dateFormatMap.put(componentConfig.getBindEndTime(), format);
}
}
for (Map<String, Object> data : dataList) {
for (Map.Entry<String, Map<String, Object>> entry : fieldValuesMap.entrySet()) {
String key = entry.getKey();
Object o = data.get(key);
if (StrUtil.isEmptyIfStr(o)) {
continue;
}
String value = String.valueOf(o);
Map<String, Object> entryValue = entry.getValue();
if (multiFieldMap.containsKey(key)) {
List<String> values = StrUtil.split(value, StringPool.COMMA);
Map<String, String> multiSettingsMap = multiFieldMap.get(key);
StringBuilder showValue = new StringBuilder();
for (int i = 0; i < values.size(); i++) {
String valueStr = values.get(i);
String separator = StringPool.COMMA;
Object showValueStr = entryValue.get(valueStr);
if (StrUtil.isEmptyIfStr(showValueStr)) {
continue;
}
if (CollectionUtils.isNotEmpty(multiSettingsMap)) {
separator = MapUtils.getString(multiSettingsMap, "separator", StringPool.COMMA);
String showFormat = MapUtils.getString(multiSettingsMap, "showFormat");
if (StrUtil.equalsIgnoreCase(showFormat, "showMostChildLevel")) {
if (i == values.size() - 1) {
showValue.append(showValueStr);
}
continue;
} else {
showValue.append(showValueStr);
}
} else {
showValue.append(showValueStr);
}
if (i < values.size() - 1) {
showValue.append(separator);
}
}
if (showValue.length() > 0) {
data.put(key, showValue.toString());
}
} else {
Object obj = entryValue.get(value);
if (StrUtil.isEmptyIfStr(obj)) {
continue;
}
data.put(key, obj);
}
}
// 时间格式化处理
for (Map.Entry<String, String> entry : dateFormatMap.entrySet()) {
String key = entry.getKey();
Object value = data.get(key);
if (value == null) {
continue;
}
if (value instanceof Timestamp) {
Timestamp timestamp = (Timestamp) value;
data.put(key, LocalDateTimeUtil.format(timestamp.toLocalDateTime(), entry.getValue()));
}
}
}
}
public static void transDataOver(List<Entity> dataList, FormDesignConfig formDesignConfig) {
if (CollectionUtils.isEmpty(dataList)) {
return;
}
List<DictionaryDetail> detailList = null;
Map<String, Object> userMap = null;
Map<String, Object> deptMap = null;
// List<Map<String, Object>> postList = null;
Map<String, Object> areaMap = null;
Map<String, Map<String, Object>> apiDataMap = new HashMap<>();
List<ComponentConfig> componentConfigList = GeneratorUtil.getFormComponentListWithMain(formDesignConfig.getFormJson().getList());
Map<String, Map<String, Object>> fieldValuesMap = new HashMap<>(componentConfigList.size());
Map<String, Map<String, String>> multiFieldMap = new HashMap<>();
Map<String, String> dateFormatMap = new HashMap<>(4);
List<String> keyList = new ArrayList<>(dataList.size());
for (ComponentConfig componentConfig : componentConfigList) {
String type = componentConfig.getType();
String bindField = componentConfig.getBindField();
keyList.add(bindField);
Map<String, Object> options = componentConfig.getOptions();
Integer infoType = MapUtils.getInteger(options, "infoType");
boolean isCascade = StrUtil.equalsIgnoreCase(ComponentTypeConstant.CASCADE, type);
if (StrUtil.equalsIgnoreCase(ComponentTypeConstant.CHECKBOX, type)
|| StrUtil.equalsIgnoreCase(ComponentTypeConstant.SELECT, type)
|| StrUtil.equalsIgnoreCase(ComponentTypeConstant.ASSOCIATE_SELECT, type)
|| StrUtil.equalsIgnoreCase(ComponentTypeConstant.ASSOCIATE_POPUP, type)
|| StrUtil.equalsIgnoreCase(ComponentTypeConstant.MULTIPLE_POPUP, type)
|| StrUtil.equalsIgnoreCase(ComponentTypeConstant.RADIO, type)
|| isCascade) {
String datasourceType = MapUtils.getString(options, "datasourceType");
if (StrUtil.equalsIgnoreCase(datasourceType, "dic")) {
if (detailList == null) {
detailList = redisUtil.get(GlobalConstant.DIC_DETAIL_CACHE_KEY, new TypeReference<List<DictionaryDetail>>() {});
}
Map<String, Object> detailMap = detailList.stream().filter(x -> StrUtil.equalsIgnoreCase(x.getItemId().toString(), MapUtils.getString(options, "itemId"))).collect(Collectors.toMap(DictionaryDetail::getValue, DictionaryDetail::getName, (e1, e2) -> e1));
fieldValuesMap.put(bindField, detailMap);
} else if (StrUtil.equalsIgnoreCase(datasourceType, "api") || isCascade) {
String apiId = MapUtils.getString(MapUtils.getMap(options, "apiConfig"), "apiId");
if (apiDataMap.get(apiId) == null) {
Object resultApiData = magcApiClient.executeApiFeign(apiId);
List<Map<String, Object>> apiDataList = null;
if (resultApiData instanceof PageResult) {
apiDataList = (List<Map<String, Object>>) ((PageResult) resultApiData).getList();
} else if (resultApiData instanceof List) {
apiDataList = (List<Map<String, Object>>) resultApiData;
}
if (isCascade) apiDataList = treeToList(apiDataList);
if (apiDataList != null ) apiDataMap.put(apiId, apiDataList.stream().filter(x -> ObjectUtil.isNotNull(x.get("value"))).collect(Collectors.toMap(data -> data.get("value").toString(), data -> data.get("label"), (e1, e2) -> e1)));
}
fieldValuesMap.put(bindField, apiDataMap.get(apiId));
}
if (StrUtil.equalsIgnoreCase(ComponentTypeConstant.MULTIPLE_POPUP, type)
|| StrUtil.equalsIgnoreCase(ComponentTypeConstant.CHECKBOX, type)
|| isCascade) {
Map<String, String> multiSettingMap = new HashMap<>(4);
multiSettingMap.put("separator", MapUtils.getString(options, "separator", com.baomidou.mybatisplus.core.toolkit.StringPool.COMMA));
multiSettingMap.put("showFormat", MapUtils.getString(options, "showFormat"));
multiFieldMap.put(bindField, multiSettingMap);
}
} else if (StrUtil.equalsIgnoreCase(ComponentTypeConstant.AREA, type)) {
Set<Long> areaValueList = new LinkedHashSet<>();
for (Map<String, Object> data : dataList) {
String values = MapUtils.getString(data, bindField);
if (StrUtil.isEmpty(values)) continue;
String[] valueArray = values.split(com.baomidou.mybatisplus.core.toolkit.StringPool.COMMA);
areaValueList.addAll(Arrays.stream(valueArray).map(NumberUtils::toLong).collect(Collectors.toSet()));
}
if (CollectionUtils.isNotEmpty(areaValueList)) {
List<Area> areaList = areaClient.getAreaListFeign(new ArrayList<>(areaValueList));
areaMap = areaList.stream().collect(Collectors.toMap(area -> area.getId().toString(), Area::getName, (e1, e2) -> e1));
} else {
areaMap = new HashMap<>(0);
}
fieldValuesMap.put(bindField, areaMap);
multiFieldMap.put(bindField, null);
}else if (StrUtil.equalsIgnoreCase(ComponentTypeConstant.ORGANIZATION, type)
|| (StrUtil.equalsIgnoreCase(ComponentTypeConstant.INFO, type) && infoType.equals(1))) {
if (deptMap == null) {
List<Department> departmentList = redisUtil.get(GlobalConstant.DEP_CACHE_KEY, new TypeReference<List<Department>>() {});
deptMap = departmentList.stream().collect(Collectors.toMap(department -> department.getId().toString(), Department::getName, (e1, e2) -> e1));
}
fieldValuesMap.put(bindField, deptMap);
} else if (StrUtil.equalsIgnoreCase(ComponentTypeConstant.USER, type)
|| (StrUtil.equalsIgnoreCase(ComponentTypeConstant.INFO, type) && infoType.equals(0))) {
if (userMap == null) {
List<User> userList = redisUtil.get(GlobalConstant.USER_CACHE_KEY, new TypeReference<List<User>>() {});
userMap = userList.stream().collect(Collectors.toMap(user -> user.getId().toString(), User::getName, (e1, e2) -> e1));
}
if (infoType == null) {
multiFieldMap.put(bindField, null);
}
fieldValuesMap.put(bindField, userMap);
} else if (StrUtil.equalsIgnoreCase(ComponentTypeConstant.DATE, type)) {
dateFormatMap.put(bindField, LocalDateTimeUtil.convertFontFormat(MapUtils.getString(options, "format")));
} else if (StrUtil.equalsIgnoreCase(ComponentTypeConstant.DATE_RANGE, type)) {
String format = LocalDateTimeUtil.convertFontFormat(MapUtils.getString(options, "format"));
dateFormatMap.put(componentConfig.getBindStartTime(), format);
dateFormatMap.put(componentConfig.getBindEndTime(), format);
}
}
for (Map<String, Object> data : dataList) {
for (String key : keyList) {
String addKey = key + com.baomidou.mybatisplus.core.toolkit.StringPool.UNDERSCORE + "api";
Object o = data.get(key);
String value = String.valueOf(o);
data.put(addKey,value);
}
for (Map.Entry<String, Map<String, Object>> entry : fieldValuesMap.entrySet()) {
String key = entry.getKey();
Object o = data.get(key);
if (StrUtil.isEmptyIfStr(o)) {
continue;
}
String value = String.valueOf(o);
Map<String, Object> entryValue = entry.getValue();
if (multiFieldMap.containsKey(key)) {
List<String> values = StrUtil.split(value, com.baomidou.mybatisplus.core.toolkit.StringPool.COMMA);
Map<String, String> multiSettingsMap = multiFieldMap.get(key);
StringBuilder showValue = new StringBuilder();
for (int i = 0; i < values.size(); i++) {
String valueStr = values.get(i);
String separator = com.baomidou.mybatisplus.core.toolkit.StringPool.COMMA;
Object showValueStr = entryValue.get(valueStr);
if (StrUtil.isEmptyIfStr(showValueStr)) continue;
if (CollectionUtils.isNotEmpty(multiSettingsMap)) {
separator = MapUtils.getString(multiSettingsMap, "separator", com.baomidou.mybatisplus.core.toolkit.StringPool.COMMA);
String showFormat = MapUtils.getString(multiSettingsMap, "showFormat");
if (StrUtil.equalsIgnoreCase(showFormat, "showMostChildLevel")) {
if (i == values.size() - 1) {
showValue.append(showValueStr);
}
continue;
} else {
showValue.append(showValueStr);
}
} else {
showValue.append(showValueStr);
}
if (i < values.size() - 1) {
showValue.append(separator);
}
}
if (showValue.length() > 0) data.put(key, showValue.toString());
} else {
Object obj = entryValue.get(value);
if (StrUtil.isEmptyIfStr(obj)) continue;
data.put(key, obj);
}
}
// 时间格式化处理
for (Map.Entry<String, String> entry : dateFormatMap.entrySet()) {
String key = entry.getKey();
Object value = data.get(key);
if (value == null) {
continue;
}
if (value instanceof Timestamp) {
Timestamp timestamp = (Timestamp) value;
data.put(key, LocalDateTimeUtil.format(timestamp.toLocalDateTime(), entry.getValue()));
}
}
}
}
private static List<Map<String, Object>> treeToList(List<Map<String, Object>> treeDataList) {
if (CollectionUtils.isEmpty(treeDataList)) {
return treeDataList;
}
List<Map<String, Object>> resultList = new ArrayList<>();
for (Map<String, Object> data : treeDataList) {
resultList.add(data);
Object obj = data.get("children");
if (obj instanceof List) {
resultList.addAll(treeToList((List<Map<String, Object>>)obj));
}
}
return resultList;
}
}

View File

@ -0,0 +1,610 @@
package com.xjrsoft.workflow.utils;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.sql.SqlBuilder;
import cn.hutool.db.sql.Wrapper;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xjrsoft.common.core.exception.MyException;
import com.xjrsoft.common.core.exception.ValidationException;
import com.xjrsoft.magicapi.vo.MagicApiInfoVo;
import com.xjrsoft.organization.entity.User;
import com.xjrsoft.tenant.util.SecureUtil;
import com.xjrsoft.workflow.constant.WorkflowConstant;
import com.xjrsoft.workflow.entity.WorkflowApproveRecord;
import com.xjrsoft.workflow.entity.WorkflowFormRelation;
import com.xjrsoft.workflow.entity.WorkflowSchema;
import com.xjrsoft.workflow.model.ApiConfig;
import com.xjrsoft.workflow.model.ApiRequestParamsConfig;
import com.xjrsoft.workflow.model.UserTaskConfig;
import com.xjrsoft.workflow.model.WorkflowSchemaConfig;
import com.xjrsoft.workflow.service.IWorkflowApproveRecordService;
import com.xjrsoft.workflow.service.IWorkflowExecuteService;
import com.xjrsoft.workflow.service.IWorkflowFormRelationService;
import com.xjrsoft.workflow.service.IWorkflowSchemaService;
import com.xjrsoft.workflow.vo.TaskInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.jdbc.SQL;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.history.HistoricActivityInstance;
import org.camunda.bpm.engine.history.HistoricVariableInstance;
import org.camunda.bpm.engine.runtime.*;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.*;
import java.util.*;
import java.util.stream.Collectors;
/**
* 工作流和业务关联的工具类
*/
@Slf4j
public class WorkflowBusinessUtil {
private static final IWorkflowSchemaService workflowSchemaService;
private static final IWorkflowFormRelationService workflowFormRelationService;
private static final IWorkflowExecuteService workflowExecuteService;
private static final HistoryService historyService;
private static final RepositoryService repositoryService;
private static final TaskService taskService;
private static final RuntimeService runtimeService;
private static final IWorkflowApproveRecordService workflowApproveRecordService;
static {
workflowSchemaService=SpringUtil.getBean(IWorkflowSchemaService.class);
workflowFormRelationService=SpringUtil.getBean(IWorkflowFormRelationService.class);
workflowExecuteService=SpringUtil.getBean(IWorkflowExecuteService.class);
historyService=SpringUtil.getBean(HistoryService.class);
repositoryService=SpringUtil.getBean(RepositoryService.class);
taskService=SpringUtil.getBean(TaskService.class);
runtimeService=SpringUtil.getBean(RuntimeService.class);
workflowApproveRecordService=SpringUtil.getBean(IWorkflowApproveRecordService.class);
}
/**
* 查询业务对应流程当前运行中的任务(反映流程状态)
*/
public static final List<Task>queryTaskListByBusinessId(Long businessId,Long schemaId){
List<Task>result=new ArrayList<>();
//先用业务id查询有没有关联的流程
List<WorkflowFormRelation>formRelationList=workflowFormRelationService.list(Wrappers.lambdaQuery(WorkflowFormRelation.class)
.eq(WorkflowFormRelation::getFormKeyValue,businessId)
);
List<String>processInstIdList=new ArrayList<>();
if(ObjectUtil.isNotEmpty(formRelationList)) {
processInstIdList.addAll(formRelationList.stream().map(WorkflowFormRelation::getProcessId).collect(Collectors.toList()));
//如果有流程配置id过滤出正确的流程实例
if(formRelationList.size()>1&&schemaId!=null){
List<HistoricVariableInstance> variableInstanceList = historyService.createHistoricVariableInstanceQuery()
.processInstanceIdIn(processInstIdList.toArray(new String[]{}))
.variableValueEquals(WorkflowConstant.PROCESS_SCHEMA_ID_KEY,schemaId)
.list();
if(ObjectUtil.isNotEmpty(variableInstanceList)){
processInstIdList=variableInstanceList.stream().map(HistoricVariableInstance::getProcessInstanceId).collect(Collectors.toList());
}
}
}
if(ObjectUtil.isNotEmpty(processInstIdList)){
//查询运行中的任务
result.addAll(taskService.createTaskQuery()
.active()
.processInstanceIdIn(processInstIdList.toArray(new String[]{}))
.list()
);
}
return result;
}
/**
* 保存退回的节点选人,按子流程拆分
* @param processInstId
* @param nextTaskUserMap
* @param isClearFirst
* @return 返回按子流程拆分的节点选人(即保存的节点选人)
*/
public static Map<String,Map<String,String>> saveRejectTaskUser(String processInstId,Map<String,String>nextTaskUserMap,Boolean isClearFirst){
//按主流程子流程拆分选人
Map<String,Map<String,String>>subProcessMap=new HashMap<>();
for(String key:nextTaskUserMap.keySet()){
if(StringUtils.isNotBlank(nextTaskUserMap.get(key))){
Map<String,Set<String>>tmp=new HashMap<>();
Arrays.stream(nextTaskUserMap.get(key).split(StringPool.COMMA)).forEach(user->{
String[]str=user.split(WorkflowConstant.SUB_PROCESS_SEPARATOR);
String k=str.length==1?WorkflowConstant.MAIN_PROCESS_ID:str[0];
String v=str.length==1?str[0]:str[1];
//没有带子流程实例id属于主流程
if(tmp.get(k)==null){
tmp.put(k,new LinkedHashSet<>());
}
tmp.get(k).add(v);
});
for(String subProcessId:tmp.keySet()){
if(subProcessMap.get(subProcessId)==null){
subProcessMap.put(subProcessId,new HashMap<>());
}
subProcessMap.get(subProcessId).put(key,StringUtils.join(tmp.get(subProcessId),StringPool.COMMA));
}
}
}
for(String subProcessId:subProcessMap.keySet()){
saveNextTaskUser(processInstId,WorkflowConstant.MAIN_PROCESS_ID.equals(subProcessId)?null:subProcessId,subProcessMap.get(subProcessId),isClearFirst);
}
return subProcessMap;
}
public static void saveNextTaskUser(String processInstId,String subProcessInstId,Map<String,String>nextTaskUserMap,Boolean isClearFirst){
saveNextTaskUser(processInstId,subProcessInstId,nextTaskUserMap,isClearFirst,false);
}
/**
* 保存流程下一节点审批人,
* 数据存储格式processId,nextTaskUser->Map<taskDefKey(_subProcessInstId),Map<currentUserId,nextTaskUserStr>>
* @param processInstId
* @param subProcessInstId
* @param nextTaskUserMap
* @param isClearFirst 是否先清除节点选人
* @param isAdd 对用户的选人,追加还是覆盖
*/
public static void saveNextTaskUser(String processInstId,String subProcessInstId,Map<String,String>nextTaskUserMap,Boolean isClearFirst,Boolean isAdd){
if(ObjectUtil.isEmpty(nextTaskUserMap)){
throw new ValidationException("审批人不能为空");
}
String userId= SecureUtil.getCurrentUserId().toString();
Object variableMaps=runtimeService.getVariable(processInstId, WorkflowConstant.NEXT_TASK_USER_VAR_FLAG);
Map<String,Object>taskUserMap=variableMaps==null?new HashMap<>():(Map<String,Object>)variableMaps;
for(String taskDefKey: nextTaskUserMap.keySet()){
if(StringUtils.isNotBlank(nextTaskUserMap.get(taskDefKey))){
String key=(StringUtils.isNotBlank(subProcessInstId)?(subProcessInstId+WorkflowConstant.SUB_PROCESS_SEPARATOR):"")+taskDefKey;
Map<String,String>taskUser=new HashMap<>();
if(!isClearFirst) {
//兼容旧数据格式
if (taskUserMap.get(key) instanceof String) {
taskUser.put(userId, (String) taskUserMap.get(key));
} else if(taskUserMap.get(key)!=null) {
taskUser.putAll((Map<String, String>) taskUserMap.get(key));
}
}
if(isAdd){
//解析已选人去重
Set<String>set=new LinkedHashSet<>();
if(StringUtils.isNotBlank(taskUser.get(userId))){
set.addAll(Arrays.asList(taskUser.get(userId).split(StringPool.COMMA)));
}
set.addAll(Arrays.asList(nextTaskUserMap.get(taskDefKey).split(StringPool.COMMA)));
taskUser.put(userId, StringUtils.join(set,StringPool.COMMA));
}else{
taskUser.put(userId, nextTaskUserMap.get(taskDefKey));
}
taskUserMap.put(key,taskUser);
}
}
runtimeService.setVariable(processInstId,WorkflowConstant.NEXT_TASK_USER_VAR_FLAG,taskUserMap);
}
/**
* 查询流程节点预审选择的候选人
* @param variableMaps
* @param processInstId
* @param subProcessInstId
* @param taskDefKey
* @return
*/
public static final List<Long>queryNextTaskUser(Map<String, Object> variableMaps,String processInstId,String subProcessInstId,String taskDefKey){
if(variableMaps==null&&StringUtils.isNotBlank(processInstId)){
List<String> variableNames = ListUtil.toList(
WorkflowConstant.NEXT_TASK_USER_VAR_FLAG);
variableMaps = runtimeService.getVariables(processInstId, variableNames);
}
Set<Long>nextTaskUserSet=new HashSet<>();
if(variableMaps!=null){
Map<String,Object> nextTaskUserMap= MapUtil.get(variableMaps,WorkflowConstant.NEXT_TASK_USER_VAR_FLAG,Map.class);
if(ObjectUtil.isNotEmpty(nextTaskUserMap)) {
String key=(StringUtils.isNotBlank(subProcessInstId) ? (subProcessInstId+WorkflowConstant.SUB_PROCESS_SEPARATOR) : "")+taskDefKey;
//兼容旧格式
Map<String, String> nextTaskUser = new HashMap<>();
if (nextTaskUserMap.get(key) instanceof String) {
nextTaskUser.put(SecureUtil.getCurrentUserId().toString(), (String) nextTaskUserMap.get(key));
} else if(nextTaskUserMap.get(key)!=null) {
nextTaskUser.putAll((Map<String, String>) nextTaskUserMap.get(key));
}
if (ObjectUtil.isNotEmpty(nextTaskUser)) {
for (String userIds : nextTaskUser.values()) {
if (StringUtils.isNotBlank(userIds)) {
Arrays.stream(userIds.split(StringPool.COMMA)).forEach(s -> nextTaskUserSet.add(Long.parseLong(s)));
}
}
}
}
}
return new ArrayList<>(nextTaskUserSet);
}
public static final List<Long>queryNextTaskUser(Map<String, Object> variableMaps,String processInstId,String taskDefKey){
return queryNextTaskUser(variableMaps,processInstId,null,taskDefKey);
}
/**
* 查询流程节点的最终审批人
* @param processInstId
* @param taskDefKey
* @param isOnlyLastOne 只要最后一个审批人
* @return
*/
public static final Map<String,List<Long>>queryAuditPerson(String processInstId,String[] taskDefKey,Boolean isOnlyLastOne){
Map<String,List<Long>>result=new HashMap<>();
List<WorkflowApproveRecord> list = workflowApproveRecordService.list(Wrappers.lambdaQuery(WorkflowApproveRecord.class)
.eq(WorkflowApproveRecord::getProcessId, processInstId)
.in(ObjectUtil.isNotEmpty(taskDefKey),WorkflowApproveRecord::getTaskDefinitionKey, taskDefKey)
.orderByDesc(WorkflowApproveRecord::getApproveTime)
);
if(ObjectUtil.isNotEmpty(list)){
list.forEach(record->{
if(result.get(record.getTaskDefinitionKey())==null){
result.put(record.getTaskDefinitionKey(), ListUtil.toList(record.getApproveUserId()));
}else if(!isOnlyLastOne){
result.get(record.getTaskDefinitionKey()).add(record.getApproveUserId());
}
});
}
return result;
}
public static final List<Long>queryTaskAuditPerson(String processInstId,String taskDefKey){
return queryAuditPerson(processInstId,new String[]{taskDefKey},false).get(taskDefKey);
}
public static final void updateSubProcessInstName(String subProcessInstId,String subProcessInstName){
HistoricActivityInstance instance=historyService.createHistoricActivityInstanceQuery().activityInstanceId(subProcessInstId).singleResult();
runtimeService.setVariable(instance.getExecutionId(),WorkflowConstant.SUB_PROCESS_INSTANCE_NAME,subProcessInstName);
}
public static final Map<String,String> querySubProcessInstId(String[]executionIds){
Map<String,String> subProcessInstIdMap = historyService.createHistoricVariableInstanceQuery().executionIdIn(executionIds)
.variableName(WorkflowConstant.SUB_PROCESS_INSTANCE_ID).list()
.stream().collect(Collectors.toMap(HistoricVariableInstance::getExecutionId,item->Convert.toStr(item.getValue())));
return subProcessInstIdMap;
}
public static final Map<String,String> querySubProcessInstName(String[]executionIds){
return querySubProcessInstName(executionIds,null);
}
public static final Map<String,String> querySubProcessInstName(String[]executionIds,Map<String,String> subProcessInstIdMap){
if(subProcessInstIdMap==null) {
subProcessInstIdMap = querySubProcessInstId(executionIds);
}
Map<String,String> subProcessInstNameMap = historyService.createHistoricVariableInstanceQuery().executionIdIn(executionIds)
.variableName(WorkflowConstant.SUB_PROCESS_INSTANCE_NAME).list()
.stream().collect(Collectors.toMap(HistoricVariableInstance::getExecutionId,item->Convert.toStr(item.getValue())));
Set<String>actInstIdToQuery=new HashSet<>();
for(String key:subProcessInstIdMap.keySet()){
if(subProcessInstNameMap.get(key)==null&&subProcessInstIdMap.get(key)!=null){
//去子流程实例的activityInst里查
actInstIdToQuery.add(subProcessInstIdMap.get(key));
}
}
if(ObjectUtil.isNotEmpty(actInstIdToQuery)) {
Wrapper wrapper=new Wrapper();
Map<String,String>subProcessActInstExecutionIdMap=historyService.createNativeHistoricActivityInstanceQuery()
.sql(SqlBuilder.create(wrapper)
.select("id_","execution_id_").from("act_hi_actinst").where("1=1 and ")
.in("id_",actInstIdToQuery.stream().map(x->"'"+x+"'").toArray()).build()
).list()
.stream().collect(Collectors.toMap(HistoricActivityInstance::getId, HistoricActivityInstance::getExecutionId));
Map<String,String> subProcessActInstExecutionNameMap = historyService.createHistoricVariableInstanceQuery()
.executionIdIn(subProcessActInstExecutionIdMap.values().toArray(new String[]{}))
.variableName(WorkflowConstant.SUB_PROCESS_INSTANCE_NAME).list()
.stream().collect(Collectors.toMap(HistoricVariableInstance::getExecutionId,item->Convert.toStr(item.getValue())));
for(String key:subProcessInstIdMap.keySet()){
if(subProcessInstNameMap.get(key)==null&&subProcessInstIdMap.get(key)!=null
&&subProcessActInstExecutionIdMap.get(subProcessInstIdMap.get(key))!=null
&&subProcessActInstExecutionNameMap.get(subProcessActInstExecutionIdMap.get(subProcessInstIdMap.get(key)))!=null){
//赋值子流程实例里的流程名称
subProcessInstNameMap.put(key,subProcessActInstExecutionNameMap.get(subProcessActInstExecutionIdMap.get(subProcessInstIdMap.get(key))));
}
}
}
return subProcessInstNameMap;
}
public static final String querySubProcessInstId(String executionId){
return querySubProcessInstId(executionId,false);
}
public static final String querySubProcessInstId(String executionId,Boolean isOriginal){
Object subProcessInstIdObj=runtimeService.getVariableLocal(executionId,
isOriginal?WorkflowConstant.ORIGINAL_SUB_PROCESS_INSTANCE_ID:WorkflowConstant.SUB_PROCESS_INSTANCE_ID);
return subProcessInstIdObj==null?(isOriginal?querySubProcessInstId(executionId):null):String.valueOf(subProcessInstIdObj);
}
public static final String querySubProcessParentInstId(String executionId){
return querySubProcessParentInstId(executionId,false);
}
public static final String querySubProcessParentInstId(String executionId,Boolean isOriginal){
Object subProcessParentInstIdObj=runtimeService.getVariableLocal(executionId,
isOriginal?WorkflowConstant.ORIGINAL_SUB_PROCESS_PARENT_INSTANCE_ID:WorkflowConstant.SUB_PROCESS_PARENT_INSTANCE_ID);
return subProcessParentInstIdObj==null?(isOriginal?querySubProcessParentInstId(executionId):null):String.valueOf(subProcessParentInstIdObj);
}
/**
* 递归查询给定活动实例下的直到活动实例id的活动实例
* @param activityInstance
* @param activityInstId
* @return
*/
public static final ActivityInstance queryActivityInstByActInstId(ActivityInstance activityInstance,String activityInstId){
if(activityInstance==null||activityInstance.getId().equals(activityInstId)){
return activityInstance;
}
if(ObjectUtil.isNotEmpty(activityInstance.getChildActivityInstances())){
for(ActivityInstance child:activityInstance.getChildActivityInstances()){
child=queryActivityInstByActInstId(child,activityInstId);
if(child!=null){
return child;
}
}
}
return null;
}
public static final Task queryTaskCurrentUserCanApprove(String taskId){
Task task = taskService.createTaskQuery().taskId(taskId)
//校验当前登录人是审批人
.taskVariableValueLike(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StringPool.PERCENT+StrUtil.toString(SecureUtil.getCurrentUserId())+StringPool.PERCENT)
.active()
.singleResult();
if(task==null){
throw new ValidationException("任务不存在或无审批权限!");
}
return task;
}
public static final HistoricActivityInstance queryHistoricActivityInstanceOfTask(Task task){
HistoricActivityInstance executionActivityInstance=null;
List<HistoricActivityInstance> taskExecutionActivityInstanceList=historyService.createHistoricActivityInstanceQuery()
.executionId(task.getExecutionId())
.activityId(task.getTaskDefinitionKey())
.list();
if(ObjectUtil.isNotEmpty(taskExecutionActivityInstanceList)){
executionActivityInstance=taskExecutionActivityInstanceList.stream().filter(x->task.getId().equals(x.getTaskId())).findFirst()
.orElse(taskExecutionActivityInstanceList.get(0));
}
return executionActivityInstance;
}
/**
* 查询流程配置相关的方法
*/
public static final WorkflowSchema queryWorkflowSchema(String processInstId){
HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstId)
.variableName(WorkflowConstant.PROCESS_SCHEMA_ID_KEY).singleResult();
if(historicVariableInstance==null){
log.error("no schemaId found from process variable:"+processInstId);
return null;
}
//排除xml 查出数据
WorkflowSchema workflowSchema = workflowSchemaService.getOne(Wrappers.lambdaQuery(WorkflowSchema.class)
.eq(WorkflowSchema::getId, historicVariableInstance.getValue()));
return workflowSchema;
}
public static final WorkflowSchema queryWorkflowSchema(Long schemaId){
return workflowSchemaService.getById(schemaId);
}
public static final List<Map<String,Object>>queryNodeConfigList(String processInstId){
WorkflowSchema workflowSchema = queryWorkflowSchema(processInstId);
//获取到整个流程模板的配置
WorkflowSchemaConfig workflowSchemaConfig = JSONUtil.toBean(workflowSchema.getJsonContent(), WorkflowSchemaConfig.class);
return workflowSchemaConfig.getChildNodeConfig();
}
public static final Map<String,Object>queryNodeConfig(String processInstId,String taskDefKey){
Optional<Map<String, Object>> configOp = queryNodeConfigList(processInstId).stream().filter(c -> c.containsValue(taskDefKey)).findFirst();
if (!configOp.isPresent()) {
throw new MyException("找不到当前节点配置!");
}
return configOp.get();
}
public static final UserTaskConfig queryUserTaskNodeConfig(String processInstId,String taskDefKey){
return Convert.convert(UserTaskConfig.class, queryNodeConfig(processInstId,taskDefKey));
}
/**
* 按照执行的层级查询已完成的活动实例
* 往上查询父层级和根层级
* @param executionActivityInstance
* @return
*/
public static final List<HistoricActivityInstance> queryFinishedActivityByParentLevel(HistoricActivityInstance executionActivityInstance){
List<HistoricActivityInstance> list=new ArrayList<>();
//从当前任务的executionId所在层级开始往上查找所有执行过的活动直到主分支层级
//不会进入其他子分支
while(executionActivityInstance!=null){
list.addAll(historyService.createHistoricActivityInstanceQuery()
.processInstanceId(executionActivityInstance.getProcessInstanceId())
.executionId(executionActivityInstance.getExecutionId())
.activityType(WorkflowConstant.BPMN_USER_TASK_TYPE_NAME)
.finished()
.orderPartiallyByOccurrence()
.desc()
.list());
if(executionActivityInstance.getParentActivityInstanceId().equals(executionActivityInstance.getProcessInstanceId())){
//将要到达顶层或已到达顶层
break;
}else {
//加入上一层
executionActivityInstance = historyService.createHistoricActivityInstanceQuery()
.activityInstanceId(executionActivityInstance.getParentActivityInstanceId()).singleResult();
}
}
if(executionActivityInstance!=null&&!executionActivityInstance.getExecutionId().equals(executionActivityInstance.getProcessInstanceId())){
//将要到达顶层的情况,补充顶层的数据
list.addAll(historyService.createHistoricActivityInstanceQuery()
.processInstanceId(executionActivityInstance.getProcessInstanceId())
.executionId(executionActivityInstance.getProcessInstanceId())
.activityType(WorkflowConstant.BPMN_USER_TASK_TYPE_NAME)
.finished()
.orderPartiallyByOccurrence()
.desc()
.list());
}
return list;
}
/**
* 查询子流程中完成的活动,按节点层级倒序排列
* @param processInstId
* @param subProcessInstId
* @param filtrateAfterActInstId 如果有值则按活动实例id过滤否则按定义id
* @param filtrateAfterActId
* @param queryIn 是否查询在子流程中
* @return
*/
public static final List<HistoricActivityInstance> queryFinishedActivityInOrNotInSubProcess(String processInstId,String subProcessInstId,String filtrateAfterActInstId,String filtrateAfterActId,Boolean queryIn){
List<HistoricActivityInstance> list=historyService.createNativeHistoricActivityInstanceQuery().sql(
"SELECT id_, parent_act_inst_id_, proc_def_key_, proc_def_id_, root_proc_inst_id_, proc_inst_id_, execution_id_, act_id_, task_id_, call_proc_inst_id_, call_case_inst_id_, act_name_, act_type_, assignee_, start_time_, end_time_, duration_, act_inst_state_, sequence_counter_, tenant_id_, removal_time_\n" +
"FROM act_hi_actinst WHERE proc_inst_id_ ='"+processInstId+"' AND execution_id_ "+(queryIn?"":"not ")+"in(\n" +
"SELECT execution_id_ FROM act_hi_varinst arv WHERE proc_inst_id_ ='"+processInstId+"' AND name_='subProcessInstanceId' "+
(StringUtils.isNotBlank(subProcessInstId)?("AND text_='"+subProcessInstId+"'"):"")+"\n" +
")ORDER BY sequence_counter_ DESC").list();
return filtrateAfterActivity(list,filtrateAfterActInstId,filtrateAfterActId,true);
}
/**
* 查询主流程中完成的活动,按节点层级倒序排列
* @param processInstId
* @param filtrateAfterActInstId
* @param filtrateAfterActId
* @return
*/
public static final List<HistoricActivityInstance> queryFinishedActivityInProcess(String processInstId,String filtrateAfterActInstId,String filtrateAfterActId){
List<HistoricActivityInstance> list=historyService.createHistoricActivityInstanceQuery()
.processInstanceId(processInstId)
.finished()
.orderPartiallyByOccurrence()
.desc()
.list();
return filtrateAfterActivity(list,filtrateAfterActInstId,filtrateAfterActId,true);
}
/**
* 传入一个按发生时间倒序的列表,从第一个元素开始过滤掉发生在指定节点之后的活动:如果有满足条件的元素,删除直到两个条件之一成立,该模式保留了最多的结果
* 可指定为逆序,即从尾部开始,直到满足条件的元素出现后,开始删除后续的元素,该模式保留最少的结果
* @param list
* @param filtrateAfterActInstId
* @param filtrateAfterActId
*/
public static final List<HistoricActivityInstance> filtrateAfterActivity(List<HistoricActivityInstance>list,String filtrateAfterActInstId,String filtrateAfterActId,Boolean isReversal){
if(ObjectUtil.isNotEmpty(list)&&(StringUtils.isNotBlank(filtrateAfterActInstId)||StringUtils.isNotBlank(filtrateAfterActId))
&&list.stream().anyMatch(x->x.getId().equals(filtrateAfterActInstId)||x.getActivityId().equals(filtrateAfterActId))){
if(isReversal){
ListIterator<HistoricActivityInstance>iterator=list.listIterator(list.size());
Boolean startRemove=false;
while(iterator.hasPrevious()){
HistoricActivityInstance item=iterator.previous();
if(!startRemove){
if(item.getId().equals(filtrateAfterActInstId)||item.getActivityId().equals(filtrateAfterActId)) {
startRemove = true;
}
}else{
iterator.remove();
}
}
}else{
Iterator<HistoricActivityInstance>iterator=list.iterator();
while(iterator.hasNext()){
HistoricActivityInstance item=iterator.next();
if(item.getId().equals(filtrateAfterActInstId)||item.getActivityId().equals(filtrateAfterActId)){
break;
}else{
iterator.remove();
}
}
}
}
return list;
}
public static final Boolean isMultiInstanceNode(FlowNode node){
return ((Activity)node).getLoopCharacteristics()!=null;
}
public static final FlowNode getFlowNode(String processDefId,String nodeDefKey){
BpmnModelInstance bpmnModel = repositoryService.getBpmnModelInstance(processDefId);
FlowNode flowNode = bpmnModel.getModelElementById(nodeDefKey);
return flowNode;
}
public static final List<String>queryPrevActivityDefKeyInModel(List<SequenceFlow> flows,List<String>includeTypes){
return queryPrevActivityDefKeyInModel(flows,includeTypes,null,null);
}
public static final List<String>queryPrevActivityDefKeyInModel(List<SequenceFlow> flows,List<String>includeTypes,List<String>excludeTypes,List<String>result){
if(result==null) {
result = new ArrayList<>();
}
for(SequenceFlow flow:flows){
FlowNode node=flow.getSource();
if(!result.contains(node.getId())){
if((ObjectUtil.isEmpty(includeTypes)||includeTypes.contains(node.getElementType().getTypeName()))
&&(ObjectUtil.isEmpty(excludeTypes)||!excludeTypes.contains(node.getElementType().getTypeName()))) {
result.add(node.getId());
}
//如果是子流程,从子流程的结束节点递归
if(node instanceof SubProcess){
for(FlowNode item:Convert.toList(FlowNode.class,node.getChildElementsByType(EndEvent.class))){
queryPrevActivityDefKeyInModel(ListUtil.toList(item.getIncoming()), includeTypes, excludeTypes, result);
}
}else if(ObjectUtil.isNotEmpty(node.getIncoming())) {
queryPrevActivityDefKeyInModel(ListUtil.toList(node.getIncoming()), includeTypes, excludeTypes, result);
}else if(node.getParentElement() instanceof FlowNode){
queryPrevActivityDefKeyInModel(ListUtil.toList(((FlowNode)node.getParentElement()).getIncoming()), includeTypes, excludeTypes, result);
}
}
}
return result;
}
/*public static final Boolean gotoNode(String processInstId,String subProcessInstId){
User operator=SecureUtil.getCurrentUser();
//如果是子流程内退回仅退回该子流程实例需要查询原始的子流程实例id
//子流程外退回,应退回整个流程(会签退回所有工作项,不想退回的话用不同意的百分比处理,不要用退回)
//发起退回的节点,源活动
String activityInstIdToReject=StringUtils.isNotBlank(subProcessInstId)?
WorkflowBusinessUtil.querySubProcessInstId(task.getExecutionId(),true):processInstId;
ActivityInstance activityInstanceToReject=WorkflowBusinessUtil.queryActivityInstByActInstId(runtimeService.getActivityInstance(processInstId),activityInstIdToReject);
List<String>cancelActInstIdsOfActInstToRejectAtLast=new ArrayList<>();
//是否退回到同个子流程中,是则特殊处理
Boolean isRejectToCurrentSubProcessInst=!processInstId.equals(activityInstIdToReject)&&ListUtil.toList(rejectToMap.keySet()).contains(activityInstIdToReject);
ProcessInstanceModificationInstantiationBuilder modifyBuilder= (ProcessInstanceModificationInstantiationBuilder) runtimeService.createProcessInstanceModification(processInstId)
.setAnnotation("【审批人:" + operator.getName() + "】 驳回 【任务:" + task.getId() + "】");
if(isRejectToCurrentSubProcessInst){
//同个子流程内退回的情况,先保留当前子流程实例内的活动实例,在发起新活动实例后,再取消掉这些已有实例
cancelActInstIdsOfActInstToRejectAtLast.addAll(Arrays.stream(activityInstanceToReject.getChildActivityInstances()).map(ActivityInstance::getId).collect(Collectors.toList()));
//TODO 会签仅退回一个?
}else{
//如果退回其他(子)流程中,或者是主流程退回,先取消当前(子)流程实例
modifyBuilder.cancelActivityInstance(activityInstIdToReject);
log.info("stop activityInstId:{} for reject to {} by {}",activityInstIdToReject,dto.getRejectNodeActivityId(),operator.getUserName());
}
//按主流程子流程执行退回到目标活动
for(String rejectToSubProcessId:rejectToMap.keySet()) {
//除了退回同子流程用子流程发起目标活动,其他都用主流程发起
modifyBuilder.startBeforeActivity(dto.getRejectNodeActivityId(), isRejectToCurrentSubProcessInst?activityInstIdToReject:processInstId);
if (!rejectToSubProcessId.equals(activityInstIdToReject)
&&!WorkflowConstant.MAIN_PROCESS_ID.equals(rejectToSubProcessId)) {
//如果退回到子流程内,重启选人的子流程(重启是发起新的子流程,但是把原子流程的流程变量复制过去)
List<HistoricVariableInstance> subProcessVarList = historyService.createHistoricVariableInstanceQuery()
.activityInstanceIdIn(rejectToSubProcessId)
.variableNameIn(WorkflowConstant.SUB_PROCESS_INSTANCE_ID, WorkflowConstant.SUB_PROCESS_PARENT_INSTANCE_ID)
.list();
Map<String, Object> subProcessVarMap = subProcessVarList.stream().collect(Collectors.toMap(x->x.getName()+"Force", HistoricVariableInstance::getValue));
modifyBuilder.setVariablesLocal(subProcessVarMap);
}
log.info("restart subProcess:{} by {}",rejectToSubProcessId,operator.getUserName());
}
//最后才取消的活动实例(退回同子流程的情况,先取消会导致子流程实例被取消,无法发起目标活动实例
for(String cancelActInstIdOfActInstToReject:cancelActInstIdsOfActInstToRejectAtLast){
modifyBuilder.cancelActivityInstance(cancelActInstIdOfActInstToReject);
log.info("stop activityInstId:{} for reject to {} by {}",cancelActInstIdOfActInstToReject,dto.getRejectNodeActivityId(),operator.getUserName());
}
modifyBuilder.execute();
return true;
}*/
}

View File

@ -0,0 +1,3 @@
spring:
profiles:
active: public

View File

@ -0,0 +1,79 @@
server:
port: 3007
spring:
application:
name: workflow-service
main:
allow-bean-definition-overriding: true
autoconfigure:
#自动化配置 例外处理
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
datasource:
type: com.alibaba.druid.pool.DruidDataSource
dynamic:
primary: master
datasource:
master:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://new-energy-mysqlt.itc.gdyd.com:3307/fcd2-msat-init?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
username: learun4dev
password: ABcd1234@
cloud:
nacos: #nacos监控
discovery:
server-addr: 10.0.252.1:8848
namespace: ITC
group: DNE
username: nacos
password: ABcd1234@
config:
server-addr: 10.0.252.1:8848 # nacos 配置中心地址
namespace: ITC
group: DNE
username: nacos
password: ABcd1234@
file-extension: yml # 指定格式 xjrsoft-demo-service-dev.yml
extension-configs:
- data-id: global-config.yml #导入全局配置
refresh: true
group: DNE
- data-id: mybatis-plus-config.yml #导入mybatisplus 配置
refresh: true
group: DNE
- data-id: sa-token-client-config.yml #导入sa-token配置
refresh: true
group: DNE
- data-id: redis-config.yml #导入redis配置
refresh: true
group: DNE
- data-id: seata-config.yml #导入seata配置
refresh: true
group: DNE
- data-id: camunda-config.yml #导入seata配置
refresh: true
group: DNE
sentinel:
transport:
dashboard: localhost:8080 #sentinel dashboard 地址
port: 8719 #默认端口, 如果 被占用,会一直+1 直到未被占用为止
springdoc:
swagger-ui:
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha
show-extensions: true
api-docs:
path: /workflow/v3/api-docs
group-configs:
- group: 'default'
paths-to-match: '/workflow/**'
packages-to-scan: com.xjrsoft.workflow
default-flat-param-object: false