diff --git a/pigx-common/pigx-common-bom/pom.xml b/pigx-common/pigx-common-bom/pom.xml index 9e7df042b..7d7e93017 100644 --- a/pigx-common/pigx-common-bom/pom.xml +++ b/pigx-common/pigx-common-bom/pom.xml @@ -20,7 +20,7 @@ 5.2.0-snapshot 3.5.3.2 1.4.5 - 3.6.0 + 4.1.3 1.2.9 5.8.21 8.0.32 @@ -35,6 +35,8 @@ 2.8.0 3.0.3 1.13.1 + 5.3.3 + 6.8.0 2.3.6.RELEASE 1.2.83 2.3.0 @@ -164,6 +166,18 @@ ${pigx.version} + + com.pig4cloud + pigx-flow-task-api + ${pigx.version} + + + + com.pig4cloud + pigx-flow-engine-api + ${pigx.version} + + org.ow2.asm asm @@ -313,6 +327,18 @@ weixin-java-common ${mp.weixin.version} + + + com.googlecode.aviator + aviator + ${aviator.version} + + + + org.flowable + flowable-spring-boot-starter-process + ${flowable.version} + com.github.javen205 diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/pom.xml b/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/pom.xml new file mode 100644 index 000000000..799c55f89 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + com.pig4cloud + pigx-flow-engine + 5.2.0-snapshot + + + pigx-flow-engine-api + + + + + com.pig4cloud + pigx-common-core + + + + com.baomidou + mybatis-plus-extension + + + + com.pig4cloud + pigx-common-feign + + + + com.pig4cloud + pigx-common-excel + + + diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/src/main/java/com/pig4cloud/pigx/flow/engine/api/feign/packge-info.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/src/main/java/com/pig4cloud/pigx/flow/engine/api/feign/packge-info.java new file mode 100644 index 000000000..b3dce91f5 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/src/main/java/com/pig4cloud/pigx/flow/engine/api/feign/packge-info.java @@ -0,0 +1,6 @@ +/* + @author pigx archetype + *

+ * feign client 存放目录,注意 @EnablePigxFeignClients 的扫描范围 + */ +package com.pig4cloud.pigx.flow.engine.api.feign; \ No newline at end of file diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/src/main/java/com/pig4cloud/pigx/flow/engine/constant/packge-info.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/src/main/java/com/pig4cloud/pigx/flow/engine/constant/packge-info.java new file mode 100644 index 000000000..99b19b1da --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/src/main/java/com/pig4cloud/pigx/flow/engine/constant/packge-info.java @@ -0,0 +1,6 @@ +/* + * @author pigx archetype + *

+ * 常量和枚举定义 + */ +package com.pig4cloud.pigx.flow.engine.constant; \ No newline at end of file diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/src/main/java/com/pig4cloud/pigx/flow/engine/dto/packge-info.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/src/main/java/com/pig4cloud/pigx/flow/engine/dto/packge-info.java new file mode 100644 index 000000000..6d96d5cdd --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/src/main/java/com/pig4cloud/pigx/flow/engine/dto/packge-info.java @@ -0,0 +1,6 @@ +/* + * @author pigx archetype + *

+ * DTO 存放目录 + */ +package com.pig4cloud.pigx.flow.engine.dto; \ No newline at end of file diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/src/main/java/com/pig4cloud/pigx/flow/engine/entity/packge-info.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/src/main/java/com/pig4cloud/pigx/flow/engine/entity/packge-info.java new file mode 100644 index 000000000..f841cdff5 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-api/src/main/java/com/pig4cloud/pigx/flow/engine/entity/packge-info.java @@ -0,0 +1,6 @@ +/* + * @author pigx archetype + *

+ * 实体 存放目录 + */ +package com.pig4cloud.pigx.flow.engine.entity; \ No newline at end of file diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/Dockerfile b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/Dockerfile new file mode 100644 index 000000000..2d03ba560 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/Dockerfile @@ -0,0 +1,18 @@ +FROM pig4cloud/java:8-jre + +MAINTAINER wangiegie@gmail.com + +ENV TZ=Asia/Shanghai +ENV JAVA_OPTS="-Xms512m -Xmx1024m -Djava.security.egd=file:/dev/./urandom" + +RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN mkdir -p /pigx-flow-engine + +WORKDIR /pigx-flow-engine + +EXPOSE 4000 + +ADD ./target/pigx-flow-engine-biz.jar ./ + +CMD sleep 60;java $JAVA_OPTS -jar pigx-flow-engine-biz.jar diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/pom.xml b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/pom.xml new file mode 100644 index 000000000..d7bcfcf09 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/pom.xml @@ -0,0 +1,134 @@ + + + 4.0.0 + + com.pig4cloud + pigx-flow-engine + 5.2.0-snapshot + + + pigx-flow-engine-biz + + + + + org.flowable + flowable-bpmn-layout + ${flowable.version} + + + org.flowable + flowable-spring-boot-starter-process + ${flowable.version} + + + + com.pig4cloud + pigx-flow-task-api + + + + com.googlecode.aviator + aviator + + + + org.springframework.boot + spring-boot-starter-undertow + + + + org.springframework.boot + spring-boot-starter-web + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + com.pig4cloud + pigx-common-data + + + + com.pig4cloud + pigx-common-security + + + + com.pig4cloud + pigx-common-xss + + + + com.pig4cloud + pigx-common-sentinel + + + + com.pig4cloud + pigx-common-feign + + + + com.pig4cloud + pigx-flow-engine-api + + + + com.pig4cloud + pigx-common-log + + + + com.baomidou + mybatis-plus-boot-starter + + + + com.alibaba + druid-spring-boot-starter + + + + com.mysql + mysql-connector-j + + + + com.pig4cloud + pigx-common-swagger + + + cn.hutool + hutool-cache + + + + org.springframework.boot + spring-boot-starter-test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + io.fabric8 + docker-maven-plugin + + + + diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/PigxFlowEngineApplication.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/PigxFlowEngineApplication.java new file mode 100644 index 000000000..fef1590f2 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/PigxFlowEngineApplication.java @@ -0,0 +1,26 @@ +package com.pig4cloud.pigx.flow.engine; + +import com.pig4cloud.pigx.common.feign.annotation.EnablePigxFeignClients; +import com.pig4cloud.pigx.common.security.annotation.EnablePigxResourceServer; +import com.pig4cloud.pigx.common.swagger.annotation.EnableOpenApi; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +/** + * @author pigx archetype + *

+ * 项目启动类 + */ +@EnableOpenApi("engine") +@EnablePigxFeignClients +@EnableDiscoveryClient +@EnablePigxResourceServer +@SpringBootApplication +public class PigxFlowEngineApplication { + + public static void main(String[] args) { + SpringApplication.run(PigxFlowEngineApplication.class, args); + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/controller/EngineFlowController.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/controller/EngineFlowController.java new file mode 100644 index 000000000..0d98c83fe --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/controller/EngineFlowController.java @@ -0,0 +1,298 @@ +package com.pig4cloud.pigx.flow.engine.controller; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.engine.utils.ModelUtil; +import com.pig4cloud.pigx.flow.task.dto.*; +import com.pig4cloud.pigx.flow.task.entity.Process; +import com.pig4cloud.pigx.flow.task.utils.NodeUtil; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.BpmnAutoLayout; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.common.engine.impl.identity.Authentication; +import org.flowable.engine.HistoryService; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.history.HistoricActivityInstanceQuery; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.Execution; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.image.impl.DefaultProcessDiagramGenerator; +import org.flowable.task.api.TaskQuery; +import org.springframework.web.bind.annotation.*; + +import java.io.InputStream; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 工作流控制器 负责流程模型的创建、启动、审批等功能 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/flow") +public class EngineFlowController { + + private final TaskService taskService; + + private final HistoryService historyService; + + private final RepositoryService repositoryService; + + private final RuntimeService runtimeService; + + private final ObjectMapper objectMapper; + + /** + * 创建流程定义 + * @param map 创建参数 + * @return 流程定义ID + */ + @PostMapping("create") + @SneakyThrows + public R create(@RequestBody Map map) { + + Long userId = MapUtil.getLong(map, "userId"); + + Process process = MapUtil.get(map, "process", Process.class); + + String flowId = "P" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN) + + RandomUtil.randomString(5).toUpperCase(); + log.info("flowId={}", flowId); + BpmnModel bpmnModel = ModelUtil.buildBpmnModel(objectMapper.readValue(process.getProcess(), Node.class), + process.getName(), flowId); + { + byte[] bpmnBytess = new BpmnXMLConverter().convertToXML(bpmnModel); + String filename = "/tmp/flowable-deployment/" + flowId + ".bpmn20.xml"; + log.debug("部署时的模型文件:{}", filename); + FileUtil.writeBytes(bpmnBytess, filename); + } + repositoryService.createDeployment().addBpmnModel(StrUtil.format("{}.bpmn20.xml", "pig"), bpmnModel).deploy(); + + return R.ok(flowId); + } + + /** + * 启动流程实例 + * @param processInstanceParamDto 启动参数 + * @return 流程实例ID + */ + @PostMapping("/start") + public R start(@RequestBody ProcessInstanceParamDto processInstanceParamDto) { + Authentication.setAuthenticatedUserId(processInstanceParamDto.getStartUserId()); + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processInstanceParamDto.getFlowId(), + processInstanceParamDto.getParamMap()); + + String processInstanceId = processInstance.getProcessInstanceId(); + return R.ok(processInstanceId); + + } + + /** + * 审批任务 + * @param taskId 任务ID + * @param approved 是否通过 + */ + @PostMapping("/approve") + public void approve(String taskId, boolean approved) { + Map variables = new HashMap<>(); + variables.put("approved", approved); + variables.put("ko", 10); + variables.put("assigneeListSub", CollUtil.newArrayList("aa", "bb")); + taskService.complete(taskId, variables); + + } + + /** + * 停止流程实例 + * @param taskParamDto 参数 + * @return 操作结果 + */ + @PostMapping("stopProcessInstance") + public R stopProcessInstance(@RequestBody TaskParamDto taskParamDto) { + List processInstanceIdList = taskParamDto.getProcessInstanceIdList(); + processInstanceIdList.forEach(processInstanceId -> { + // 查询流程实例 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(processInstanceId) + .singleResult(); + if (Optional.ofNullable(processInstance).isPresent()) { + // 查询执行实例 + List executionIds = runtimeService.createExecutionQuery() + .parentId(processInstanceId) + .list() + .stream() + .map(Execution::getId) + .collect(Collectors.toList()); + // 更改活动状态为结束 + runtimeService.createChangeActivityStateBuilder() + .moveExecutionsToSingleActivityId(executionIds, "end") + .changeState(); + } + }); + return R.ok(); + } + + /** + * 查询用户已办任务 + * @param taskQueryParamDto 查询参数 + * @return 分页任务信息 + */ + @PostMapping("/queryCompletedTask") + public R queryCompletedTask(@RequestBody TaskQueryParamDto taskQueryParamDto) { + HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService + .createHistoricActivityInstanceQuery(); + HistoricActivityInstanceQuery activityInstanceQuery = historicActivityInstanceQuery + .taskAssignee(taskQueryParamDto.getAssign()) + .finished() + .orderByHistoricActivityInstanceEndTime() + .desc(); + + if (ArrayUtil.isNotEmpty(taskQueryParamDto.getTaskTime())) { + ZoneId zoneId = ZoneId.systemDefault(); + ZonedDateTime zonedBeforeDateTime = taskQueryParamDto.getTaskTime()[0].atZone(zoneId); + ZonedDateTime zonedAfterDateTime = taskQueryParamDto.getTaskTime()[1].atZone(zoneId); + Date beforeDate = Date.from(zonedBeforeDateTime.toInstant()); + Date afterDate = Date.from(zonedAfterDateTime.toInstant()); + activityInstanceQuery.finishedBefore(afterDate).finishedAfter(beforeDate); + } + + List list = activityInstanceQuery.listPage( + (taskQueryParamDto.getPageNum() - 1) * taskQueryParamDto.getPageSize(), + taskQueryParamDto.getPageSize()); + + long count = activityInstanceQuery.count(); + List taskDtoList = new ArrayList<>(); + + for (HistoricActivityInstance historicActivityInstance : list) { + String activityId = historicActivityInstance.getActivityId(); + String activityName = historicActivityInstance.getActivityName(); + String executionId = historicActivityInstance.getExecutionId(); + String taskId = historicActivityInstance.getTaskId(); + Date startTime = historicActivityInstance.getStartTime(); + Date endTime = historicActivityInstance.getEndTime(); + Long durationInMillis = historicActivityInstance.getDurationInMillis(); + String processInstanceId = historicActivityInstance.getProcessInstanceId(); + + String processDefinitionId = historicActivityInstance.getProcessDefinitionId(); + // 流程id + String flowId = NodeUtil.getFlowId(processDefinitionId); + + TaskDto taskDto = new TaskDto(); + taskDto.setFlowId(flowId); + taskDto.setTaskCreateTime(startTime); + taskDto.setTaskEndTime(endTime); + taskDto.setNodeId(activityId); + taskDto.setExecutionId(executionId); + taskDto.setProcessInstanceId(processInstanceId); + taskDto.setDurationInMillis(durationInMillis); + taskDto.setTaskId(taskId); + taskDto.setAssign(historicActivityInstance.getAssignee()); + taskDto.setTaskName(activityName); + + taskDtoList.add(taskDto); + } + + Page pageResultDto = new Page<>(); + pageResultDto.setTotal(count); + pageResultDto.setRecords(taskDtoList); + return R.ok(pageResultDto); + } + + /** + * 查询用户待办任务 + * @param taskQueryParamDto 查询参数 + * @return 分页任务信息 + */ + @PostMapping("/queryAssignTask") + public R queryAssignTask(@RequestBody TaskQueryParamDto taskQueryParamDto) { + + String assign = taskQueryParamDto.getAssign(); + + List taskDtoList = new ArrayList<>(); + + int pageIndex = taskQueryParamDto.getPageNum() - 1; + int pageSize = taskQueryParamDto.getPageSize(); + + Page pageResultDto = new Page<>(); + TaskQuery taskQuery = taskService.createTaskQuery().taskAssignee(assign).orderByTaskCreateTime().desc(); + if (StrUtil.isNotBlank(taskQueryParamDto.getProcessName())) { + List processInstanceList = runtimeService.createProcessInstanceQuery() + .processInstanceNameLikeIgnoreCase(taskQueryParamDto.getProcessName()) + .list(); + if (CollUtil.isNotEmpty(processInstanceList)) { + taskQuery.processInstanceIdIn(processInstanceList.stream() + .map(ProcessInstance::getProcessDefinitionId) + .collect(Collectors.toList())); + } + } + + if (ArrayUtil.isNotEmpty(taskQueryParamDto.getTaskTime())) { + ZoneId zoneId = ZoneId.systemDefault(); + ZonedDateTime zonedBeforeDateTime = taskQueryParamDto.getTaskTime()[0].atZone(zoneId); + ZonedDateTime zonedAfterDateTime = taskQueryParamDto.getTaskTime()[1].atZone(zoneId); + Date beforeDate = Date.from(zonedBeforeDateTime.toInstant()); + Date afterDate = Date.from(zonedAfterDateTime.toInstant()); + taskQuery.taskCreatedBefore(afterDate).taskCreatedAfter(beforeDate); + } + + taskQuery.listPage(pageIndex * pageSize, pageSize).forEach(task -> { + String taskId = task.getId(); + String processInstanceId = task.getProcessInstanceId(); + log.debug("(taskId) " + task.getName() + " processInstanceId={} executrionId={}", processInstanceId, + task.getExecutionId()); + + Map taskServiceVariables = taskService.getVariables(taskId); + Map runtimeServiceVariables = runtimeService.getVariables(processInstanceId); + Map variables = runtimeService.getVariables(task.getExecutionId()); + log.debug("任务变量:{}", JSONUtil.toJsonStr(taskServiceVariables)); + log.debug("流程节点:{}", JSONUtil.toJsonStr(runtimeServiceVariables)); + log.debug("执行节点变量:{}", JSONUtil.toJsonStr(variables)); + + String taskDefinitionKey = task.getTaskDefinitionKey(); + String processDefinitionId = task.getProcessDefinitionId(); + String flowId = NodeUtil.getFlowId(processDefinitionId); + + TaskDto taskDto = new TaskDto(); + taskDto.setFlowId(flowId); + taskDto.setTaskCreateTime(task.getCreateTime()); + taskDto.setNodeId(taskDefinitionKey); + taskDto.setParamMap(taskServiceVariables); + taskDto.setProcessInstanceId(processInstanceId); + taskDto.setTaskId(taskId); + taskDto.setAssign(task.getAssignee()); + taskDto.setTaskName(task.getName()); + + taskDtoList.add(taskDto); + }); + + long count = taskQuery.count(); + + log.debug("当前有" + count + " 个任务:"); + + pageResultDto.setTotal(count); + pageResultDto.setRecords(taskDtoList); + + return R.ok(pageResultDto); + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/controller/EngineProcessInstanceController.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/controller/EngineProcessInstanceController.java new file mode 100644 index 000000000..be2d2b0e5 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/controller/EngineProcessInstanceController.java @@ -0,0 +1,71 @@ +package com.pig4cloud.pigx.flow.engine.controller; + +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.IndexPageStatistics; +import com.pig4cloud.pigx.flow.task.dto.VariableQueryParamDto; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.HistoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.history.HistoricActivityInstanceQuery; +import org.flowable.task.api.TaskQuery; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 任务控制器 + */ +@RestController +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/process-instance") +public class EngineProcessInstanceController { + + private final TaskService taskService; + + private final HistoryService historyService; + + private final RuntimeService runtimeService; + + /** + * 查询首页统计数量 + * @param userId 用户ID + * @return 统计结果 + */ + @GetMapping("querySimpleData") + public R querySimpleData(long userId) { + TaskQuery taskQuery = taskService.createTaskQuery(); + + // 待办数量 + long pendingNum = taskQuery.taskAssignee(String.valueOf(userId)).count(); + // 已完成任务 + HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService + .createHistoricActivityInstanceQuery(); + + long completedNum = historicActivityInstanceQuery.taskAssignee(String.valueOf(userId)).finished().count(); + + IndexPageStatistics indexPageStatistics = IndexPageStatistics.builder() + .pendingNum(pendingNum) + .completedNum(completedNum) + .build(); + + return R.ok(indexPageStatistics); + } + + /** + * 查询流程变量 + * @param paramDto 查询参数 + * @return 流程变量 + */ + @PostMapping("queryVariables") + public R queryVariables(@RequestBody VariableQueryParamDto paramDto) { + + Map variables = runtimeService.getVariables(paramDto.getExecutionId()); + + return R.ok(variables); + + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/controller/EngineTaskController.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/controller/EngineTaskController.java new file mode 100644 index 000000000..4171408d1 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/controller/EngineTaskController.java @@ -0,0 +1,149 @@ +package com.pig4cloud.pigx.flow.engine.controller; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.TaskParamDto; +import com.pig4cloud.pigx.flow.task.dto.TaskResultDto; +import com.pig4cloud.pigx.flow.task.dto.VariableQueryParamDto; +import com.pig4cloud.pigx.flow.task.utils.NodeUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.HistoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.task.api.DelegationState; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * 任务控制器 + */ +@RestController +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/task") +public class EngineTaskController { + + private final TaskService taskService; + + private final HistoryService historyService; + + private final RuntimeService runtimeService; + + /** + * 查询任务变量 + * @param paramDto 参数DTO + * @return 变量Map + */ + @PostMapping("queryTaskVariables") + public R queryTaskVariables(@RequestBody VariableQueryParamDto paramDto) { + + List keyList = paramDto.getKeyList(); + if (CollUtil.isEmpty(keyList)) { + TaskQuery taskQuery = taskService.createTaskQuery(); + + Task task = taskQuery.taskId(paramDto.getTaskId()).singleResult(); + if (task == null) { + return R.failed("任务不存在"); + } + + Map variables = runtimeService.getVariables(task.getExecutionId()); + + return R.ok(variables); + } + + Map variables = taskService.getVariables(paramDto.getTaskId(), keyList); + return R.ok(variables); + + } + + /** + * 查询任务详情 + * @param taskId 任务ID + * @param userId 用户ID + * @return 任务结果DTO + */ + @GetMapping("/engine/queryTask") + public R queryTask(String taskId, String userId) { + Optional task = Optional + .ofNullable(taskService.createTaskQuery().taskId(taskId).taskAssignee(userId).singleResult()); + + if (task.isPresent()) { + String processDefinitionId = task.get().getProcessDefinitionId(); + String taskDefinitionKey = task.get().getTaskDefinitionKey(); + DelegationState delegationState = task.get().getDelegationState(); + String processInstanceId = task.get().getProcessInstanceId(); + Object delegateVariable = taskService.getVariableLocal(taskId, "delegate"); + + String flowId = NodeUtil.getFlowId(processDefinitionId); + Map variables = taskService.getVariables(taskId); + Map variableAll = new HashMap<>(variables); + + TaskResultDto taskResultDto = new TaskResultDto(); + taskResultDto.setFlowId(flowId); + taskResultDto.setProcessInstanceId(processDefinitionId); + taskResultDto.setNodeId(taskDefinitionKey); + taskResultDto.setCurrentTask(true); + taskResultDto.setDelegate(Convert.toBool(delegateVariable, false)); + taskResultDto.setVariableAll(variableAll); + taskResultDto.setProcessInstanceId(processInstanceId); + taskResultDto.setDelegationState(delegationState == null ? null : delegationState.toString()); + + return R.ok(taskResultDto); + } + else { + Optional historicTaskInstance = Optional + .ofNullable(historyService.createHistoricTaskInstanceQuery() + .taskId(taskId) + .taskAssignee(userId) + .singleResult()); + + if (historicTaskInstance.isPresent()) { + String processDefinitionId = historicTaskInstance.get().getProcessDefinitionId(); + String taskDefinitionKey = historicTaskInstance.get().getTaskDefinitionKey(); + String processInstanceId = historicTaskInstance.get().getProcessInstanceId(); + + String flowId = NodeUtil.getFlowId(processDefinitionId); + Map variableAll = new HashMap<>(); + + TaskResultDto taskResultDto = new TaskResultDto(); + taskResultDto.setFlowId(flowId); + taskResultDto.setNodeId(taskDefinitionKey); + taskResultDto.setCurrentTask(false); + taskResultDto.setVariableAll(variableAll); + taskResultDto.setProcessInstanceId(processInstanceId); + + return R.ok(taskResultDto); + } + else { + return R.failed("任务不存在"); + } + } + } + + /** + * 完成任务 + * @param taskParamDto 任务参数DTO + * @return 操作结果 + */ + @PostMapping("/complete") + public R complete(@RequestBody TaskParamDto taskParamDto) { + Map taskLocalParamMap = taskParamDto.getTaskLocalParamMap(); + if (CollUtil.isNotEmpty(taskLocalParamMap)) { + taskService.setVariablesLocal(taskParamDto.getTaskId(), taskLocalParamMap); + } + + taskService.complete(taskParamDto.getTaskId(), taskParamDto.getParamMap()); + + return R.ok(); + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/ExpressionHandler.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/ExpressionHandler.java new file mode 100644 index 000000000..a0c80faa6 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/ExpressionHandler.java @@ -0,0 +1,311 @@ +package com.pig4cloud.pigx.flow.engine.expression; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.EscapeUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.googlecode.aviator.AviatorEvaluator; +import com.pig4cloud.pigx.admin.api.entity.SysUser; +import com.pig4cloud.pigx.admin.api.feign.RemoteUserService; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.constant.NodeUserTypeEnum; +import com.pig4cloud.pigx.flow.task.dto.NodeUser; +import com.pig4cloud.pigx.flow.task.dto.SelectValue; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 表达式解析 + */ +@Slf4j +@Component("expressionHandler") +@RequiredArgsConstructor +public class ExpressionHandler { + + private final RemoteUserService remoteUserService; + + private final ObjectMapper objectMapper; + + @SneakyThrows + public Long getUserId(String key, DelegateExecution execution) { + Object variable = execution.getVariable(key); + NodeUser nodeUserDto = objectMapper + .readValue(objectMapper.writeValueAsString(variable), new TypeReference>() { + }) + .get(0); + return nodeUserDto.getId(); + } + + /** + * 日期时间比较 + * @param key + * @param symbol + * @param param + * @param execution + * @param format 时间格式化模式 + * @return + */ + public boolean dateTimeCompare(String key, String symbol, Object param, DelegateExecution execution, + String format) { + + Object value = execution.getVariable(key); + + // 表单值为空 + if (value == null) { + return false; + } + + long valueTime = DateUtil.parse(value.toString(), format).getTime(); + long paramTime = DateUtil.parse(param.toString(), format).getTime(); + + return compare(StrUtil.format("key{}{}", symbol, paramTime), Dict.create().set("key", valueTime)); + } + + /** + * 数字类型比较 + * @param key 表单key + * @param symbol 比较符号 + * @param param 表单参数 + * @param execution + * @return + */ + public boolean numberCompare(String key, String symbol, Object param, DelegateExecution execution) { + + Object value = execution.getVariable(key); + + // 表单值为空 + if (value == null) { + return false; + } + + return compare(StrUtil.format("key{}{}", symbol, param), Dict.create().set("key", Convert.toNumber(value))); + + } + + private Boolean compare(String symbol, Dict value) { + Object result = AviatorEvaluator.getInstance().execute(symbol, value); + // 渲染结果 + log.debug("验证结果:{}", result); + return Convert.toBool(result, false); + } + + /** + * 日期类型对比 + * @param key 表单key + * @param symbol 比较符号 + * @param param 比较参数值 + * @param execution 上下午执行前 + * @param format 日期格式化字符串 + * @return + */ + public boolean dateCompare(String key, String symbol, Object param, DelegateExecution execution, String format) { + + Object value = execution.getVariable(key); + + // 表单值为空 + if (value == null) { + return false; + } + + // 处理表单值 + DateTime valueDateTime = DateUtil.parse(value.toString(), format); + log.debug("表单值:{} 格式化显示:{}", valueDateTime.getTime(), DateUtil.formatDateTime(valueDateTime)); + // 处理参数值 + DateTime paramDateTime = DateUtil.parse(param.toString(), format); + log.debug("参数值:{} 格式化显示:{}", paramDateTime.getTime(), DateUtil.formatDateTime(paramDateTime)); + + // 获取模板 + return compare(StrUtil.format("key{}{}", symbol, paramDateTime.getTime()), + Dict.create().set("key", valueDateTime.getTime())); + + } + + /** + * 判断数字数组包含 + * @param key 表单key + * @param array 条件值 + * @return + */ + public boolean numberContain(String key, DelegateExecution execution, Object... array) { + + Object value = execution.getVariable(key); + + if (value == null) { + return false; + } + + return numberContain(value, array); + } + + private static boolean numberContain(Object value, Object[] array) { + BigDecimal valueBigDecimal = Convert.toBigDecimal(value); + + for (Object aLong : array) { + if (valueBigDecimal.compareTo(Convert.toBigDecimal(aLong)) == 0) { + return true; + } + } + return false; + } + + /** + * 单选处理 + * @param key 表单key + * @param array 条件值 + * @return + */ + public boolean singleSelectHandler(String key, DelegateExecution execution, String... array) { + Object value = execution.getVariable(key); + + if (value == null) { + return false; + } + List list = Convert.toList(SelectValue.class, value); + if (CollUtil.isEmpty(list)) { + return false; + } + return ArrayUtil.contains(array, list.get(0).getKey()); + } + + /** + * 字符串判断包含 + * @param key 表单key + * @param param 参数 + * @return + */ + public boolean stringContain(String key, String param, DelegateExecution execution) { + Object value = execution.getVariable(key); + + if (value == null) { + return false; + } + return StrUtil.contains(value.toString(), param); + } + + /** + * 字符串判断相等 + * @param key 表单key + * @param param 参数 + * @return + */ + public boolean stringEqual(String key, String param, DelegateExecution execution) { + Object value = execution.getVariable(key); + + if (value == null) { + return false; + } + return StrUtil.equals(value.toString(), param); + } + + @SneakyThrows + public boolean deptCompare(String key, String param, String symbol, DelegateExecution execution) { + param = EscapeUtil.unescape(param); + + Object value = execution.getVariable(key); + + String jsonString = objectMapper.writeValueAsString(value); + log.debug("表单值:key={} value={} symbol={}", key, jsonString, symbol); + log.debug("条件 参数:{}", param); + if (value == null) { + return false; + } + + // 表单值 + List nodeUserDtoList = objectMapper.readValue(jsonString, new TypeReference>() { + }); + if (CollUtil.isEmpty(nodeUserDtoList) || nodeUserDtoList.size() != 1) { + return false; + } + NodeUser nodeUserDto = nodeUserDtoList.get(0); + + // 参数 + + List paramDeptList = objectMapper.readValue(param, new TypeReference>() { + }); + Long deptId = nodeUserDto.getId(); + List deptIdList = paramDeptList.stream().map(NodeUser::getId).collect(Collectors.toList()); + + return inCompare(symbol, deptId, deptIdList); + + } + + private static boolean inCompare(String symbol, Long deptId, List deptIdList) { + if (StrUtil.equalsAny(symbol, "in", "==")) { + // 属于 + return deptIdList.contains(deptId); + } + if (StrUtil.equalsAny(symbol, "notin", "!=")) { + // 属于 + return !deptIdList.contains(deptId); + } + + return false; + } + + /** + * user判断 + * @param key 表单key + * @param param 参数 + * @return + */ + @SneakyThrows + public boolean userCompare(String key, String param, String symbol, DelegateExecution execution) { + param = EscapeUtil.unescape(param); + + Object value = execution.getVariable(key); + + String jsonString = objectMapper.writeValueAsString(value); + log.debug("表单值:key={} value={} symbol={} ", key, jsonString, symbol); + log.debug("条件 参数:{}", param); + + if (value == null) { + return false; + } + + // 表单值 + List nodeUserDtoList = objectMapper.readValue(jsonString, new TypeReference>() { + }); + + if (CollUtil.isEmpty(nodeUserDtoList) || nodeUserDtoList.size() != 1) { + return false; + } + + NodeUser nodeUserDto = nodeUserDtoList.get(0); + + // 参数 + List paramDeptList = objectMapper.readValue(param, new TypeReference>() { + }); + + List deptIdList = paramDeptList.stream() + .filter(w -> StrUtil.equals(w.getType(), NodeUserTypeEnum.DEPT.getKey())) + .map(NodeUser::getId) + .collect(Collectors.toList()); + + List userIdList = paramDeptList.stream() + .filter(w -> StrUtil.equals(w.getType(), NodeUserTypeEnum.USER.getKey())) + .map(NodeUser::getId) + .collect(Collectors.toList()); + + if (CollUtil.isNotEmpty(deptIdList)) { + R> r = remoteUserService.getUserIdListByDeptIdList(deptIdList); + List data = r.getData().stream().map(SysUser::getUserId).collect(Collectors.toList()); + userIdList.addAll(data); + } + + return inCompare(symbol, Convert.toLong(nodeUserDto.getId()), userIdList); + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/NodeConditionStrategy.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/NodeConditionStrategy.java new file mode 100644 index 000000000..6eca2fd28 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/NodeConditionStrategy.java @@ -0,0 +1,15 @@ +package com.pig4cloud.pigx.flow.engine.expression.condition; + +import com.pig4cloud.pigx.flow.task.dto.Condition; + +/** + * 节点单个条件处理器 + */ +public interface NodeConditionStrategy { + + /** + * 抽象方法 处理表达式 + */ + String handle(Condition condition); + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/NodeExpressionStrategyFactory.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/NodeExpressionStrategyFactory.java new file mode 100644 index 000000000..150766156 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/NodeExpressionStrategyFactory.java @@ -0,0 +1,99 @@ +package com.pig4cloud.pigx.flow.engine.expression.condition; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.pig4cloud.pigx.flow.task.dto.Condition; +import com.pig4cloud.pigx.flow.task.dto.GroupCondition; +import com.pig4cloud.pigx.flow.task.dto.Node; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class NodeExpressionStrategyFactory { + + public static String handleSingleCondition(Condition nodeConditionDto) { + Map nodeConditionStrategyMap = SpringUtil + .getBeansOfType(NodeConditionStrategy.class); + NodeConditionStrategy nodeConditionHandler = nodeConditionStrategyMap + .get(nodeConditionDto.getKeyType() + "NodeConditionStrategy"); + if (nodeConditionHandler == null) { + return "(1==1)"; + } + return nodeConditionHandler.handle(nodeConditionDto); + } + + /** + * 组内处理表达式 + * @param groupDto + * @return + */ + public static String handleGroupCondition(GroupCondition groupDto) { + + List exps = new ArrayList<>(); + + for (Condition condition : groupDto.getConditionList()) { + String singleExpression = handleSingleCondition(condition); + exps.add(singleExpression); + } + Boolean mode = groupDto.getMode(); + + if (!mode) { + String join = CollUtil.join(exps, "||"); + + return "(" + join + ")"; + } + + String join = CollUtil.join(exps, "&&"); + return "(" + join + ")"; + } + + /** + * 处理单个分支表达式 + * @return + */ + public static String handle(Node node) { + + List exps = new ArrayList<>(); + + List groups = node.getConditionList(); + if (CollUtil.isEmpty(groups)) { + return "${1==1}"; + } + for (GroupCondition group : groups) { + String s = handleGroupCondition(group); + exps.add(s); + } + + if (!node.getGroupMode()) { + String join = CollUtil.join(exps, "||"); + return "${(" + join + ")}"; + } + + String join = CollUtil.join(exps, "&&"); + return "${(" + join + ")}"; + } + + public static String handleDefaultBranch(List branchs, int currentIndex) { + + List expList = new ArrayList<>(); + + int index = 1; + for (Node branch : branchs) { + + if (index == currentIndex + 1) { + continue; + } + + String exp = handle(branch); + String s = StrUtil.subBetween(exp, "${", "}"); + expList.add(StrUtil.format("({})", s)); + + index++; + } + String join = StrUtil.format("!({})", CollUtil.join(expList, "||")); + return "${" + join + "}"; + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/DateNodeConditionStrategy.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/DateNodeConditionStrategy.java new file mode 100644 index 000000000..85a17089f --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/DateNodeConditionStrategy.java @@ -0,0 +1,29 @@ +package com.pig4cloud.pigx.flow.engine.expression.condition.impl; + +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy; +import com.pig4cloud.pigx.flow.task.dto.Condition; +import org.springframework.stereotype.Component; + +/** + * 字符类型处理器 + */ +@Component("DateNodeConditionStrategy") +public class DateNodeConditionStrategy implements NodeConditionStrategy { + + /** + * 抽象方法 处理表达式 + */ + @Override + public String handle(Condition condition) { + + String compare = condition.getExpression(); + String id = condition.getKey(); + Object value = condition.getValue(); + + return StrUtil.format("(expressionHandler.dateTimeCompare(\"{}\",\"{}\",\"{}\",execution,\"yyyy-MM-dd\"))", id, + compare, value); + + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/DateTimeNodeConditionStrategy.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/DateTimeNodeConditionStrategy.java new file mode 100644 index 000000000..c6e891525 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/DateTimeNodeConditionStrategy.java @@ -0,0 +1,30 @@ +package com.pig4cloud.pigx.flow.engine.expression.condition.impl; + +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy; +import com.pig4cloud.pigx.flow.task.dto.Condition; +import org.springframework.stereotype.Component; + +/** + * 字符类型处理器 + */ +@Component("DateTimeNodeConditionStrategy") +public class DateTimeNodeConditionStrategy implements NodeConditionStrategy { + + /** + * 抽象方法 处理表达式 + */ + @Override + public String handle(Condition condition) { + + String compare = condition.getExpression(); + String id = condition.getKey(); + Object value = condition.getValue(); + + return StrUtil.format( + "(expressionHandler.dateTimeCompare(\"{}\",\"{}\",\"{}\",execution,\"yyyy-MM-dd " + "HH:mm:ss\"))", id, + compare, value); + + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/InputNodeConditionStrategy.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/InputNodeConditionStrategy.java new file mode 100644 index 000000000..6176f80b6 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/InputNodeConditionStrategy.java @@ -0,0 +1,39 @@ +package com.pig4cloud.pigx.flow.engine.expression.condition.impl; + +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy; +import com.pig4cloud.pigx.flow.task.dto.Condition; +import org.springframework.stereotype.Component; + +/** + * 字符类型处理器 + */ +@Component("InputNodeConditionStrategy") +public class InputNodeConditionStrategy implements NodeConditionStrategy { + + /** + * 抽象方法 处理表达式 + * + */ + @Override + public String handle(Condition condition) { + + String compare = condition.getExpression(); + String id = condition.getKey(); + Object value = condition.getValue(); + if (StrUtil.equals(compare, "==")) { + return StrUtil.format("(expressionHandler.stringEqual(\"{}\",\"{}\",execution))", id, value); + } + if (StrUtil.equals(compare, "contain")) { + return StrUtil.format("(expressionHandler.stringContain(\"{}\",\"{}\",execution))", id, value); + } + if (StrUtil.equals(compare, "notcontain")) { + return StrUtil.format("(!expressionHandler.stringContain(\"{}\",\"{}\",execution))", id, value); + } + if (StrUtil.equals(compare, "!=")) { + return StrUtil.format("(!expressionHandler.stringEqual(\"{}\",\"{}\",execution))", id, value); + } + return "(2==2)"; + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/MoneyNodeConditionStrategy.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/MoneyNodeConditionStrategy.java new file mode 100644 index 000000000..90e720521 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/MoneyNodeConditionStrategy.java @@ -0,0 +1,28 @@ +package com.pig4cloud.pigx.flow.engine.expression.condition.impl; + +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy; +import com.pig4cloud.pigx.flow.task.dto.Condition; +import org.springframework.stereotype.Component; + +/** + * 字符类型处理器 + */ +@Component("MoneyNodeConditionStrategy") +public class MoneyNodeConditionStrategy implements NodeConditionStrategy { + + /** + * 抽象方法 处理表达式 + */ + @Override + public String handle(Condition condition) { + + String compare = condition.getExpression(); + String id = condition.getKey(); + Object value = condition.getValue(); + + return StrUtil.format("(expressionHandler.numberCompare(\"{}\",\"{}\",{},execution))", id, compare, value); + + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/NumberNodeConditionStrategy.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/NumberNodeConditionStrategy.java new file mode 100644 index 000000000..a55086c55 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/NumberNodeConditionStrategy.java @@ -0,0 +1,28 @@ +package com.pig4cloud.pigx.flow.engine.expression.condition.impl; + +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy; +import com.pig4cloud.pigx.flow.task.dto.Condition; +import org.springframework.stereotype.Component; + +/** + * 字符类型处理器 + */ +@Component("NumberNodeConditionStrategy") +public class NumberNodeConditionStrategy implements NodeConditionStrategy { + + /** + * 抽象方法 处理表达式 + */ + @Override + public String handle(Condition condition) { + + String compare = condition.getExpression(); + String id = condition.getKey(); + Object value = condition.getValue(); + + return StrUtil.format("(expressionHandler.numberCompare(\"{}\",\"{}\",{},execution))", id, compare, value); + + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/SelectDeptNodeConditionStrategy.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/SelectDeptNodeConditionStrategy.java new file mode 100644 index 000000000..6694dfc13 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/SelectDeptNodeConditionStrategy.java @@ -0,0 +1,37 @@ +package com.pig4cloud.pigx.flow.engine.expression.condition.impl; + +import cn.hutool.core.util.EscapeUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy; +import com.pig4cloud.pigx.flow.task.dto.Condition; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.stereotype.Component; + +/** + * 字符类型处理器 + */ +@RequiredArgsConstructor +@Component("SelectDeptNodeConditionStrategy") +public class SelectDeptNodeConditionStrategy implements NodeConditionStrategy { + + private final ObjectMapper objectMapper; + + /** + * 抽象方法 处理表达式 + */ + @Override + @SneakyThrows + public String handle(Condition condition) { + + String compare = condition.getExpression(); + String id = condition.getKey(); + Object value = condition.getValue(); + + return StrUtil.format("(expressionHandler.deptCompare(\"{}\",\"{}\",\"{}\", execution))", id, + EscapeUtil.escape(objectMapper.writeValueAsString(value)), compare); + + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/SelectUserNodeConditionStrategy.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/SelectUserNodeConditionStrategy.java new file mode 100644 index 000000000..3af9c9749 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/SelectUserNodeConditionStrategy.java @@ -0,0 +1,37 @@ +package com.pig4cloud.pigx.flow.engine.expression.condition.impl; + +import cn.hutool.core.util.EscapeUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy; +import com.pig4cloud.pigx.flow.task.dto.Condition; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.stereotype.Component; + +/** + * 字符类型处理器 + */ +@RequiredArgsConstructor +@Component("SelectUserNodeConditionStrategy") +public class SelectUserNodeConditionStrategy implements NodeConditionStrategy { + + private final ObjectMapper objectMapper; + + /** + * 抽象方法 处理表达式 + */ + @Override + @SneakyThrows + public String handle(Condition condition) { + + String compare = condition.getExpression(); + String id = condition.getKey(); + Object value = condition.getValue(); + + return StrUtil.format("(expressionHandler.userCompare(\"{}\",\"{}\",\"{}\", execution))", id, + EscapeUtil.escape(objectMapper.writeValueAsString(value)), compare); + + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/SingleSelectNodeConditionStrategy.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/SingleSelectNodeConditionStrategy.java new file mode 100644 index 000000000..7b4496b0b --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/SingleSelectNodeConditionStrategy.java @@ -0,0 +1,48 @@ +package com.pig4cloud.pigx.flow.engine.expression.condition.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy; +import com.pig4cloud.pigx.flow.task.dto.Condition; +import com.pig4cloud.pigx.flow.task.dto.SelectValue; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 字符类型处理器 + */ +@Component("SingleSelectNodeConditionStrategy") +public class SingleSelectNodeConditionStrategy implements NodeConditionStrategy { + + /** + * 抽象方法 处理表达式 + */ + @Override + public String handle(Condition condition) { + + String compare = condition.getExpression(); + String id = condition.getKey(); + Object value = condition.getValue(); + + List list = Convert.toList(SelectValue.class, value); + + StringBuilder sb = new StringBuilder(); + + for (SelectValue o : list) { + sb.append(",\"").append(o.getKey()).append("\""); + } + String string = sb.toString(); + if (CollUtil.isNotEmpty(list)) { + string = string.substring(1); + } + if (compare.equals("in")) { + return StrUtil.format("(expressionHandler.singleSelectHandler(\"{}\", execution,{}))", id, string); + } + + return StrUtil.format("(!expressionHandler.singleSelectHandler(\"{}\", execution,{}))", id, string); + + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/TextareaNodeConditionStrategy.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/TextareaNodeConditionStrategy.java new file mode 100644 index 000000000..d4c8b219d --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/TextareaNodeConditionStrategy.java @@ -0,0 +1,33 @@ +package com.pig4cloud.pigx.flow.engine.expression.condition.impl; + +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy; +import com.pig4cloud.pigx.flow.task.dto.Condition; +import org.springframework.stereotype.Component; + +/** + * 字符类型处理器 + */ +@Component("TextareaNodeConditionStrategy") +public class TextareaNodeConditionStrategy implements NodeConditionStrategy { + + /** + * 抽象方法 处理表达式 + * + */ + @Override + public String handle(Condition condition) { + + String compare = condition.getExpression(); + String id = condition.getKey(); + Object value = condition.getValue(); + if (StrUtil.equals(compare, "==")) { + return StrUtil.format("(expressionHandler.stringEqual(\"{}\",\"{}\",execution))", id, value); + } + if (StrUtil.equals(compare, "!=")) { + return StrUtil.format("(!expressionHandler.stringEqual(\"{}\",\"{}\",execution))", id, value); + } + return "(2==2)"; + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/TimeNodeConditionStrategy.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/TimeNodeConditionStrategy.java new file mode 100644 index 000000000..44db0c2f0 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/expression/condition/impl/TimeNodeConditionStrategy.java @@ -0,0 +1,29 @@ +package com.pig4cloud.pigx.flow.engine.expression.condition.impl; + +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pigx.flow.engine.expression.condition.NodeConditionStrategy; +import com.pig4cloud.pigx.flow.task.dto.Condition; +import org.springframework.stereotype.Component; + +/** + * 字符类型处理器 + */ +@Component("TimeNodeConditionStrategy") +public class TimeNodeConditionStrategy implements NodeConditionStrategy { + + /** + * 抽象方法 处理表达式 + */ + @Override + public String handle(Condition condition) { + + String compare = condition.getExpression(); + String id = condition.getKey(); + Object value = condition.getValue(); + + return StrUtil.format("(expressionHandler.dateTimeCompare(\"{}\",\"{}\",\"{}\",execution,\"HH:mm:ss\"))", id, + compare, value); + + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/listeners/ApprovalCreateListener.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/listeners/ApprovalCreateListener.java new file mode 100644 index 000000000..5fbdc3989 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/listeners/ApprovalCreateListener.java @@ -0,0 +1,91 @@ +package com.pig4cloud.pigx.flow.engine.listeners; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowTaskService; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.Nobody; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.NodeUser; +import com.pig4cloud.pigx.flow.task.utils.NodeUtil; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.TaskService; +import org.flowable.task.service.delegate.DelegateTask; +import org.flowable.task.service.delegate.TaskListener; +import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; + +/** + * 审批创建监听器 + */ +@Slf4j +public class ApprovalCreateListener implements TaskListener { + + /** + * 当任务被触发时,执行该方法 + * @param delegateTask 委派的任务对象 + */ + @Override + public void notify(DelegateTask delegateTask) { + log.debug(delegateTask.getClass().getCanonicalName()); + TaskService taskService = SpringUtil.getBean(TaskService.class); + + String assignee = delegateTask.getAssignee(); + String name = delegateTask.getName(); + log.debug("任务{}-执行人:{}", name, assignee); + TaskEntityImpl taskEntity = (TaskEntityImpl) delegateTask; + String nodeId = taskEntity.getTaskDefinitionKey(); + String processDefinitionId = taskEntity.getProcessDefinitionId(); + + // 获取流程id + String flowId = NodeUtil.getFlowId(processDefinitionId); + + if (StrUtil.isBlank(assignee) + || StrUtil.equals(ProcessInstanceConstant.DEFAULT_EMPTY_ASSIGN.toString(), assignee)) { + + RemoteFlowTaskService remoteFlowTaskService = SpringUtil.getBean(RemoteFlowTaskService.class); + + // 查询节点原始数据 + Node node = remoteFlowTaskService.queryNodeOriData(flowId, nodeId).getData(); + + Nobody nobody = node.getNobody(); + + String handler = nobody.getHandler(); + + if (StrUtil.equals(handler, ProcessInstanceConstant.USER_TASK_NOBODY_HANDLER_TO_PASS)) { + // 直接通过 + Dict param = Dict.create().set(StrUtil.format("{}_approve_condition", nodeId), true); + taskService.complete(taskEntity.getId(), param); + } + + if (StrUtil.equals(handler, ProcessInstanceConstant.USER_TASK_NOBODY_HANDLER_TO_ADMIN)) { + // 指派给管理员 + + R longR = remoteFlowTaskService.queryProcessAdmin(flowId); + + Long adminId = longR.getData(); + + taskService.setAssignee(taskEntity.getId(), String.valueOf(adminId)); + } + + if (StrUtil.equals(handler, ProcessInstanceConstant.USER_TASK_NOBODY_HANDLER_TO_USER)) { + // 指定用户 + + NodeUser nodeUser = nobody.getAssignedUser().get(0); + + taskService.setAssignee(taskEntity.getId(), nodeUser.getId().toString()); + } + + if (StrUtil.equals(handler, ProcessInstanceConstant.USER_TASK_NOBODY_HANDLER_TO_REFUSE)) { + // 结束 + Dict param = Dict.create().set(StrUtil.format("{}_approve_condition", nodeId), false); + taskService.complete(taskEntity.getId(), param); + + } + + } + + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/listeners/FlowProcessEventListener.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/listeners/FlowProcessEventListener.java new file mode 100644 index 000000000..4519627f6 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/listeners/FlowProcessEventListener.java @@ -0,0 +1,296 @@ +package com.pig4cloud.pigx.flow.engine.listeners; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowTaskService; +import com.pig4cloud.pigx.flow.task.dto.*; +import com.pig4cloud.pigx.flow.task.utils.NodeUtil; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.common.engine.api.delegate.event.FlowableEntityEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEventListener; +import org.flowable.engine.TaskService; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.event.impl.FlowableActivityEventImpl; +import org.flowable.engine.delegate.event.impl.FlowableMultiInstanceActivityCompletedEventImpl; +import org.flowable.engine.delegate.event.impl.FlowableProcessStartedEventImpl; +import org.flowable.engine.delegate.event.impl.FlowableProcessTerminatedEventImpl; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl; +import org.flowable.task.api.DelegationState; +import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; +import org.flowable.variable.api.event.FlowableVariableEvent; + +import java.util.List; +import java.util.Map; + +/** + * 流程监听器 + */ +@Slf4j +public class FlowProcessEventListener implements FlowableEventListener { + + /** + * 当事件被触发时调用 + * @param event 事件对象 + */ + @SneakyThrows + @Override + public void onEvent(FlowableEvent event) { + RemoteFlowTaskService remoteFlowTaskService = SpringUtil.getBean(RemoteFlowTaskService.class); + + log.info("分支监听器 类型={} class={}", event.getType(), event.getClass().getCanonicalName()); + if (event.getType().toString().equals(FlowableEngineEventType.ACTIVITY_STARTED.toString())) { + // 节点开始执行 + FlowableActivityEventImpl flowableActivityEvent = (FlowableActivityEventImpl) event; + String activityId = flowableActivityEvent.getActivityId(); + String activityName = flowableActivityEvent.getActivityName(); + log.info("节点id:{} 名字:{}", activityId, activityName); + + String processInstanceId = flowableActivityEvent.getProcessInstanceId(); + + String processDefinitionId = flowableActivityEvent.getProcessDefinitionId(); + String flowId = NodeUtil.getFlowId(processDefinitionId); + + Node node = remoteFlowTaskService.queryNodeOriData(flowId, activityId).getData(); + + ProcessNodeRecordParamDto processNodeRecordParamDto = new ProcessNodeRecordParamDto(); + processNodeRecordParamDto.setFlowId(flowId); + processNodeRecordParamDto.setProcessInstanceId(processInstanceId); + processNodeRecordParamDto.setNodeId(activityId); + if (node != null) { + processNodeRecordParamDto.setNodeType(String.valueOf(node.getType())); + } + processNodeRecordParamDto.setNodeName(activityName); + processNodeRecordParamDto.setExecutionId(flowableActivityEvent.getExecutionId()); + remoteFlowTaskService.startNodeEvent(processNodeRecordParamDto); + } + + if (event.getType() + .toString() + .equals(FlowableEngineEventType.MULTI_INSTANCE_ACTIVITY_COMPLETED_WITH_CONDITION.toString()) + || event.getType() + .toString() + .equals(FlowableEngineEventType.MULTI_INSTANCE_ACTIVITY_COMPLETED.toString())) { + // 多实例任务 + FlowableMultiInstanceActivityCompletedEventImpl flowableActivityEvent = (FlowableMultiInstanceActivityCompletedEventImpl) event; + String activityId = flowableActivityEvent.getActivityId(); + String activityName = flowableActivityEvent.getActivityName(); + log.info("节点id:{} 名字:{}", activityId, activityName); + + String processInstanceId = flowableActivityEvent.getProcessInstanceId(); + + String processDefinitionId = flowableActivityEvent.getProcessDefinitionId(); + String flowId = NodeUtil.getFlowId(processDefinitionId); + + ProcessNodeRecordParamDto processNodeRecordParamDto = new ProcessNodeRecordParamDto(); + processNodeRecordParamDto.setFlowId(flowId); + processNodeRecordParamDto.setExecutionId(flowableActivityEvent.getExecutionId()); + processNodeRecordParamDto.setProcessInstanceId(processInstanceId); + // processNodeRecordParamDto.setData(JSON.toJSONString(processVariables)); + processNodeRecordParamDto.setNodeId(activityId); + // processNodeRecordParamDto.setNodeType(nodeDto.getType()); + processNodeRecordParamDto.setNodeName(activityName); + + remoteFlowTaskService.endNodeEvent(processNodeRecordParamDto); + } + if (event.getType().toString().equals(FlowableEngineEventType.ACTIVITY_COMPLETED.toString())) { + // 节点完成执行 + + FlowableActivityEventImpl flowableActivityEvent = (FlowableActivityEventImpl) event; + String activityId = flowableActivityEvent.getActivityId(); + String activityName = flowableActivityEvent.getActivityName(); + log.info("节点id:{} 名字:{}", activityId, activityName); + + String processInstanceId = flowableActivityEvent.getProcessInstanceId(); + + String processDefinitionId = flowableActivityEvent.getProcessDefinitionId(); + String flowId = NodeUtil.getFlowId(processDefinitionId); + + ProcessNodeRecordParamDto processNodeRecordParamDto = new ProcessNodeRecordParamDto(); + processNodeRecordParamDto.setFlowId(flowId); + processNodeRecordParamDto.setExecutionId(flowableActivityEvent.getExecutionId()); + processNodeRecordParamDto.setProcessInstanceId(processInstanceId); + processNodeRecordParamDto.setNodeId(activityId); + processNodeRecordParamDto.setNodeName(activityName); + + remoteFlowTaskService.endNodeEvent(processNodeRecordParamDto); + + } + + if (event.getType().toString().equals(FlowableEngineEventType.VARIABLE_UPDATED.toString())) { + // 变量变化了 + FlowableVariableEvent flowableVariableEvent = (FlowableVariableEvent) event; + log.debug("变量[{}]变化了:{} ", flowableVariableEvent.getVariableName(), + flowableVariableEvent.getVariableValue()); + } + + if (event.getType().toString().equals(FlowableEngineEventType.VARIABLE_CREATED.toString())) { + // 变量创建了 + FlowableVariableEvent flowableVariableEvent = (FlowableVariableEvent) event; + log.debug("变量[{}]创建了:{} ", flowableVariableEvent.getVariableName(), + flowableVariableEvent.getVariableValue()); + } + if (event.getType().toString().equals(FlowableEngineEventType.VARIABLE_DELETED.toString())) { + // 变量删除了 + FlowableVariableEvent flowableVariableEvent = (FlowableVariableEvent) event; + log.debug("变量[{}]删除了:{} ", flowableVariableEvent.getVariableName(), + flowableVariableEvent.getVariableValue()); + } + if (event.getType() + .toString() + .equals(FlowableEngineEventType.PROCESS_COMPLETED_WITH_TERMINATE_END_EVENT.toString())) { + // 流程开完成 + FlowableProcessTerminatedEventImpl e = (FlowableProcessTerminatedEventImpl) event; + DelegateExecution execution = e.getExecution(); + String processInstanceId = e.getProcessInstanceId(); + ExecutionEntityImpl entity = (ExecutionEntityImpl) e.getEntity(); + + ProcessInstanceParamDto processInstanceParamDto = new ProcessInstanceParamDto(); + processInstanceParamDto.setProcessInstanceId(processInstanceId); + remoteFlowTaskService.endProcessEvent(processInstanceParamDto); + + } + + ObjectMapper objectMapper = SpringUtil.getBean(ObjectMapper.class); + if (event.getType().toString().equals(FlowableEngineEventType.TASK_COMPLETED.toString())) { + + TaskService taskService = SpringUtil.getBean(TaskService.class); + + // 任务完成 + FlowableEntityEvent flowableEntityEvent = (FlowableEntityEvent) event; + TaskEntityImpl task = (TaskEntityImpl) flowableEntityEvent.getEntity(); + // 执行人id + String assignee = task.getAssignee(); + + // nodeid + String taskDefinitionKey = task.getTaskDefinitionKey(); + + // 实例id + String processInstanceId = task.getProcessInstanceId(); + + String processDefinitionId = task.getProcessDefinitionId(); + // 流程id + String flowId = NodeUtil.getFlowId(processDefinitionId); + ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto = new ProcessNodeRecordAssignUserParamDto(); + processNodeRecordAssignUserParamDto.setFlowId(flowId); + processNodeRecordAssignUserParamDto.setProcessInstanceId(processInstanceId); + processNodeRecordAssignUserParamDto + .setData(objectMapper.writeValueAsString(taskService.getVariables(task.getId()))); + processNodeRecordAssignUserParamDto + .setLocalData(objectMapper.writeValueAsString(taskService.getVariablesLocal(task.getId()))); + processNodeRecordAssignUserParamDto.setNodeId(taskDefinitionKey); + processNodeRecordAssignUserParamDto.setUserId(Long.parseLong(assignee)); + processNodeRecordAssignUserParamDto.setTaskId(task.getId()); + processNodeRecordAssignUserParamDto.setNodeName(task.getName()); + processNodeRecordAssignUserParamDto.setTaskType("COMPLETE"); + processNodeRecordAssignUserParamDto.setApproveDesc(Convert.toStr(task.getVariableLocal("approveDesc"))); + processNodeRecordAssignUserParamDto.setExecutionId(task.getExecutionId()); + + remoteFlowTaskService.taskEndEvent(processNodeRecordAssignUserParamDto); + + } + if (event.getType().toString().equals(FlowableEngineEventType.TASK_ASSIGNED.toString())) { + // 任务被指派了人员 + FlowableEntityEvent flowableEntityEvent = (FlowableEntityEvent) event; + TaskEntityImpl task = (TaskEntityImpl) flowableEntityEvent.getEntity(); + // 执行人id + String assignee = task.getAssignee(); + // 任务拥有者 + String owner = task.getOwner(); + // + String delegationStateString = task.getDelegationStateString(); + + // nodeid + String taskDefinitionKey = task.getTaskDefinitionKey(); + + // 实例id + String processInstanceId = task.getProcessInstanceId(); + + String processDefinitionId = task.getProcessDefinitionId(); + // 流程id + String flowId = NodeUtil.getFlowId(processDefinitionId); + ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto = new ProcessNodeRecordAssignUserParamDto(); + processNodeRecordAssignUserParamDto.setFlowId(flowId); + processNodeRecordAssignUserParamDto.setProcessInstanceId(processInstanceId); + // processNodeRecordAssignUserParamDto.setData(); + processNodeRecordAssignUserParamDto.setNodeId(taskDefinitionKey); + processNodeRecordAssignUserParamDto.setUserId(Long.parseLong(assignee)); + processNodeRecordAssignUserParamDto.setTaskId(task.getId()); + processNodeRecordAssignUserParamDto.setNodeName(task.getName()); + processNodeRecordAssignUserParamDto + .setTaskType(StrUtil.equals(DelegationState.PENDING.toString(), delegationStateString) ? "DELEGATION" + : (StrUtil.equals(DelegationState.RESOLVED.toString(), delegationStateString) ? "RESOLVED" + : "")); + processNodeRecordAssignUserParamDto.setApproveDesc(Convert.toStr(task.getVariableLocal("approveDesc"))); + processNodeRecordAssignUserParamDto.setExecutionId(task.getExecutionId()); + + remoteFlowTaskService.startAssignUser(processNodeRecordAssignUserParamDto); + + } + + if (event.getType().toString().equals(FlowableEngineEventType.PROCESS_STARTED.toString())) { + // 流程开始了 + FlowableProcessStartedEventImpl flowableProcessStartedEvent = (FlowableProcessStartedEventImpl) event; + + ExecutionEntityImpl entity = (ExecutionEntityImpl) flowableProcessStartedEvent.getEntity(); + DelegateExecution execution = flowableProcessStartedEvent.getExecution(); + String processInstanceId = flowableProcessStartedEvent.getProcessInstanceId(); + { + // 上级实例id + String nestedProcessInstanceId = flowableProcessStartedEvent.getNestedProcessInstanceId(); + + String flowId = entity.getProcessDefinitionKey(); + + Object variable = execution.getVariable("root"); + + List nodeUsers = objectMapper.readValue(objectMapper.writeValueAsString(variable), + new TypeReference>() { + }); + Long startUserId = nodeUsers.get(0).getId(); + Map variables = execution.getVariables(); + + ProcessInstanceRecordParamDto processInstanceRecordParamDto = new ProcessInstanceRecordParamDto(); + processInstanceRecordParamDto.setUserId(startUserId); + processInstanceRecordParamDto.setParentProcessInstanceId(nestedProcessInstanceId); + processInstanceRecordParamDto.setFlowId(flowId); + processInstanceRecordParamDto.setProcessInstanceId(processInstanceId); + processInstanceRecordParamDto.setFormData(objectMapper.writeValueAsString(variables)); + remoteFlowTaskService.createProcessEvent(processInstanceRecordParamDto); + } + + } + } + + /** + * 如果监听器抛出异常是否终止当前操作 + * @return 是否终止当前操作 + */ + @Override + public boolean isFailOnException() { + return false; + } + + /** + * 返回监听器是否在事务生命周期事件发生时立即触发 + * @return 是否在事务生命周期事件上触发 + */ + @Override + public boolean isFireOnTransactionLifecycleEvent() { + return false; + } + + /** + * 如果非空,表示在当前事务的生命周期中的触发点 + * @return 事务生命周期中的触发点 + */ + @Override + public String getOnTransaction() { + return null; + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/AssignUserStrategy.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/AssignUserStrategy.java new file mode 100644 index 000000000..2850070be --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/AssignUserStrategy.java @@ -0,0 +1,22 @@ +package com.pig4cloud.pigx.flow.engine.node; + +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.NodeUser; + +import java.util.List; +import java.util.Map; + +/** + * 指定用户策略处理器 + */ +public interface AssignUserStrategy { + + /** + * 抽象方法 处理表达式 + * @param node + * @param rootUser + * @param variables + */ + List handle(Node node, NodeUser rootUser, Map variables); + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/MultiInstanceHandler.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/MultiInstanceHandler.java new file mode 100644 index 000000000..bc45fe858 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/MultiInstanceHandler.java @@ -0,0 +1,137 @@ +package com.pig4cloud.pigx.flow.engine.node; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowTaskService; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.NodeUser; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +@Slf4j +@RequiredArgsConstructor +@Component("multiInstanceHandler") +public class MultiInstanceHandler { + + private final RemoteFlowTaskService remoteFlowTaskService; + + private final Map assignUserStrategyMap; + + private final ObjectMapper objectMapper; + + /** + * 解析执行人 + * @param execution 流程执行对象 + * @return 执行人集合 + */ + @SneakyThrows + public List resolveAssignee(DelegateExecution execution) { + // 执行人集合 + List assignList = new ArrayList<>(); + + ExecutionEntityImpl entity = (ExecutionEntityImpl) execution; + + String flowId = entity.getProcessDefinitionKey(); + String nodeId = entity.getActivityId(); + + log.debug("nodeId={} nodeName={}", nodeId, entity.getActivityName()); + + // 发起人 + Object rootUserObj = execution.getVariable("root"); + String rootUserJson = objectMapper.writeValueAsString(rootUserObj); + NodeUser rootUser = objectMapper.readValue(rootUserJson, new TypeReference>() { + }).get(0); + + // 节点数据 + Node node = remoteFlowTaskService.queryNodeOriData(flowId, nodeId).getData(); + if (node != null) { + Map variables = execution.getVariables(); + Integer assignedType = node.getAssignedType(); + List userIdList = assignUserStrategyMap.get(assignedType + "AssignUserStrategy") + .handle(node, rootUser, variables); + assignList.addAll(userIdList); + } + else { + // 默认值 + String format = StrUtil.format("{}_assignee_default_list", nodeId); + Object variable = execution.getVariable(format); + String variableJson = objectMapper.writeValueAsString(variable); + + List nodeUserDtos = objectMapper.readValue(variableJson, new TypeReference>() { + }); + if (CollUtil.isNotEmpty(nodeUserDtos)) { + List collect = nodeUserDtos.stream().map(NodeUser::getId).collect(Collectors.toList()); + assignList.addAll(collect); + } + } + + Optional.of(assignList).orElseGet(() -> { + List defaultList = new ArrayList<>(); + defaultList.add(ProcessInstanceConstant.DEFAULT_EMPTY_ASSIGN); + return defaultList; + }); + + return assignList; + } + + /** + * 会签或者或签完成条件检查 检查节点是否满足会签或者或签的完成条件 + * @param execution 执行实例对象 + * @return boolean 如果节点满足条件则返回true,否则返回false + */ + public boolean completionCondition(DelegateExecution execution) { + ExecutionEntityImpl entity = (ExecutionEntityImpl) execution; + String processDefinitionKey = entity.getProcessDefinitionKey(); + String nodeId = execution.getCurrentActivityId(); + + Node node = remoteFlowTaskService.queryNodeOriData(processDefinitionKey, nodeId).getData(); + Integer multipleMode = node.getMultipleMode(); + BigDecimal modePercentage = BigDecimal.valueOf(100); + + Object variable = execution.getVariable(StrUtil.format("{}_approve_condition", nodeId)); + log.debug("当前节点审批结果:{}", variable); + Boolean approve = Convert.toBool(variable); + + if (multipleMode == ProcessInstanceConstant.MULTIPLE_MODE_AL_SAME + || multipleMode == ProcessInstanceConstant.MULTIPLE_MODE_ALL_SORT) { + // 如果是会签或者顺序签署 + if (!approve) { + return true; + } + } + + if (multipleMode == ProcessInstanceConstant.MULTIPLE_MODE_ONE) { + // 如果是或签 + if (approve) { + return true; + } + } + + int nrOfInstances = (int) execution.getVariable("nrOfInstances"); + int nrOfCompletedInstances = (int) execution.getVariable("nrOfCompletedInstances"); + log.debug("当前节点完成实例数:{} 总实例数:{} 需要完成比例:{}", nrOfCompletedInstances, nrOfInstances, modePercentage); + + if (multipleMode == ProcessInstanceConstant.MULTIPLE_MODE_AL_SAME) { + return BigDecimal.valueOf(nrOfCompletedInstances * 100L) + .compareTo(BigDecimal.valueOf(nrOfCompletedInstances).multiply(modePercentage)) > 0; + } + + return nrOfCompletedInstances == nrOfInstances; + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserFixedStrategyImpl.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserFixedStrategyImpl.java new file mode 100644 index 000000000..63e1f893a --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserFixedStrategyImpl.java @@ -0,0 +1,70 @@ +package com.pig4cloud.pigx.flow.engine.node.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pigx.admin.api.entity.SysUser; +import com.pig4cloud.pigx.admin.api.feign.RemoteUserService; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.engine.node.AssignUserStrategy; +import com.pig4cloud.pigx.flow.task.constant.NodeUserTypeEnum; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.NodeUser; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 指定具体用户 + * + * @author Huijun Zhao + * @description + * @date 2023-07-07 13:42 + */ +@RequiredArgsConstructor +@Component(ProcessInstanceConstant.AssignedTypeClass.USER + "AssignUserStrategy") +public class AssignUserFixedStrategyImpl implements AssignUserStrategy { + + private final RemoteUserService remoteUserService; + + /** + * 处理节点并返回用户ID列表。 + * @param node 节点 + * @param rootUser 根用户 + * @param variables 变量 + * @return 用户ID列表 + */ + @Override + public List handle(Node node, NodeUser rootUser, Map variables) { + + // 指定人员 + List userDtoList = node.getNodeUserList(); + // 用户ID列表 + List userIdList = userDtoList.stream() + .filter(w -> StrUtil.equals(w.getType(), NodeUserTypeEnum.USER.getKey())) + .map(NodeUser::getId) + .collect(Collectors.toList()); + // 部门ID列表 + List deptIdList = userDtoList.stream() + .filter(w -> StrUtil.equals(w.getType(), NodeUserTypeEnum.DEPT.getKey())) + .map(NodeUser::getId) + .collect(Collectors.toList()); + + if (CollUtil.isNotEmpty(deptIdList)) { + R> r = remoteUserService.getUserIdListByDeptIdList(deptIdList); + if (CollUtil.isNotEmpty(r.getData())) { + for (SysUser user : r.getData()) { + if (!userIdList.contains(user.getUserId())) { + userIdList.add(user.getUserId()); + } + } + } + } + + return userIdList; + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserFormStrategyImpl.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserFormStrategyImpl.java new file mode 100644 index 000000000..2facc3138 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserFormStrategyImpl.java @@ -0,0 +1,47 @@ +package com.pig4cloud.pigx.flow.engine.node.impl; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.flow.engine.node.AssignUserStrategy; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.NodeUser; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 来自表单 + * + * @author Huijun Zhao + * @description + * @date 2023-07-07 13:42 + */ +@RequiredArgsConstructor +@Component(ProcessInstanceConstant.AssignedTypeClass.FORM_USER + "AssignUserStrategy") +public class AssignUserFormStrategyImpl implements AssignUserStrategy { + + private final ObjectMapper objectMapper; + + @SneakyThrows + @Override + public List handle(Node node, NodeUser rootUser, Map variables) { + List assignList = new ArrayList<>(); + + Object variable = variables.get(node.getFormUserId()); + if (variable != null && !StrUtil.isBlankIfStr(variable)) { + String jsonString = objectMapper.writeValueAsString(variable); + List nodeUserDtoList = JSONUtil.toList(jsonString, NodeUser.class); + assignList.addAll(nodeUserDtoList.stream().map(NodeUser::getId).collect(Collectors.toList())); + } + + return assignList; + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserLeaderStrategyImpl.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserLeaderStrategyImpl.java new file mode 100644 index 000000000..8c6b7e822 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserLeaderStrategyImpl.java @@ -0,0 +1,42 @@ +package com.pig4cloud.pigx.flow.engine.node.impl; + +import com.pig4cloud.pigx.admin.api.feign.RemoteDeptService; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.engine.node.AssignUserStrategy; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.NodeUser; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 指定主管 + * + * @author Huijun Zhao + * @description + * @date 2023-07-07 13:42 + */ +@RequiredArgsConstructor +@Component(ProcessInstanceConstant.AssignedTypeClass.LEADER + "AssignUserStrategy") +public class AssignUserLeaderStrategyImpl implements AssignUserStrategy { + + private final RemoteDeptService deptService; + + @Override + public List handle(Node node, NodeUser rootUser, Map variables) { + // 获取部门ID + return node.getNodeUserList() + .stream() + .map(nodeUser -> deptService.getAllDeptLeader(nodeUser.getId())) + .flatMap((Function>, Stream>) listR -> listR.getData() == null ? null + : listR.getData().stream()) + .collect(Collectors.toList()); + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserRoleStrategyImpl.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserRoleStrategyImpl.java new file mode 100644 index 000000000..18e3a3100 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserRoleStrategyImpl.java @@ -0,0 +1,51 @@ +package com.pig4cloud.pigx.flow.engine.node.impl; + +import com.pig4cloud.pigx.admin.api.feign.RemoteUserService; +import com.pig4cloud.pigx.common.core.util.RetOps; +import com.pig4cloud.pigx.flow.engine.node.AssignUserStrategy; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.NodeUser; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 来自角色 + * + * @author Huijun Zhao + * @description + * @date 2023-07-07 13:42 + */ +@RequiredArgsConstructor +@Component(ProcessInstanceConstant.AssignedTypeClass.ROLE + "AssignUserStrategy") +public class AssignUserRoleStrategyImpl implements AssignUserStrategy { + + private final RemoteUserService remoteUserService; + + /** + * 处理节点并返回用户ID列表。 + * @param node 节点 + * @param rootUser 根用户 + * @param variables 变量 + * @return 用户ID列表 + */ + @Override + public List handle(Node node, NodeUser rootUser, Map variables) { + // 使用 lambda 表达式和方法引用从 NodeUser 列表中提取角色 ID + List roleIds = node.getNodeUserList().stream().map(NodeUser::getId).collect(Collectors.toList()); + // 提取 Optional 结果中的数据 + List data = RetOps.of(remoteUserService.getUserIdListByRoleIdList(roleIds)) + .getData() + .orElseGet(Collections::emptyList); + + // 返回用户 ID 列表 + return new ArrayList<>(data); + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserSelfSelectStrategyImpl.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserSelfSelectStrategyImpl.java new file mode 100644 index 000000000..fe2641c1f --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserSelfSelectStrategyImpl.java @@ -0,0 +1,56 @@ +package com.pig4cloud.pigx.flow.engine.node.impl; + +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.flow.engine.node.AssignUserStrategy; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.NodeUser; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 发起人自选 + * + * @author Huijun Zhao + * @description + * @date 2023-07-07 13:42 + */ +@Slf4j +@RequiredArgsConstructor +@Component(ProcessInstanceConstant.AssignedTypeClass.SELF_SELECT + "AssignUserStrategy") +public class AssignUserSelfSelectStrategyImpl implements AssignUserStrategy { + + private final ObjectMapper objectMapper; + + @SneakyThrows + @Override + public List handle(Node node, NodeUser rootUser, Map variables) { + + List assignList = new ArrayList<>(); + + Object variable = variables.get(StrUtil.format("{}_assignee_select", node.getId())); + log.info("{}-发起人自选参数:{}", node.getName(), variable); + if (variable == null) { + return assignList; + } + + List nodeUserDtos = objectMapper.readValue(objectMapper.writeValueAsString(variable), + new TypeReference>() { + }); + + List collect = nodeUserDtos.stream().map(NodeUser::getId).collect(Collectors.toList()); + + assignList.addAll(collect); + return assignList; + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserSelfStrategyImpl.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserSelfStrategyImpl.java new file mode 100644 index 000000000..2c6227683 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/node/impl/AssignUserSelfStrategyImpl.java @@ -0,0 +1,28 @@ +package com.pig4cloud.pigx.flow.engine.node.impl; + +import cn.hutool.core.collection.CollUtil; +import com.pig4cloud.pigx.flow.engine.node.AssignUserStrategy; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.NodeUser; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * 发起人自己 + * + * @author Huijun Zhao + * @description + * @date 2023-07-07 13:42 + */ +@Component(ProcessInstanceConstant.AssignedTypeClass.SELF + "AssignUserStrategy") +public class AssignUserSelfStrategyImpl implements AssignUserStrategy { + + @Override + public List handle(Node node, NodeUser rootUser, Map variables) { + return CollUtil.newArrayList(rootUser.getId()); + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/service/ApproveServiceTask.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/service/ApproveServiceTask.java new file mode 100644 index 000000000..eb62a2436 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/service/ApproveServiceTask.java @@ -0,0 +1,63 @@ +package com.pig4cloud.pigx.flow.engine.service; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowTaskService; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.Refuse; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl; + +/** + * 审批任务处理器--java服务任务 + */ +@Slf4j +public class ApproveServiceTask implements JavaDelegate { + + @Override + public void execute(DelegateExecution execution) { + + ExecutionEntityImpl entity = (ExecutionEntityImpl) execution; + String nodeIdO = entity.getActivityId(); + String flowId = entity.getProcessDefinitionKey(); + String processInstanceId = entity.getProcessInstanceId(); + + String nodeId = StrUtil.subAfter(nodeIdO, "approve_service_task_", true); + + Boolean approve = execution.getVariable(StrUtil.format("{}_approve_condition", nodeId), Boolean.class); + + if (approve != null) { + + RuntimeService runtimeService = SpringUtil.getBean(RuntimeService.class); + + if (!approve) { + // 跳转 + RemoteFlowTaskService remoteFlowTaskService = SpringUtil.getBean(RemoteFlowTaskService.class); + Node node = remoteFlowTaskService.queryNodeOriData(flowId, nodeId).getData(); + Refuse refuse = node.getRefuse(); + if (refuse != null) { + String handler = refuse.getHandler(); + if (StrUtil.equals(handler, "TO_NODE")) { + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(processInstanceId) + .moveActivityIdTo(nodeIdO, refuse.getNodeId()) + .changeState(); + } + else { + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(processInstanceId) + .moveActivityIdTo(nodeIdO, "end") + .changeState(); + } + } + + } + + } + + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/service/CopyServiceTask.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/service/CopyServiceTask.java new file mode 100644 index 000000000..72f9e5c38 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/service/CopyServiceTask.java @@ -0,0 +1,98 @@ +package com.pig4cloud.pigx.flow.engine.service; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowTaskService; +import com.pig4cloud.pigx.flow.task.constant.NodeUserTypeEnum; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.NodeUser; +import com.pig4cloud.pigx.flow.task.dto.ProcessCopyDto; +import lombok.SneakyThrows; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 抄送任务处理器--java服务任务 + */ +public class CopyServiceTask implements JavaDelegate { + + /** + * 执行给定执行的任务。 + * @param execution 要处理的执行 + */ + @SneakyThrows + @Override + public void execute(DelegateExecution execution) { + + ExecutionEntityImpl entity = (ExecutionEntityImpl) execution; + String nodeId = entity.getActivityId(); + String flowId = entity.getProcessDefinitionKey(); + + RemoteFlowTaskService remoteFlowTaskService = SpringUtil.getBean(RemoteFlowTaskService.class); + Node node = remoteFlowTaskService.queryNodeOriData(flowId, nodeId).getData(); + + // 获取指定人员 + List userDtoList = node.getNodeUserList(); + // 用户ID列表 + List userIdList = userDtoList.stream() + .filter(w -> StrUtil.equals(w.getType(), NodeUserTypeEnum.USER.getKey())) + .map(w -> Convert.toStr(w.getId())) + .collect(Collectors.toList()); + // 部门ID列表 + List deptIdList = userDtoList.stream() + .filter(w -> StrUtil.equals(w.getType(), NodeUserTypeEnum.DEPT.getKey())) + .map(w -> Convert.toStr(w.getId())) + .collect(Collectors.toList()); + + if (CollUtil.isNotEmpty(deptIdList)) { + + R> r = remoteFlowTaskService.queryUserIdListByDepIdList(deptIdList); + + List data = r.getData(); + if (CollUtil.isNotEmpty(data)) { + for (String datum : data) { + if (!userIdList.contains(datum)) { + userIdList.add(datum); + } + } + } + } + + ObjectMapper objectMapper = SpringUtil.getBean(ObjectMapper.class); + // 获取发起人 + Object rootUserObj = execution.getVariable("root"); + NodeUser rootUser = objectMapper + .readValue(objectMapper.writeValueAsString(rootUserObj), new TypeReference>() { + }) + .get(0); + + Map variables = execution.getVariables(); + + for (String userIds : userIdList) { + // 发送抄送任务 + ProcessCopyDto processCopyDto = new ProcessCopyDto(); + processCopyDto.setNodeTime(LocalDateTime.now()); + processCopyDto.setStartUserId(rootUser.getId()); + processCopyDto.setFlowId(flowId); + processCopyDto.setProcessInstanceId(execution.getProcessInstanceId()); + processCopyDto.setNodeId(nodeId); + processCopyDto.setNodeName(node.getName()); + processCopyDto.setFormData(objectMapper.writeValueAsString(variables)); + processCopyDto.setUserId(Long.parseLong(userIds)); + remoteFlowTaskService.saveCC(processCopyDto); + } + + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/service/packge-info.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/service/packge-info.java new file mode 100644 index 000000000..d749389d8 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/service/packge-info.java @@ -0,0 +1,4 @@ +/* + * @author pigx archetype + */ +package com.pig4cloud.pigx.flow.engine.service; \ No newline at end of file diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/utils/FlowableUtils.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/utils/FlowableUtils.java new file mode 100644 index 000000000..66bd02153 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/utils/FlowableUtils.java @@ -0,0 +1,664 @@ +package com.pig4cloud.pigx.flow.engine.utils; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.flowable.bpmn.model.*; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; + +import java.util.*; + +/** + * 流程引擎工具类封装 + * + * @author: linjinp + * @create: 2019-12-24 13:51 + **/ +public class FlowableUtils { + + public static final Logger logger = LogManager.getLogger(FlowableUtils.class); + + /** + * 根据节点获取入口连线集合 + * @param source 流程元素节点 + * @return 入口连线集合 + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof Task) { + sequenceFlows = ((Task) source).getIncomingFlows(); + } + else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getIncomingFlows(); + } + else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getIncomingFlows(); + } + else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getIncomingFlows(); + } + else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getIncomingFlows(); + } + return sequenceFlows; + } + + /** + * 根据节点获取出口连线集合 + * @param source 流程元素节点 + * @return 出口连线集合 + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof Task) { + sequenceFlows = ((Task) source).getOutgoingFlows(); + } + else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getOutgoingFlows(); + } + else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getOutgoingFlows(); + } + else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getOutgoingFlows(); + } + else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getOutgoingFlows(); + } + return sequenceFlows; + } + + /** + * 递归获取流程定义所有的流程元素 + * @param flowElements 当前级别流程元素集合 + * @param allElements 用于存储的全部流程元素集合 + * @return 全部流程元素集合 + */ + public static Collection getAllElements(Collection flowElements, + Collection allElements) { + allElements = allElements == null ? new ArrayList<>() : allElements; + + for (FlowElement flowElement : flowElements) { + allElements.add(flowElement); + if (flowElement instanceof SubProcess) { + // 继续深入子流程,进一步获取子流程 + allElements = FlowableUtils.getAllElements(((SubProcess) flowElement).getFlowElements(), allElements); + } + } + return allElements; + } + + /** + * 从后向前递归获取父级用户任务节点 + * @param source 当前节点 + * @param hasSequenceFlow 已处理过的顺序流集合,避免循环 + * @param userTaskList 递归获取的用户任务节点集合 + * @return 用户任务节点集合 + */ + public static List iteratorFindParentUserTasks(FlowElement source, Set hasSequenceFlow, + List userTaskList) { + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + userTaskList = iteratorFindParentUserTasks(source.getSubProcess(), hasSequenceFlow, userTaskList); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 类型为用户节点,则新增父级节点 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); + continue; + } + // 类型为子流程,则添加子流程开始节点出口处相连的节点 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + // 获取子流程用户任务节点 + List childUserTaskList = findChildProcessUserTasks( + (StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements() + .toArray()[0], + null, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(), + new HashSet<>(hasSequenceFlow), userTaskList); + } + } + return userTaskList; + } + + /** + * 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 + * @param source 起始节点 + * @param runActiveIdList 正在运行的任务 Key,用于校验任务节点是否是正在运行的节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param flowElementList 需要撤回的用户任务列表 + * @return + */ + public static List iteratorFindChildUserTasks(FlowElement source, List runActiveIdList, + Set hasSequenceFlow, List flowElementList) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + flowElementList = flowElementList == null ? new ArrayList<>() : flowElementList; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof EndEvent && source.getSubProcess() != null) { + flowElementList = iteratorFindChildUserTasks(source.getSubProcess(), runActiveIdList, hasSequenceFlow, + flowElementList); + } + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果为用户任务类型,或者为网关 + // 活动节点ID 在运行的任务中存在,添加 + if ((sequenceFlow.getTargetFlowElement() instanceof UserTask + || sequenceFlow.getTargetFlowElement() instanceof Gateway) + && runActiveIdList.contains((sequenceFlow.getTargetFlowElement()).getId())) { + flowElementList.add(sequenceFlow.getTargetFlowElement()); + continue; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + List childUserTaskList = iteratorFindChildUserTasks( + (FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements() + .toArray()[0]), + runActiveIdList, hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + flowElementList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + flowElementList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runActiveIdList, + new HashSet<>(hasSequenceFlow), flowElementList); + } + } + return flowElementList; + } + + /** + * 迭代获取子流程用户任务节点 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 需要撤回的用户任务列表 + * @return + */ + public static List findChildProcessUserTasks(FlowElement source, Set hasSequenceFlow, + List userTaskList) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 + if (sequenceFlow.getTargetFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); + continue; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + List childUserTaskList = findChildProcessUserTasks( + (FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements() + .toArray()[0]), + hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), + new HashSet<>(hasSequenceFlow), userTaskList); + } + } + return userTaskList; + } + + /** + * 从后向前寻路,获取所有脏线路上的点 + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targets 目标脏线路终点 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 + * @return + */ + public static Set iteratorFindDirtyRoads(FlowElement source, List passRoads, + Set hasSequenceFlow, List targets, Set dirtyRoads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + dirtyRoads = iteratorFindDirtyRoads(source.getSubProcess(), passRoads, hasSequenceFlow, targets, + dirtyRoads); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 新增经过的路线 + passRoads.add(sequenceFlow.getSourceFlowElement().getId()); + // 如果此点为目标点,确定经过的路线为脏线路,添加点到脏线路中,然后找下个连线 + if (targets.contains(sequenceFlow.getSourceFlowElement().getId())) { + dirtyRoads.addAll(passRoads); + continue; + } + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad( + (StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements() + .toArray()[0], + null, dirtyRoads); + // 是否存在子流程上,true 是,false 否 + Boolean isInChildProcess = dirtyTargetInChildProcess( + (StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements() + .toArray()[0], + null, targets, null); + if (isInChildProcess) { + // 已在子流程上找到,该路线结束 + continue; + } + } + // 继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), new ArrayList<>(passRoads), + new HashSet<>(hasSequenceFlow), targets, dirtyRoads); + } + } + return dirtyRoads; + } + + /** + * 迭代获取子流程脏路线 说明,假如回退的点就是子流程,那么也肯定会回退到子流程最初的用户任务节点,因此子流程中的节点全是脏路线 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 + * @return + */ + public static Set findChildProcessAllDirtyRoad(FlowElement source, Set hasSequenceFlow, + Set dirtyRoads) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加脏路线 + dirtyRoads.add(sequenceFlow.getTargetFlowElement().getId()); + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad( + (FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements() + .toArray()[0]), + hasSequenceFlow, dirtyRoads); + } + // 继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(), + new HashSet<>(hasSequenceFlow), dirtyRoads); + } + } + return dirtyRoads; + } + + /** + * 判断脏路线结束节点是否在子流程上 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targets 判断脏路线节点是否存在子流程上,只要存在一个,说明脏路线只到子流程为止 + * @param inChildProcess 是否存在子流程上,true 是,false 否 + * @return + */ + public static Boolean dirtyTargetInChildProcess(FlowElement source, Set hasSequenceFlow, + List targets, Boolean inChildProcess) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + inChildProcess = inChildProcess == null ? false : inChildProcess; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null && !inChildProcess) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果发现目标点在子流程上存在,说明只到子流程为止 + if (targets.contains(sequenceFlow.getTargetFlowElement().getId())) { + inChildProcess = true; + break; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + inChildProcess = dirtyTargetInChildProcess( + (FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements() + .toArray()[0]), + hasSequenceFlow, targets, inChildProcess); + } + // 继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(), + new HashSet<>(hasSequenceFlow), targets, inChildProcess); + } + } + return inChildProcess; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 + * @param source 起始节点 + * @param isSequential 是否串行 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targetKsy 目标节点 + * @return + */ + public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy, + Set hasSequenceFlow, Boolean isSequential) { + isSequential = isSequential == null ? true : isSequential; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow, + isSequential); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果目标节点已被判断为并行,后面都不需要执行,直接返回 + if (isSequential == false) { + break; + } + // 这条线路存在目标节点,这条线路完成,进入下个线路 + if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) { + continue; + } + if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) { + isSequential = false; + break; + } + // 否则就继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy, + new HashSet<>(hasSequenceFlow), isSequential); + } + } + return isSequential; + } + + /** + * 从后向前寻路,获取到达节点的所有路线 不存在直接回退到子流程,但是存在回退到父级流程的情况 + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param roads 路线 + * @return + */ + public static List> findRoad(FlowElement source, List passRoads, + Set hasSequenceFlow, List> roads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + roads = roads == null ? new ArrayList<>() : roads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + roads = findRoad(source.getSubProcess(), passRoads, hasSequenceFlow, roads); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null && sequenceFlows.size() != 0) { + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加经过路线 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + passRoads.add((UserTask) sequenceFlow.getSourceFlowElement()); + } + // 继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + roads = findRoad(sequenceFlow.getSourceFlowElement(), new ArrayList<>(passRoads), + new HashSet<>(hasSequenceFlow), roads); + } + } + else { + // 添加路线 + roads.add(passRoads); + } + return roads; + } + + /** + * 历史节点数据清洗,清洗掉又回滚导致的脏数据 + * @param allElements 全部节点信息 + * @param historicActivityIdList 历史任务实例信息,数据采用开始时间升序 + * @return + */ + public static List historicTaskInstanceClean(Collection allElements, + List historicActivityIdList) { + // 会签节点收集 + List multiTask = new ArrayList<>(); + allElements.forEach(flowElement -> { + if (flowElement instanceof UserTask) { + // 如果该节点的行为为会签行为,说明该节点为会签节点 + if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior + || ((UserTask) flowElement).getBehavior() instanceof SequentialMultiInstanceBehavior) { + multiTask.add(flowElement.getId()); + } + } + }); + // 循环放入栈,栈 LIFO:后进先出 + Stack stack = new Stack<>(); + historicActivityIdList.forEach(item -> stack.push(item)); + // 清洗后的历史任务实例 + List lastHistoricTaskInstanceList = new ArrayList<>(); + // 网关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗 + // 临时用户任务 key + StringBuilder userTaskKey = null; + // 临时被删掉的任务 key,存在并行情况 + List deleteKeyList = new ArrayList<>(); + // 临时脏数据线路 + List> dirtyDataLineList = new ArrayList<>(); + // 由某个点跳到会签点,此时出现多个会签实例对应 1 个跳转情况,需要把这些连续脏数据都找到 + // 会签特殊处理下标 + int multiIndex = -1; + // 会签特殊处理 key + StringBuilder multiKey = null; + // 会签特殊处理操作标识 + boolean multiOpera = false; + while (!stack.empty()) { + // 从这里开始 userTaskKey 都还是上个栈的 key + // 是否是脏数据线路上的点 + final boolean[] isDirtyData = { false }; + for (Set oldDirtyDataLine : dirtyDataLineList) { + if (oldDirtyDataLine.contains(stack.peek().getActivityId())) { + isDirtyData[0] = true; + } + } + // 删除原因不为空,说明从这条数据开始回跳或者回退的 + // MI_END:会签完成后,其他未签到节点的删除原因,不在处理范围内 + if (stack.peek().getDeleteReason() != null && !stack.peek().getDeleteReason().equals("MI_END")) { + // 可以理解为脏线路起点 + String dirtyPoint = ""; + if (stack.peek().getDeleteReason().indexOf("Change activity to ") >= 0) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", ""); + } + // 会签回退删除原因有点不同 + if (stack.peek().getDeleteReason().indexOf("Change parent activity to ") >= 0) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", ""); + } + FlowElement dirtyTask = null; + // 获取变更节点的对应的入口处连线 + // 如果是网关并行回退情况,会变成两条脏数据路线,效果一样 + for (FlowElement flowElement : allElements) { + if (flowElement.getId().equals(stack.peek().getActivityId())) { + dirtyTask = flowElement; + } + } + // 获取脏数据线路 + Set dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null, + Arrays.asList(dirtyPoint.split(",")), null); + // 自己本身也是脏线路上的点,加进去 + dirtyDataLine.add(stack.peek().getActivityId()); + logger.info(stack.peek().getActivityId() + "点脏路线集合:" + dirtyDataLine); + // 是全新的需要添加的脏线路 + boolean isNewDirtyData = true; + for (int i = 0; i < dirtyDataLineList.size(); i++) { + // 如果发现他的上个节点在脏线路内,说明这个点可能是并行的节点,或者连续驳回 + // 这时,都以之前的脏线路节点为标准,只需合并脏线路即可,也就是路线补全 + if (dirtyDataLineList.get(i).contains(userTaskKey.toString())) { + isNewDirtyData = false; + dirtyDataLineList.get(i).addAll(dirtyDataLine); + } + } + // 已确定时全新的脏线路 + if (isNewDirtyData) { + // deleteKey 单一路线驳回到并行,这种同时生成多个新实例记录情况,这时 deleteKey 其实是由多个值组成 + // 按照逻辑,回退后立刻生成的实例记录就是回退的记录 + // 至于驳回所生成的 Key,直接从删除原因中获取,因为存在驳回到并行的情况 + deleteKeyList.add(dirtyPoint + ","); + dirtyDataLineList.add(dirtyDataLine); + } + // 添加后,现在这个点变成脏线路上的点了 + isDirtyData[0] = true; + } + // 如果不是脏线路上的点,说明是有效数据,添加历史实例 Key + if (!isDirtyData[0]) { + lastHistoricTaskInstanceList.add(stack.peek().getActivityId()); + } + // 校验脏线路是否结束 + for (int i = 0; i < deleteKeyList.size(); i++) { + // 如果发现脏数据属于会签,记录下下标与对应 Key,以备后续比对,会签脏数据范畴开始 + if (multiKey == null && multiTask.contains(stack.peek().getActivityId()) + && deleteKeyList.get(i).contains(stack.peek().getActivityId())) { + multiIndex = i; + multiKey = new StringBuilder(stack.peek().getActivityId()); + } + // 会签脏数据处理,节点退回会签清空 + // 如果在会签脏数据范畴中发现 Key改变,说明会签脏数据在上个节点就结束了,可以把会签脏数据删掉 + if (multiKey != null && !multiKey.toString().equals(stack.peek().getActivityId())) { + deleteKeyList.set(multiIndex, + deleteKeyList.get(multiIndex).replace(stack.peek().getActivityId() + ",", "")); + multiKey = null; + // 结束进行下校验删除 + multiOpera = true; + } + // 其他脏数据处理 + // 发现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息 + // 脏数据产生的新实例中是否包含这条数据 + if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getActivityId())) { + // 删除匹配到的部分 + deleteKeyList.set(i, deleteKeyList.get(i).replace(stack.peek().getActivityId() + ",", "")); + } + // 如果每组中的元素都以匹配过,说明脏数据结束 + if ("".equals(deleteKeyList.get(i))) { + // 同时删除脏数据 + deleteKeyList.remove(i); + dirtyDataLineList.remove(i); + break; + } + } + // 会签数据处理需要在循环外处理,否则可能导致溢出 + // 会签的数据肯定是之前放进去的所以理论上不会溢出,但还是校验下 + if (multiOpera && deleteKeyList.size() > multiIndex && "".equals(deleteKeyList.get(multiIndex))) { + // 同时删除脏数据 + deleteKeyList.remove(multiIndex); + dirtyDataLineList.remove(multiIndex); + multiIndex = -1; + multiOpera = false; + } + // pop() 方法与 peek() 方法不同,在返回值的同时,会把值从栈中移除 + // 保存新的 userTaskKey 在下个循环中使用 + userTaskKey = new StringBuilder(stack.pop().getActivityId()); + } + logger.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList); + return lastHistoricTaskInstanceList; + } + +} diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/utils/ModelUtil.java b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/utils/ModelUtil.java new file mode 100644 index 000000000..476461487 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/java/com/pig4cloud/pigx/flow/engine/utils/ModelUtil.java @@ -0,0 +1,697 @@ +package com.pig4cloud.pigx.flow.engine.utils; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.json.JSONUtil; +import com.pig4cloud.pigx.flow.engine.expression.condition.NodeExpressionStrategyFactory; +import com.pig4cloud.pigx.flow.engine.listeners.ApprovalCreateListener; +import com.pig4cloud.pigx.flow.engine.listeners.FlowProcessEventListener; +import com.pig4cloud.pigx.flow.engine.service.ApproveServiceTask; +import com.pig4cloud.pigx.flow.engine.service.CopyServiceTask; +import com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowTaskService; +import com.pig4cloud.pigx.flow.task.constant.NodeTypeEnum; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.ProcessNodeDataDto; +import com.pig4cloud.pigx.flow.task.utils.NodeUtil; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.Process; +import org.flowable.bpmn.model.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * 模型工具类 处理模型构建相关的 + */ +@Slf4j +public class ModelUtil { + /** + * 构建模型 + * + * @param nodeDto 前端传输节点 + * @return + */ + public static BpmnModel buildBpmnModel(Node nodeDto, String processName, String flowId) { + BpmnModel bpmnModel = new BpmnModel(); + bpmnModel.setTargetNamespace("pig"); + + Process process = new Process(); + process.setId(flowId); + process.setName(processName); + + //流程监听器 + ArrayList eventListeners = new ArrayList<>(); + + { + //流程实例监听器 + EventListener eventListener = new EventListener(); + + eventListener.setImplementationType("class"); + eventListener.setImplementation(FlowProcessEventListener.class.getCanonicalName()); + + + eventListeners.add(eventListener); + + } + process.setEventListeners(eventListeners); + + NodeUtil.addEndNode(nodeDto); + + + //创建所有的节点 + buildAllNode(process, nodeDto, flowId); + //创建所有的内部节点连接线 + buildAllNodeInnerSequence(process, nodeDto, flowId); + //创建节点间连线 + buildAllNodeOuterSequence(process, nodeDto, null); + //处理分支和下级连线 + + + bpmnModel.addProcess(process); + return bpmnModel; + } + + + /** + * 先创建所有的节点 + * + * @param process + * @param nodeDto + * @param flowId + */ + public static void buildAllNode(Process process, Node nodeDto, String flowId) { + if (!NodeUtil.isNode(nodeDto)) { + return; + } + + + List flowElementList = buildNode(nodeDto, flowId); + for (FlowElement flowElement : flowElementList) { + if (process.getFlowElement(flowElement.getId()) == null) { + process.addFlowElement(flowElement); + } + } + + //子节点 + Node children = nodeDto.getChildren(); + + if (NodeTypeEnum.getByValue(nodeDto.getType()).getBranch()) { + + //条件分支 + List branchs = nodeDto.getConditionNodes(); + for (Node branch : branchs) { + buildAllNode(process, branch.getChildren(), + flowId); + + + } + if (NodeUtil.isNode(children)) { + buildAllNode(process, children, flowId); + } + + } else { + + if (NodeUtil.isNode(children)) { + buildAllNode(process, children, flowId); + } + } + + + } + + /** + * 先创建所有的内部节点连接线 + * + * @param process + * @param nodeDto + * @param flowId + */ + public static void buildAllNodeInnerSequence(Process process, Node nodeDto, String flowId) { + if (!NodeUtil.isNode(nodeDto)) { + return; + } + + //画内部线 + List flowList = buildInnerSequenceFlow(nodeDto, flowId); + for (SequenceFlow sequenceFlow : flowList) { + process.addFlowElement(sequenceFlow); + } + + //子节点 + Node children = nodeDto.getChildren(); + if (NodeTypeEnum.getByValue(nodeDto.getType()).getBranch() ) { + //条件分支 + List branchs = nodeDto.getConditionNodes(); + for (Node branch : branchs) { + buildAllNodeInnerSequence(process, branch.getChildren(), + flowId); + + + } + if (NodeUtil.isNode(children)) { + buildAllNodeInnerSequence(process, children, flowId); + } + + } else { + + if (NodeUtil.isNode(children)) { + buildAllNodeInnerSequence(process, children, flowId); + } + } + + + } + + + /** + * 递归创建节点间连线 + * + * @param process 流程 + * @param nodeDto 节点对象 + * @param nextId + */ + public static void buildAllNodeOuterSequence(Process process, Node nodeDto, String nextId) { + + if (!NodeUtil.isNode(nodeDto)) { + return; + } + + //子节点 + Node children = nodeDto.getChildren(); + if (NodeTypeEnum.getByValue(nodeDto.getType()).getBranch() ) { +// children = children.getChildren(); + //条件分支 + List branchs = nodeDto.getConditionNodes(); + int ord = 1; + int size = branchs.size(); + for (Node branch : branchs) { + + + buildAllNodeOuterSequence(process, branch.getChildren(), nodeDto.getTailId()); + + String expression = null; + + if (nodeDto.getType() == NodeTypeEnum.EXCLUSIVE_GATEWAY.getValue().intValue() ) { + if (ord == size) { + expression = NodeExpressionStrategyFactory.handleDefaultBranch(branchs, ord - 1); + } else if (nodeDto.getType() == NodeTypeEnum.EXCLUSIVE_GATEWAY.getValue().intValue() && ord > 1) { + expression = NodeExpressionStrategyFactory.handleDefaultBranch(branchs, ord - 1); + } else { + expression = NodeExpressionStrategyFactory.handle(branch); + } + + } + + + //添加连线 + if (!NodeUtil.isNode(branch.getChildren())) { + //当前分支 没有其他节点了 所有就是网关和网关后面节点直接连线 + + SequenceFlow sequenceFlow = buildSingleSequenceFlow(nodeDto.getId(), nodeDto.getTailId(), + expression, + StrUtil.format("{}->{}", nodeDto.getName(), nodeDto.getName()) + ); + process.addFlowElement(sequenceFlow); + } else { + + SequenceFlow sequenceFlow = buildSingleSequenceFlow(nodeDto.getId(), branch.getChildren().getHeadId(), + expression, + StrUtil.format("{}->{}", nodeDto.getName(), branch.getChildren().getName()) + ); + process.addFlowElement(sequenceFlow); + } + ord++; + + } + //分支结尾的合并分支节点-》下一个节点 + if (children != null && StrUtil.isNotBlank(children.getHeadId()) && StrUtil.isNotBlank(nodeDto.getTailId())) { + + SequenceFlow sequenceFlow = buildSingleSequenceFlow(nodeDto.getTailId(), children.getHeadId(), + "", + StrUtil.format("{}->{}", nodeDto.getName(), children.getName()) + ); + process.addFlowElement(sequenceFlow); + + } else if (StrUtil.isAllNotBlank(nodeDto.getTailId(), nextId)) { + SequenceFlow sequenceFlow = buildSingleSequenceFlow(nodeDto.getTailId(), nextId, + "", + StrUtil.format("{}->{}", nodeDto.getName(), nextId) + ); + process.addFlowElement(sequenceFlow); + } + + + buildAllNodeOuterSequence(process, children, nextId); + + + } else { + //添加连线 + if (NodeUtil.isNode(children)) { + List sequenceFlowList = buildSequenceFlow(children, nodeDto, ""); + for (SequenceFlow sequenceFlow : sequenceFlowList) { + process.addFlowElement(sequenceFlow); + } + buildAllNodeOuterSequence(process, children, nextId); + } else if (nodeDto.getType() != NodeTypeEnum.END.getValue().intValue()) { + SequenceFlow seq = buildSingleSequenceFlow(nodeDto.getTailId(), nextId, "", StrUtil.format("{}->{}", + nodeDto.getName(), nextId)); + + process.addFlowElement(seq); + + } + } + + } + + + /** + * 构建节点 + * + * @param node 前端传输节点 + * @param flowId + * @return + */ + private static List buildNode(Node node, String flowId) { + List flowElementList = new ArrayList<>(); + if (!NodeUtil.isNode(node)) { + return flowElementList; + } + + //设置节点的连线头节点 + node.setHeadId(node.getId()); + //设置节点的连线尾节点 + node.setTailId(node.getId()); + node.setName(StrUtil.format("{}[{}]", node.getName(), RandomUtil.randomNumbers(5))); + + //存储节点数据 + RemoteFlowTaskService remoteFlowTaskService = SpringUtil.getBean(RemoteFlowTaskService.class); + ProcessNodeDataDto processNodeDataDto = new ProcessNodeDataDto(); + processNodeDataDto.setFlowId(flowId); + processNodeDataDto.setNodeId(node.getId()); + processNodeDataDto.setData(JSONUtil.toJsonStr(node)); + remoteFlowTaskService.saveNodeOriData(processNodeDataDto); + + //开始 + if (node.getType() == NodeTypeEnum.ROOT.getValue().intValue()) { + flowElementList.addAll(buildStartNode(node)); + } + + //结束 + if (node.getType() == NodeTypeEnum.END.getValue().intValue()) { + flowElementList.add(buildEndNode(node, false)); + } + + //审批 + if (node.getType() == NodeTypeEnum.APPROVAL.getValue().intValue()) { + + + flowElementList.addAll(buildApproveNode(node)); + } + + //抄送 + if (node.getType() == NodeTypeEnum.CC.getValue().intValue()) { + flowElementList.add(buildCCNode(node)); + } + //条件分支 + if (node.getType() == NodeTypeEnum.EXCLUSIVE_GATEWAY.getValue().intValue()) { + flowElementList.addAll(buildInclusiveGatewayNode(node)); + } + //并行分支 + if (node.getType() == NodeTypeEnum.PARALLEL_GATEWAY.getValue().intValue()) { + flowElementList.addAll(buildParallelGatewayNode(node)); + } + + return flowElementList; + } + + /** + * 构建开始节点 + * 添加一个自动完成任务的用户任务节点 + * + * @param node 前端传输节点 + * @return + */ + private static List buildStartNode(Node node) { + + List flowElementList = new ArrayList<>(); + + + StartEvent startEvent = new StartEvent(); + startEvent.setId(node.getId()); + startEvent.setName(node.getName()); + + flowElementList.add(startEvent); + + + + + return flowElementList; + } + + /** + * 构建审批节点 + * + * @param node + * @return + */ + private static List buildApproveNode(Node node) { + List flowElementList = new ArrayList<>(); + node.setTailId(StrUtil.format("approve_service_task_{}", node.getId())); + + + //创建了任务执行监听器 + //先执行指派人 后创建 + //https://tkjohn.github.io/flowable-userguide/#eventDispatcher + FlowableListener createListener = new FlowableListener(); + createListener.setImplementation(ApprovalCreateListener.class.getCanonicalName()); + createListener.setImplementationType("class"); + createListener.setEvent("create"); + + + UserTask userTask = buildUserTask(node, createListener); + flowElementList.add(userTask); + + + + ServiceTask serviceTask = new ServiceTask(); + serviceTask.setId(StrUtil.format("approve_service_task_{}", node.getId())); + serviceTask.setName(StrUtil.format("{}_服务任务", node.getName())); + serviceTask.setImplementationType("class"); + serviceTask.setImplementation(ApproveServiceTask.class.getCanonicalName()); + serviceTask.setAsynchronous(false); + + + + flowElementList.add(serviceTask); + + + { + + + //执行人处理 + + String inputDataItem = "${multiInstanceHandler.resolveAssignee(execution)}"; + + + //串行 + + boolean isSequential = true; + + Integer multipleMode = node.getMultipleMode(); + //多人 + if ((multipleMode == ProcessInstanceConstant.MULTIPLE_MODE_AL_SAME)) { + //并行会签 + isSequential = false; + } + if ((multipleMode == ProcessInstanceConstant.MULTIPLE_MODE_ALL_SORT)) { + + //串行会签 + } + if ((multipleMode == ProcessInstanceConstant.MULTIPLE_MODE_ONE)) { + + //或签 + isSequential = false; + } + + MultiInstanceLoopCharacteristics loopCharacteristics = new MultiInstanceLoopCharacteristics(); + loopCharacteristics.setSequential(isSequential); + loopCharacteristics.setInputDataItem(inputDataItem); + loopCharacteristics.setElementVariable(StrUtil.format("{}_assignee_temp", node.getId())); + + loopCharacteristics.setCompletionCondition("${multiInstanceHandler.completionCondition(execution)}"); + + userTask.setLoopCharacteristics(loopCharacteristics); + String format = StrUtil.format("${{}_assignee_temp}", node.getId()); + userTask.setAssignee(format); + + } + return flowElementList; + } + + + + /** + * 创建用户任务 + * + * @param node 前端传输节点 + * @return + */ + private static UserTask buildUserTask(Node node, FlowableListener... flowableListeners) { + UserTask userTask = new UserTask(); + userTask.setId(node.getId()); + userTask.setName(node.getName()); + + if (flowableListeners != null) { + List taskListeners = new ArrayList<>(); + + for (FlowableListener flowableListener : flowableListeners) { + taskListeners.add(flowableListener); + + } + userTask.setTaskListeners(taskListeners); + } + + return userTask; + } + + /** + * 构建简单的包容网关 + * + * @param node + * @return + */ + private static FlowElement buildSimpleExclusiveGatewayNode(Node node) { + + + ExclusiveGateway exclusiveGateway = new ExclusiveGateway(); + exclusiveGateway.setId(node.getId()); + exclusiveGateway.setName(node.getName()); + + return exclusiveGateway; + + } + + + /** + * 构建并行网关 + * + * @param node + * @return + */ + private static List buildParallelGatewayNode(Node node) { + node.setTailId(StrUtil.format("{}_merge_gateway", node.getId())); + List flowElementList = new ArrayList<>(); + + ParallelGateway inclusiveGateway = new ParallelGateway(); + inclusiveGateway.setId(node.getId()); + inclusiveGateway.setName(node.getName()); + flowElementList.add(inclusiveGateway); + + //合并网关 + ParallelGateway parallelGateway = new ParallelGateway(); + parallelGateway.setId(StrUtil.format("{}_merge_gateway", node.getId())); + parallelGateway.setName(StrUtil.format("{}_合并网关", node.getName())); + flowElementList.add(parallelGateway); + + return flowElementList; + } + + /** + * 构建包容网关 + * + * @param node + * @return + */ + private static List buildInclusiveGatewayNode(Node node) { + + node.setTailId(StrUtil.format("{}_merge_gateway", node.getId())); + + List flowElementList = new ArrayList<>(); + + InclusiveGateway inclusiveGateway = new InclusiveGateway(); + inclusiveGateway.setId(node.getId()); + inclusiveGateway.setName(node.getName()); + flowElementList.add(inclusiveGateway); + + //合并网关 + InclusiveGateway gateway = new InclusiveGateway(); + gateway.setId(StrUtil.format("{}_merge_gateway", node.getId())); + gateway.setName(StrUtil.format("{}_合并网关", node.getName())); + flowElementList.add(gateway); + + return flowElementList; + } + + /** + * 构建结束节点 + * + * @param node 前端传输节点 + * @param terminateAll + * @return + */ + private static EndEvent buildEndNode(Node node, boolean terminateAll) { + EndEvent endEvent = new EndEvent(); + endEvent.setId(node.getId()); + endEvent.setName(node.getName()); + + + List definitionList = new ArrayList<>(); + TerminateEventDefinition definition = new TerminateEventDefinition(); + definition.setTerminateAll(terminateAll); + definitionList.add(definition); + endEvent.setEventDefinitions(definitionList); + + return endEvent; + } + + /** + * 创建连接线 + * + * @param node 子级节点 + * @param parentNode 父级节点 + * @param expression + * @return 所有连接线 + */ + private static List buildSequenceFlow(Node node, Node parentNode, String expression + ) { + List sequenceFlowList = new ArrayList<>(); + //没有子级了 + if (!NodeUtil.isNode(node)) { + return sequenceFlowList; + } + + String pid = parentNode.getId(); + + if (StrUtil.hasBlank(pid, node.getId())) { + return sequenceFlowList; + } + + + SequenceFlow sequenceFlow = buildSingleSequenceFlow(parentNode.getTailId(), node.getHeadId(), expression, + StrUtil.format("{}->{}", parentNode.getName(), node.getName()) + ); + sequenceFlowList.add(sequenceFlow); + + + return sequenceFlowList; + } + + /** + * 生成扩展数据 + * + * @param key + * @param val + * @return + */ + public static ExtensionAttribute generateExtensionAttribute(String key, String val) { + ExtensionAttribute ea = new ExtensionAttribute(); + + ea.setName(key); + ea.setValue(val); + return ea; + } + + /** + * 创建抄送节点 + * + * @param node + * @return + */ + private static FlowElement buildCCNode(Node node) { + + ServiceTask serviceTask = new ServiceTask(); + serviceTask.setId(node.getId()); + serviceTask.setName(node.getName()); + serviceTask.setAsynchronous(false); + serviceTask.setImplementationType("class"); + serviceTask.setImplementation(CopyServiceTask.class.getCanonicalName()); + + ExtensionElement e = new ExtensionElement(); + { + + + e.setName("flowable:failedJobRetryTimeCycle"); + //上面的例子会让作业执行器重试5次,并在每次重试前等待1分钟。 + e.setElementText("R5/PT1M"); + + } + + serviceTask.addExtensionElement(e); + return serviceTask; + } + + + + /** + * 创建连接线 + * + * @param node 父级节点 + * @param flowId + * @return 所有连接线 + */ + private static List buildInnerSequenceFlow(Node node, String flowId + ) { + + + List sequenceFlowList = new ArrayList<>(); + if (!NodeUtil.isNode(node)) { + return sequenceFlowList; + } + + + String nodeId = node.getId(); + if (StrUtil.hasBlank(nodeId)) { + return sequenceFlowList; + } + + + if (node.getType() == NodeTypeEnum.APPROVAL.getValue().intValue()) { + + + String gatewayId = StrUtil.format("approve_service_task_{}", nodeId); + + + { + SequenceFlow sequenceFlow = buildSingleSequenceFlow(nodeId, gatewayId, "${12==12}", null); + sequenceFlowList.add(sequenceFlow); + } + + + } + + + + + + return sequenceFlowList; + } + + /** + * 创建单个连接线 + * + * @param pId 父级id + * @param childId 子级id + * @param expression 表达式 + * @param name + * @return + */ + private static SequenceFlow buildSingleSequenceFlow(String pId, String childId, String expression, String name) { + if (StrUtil.hasBlank(pId, childId)) { + return null; + } + SequenceFlow sequenceFlow = new SequenceFlow(pId, childId); + sequenceFlow.setConditionExpression(expression); + sequenceFlow.setName(StrUtil.format("{}|{}", pId, childId)); + sequenceFlow.setName(StrUtil.format("连线[{}]", RandomUtil.randomString(5))); + if (StrUtil.isNotBlank(name)) { + sequenceFlow.setName(name); + } + sequenceFlow.setId(StrUtil.format("sq-id-{}-{}", IdUtil.fastSimpleUUID(), RandomUtil.randomInt(1, 10000000))); + return sequenceFlow; + } + + +} diff --git a/pigx-visual/pigx-oa-platform/src/main/resources/application.yml b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/resources/application.yml similarity index 97% rename from pigx-visual/pigx-oa-platform/src/main/resources/application.yml rename to pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/resources/application.yml index ef069930c..c8907744f 100644 --- a/pigx-visual/pigx-oa-platform/src/main/resources/application.yml +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/resources/application.yml @@ -1,5 +1,5 @@ server: - port: 5005 + port: 9020 spring: application: diff --git a/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/resources/logback-spring.xml b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/resources/logback-spring.xml new file mode 100644 index 000000000..0cd6c2983 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pigx-flow-engine-biz/src/main/resources/logback-spring.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + + + + + + ${log.path}/debug.log + + ${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz + 50MB + 30 + + + %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n + + + + + + ${log.path}/error.log + + ${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz + 50MB + 30 + + + %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n + + + ERROR + + + + + + + + + + + + + + + + + diff --git a/pigx-flow/pigx-flow-engine/pom.xml b/pigx-flow/pigx-flow-engine/pom.xml new file mode 100644 index 000000000..9b9127c06 --- /dev/null +++ b/pigx-flow/pigx-flow-engine/pom.xml @@ -0,0 +1,22 @@ + + + + 4.0.0 + + + com.pig4cloud + pigx-flow + 5.2.0-snapshot + + + pigx-flow-engine + pom + flowable 工作流核心引擎 + + + + pigx-flow-engine-api + pigx-flow-engine-biz + + diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/pom.xml b/pigx-flow/pigx-flow-task/pigx-flow-task-api/pom.xml new file mode 100644 index 000000000..619bd9d9b --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + com.pig4cloud + pigx-flow-task + 5.2.0-snapshot + + + pigx-flow-task-api + + + + + com.pig4cloud + pigx-common-core + + + + com.baomidou + mybatis-plus-extension + + + + com.pig4cloud + pigx-common-feign + + + + com.pig4cloud + pigx-common-excel + + + diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/api/feign/RemoteFlowEngineService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/api/feign/RemoteFlowEngineService.java new file mode 100644 index 000000000..6bb830147 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/api/feign/RemoteFlowEngineService.java @@ -0,0 +1,128 @@ +package com.pig4cloud.pigx.flow.task.api.feign; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.*; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.Map; + +/** + * @author lengleng + * @date 2023/7/14 + */ +@FeignClient(contextId = "remoteFlowEngineService", value = ServiceNameConstants.FLOW_ENGINE_SERVER) +public interface RemoteFlowEngineService { + + /** + * 查询任务变量 + * @param variableQueryParamDto 变量查询参数 + * @return 任务变量结果 + */ + @PostMapping("/task/queryTaskVariables") + R> queryTaskVariables(@RequestBody VariableQueryParamDto variableQueryParamDto); + + /** + * 查询简单数据 + * @param userId 用户ID + * @return 索引页统计数据 + */ + @GetMapping("/process-instance/querySimpleData") + R querySimpleData(@RequestParam("userId") Long userId); + + /** + * 创建流程 + * @param map 流程JSON对象 + * @return 创建流程结果 + */ + @PostMapping("/flow/create") + R createFlow(@RequestBody Map map); + + /** + * 启动流程实例 + * @param processInstanceParamDto 流程实例参数 + * @return 启动流程结果 + */ + @PostMapping("/flow/start") + R startProcess(@RequestBody ProcessInstanceParamDto processInstanceParamDto); + + /** + * 查询待办任务 + * @param paramDto 任务查询参数 + * @return 待办任务列表 + */ + @PostMapping("/flow/queryAssignTask") + R> queryAssignTask(@RequestBody TaskQueryParamDto paramDto); + + /** + * 查询已完成任务 + * @param paramDto 任务查询参数 + * @return 已完成任务列表 + */ + @PostMapping("/flow/queryCompletedTask") + R> queryCompletedTask(@RequestBody TaskQueryParamDto paramDto); + + /** + * 完成任务 + * @param taskParamDto 任务参数 + * @return 完成任务结果 + */ + @PostMapping("/task/complete") + R completeTask(@RequestBody TaskParamDto taskParamDto); + + /** + * 设置任务负责人 + * @param taskParamDto 任务参数 + * @return 设置负责人结果 + */ + @PostMapping("/task/setAssignee") + R setAssignee(@RequestBody TaskParamDto taskParamDto); + + /** + * 停止流程实例 + * @param taskParamDto 任务参数 + * @return 停止流程实例结果 + */ + @PostMapping("/flow/stopProcessInstance") + R stopProcessInstance(@RequestBody TaskParamDto taskParamDto); + + /** + * 解决任务 + * @param taskParamDto 任务参数 + * @return 解决任务结果 + */ + @PostMapping("/task/resolveTask") + R resolveTask(@RequestBody TaskParamDto taskParamDto); + + /** + * 退回任务 + * @param taskParamDto 任务参数 + * @return 退回任务结果 + */ + @PostMapping("/task/back") + R back(@RequestBody TaskParamDto taskParamDto); + + /** + * 委派任务 + * @param taskParamDto 任务参数 + * @return 委派任务结果 + */ + @PostMapping("/task/delegateTask") + R delegateTask(@RequestBody TaskParamDto taskParamDto); + + + /** + * 查询任务 + * @param taskId 任务ID + * @param userId 用户ID + * @return 任务查询结果 + */ + @GetMapping("/task/engine/queryTask") + R queryTask(@RequestParam("taskId") String taskId, @RequestParam("userId") Long userId); + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/api/feign/RemoteFlowTaskService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/api/feign/RemoteFlowTaskService.java new file mode 100644 index 000000000..caf7aadfc --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/api/feign/RemoteFlowTaskService.java @@ -0,0 +1,107 @@ +package com.pig4cloud.pigx.flow.task.api.feign; + +import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.*; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + * @author lengleng + * @date 2023/7/14 + */ +@FeignClient(contextId = "remoteFlowTaskService", value = ServiceNameConstants.FLOW_TASK_SERVER) +public interface RemoteFlowTaskService { + + /** + * 节点开始事件 + * @param nodeRecordParamDto + */ + @PostMapping("/remote/startNodeEvent") + void startNodeEvent(@RequestBody ProcessNodeRecordParamDto nodeRecordParamDto); + + /** + * 节点结束事件 + * @param nodeRecordParamDto + */ + @PostMapping("/remote/endNodeEvent") + void endNodeEvent(@RequestBody ProcessNodeRecordParamDto nodeRecordParamDto); + + /** + * 流程结束事件 + * @param processInstanceParamDto + */ + @PostMapping("/remote/endProcess") + void endProcessEvent(@RequestBody ProcessInstanceParamDto processInstanceParamDto); + + /** + * 创建流程事件 + * @param processInstanceRecordParamDto + */ + @PostMapping("/remote/createProcessEvent") + void createProcessEvent(@RequestBody ProcessInstanceRecordParamDto processInstanceRecordParamDto); + + /** + * 根据角色id集合查询用户id集合 + * @param roleIdList + */ + @PostMapping("/remote/queryUserIdListByRoleIdList") + R> queryUserIdListByRoleIdList(@RequestBody List roleIdList); + + /** + * 根据部门id集合查询所有的用户id集合 + * @param deptIdList + */ + @PostMapping("/remote/queryUserIdListByDepIdList") + R> queryUserIdListByDepIdList(@RequestBody List deptIdList); + + /** + * 查询流程管理员 + * @param flowId 流程id + */ + @GetMapping("/remote/queryProcessAdmin") + R queryProcessAdmin(@RequestParam("flowId") String flowId); + + /** + * 查询节点数据 + * @param flowId 流程id + * @param nodeId 节点id + * @return + */ + @GetMapping("/processNodeData/getNodeData") + R queryNodeOriData(@RequestParam("flowId") String flowId, @RequestParam("nodeId") String nodeId); + + /** + * 节点开始指派用户了 + * @param processNodeRecordAssignUserParamDto + */ + @PostMapping("/remote/startAssignUser") + R startAssignUser(@RequestBody ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto); + + /** + * 任务结束事件 + * @param processNodeRecordAssignUserParamDto + */ + @PostMapping("/remote/taskEndEvent") + R taskEndEvent(@RequestBody ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto); + + /** + * 保存抄送数据 + * @param processCopyDto + */ + @PostMapping("/remote/savecc") + R saveCC(@RequestBody ProcessCopyDto processCopyDto); + + /** + * 保存节点原始数据 + * @param processNodeDataDto + */ + @PostMapping("/processNodeData/saveNodeData") + R saveNodeOriData(@RequestBody ProcessNodeDataDto processNodeDataDto); + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/api/feign/packge-info.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/api/feign/packge-info.java new file mode 100644 index 000000000..5ffbac507 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/api/feign/packge-info.java @@ -0,0 +1,6 @@ +/* + @author pigx archetype + *

+ * feign client 存放目录,注意 @EnablePigxFeignClients 的扫描范围 + */ +package com.pig4cloud.pigx.flow.task.api.feign; \ No newline at end of file diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/CaptchaTypeEnum.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/CaptchaTypeEnum.java new file mode 100644 index 000000000..8418b55ae --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/CaptchaTypeEnum.java @@ -0,0 +1,28 @@ +package com.pig4cloud.pigx.flow.task.constant; + +/** + * EasyCaptcha 验证码类型枚举 + * + * @author haoxr + * @since 2023/03/24 + */ +public enum CaptchaTypeEnum { + + /** + * 算数 + */ + ARITHMETIC, + /** + * 中文 + */ + CHINESE, + /** + * 中文闪图 + */ + CHINESE_GIF, + /** + * 闪图 + */ + GIF, SPEC + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/FormTypeEnum.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/FormTypeEnum.java new file mode 100644 index 000000000..722c7f1fb --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/FormTypeEnum.java @@ -0,0 +1,30 @@ +package com.pig4cloud.pigx.flow.task.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.ArrayList; + +/** + * 表单类型枚举 + */ +@Getter +@AllArgsConstructor +public enum FormTypeEnum { + + INPUT("Input", "单行文本", ""), TEXTAREA("Textarea", "多行文本", ""), NUMBER("Number", "数字", null), + DATE("Date", "日期", null), DATE_TIME("DateTime", "日期时间", null), + + SEQUENCE("Sequence", "发号器", null), LAYOUT("Layout", "明细", null), TIME("Time", "时间", null), + MONEY("Money", "金额", null), SINGLE_SELECT("SingleSelect", "单选", new ArrayList<>()), + SELECT_DEPT("SelectDept", "部门", new ArrayList<>()), SELECT_USER("SelectUser", "用户", new ArrayList<>()), + + ; + + private String type; + + private String name; + + private Object defaultValue; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/IBaseEnum.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/IBaseEnum.java new file mode 100644 index 000000000..ce365d614 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/IBaseEnum.java @@ -0,0 +1,76 @@ +package com.pig4cloud.pigx.flow.task.constant; + +import cn.hutool.core.util.ObjectUtil; + +import java.util.EnumSet; +import java.util.Objects; + +/** + * 枚举通用接口 + * + * @author haoxr + * @since 2022/3/27 12:06 + */ +public interface IBaseEnum { + + T getValue(); + + String getLabel(); + + /** + * 根据值获取枚举 + * @param value + * @param clazz + * @param 枚举 + * @return + */ + static & IBaseEnum> E getEnumByValue(Object value, Class clazz) { + Objects.requireNonNull(value); + EnumSet allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举 + E matchEnum = allEnums.stream().filter(e -> ObjectUtil.equal(e.getValue(), value)).findFirst().orElse(null); + return matchEnum; + } + + /** + * 根据文本标签获取值 + * @param value + * @param clazz + * @param + * @return + */ + static & IBaseEnum> String getLabelByValue(Object value, Class clazz) { + Objects.requireNonNull(value); + EnumSet allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举 + E matchEnum = allEnums.stream().filter(e -> ObjectUtil.equal(e.getValue(), value)).findFirst().orElse(null); + + String label = null; + if (matchEnum != null) { + label = matchEnum.getLabel(); + } + return label; + } + + /** + * 根据文本标签获取值 + * @param label + * @param clazz + * @param + * @return + */ + static & IBaseEnum> Object getValueByLabel(String label, Class clazz) { + Objects.requireNonNull(label); + EnumSet allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举 + String finalLabel = label; + E matchEnum = allEnums.stream() + .filter(e -> ObjectUtil.equal(e.getLabel(), finalLabel)) + .findFirst() + .orElse(null); + + Object value = null; + if (matchEnum != null) { + value = matchEnum.getValue(); + } + return value; + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/MenuTypeEnum.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/MenuTypeEnum.java new file mode 100644 index 000000000..9baadad58 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/MenuTypeEnum.java @@ -0,0 +1,30 @@ +package com.pig4cloud.pigx.flow.task.constant; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import lombok.Getter; + +/** + * 菜单类型枚举 + * + * @author haoxr + * @since 2022/4/23 9:36 + */ + +public enum MenuTypeEnum implements IBaseEnum { + + NULL(0, null), MENU(1, "菜单"), CATALOG(2, "目录"), EXTLINK(3, "外链"), BUTTON(4, "按钮"); + + @Getter + @EnumValue // Mybatis-Plus 提供注解表示插入数据库时插入该值 + private Integer value; + + @Getter + // @JsonValue // 表示对枚举序列化时返回此字段 + private String label; + + MenuTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/NodeStatusEnum.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/NodeStatusEnum.java new file mode 100644 index 000000000..3d5b581ec --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/NodeStatusEnum.java @@ -0,0 +1,16 @@ +package com.pig4cloud.pigx.flow.task.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum NodeStatusEnum { + + WKS(0, "未开始"), JXZ(1, "进行中"), YJS(2, "已结束"),; + + private int code; + + private String name; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/NodeTypeEnum.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/NodeTypeEnum.java new file mode 100644 index 000000000..d441cedd8 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/NodeTypeEnum.java @@ -0,0 +1,37 @@ +package com.pig4cloud.pigx.flow.task.constant; + +import lombok.Getter; + +import java.util.Arrays; + +/** + * 节点枚举 + */ +@Getter +public enum NodeTypeEnum { + + ROOT("根节点", 0, false), END("结束节点", -1, false), + + APPROVAL("审批节点", 1, false), CC("抄送节点", 2, false), EXCLUSIVE_GATEWAY("条件分支", 4, true), + + PARALLEL_GATEWAY("并行分支", 5, true), EMPTY("空", 3, false), + + ; + + public static NodeTypeEnum getByValue(int value) { + return Arrays.stream(NodeTypeEnum.values()).filter(w -> w.getValue() == value).findAny().orElse(null); + } + + NodeTypeEnum(String name, Integer value, Boolean branch) { + this.name = name; + this.value = value; + this.branch = branch; + } + + private String name; + + private Integer value; + + private Boolean branch; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/NodeUserTypeEnum.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/NodeUserTypeEnum.java new file mode 100644 index 000000000..860ddad41 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/NodeUserTypeEnum.java @@ -0,0 +1,19 @@ +package com.pig4cloud.pigx.flow.task.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 用户节点类型枚举 + */ +@Getter +@AllArgsConstructor +public enum NodeUserTypeEnum { + + USER("user", "用户"), DEPT("dept", "部门"), ROLE("role", "角色"),; + + private String key; + + private String name; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/ProcessInstanceConstant.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/ProcessInstanceConstant.java new file mode 100644 index 000000000..9ec61d0f9 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/ProcessInstanceConstant.java @@ -0,0 +1,95 @@ +package com.pig4cloud.pigx.flow.task.constant; + +public class ProcessInstanceConstant { + + /** + * 空执行人 + */ + public static final Long DEFAULT_EMPTY_ASSIGN = -99999999L; + + /** + * 用户任务没有执行人的情况下如何处理 自动通过 + */ + public static final String USER_TASK_NOBODY_HANDLER_TO_PASS = "TO_PASS"; + + /** + * 转交给管理员 + */ + public static final String USER_TASK_NOBODY_HANDLER_TO_ADMIN = "TO_ADMIN"; + + /** + * 指定人员 + */ + public static final String USER_TASK_NOBODY_HANDLER_TO_USER = "TO_USER"; + + /** + * 结束流程 + */ + public static final String USER_TASK_NOBODY_HANDLER_TO_END = "TO_END"; + + public static final String USER_TASK_NOBODY_HANDLER_TO_REFUSE = "TO_REFUSE"; + + /** + * 拒绝之后 结束流程 + */ + public static final String USER_TASK_REFUSE_TYPE_TO_END = "TO_END"; + + /** + * 拒绝之后 到某个节点 + */ + public static final String USER_TASK_REFUSE_TYPE_TO_NODE = "TO_NODE"; + + /** + * 会签 + */ + public static final int MULTIPLE_MODE_AL_SAME = 1; + + /** + * 或签 + */ + public static final int MULTIPLE_MODE_ONE = 2; + + /** + * 顺签 + */ + public static final int MULTIPLE_MODE_ALL_SORT = 3; + + public static class AssignedTypeClass { + + // 指定用户 + public static final int USER = 1; + + // 指定主管 + public static final int LEADER = 2; + + // 发起人自己 + public static final int SELF = 5; + + // 表单人员 + public static final int FORM_USER = 8; + + // 发起人自选 + public static final int SELF_SELECT = 4; + + // 角色 + public static final int ROLE = 3; + + } + + /** + * 表单权限 + */ + public static class FormPermClass { + + // 隐藏 + public static final String HIDE = "H"; + + // 只读 + public static final String READ = "R"; + + // 编辑 + public static final String EDIT = "E"; + + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/StatusEnum.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/StatusEnum.java new file mode 100644 index 000000000..3a5c2bd90 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/StatusEnum.java @@ -0,0 +1,26 @@ +package com.pig4cloud.pigx.flow.task.constant; + +import lombok.Getter; + +/** + * 状态枚举 + * + * @author haoxr + * @since 2022/10/14 + */ +public enum StatusEnum implements IBaseEnum { + + ENABLE(1, "启用"), DISABLE(0, "禁用"); + + @Getter + private Integer value; + + @Getter + private String label; + + StatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/SystemConstants.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/SystemConstants.java new file mode 100644 index 000000000..8f0518f8a --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/SystemConstants.java @@ -0,0 +1,26 @@ +package com.pig4cloud.pigx.flow.task.constant; + +/** + * 系统常量 + * + * @author haoxr + * @since 2022/10/22 + */ +public interface SystemConstants { + + /** + * 根节点ID + */ + Long ROOT_NODE_ID = 0L; + + /** + * 系统默认密码 + */ + String DEFAULT_PASSWORD = "123456"; + + /** + * 超级管理员角色编码 + */ + String ROOT_ROLE_CODE = "ROOT"; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/packge-info.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/packge-info.java new file mode 100644 index 000000000..990c3b3fa --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/constant/packge-info.java @@ -0,0 +1,6 @@ +/* + * @author pigx archetype + *

+ * 常量和枚举定义 + */ +package com.pig4cloud.pigx.flow.task.constant; \ No newline at end of file diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/CheckChildDto.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/CheckChildDto.java new file mode 100644 index 000000000..43f95a194 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/CheckChildDto.java @@ -0,0 +1,23 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 检查子部门DTO + */ +@Data +public class CheckChildDto { + + /** + * 子部门ID + */ + private Long childId; + + /** + * 父部门ID列表 + */ + private List deptIdList; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/CheckParentDto.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/CheckParentDto.java new file mode 100644 index 000000000..75bbc4846 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/CheckParentDto.java @@ -0,0 +1,18 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 检查是否是给定的父级 + * + */ +@Data +public class CheckParentDto { + + private Long parentId; + + private List deptIdList; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/Condition.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/Condition.java new file mode 100644 index 000000000..a82d12566 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/Condition.java @@ -0,0 +1,31 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +/** + * 条件类 + */ +@Data +public class Condition { + + /** + * 条件键 + */ + private String key; + + /** + * 表达式 + */ + private String expression; + + /** + * 值 + */ + private Object value; + + /** + * 键类型 + */ + private String keyType; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/GroupCondition.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/GroupCondition.java new file mode 100644 index 000000000..2cfb9b461 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/GroupCondition.java @@ -0,0 +1,23 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 分组条件类 + */ +@Data +public class GroupCondition { + + /** + * 是否并行 + */ + private Boolean mode; + + /** + * 条件列表 + */ + private List conditionList; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/IndexPageStatistics.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/IndexPageStatistics.java new file mode 100644 index 000000000..8ee9cca74 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/IndexPageStatistics.java @@ -0,0 +1,37 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 首页统计数据 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class IndexPageStatistics { + + /** + * 待办数量 + */ + private Long pendingNum; + + /** + * 发起数量 + */ + private Long startedNum; + + /** + * 抄送任务 + */ + private Long copyNum; + + /** + * 完成数量 + */ + private Long completedNum; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/Nobody.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/Nobody.java new file mode 100644 index 000000000..b43f4cd40 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/Nobody.java @@ -0,0 +1,14 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +import java.util.List; + +@Data +public class Nobody { + + private String handler; + + private List assignedUser; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/Node.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/Node.java new file mode 100644 index 000000000..54ae18d38 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/Node.java @@ -0,0 +1,58 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class Node { + + private String id; + + private String parentId; + + private String headId; + + private String tailId; + + private String placeHolder; + + private Integer type; + + @JsonProperty(value = "nodeName") + private String name; + + private Boolean error; + + @JsonProperty("childNode") + private Node children; + + private Integer assignedType; + + private Boolean multiple; + + private Integer multipleMode; + + private Integer deptLeaderLevel; + + private String formUserId; + + private String formUserName; + + private List nodeUserList; + + private List conditionNodes; + + private Map formPerms; + + private Nobody nobody; + + private Boolean groupMode; + + private List conditionList; + + private Refuse refuse; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/NodeHttpResultVO.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/NodeHttpResultVO.java new file mode 100644 index 000000000..2686c4ffc --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/NodeHttpResultVO.java @@ -0,0 +1,17 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +import java.util.Map; + +/** + * 节点处理http请求结果对象 + */ +@Data +public class NodeHttpResultVO { + + private Boolean ok; + + private Map data; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/NodeUser.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/NodeUser.java new file mode 100644 index 000000000..a8fa54ca4 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/NodeUser.java @@ -0,0 +1,39 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 节点用户对象 + */ +@NoArgsConstructor +@Data +@Builder +@AllArgsConstructor +public class NodeUser { + + /** + * 用户od + */ + private Long id; + + /** + * 用户名称 + */ + private String name; + + /** + * 类型 + */ + private String type; + + /** + * 选择 + */ + private Boolean selected; + + private String avatar; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessCopyDto.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessCopyDto.java new file mode 100644 index 000000000..f6eb84f26 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessCopyDto.java @@ -0,0 +1,50 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class ProcessCopyDto { + + /** + * 当前节点时间 + */ + private LocalDateTime nodeTime; + + /** + * 发起人 + */ + private Long startUserId; + + /** + * 流程id + */ + private String flowId; + + /** + * 实例id + */ + private String processInstanceId; + + /** + * 节点id + */ + private String nodeId; + + /** + * 节点 名称 + */ + private String nodeName; + + /** + * 表单数据 + */ + private String formData; + + /** + * 抄送人id + */ + private Long userId; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessInstanceParamDto.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessInstanceParamDto.java new file mode 100644 index 000000000..d395fb999 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessInstanceParamDto.java @@ -0,0 +1,34 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +import java.util.HashMap; +import java.util.Map; + +/** + * 流程实例参数对象 + */ +@Data +public class ProcessInstanceParamDto { + + /** + * 流程id + */ + private String flowId; + + /** + * 参数集合 + */ + private Map paramMap = new HashMap<>(); + + /** + * 发起人 + */ + private String startUserId; + + /** + * 实例id + */ + private String processInstanceId; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessInstanceRecordParamDto.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessInstanceRecordParamDto.java new file mode 100644 index 000000000..41713a43d --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessInstanceRecordParamDto.java @@ -0,0 +1,40 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Getter; +import lombok.Setter; + +/** + *

+ * 流程记录 + *

+ * + * @author Vincent + * @since 2023-05-07 + */ +@Getter +@Setter +public class ProcessInstanceRecordParamDto { + + /** + * 用户id + */ + private Long userId; + + /** + * 流程id + */ + private String flowId; + + /** + * 流程实例id + */ + private String processInstanceId; + + private String parentProcessInstanceId; + + /** + * 表单数据 + */ + private String formData; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessNodeDataDto.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessNodeDataDto.java new file mode 100644 index 000000000..ff3ada016 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessNodeDataDto.java @@ -0,0 +1,14 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +@Data +public class ProcessNodeDataDto { + + private String flowId; + + private String nodeId; + + private String data; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessNodeRecordAssignUserParamDto.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessNodeRecordAssignUserParamDto.java new file mode 100644 index 000000000..b640f866d --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessNodeRecordAssignUserParamDto.java @@ -0,0 +1,66 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +/** + * 流程节点记录-执行人 + */ +@Data +public class ProcessNodeRecordAssignUserParamDto { + + /** + * 流程id (process id) + */ + private String flowId; + + /** + * 流程实例id (process instance id) + */ + private String processInstanceId; + + /** + * 表单数据 (form data) + */ + private String data; + + /** + * 本地数据 (local data) + */ + private String localData; + + /** + * 节点id (node id) + */ + private String nodeId; + + /** + * 用户id (user id) + */ + private Long userId; + + /** + * 执行id (execution id) + */ + private String executionId; + + /** + * 任务id (task id) + */ + private String taskId; + + /** + * 审批描述 (approval description) + */ + private String approveDesc; + + /** + * 节点名称 (node name) + */ + private String nodeName; + + /** + * 任务类型 (task type) + */ + private String taskType; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessNodeRecordParamDto.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessNodeRecordParamDto.java new file mode 100644 index 000000000..c8c19613d --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/ProcessNodeRecordParamDto.java @@ -0,0 +1,61 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +/** + * 流程节点记录 + */ +@Data +public class ProcessNodeRecordParamDto { + + /** + * 流程id (process id) + */ + private String flowId; + + /** + * 流程实例id (process instance id) + */ + private String processInstanceId; + + /** + * 表单数据 (form data) + */ + private String data; + + /** + * 节点id (node id) + */ + private String nodeId; + + /** + * 节点类型 (node type) + */ + private String nodeType; + + /** + * 节点名字 (node name) + */ + private String nodeName; + + /** + * 执行id (execution id) + */ + private String executionId; + + /** + * 任务id (task id) + */ + private String taskId; + + /** + * 审批描述 (approval description) + */ + private String approveDesc; + + /** + * 任务类型 (task type) + */ + private String taskType; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/Refuse.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/Refuse.java new file mode 100644 index 000000000..216310ed9 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/Refuse.java @@ -0,0 +1,12 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +@Data +public class Refuse { + + private String handler; + + private String nodeId; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/SelectValue.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/SelectValue.java new file mode 100644 index 000000000..890d27e9d --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/SelectValue.java @@ -0,0 +1,17 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +/** + * @author Huijun Zhao + * @description + * @date 2023-07-28 10:36 + */ +@Data +public class SelectValue { + + private String key; + + private String value; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/TaskDto.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/TaskDto.java new file mode 100644 index 000000000..032417ba3 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/TaskDto.java @@ -0,0 +1,97 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Date; +import java.util.Map; + +/** + * 任务对象 + */ +@Data +public class TaskDto { + + /** + * 流程id + */ + private String flowId; + + /** + * 参数集合 + */ + private Map paramMap; + + /** + * 实例id + */ + private String processInstanceId; + + /** + * 执行id + */ + private String executionId; + + /** + * 耗时 + */ + private Long durationInMillis; + + /** + * 任务id + */ + private String taskId; + + /** + * 执行人 + */ + private String assign; + + /** + * 任务名称 + */ + private String taskName; + + /** + * 节点id + */ + private String nodeId; + + /** + * 任务创建时间 + */ + private Date taskCreateTime; + + private Date taskEndTime; + + /** + * 流程组名字 + */ + private String groupName; + + /** + * 发起人id + */ + private Long rootUserId; + + /** + * 发起人名字 + */ + private String rootUserName; + + /** + * 发起人头像 + */ + private String rootUserAvatarUrl; + + /** + * 发起时间 + */ + private LocalDateTime startTime; + + /** + * 流程名称 + */ + private String processName; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/TaskParamDto.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/TaskParamDto.java new file mode 100644 index 000000000..36689eeae --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/TaskParamDto.java @@ -0,0 +1,59 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * 任务完成参数对象 + * + */ +@Data +public class TaskParamDto { + + private String processInstanceId; + + private List processInstanceIdList; + + /** + * 节点id + */ + private String nodeId; + + /** + * 添加子流程发起人 + */ + private Boolean appendChildProcessRootId; + + /** + * 任务id + */ + private String taskId; + + /** + * 用户id + */ + private String userId; + + /** + * 模板用户id + */ + private String targetUserId; + + /** + * 参数 + */ + private Map paramMap; + + /** + * 任务本地变量 + */ + private Map taskLocalParamMap; + + /** + * 模板节点 + */ + private String targetNodeId; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/TaskQueryParamDto.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/TaskQueryParamDto.java new file mode 100644 index 000000000..f05afebfe --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/TaskQueryParamDto.java @@ -0,0 +1,43 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 用户任务查询参数 + */ +@Data +public class TaskQueryParamDto { + + /** + * 任务执行人 + */ + private String assign; + + /** + * 页码 + */ + private Integer pageNum; + + /** + * 每页的数量 + */ + private Integer pageSize; + + /** + * 流程名称 + */ + private String processName; + + /** + * 任务时间 + */ + private LocalDateTime[] taskTime; + + /** + * 状态 + */ + private Integer status; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/TaskResultDto.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/TaskResultDto.java new file mode 100644 index 000000000..d7c501f1c --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/TaskResultDto.java @@ -0,0 +1,45 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +import java.util.Map; + +/** + * 任务结果对象 + * + */ +@Data +public class TaskResultDto { + + private Boolean currentTask; + + /** + * 流程id + */ + private String flowId; + + private Node taskNode; + + private String nodeId; + + /** + * 实例id + */ + private String processInstanceId; + + /** + * 委派状态 + */ + private String delegationState; + + /** + * 是否允许继续委派 + */ + private Boolean delegate; + + /** + * 所有变量 + */ + private Map variableAll; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/VariableQueryParamDto.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/VariableQueryParamDto.java new file mode 100644 index 000000000..6f07f677d --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/VariableQueryParamDto.java @@ -0,0 +1,16 @@ +package com.pig4cloud.pigx.flow.task.dto; + +import lombok.Data; + +import java.util.List; + +@Data +public class VariableQueryParamDto { + + private String taskId; + + private List keyList; + + private String executionId; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/packge-info.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/packge-info.java new file mode 100644 index 000000000..d83dba1e1 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/dto/packge-info.java @@ -0,0 +1,6 @@ +/* + * @author pigx archetype + *

+ * DTO 存放目录 + */ +package com.pig4cloud.pigx.flow.task.dto; \ No newline at end of file diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/Process.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/Process.java new file mode 100644 index 000000000..96c17b517 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/Process.java @@ -0,0 +1,136 @@ +package com.pig4cloud.pigx.flow.task.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +/** + *

+ * + *

+ * + * @author Vincent + * @since 2023-07-06 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("process") +public class Process { + + /** + * 表单ID + */ + @TableField("`flow_id`") + private String flowId; + + /** + * 表单名称 + */ + @TableField("`name`") + private String name; + + /** + * 图标配置 + */ + @TableField("`logo`") + private String logo; + + /** + * 设置项 + */ + @TableField("`settings`") + private String settings; + + /** + * 分组ID + */ + @TableField("`group_id`") + private Long groupId; + + /** + * 表单设置内容 + */ + @TableField("`form_items`") + private String formItems; + + /** + * 流程设置内容 + */ + @TableField("`process`") + private String process; + + /** + * 备注 + */ + @TableField("`remark`") + private String remark; + + @TableField("`sort`") + private Integer sort; + + /** + * 0 正常 1=隐藏 + */ + @TableField("`is_hidden`") + private Boolean hidden; + + /** + * 0 正常 1=停用 + */ + @TableField("`is_stop`") + private Boolean stop; + + /** + * 流程管理员 + */ + @TableField("`admin_id`") + private Long adminId; + + /** + * 唯一性id + */ + @TableField("`unique_id`") + private String uniqueId; + + /** + * 管理员 + */ + @TableField("`admin`") + private String admin; + + /** + * 范围描述显示 + */ + @TableField("`range_show`") + private String rangeShow; + + /** + * 用户id + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 逻辑删除字段 + */ + @TableLogic + @TableField(fill = FieldFill.INSERT) + private String delFlag; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessCopy.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessCopy.java new file mode 100644 index 000000000..d918ef5cd --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessCopy.java @@ -0,0 +1,121 @@ +package com.pig4cloud.pigx.flow.task.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +/** + *

+ * 流程抄送数据 + *

+ * + * @author Vincent + * @since 2023-07-06 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("process_copy") +public class ProcessCopy { + + /** + * 用户id + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 流程发起时间 + */ + @TableField("`start_time`") + private LocalDateTime startTime; + + /** + * 当前节点时间 + */ + @TableField("`node_time`") + private LocalDateTime nodeTime; + + /** + * 发起人 + */ + @TableField("`start_user_id`") + private Long startUserId; + + /** + * 流程id + */ + @TableField("`flow_id`") + private String flowId; + + /** + * 实例id + */ + @TableField("`process_instance_id`") + private String processInstanceId; + + /** + * 节点id + */ + @TableField("`node_id`") + private String nodeId; + + /** + * 分组id + */ + @TableField("`group_id`") + private Long groupId; + + /** + * 分组名称 + */ + @TableField("`group_name`") + private String groupName; + + /** + * 流程名称 + */ + @TableField("`process_name`") + private String processName; + + /** + * 节点 名称 + */ + @TableField("`node_name`") + private String nodeName; + + /** + * 表单数据 + */ + @TableField("`form_data`") + private String formData; + + /** + * 抄送人id + */ + @TableField("`user_id`") + private Long userId; + + /** + * 逻辑删除字段 + */ + @TableLogic + @TableField(fill = FieldFill.INSERT) + private String delFlag; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessGroup.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessGroup.java new file mode 100644 index 000000000..2de556b74 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessGroup.java @@ -0,0 +1,61 @@ +package com.pig4cloud.pigx.flow.task.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +/** + *

+ * + *

+ * + * @author Vincent + * @since 2023-07-06 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("process_group") +public class ProcessGroup { + + /** + * 分组名 + */ + @TableField("`group_name`") + private String groupName; + + /** + * 排序 + */ + @TableField("`sort`") + private Integer sort; + + /** + * 用户id + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 逻辑删除字段 + */ + @TableLogic + @TableField(fill = FieldFill.INSERT) + private String delFlag; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessInstanceRecord.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessInstanceRecord.java new file mode 100644 index 000000000..f99822958 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessInstanceRecord.java @@ -0,0 +1,116 @@ +package com.pig4cloud.pigx.flow.task.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; +import java.util.Date; + +/** + *

+ * 流程记录 + *

+ * + * @author Vincent + * @since 2023-07-06 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("process_instance_record") +public class ProcessInstanceRecord { + + /** + * 流程名字 + */ + @TableField("`name`") + private String name; + + /** + * 头像 + */ + @TableField("`logo`") + private String logo; + + /** + * 用户id + */ + @TableField("`user_id`") + private Long userId; + + /** + * 流程id + */ + @TableField("`flow_id`") + private String flowId; + + /** + * 流程实例id + */ + @TableField("`process_instance_id`") + private String processInstanceId; + + /** + * 表单数据 + */ + @TableField("`form_data`") + private String formData; + + /** + * 组id + */ + @TableField("`group_id`") + private Long groupId; + + /** + * 组名称 + */ + @TableField("`group_name`") + private String groupName; + + /** + * 状态 + */ + @TableField("`status`") + private Integer status; + + /** + * 结束时间 + */ + @TableField("`end_time`") + private Date endTime; + + /** + * 上级流程实例id + */ + @TableField("`parent_process_instance_id`") + private String parentProcessInstanceId; + + /** + * 用户id + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 逻辑删除字段 + */ + @TableLogic + @TableField(fill = FieldFill.INSERT) + private String delFlag; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessNodeData.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessNodeData.java new file mode 100644 index 000000000..f05d20c55 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessNodeData.java @@ -0,0 +1,64 @@ +package com.pig4cloud.pigx.flow.task.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +/** + *

+ * 流程节点数据 + *

+ * + * @author Vincent + * @since 2023-07-06 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("process_node_data") +public class ProcessNodeData { + + /** + * 流程id + */ + @TableField("`flow_id`") + private String flowId; + + /** + * 表单数据 + */ + @TableField("`data`") + private String data; + + @TableField("`node_id`") + private String nodeId; + + /** + * 用户id + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 逻辑删除字段 + */ + @TableLogic + @TableField(fill = FieldFill.INSERT) + private String delFlag; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessNodeRecord.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessNodeRecord.java new file mode 100644 index 000000000..027b8c5f4 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessNodeRecord.java @@ -0,0 +1,107 @@ +package com.pig4cloud.pigx.flow.task.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; +import java.util.Date; + +/** + *

+ * 流程节点记录 + *

+ * + * @author Vincent + * @since 2023-07-06 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("process_node_record") +public class ProcessNodeRecord { + + /** + * 流程id + */ + @TableField("`flow_id`") + private String flowId; + + /** + * 流程实例id + */ + @TableField("`process_instance_id`") + private String processInstanceId; + + /** + * 表单数据 + */ + @TableField("`data`") + private String data; + + @TableField("`node_id`") + private String nodeId; + + /** + * 节点类型 + */ + @TableField("`node_type`") + private String nodeType; + + /** + * 节点名字 + */ + @TableField("`node_name`") + private String nodeName; + + /** + * 节点状态 + */ + @TableField("`status`") + private Integer status; + + /** + * 开始时间 + */ + @TableField("`start_time`") + private Date startTime; + + /** + * 结束时间 + */ + @TableField("`end_time`") + private Date endTime; + + /** + * 执行id + */ + @TableField("`execution_id`") + private String executionId; + + /** + * 用户id + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 逻辑删除字段 + */ + @TableLogic + @TableField(fill = FieldFill.INSERT) + private String delFlag; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessNodeRecordAssignUser.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessNodeRecordAssignUser.java new file mode 100644 index 000000000..574e0ba24 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessNodeRecordAssignUser.java @@ -0,0 +1,130 @@ +package com.pig4cloud.pigx.flow.task.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +/** + *

+ * 流程节点记录-执行人 + *

+ * + * @author Vincent + * @since 2023-07-06 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("process_node_record_assign_user") +public class ProcessNodeRecordAssignUser { + + /** + * 流程id + */ + @TableField("`flow_id`") + private String flowId; + + /** + * 流程实例id + */ + @TableField("`process_instance_id`") + private String processInstanceId; + + /** + * 表单数据 + */ + @TableField("`data`") + private String data; + + @TableField("`node_id`") + private String nodeId; + + /** + * 用户id + */ + @TableField("`user_id`") + private String userId; + + /** + * 节点状态 + */ + @TableField("`status`") + private Integer status; + + /** + * 开始时间 + */ + @TableField("`start_time`") + private LocalDateTime startTime; + + /** + * 结束时间 + */ + @TableField("`end_time`") + private LocalDateTime endTime; + + /** + * 执行id + */ + @TableField("`execution_id`") + private String executionId; + + /** + * 任务id + */ + @TableField("`task_id`") + private String taskId; + + /** + * 审批意见 + */ + @TableField("`approve_desc`") + private String approveDesc; + + /** + * 节点名称 + */ + @TableField("`node_name`") + private String nodeName; + + /** + * 任务类型 + */ + @TableField("`task_type`") + private String taskType; + + /** + * 表单本地数据 + */ + @TableField("`local_data`") + private String localData; + + /** + * 用户id + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 逻辑删除字段 + */ + @TableLogic + @TableField(fill = FieldFill.INSERT) + private String delFlag; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessStarter.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessStarter.java new file mode 100644 index 000000000..f76e89d12 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/ProcessStarter.java @@ -0,0 +1,67 @@ +package com.pig4cloud.pigx.flow.task.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +/** + *

+ * 流程发起人 + *

+ * + * @author Vincent + * @since 2023-07-06 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("process_starter") +public class ProcessStarter { + + /** + * 用户id或者部门id + */ + @TableField("`type_id`") + private Long typeId; + + /** + * 类型 user dept + */ + @TableField("`type`") + private String type; + + /** + * 流程id + */ + @TableField("`process_id`") + private Long processId; + + /** + * 用户id + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 逻辑删除字段 + */ + @TableLogic + @TableField(fill = FieldFill.INSERT) + private String delFlag; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/packge-info.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/packge-info.java new file mode 100644 index 000000000..baecb2823 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/entity/packge-info.java @@ -0,0 +1,6 @@ +/* + * @author pigx archetype + *

+ * 实体 存放目录 + */ +package com.pig4cloud.pigx.flow.task.entity; \ No newline at end of file diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/utils/NodeUtil.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/utils/NodeUtil.java new file mode 100644 index 000000000..bdb3e7253 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/utils/NodeUtil.java @@ -0,0 +1,82 @@ +package com.pig4cloud.pigx.flow.task.utils; + +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pigx.flow.task.constant.NodeTypeEnum; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.Node; + +import java.util.ArrayList; +import java.util.List; + +public class NodeUtil { + + public static String getFlowId(String processDefinitionId) { + return StrUtil.subBefore(processDefinitionId, ":", false); + } + + public static boolean isNode(Node childNode) { + return childNode != null && StrUtil.isNotBlank(childNode.getId()); + } + + /** + * 添加结束节点 + * @param node + */ + public static void addEndNode(Node node) { + + Node children = node.getChildren(); + if (isNode(children)) { + addEndNode(children); + } + else { + Node end = new Node(); + end.setId("end"); + end.setType(NodeTypeEnum.END.getValue()); + end.setName("结束节点"); + end.setParentId(node.getId()); + node.setChildren(end); + } + + } + + /** + * 需要发起人选择用户的节点 + * @param node + */ + public static List selectUserNodeId(Node node) { + List list = new ArrayList<>(); + + if (!isNode(node)) { + return list; + } + + Integer type = node.getType(); + + if (type == NodeTypeEnum.APPROVAL.getValue().intValue()) { + + Integer assignedType = node.getAssignedType(); + + boolean selfSelect = assignedType == ProcessInstanceConstant.AssignedTypeClass.SELF_SELECT; + if (selfSelect) { + list.add(node.getId()); + } + } + + if (type == NodeTypeEnum.EXCLUSIVE_GATEWAY.getValue().intValue() + || type == NodeTypeEnum.PARALLEL_GATEWAY.getValue().intValue()) { + + // 条件分支 + List branchs = node.getConditionNodes(); + for (Node branch : branchs) { + Node children = branch.getChildren(); + List strings = selectUserNodeId(children); + list.addAll(strings); + } + } + + List next = selectUserNodeId(node.getChildren()); + list.addAll(next); + return list; + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/ErrorRspVo.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/ErrorRspVo.java new file mode 100644 index 000000000..25339cf47 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/ErrorRspVo.java @@ -0,0 +1,20 @@ +package com.pig4cloud.pigx.flow.task.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * @author : willian fu + * @date : 2022/7/4 + */ +@Data +@AllArgsConstructor +public class ErrorRspVo { + + private Integer code; + + private String msg; + + private String desp; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/FormGroupVo.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/FormGroupVo.java new file mode 100644 index 000000000..dc3779fd4 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/FormGroupVo.java @@ -0,0 +1,57 @@ +package com.pig4cloud.pigx.flow.task.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * @author : willian fu + * @date : 2020/9/21 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FormGroupVo { + + private Long id; + + /** + * 流程名字 + */ + private String name; + + /** + * 流程 + */ + private List items; + + @Data + @Builder + @AllArgsConstructor + public static class FlowVo { + + private String flowId; + + /** + * 发起范围 + */ + private String rangeShow; + + private String name; + + private String logo; + + private Boolean stop; + + private String remark; + + private LocalDateTime updated; + + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/FormItemVO.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/FormItemVO.java new file mode 100644 index 000000000..a689526cc --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/FormItemVO.java @@ -0,0 +1,59 @@ +package com.pig4cloud.pigx.flow.task.vo; + +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 表单 + */ +@Data +@NoArgsConstructor +public class FormItemVO { + + private String id; + + private String perm; + + private String icon; + + private String name; + + private String type; + + private Boolean required; + + private String typeName; + + private String placeholder; + + private Props props; + + @Data + @NoArgsConstructor + public static class Props { + + private Object value; + + private Object dictValue; + + private Object options; + + private Boolean self; + + private Boolean multi; + + private Object oriForm; + + private Object maxLength; + + private Object minLength; + + private Object regex; + + private Object regexDesc; + + private String prefix; + + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/NodeFormatParamVo.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/NodeFormatParamVo.java new file mode 100644 index 000000000..14be488be --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/NodeFormatParamVo.java @@ -0,0 +1,18 @@ +package com.pig4cloud.pigx.flow.task.vo; + +import lombok.Data; + +import java.util.Map; + +@Data +public class NodeFormatParamVo { + + private String flowId; + + private String processInstanceId; + + private String taskId; + + private Map paramMap; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/NodeVo.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/NodeVo.java new file mode 100644 index 000000000..37060f6e1 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/NodeVo.java @@ -0,0 +1,63 @@ +package com.pig4cloud.pigx.flow.task.vo; + +import lombok.Data; + +import java.util.List; + +/** + * 流程节点显示对象 + */ +@Data +public class NodeVo { + + /** + * nodeId + */ + private String id; + + /** + * 用户列表 + */ + private List userVoList; + + /** + * 显示 + */ + private String placeholder; + + /** + * 状态 1进行中2已完成 + */ + private Integer status; + + /** + * 节点名称 + */ + private String name; + + /** + * 节点类型 + */ + private Object type; + + /** + * 发起人选择用户 + */ + private Boolean selectUser; + + /** + * 是否多选 + */ + private Boolean multiple; + + /** + * 子级列表 + */ + private List children; + + /** + * 分支列表 + */ + private List branch; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/OrgTreeVo.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/OrgTreeVo.java new file mode 100644 index 000000000..f105bdb95 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/OrgTreeVo.java @@ -0,0 +1,42 @@ +package com.pig4cloud.pigx.flow.task.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author : willian fu + * @version : 1.0 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OrgTreeVo { + + /** + * 用户od + */ + private Long id; + + /** + * 用户名称 + */ + private String name; + + /** + * 类型 + */ + private String type; + + /** + * 选择 + */ + private Boolean selected; + + private String avatar; + + private Integer status = 1; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/ProcessCopyVo.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/ProcessCopyVo.java new file mode 100644 index 000000000..b66206317 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/ProcessCopyVo.java @@ -0,0 +1,11 @@ +package com.pig4cloud.pigx.flow.task.vo; + +import com.pig4cloud.pigx.flow.task.entity.ProcessCopy; +import lombok.Data; + +@Data +public class ProcessCopyVo extends ProcessCopy { + + private String startUserName; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/ProcessVO.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/ProcessVO.java new file mode 100644 index 000000000..26898351a --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/ProcessVO.java @@ -0,0 +1,19 @@ +package com.pig4cloud.pigx.flow.task.vo; + +import com.pig4cloud.pigx.flow.task.entity.Process; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class ProcessVO extends Process { + + /** + * 需要发起人选择的节点id + */ + private List selectUserNodeId; + + private Map variableMap; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/UserVo.java b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/UserVo.java new file mode 100644 index 000000000..1d835f483 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/java/com/pig4cloud/pigx/flow/task/vo/UserVo.java @@ -0,0 +1,42 @@ +package com.pig4cloud.pigx.flow.task.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UserVo { + + /** + * 用户od + */ + private Long id; + + /** + * 用户名称 + */ + private String name; + + private LocalDateTime showTime; + + private String avatar; + + /** + * 意见 + */ + private String approveDesc; + + private String operType; + + /** + * 状态 1进行中2已完成 + */ + private Integer status = 0; + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/resources/META-INF/spring/org.springframework.cloud.openfeign.FeignClient.imports b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/resources/META-INF/spring/org.springframework.cloud.openfeign.FeignClient.imports new file mode 100644 index 000000000..05c39b3e7 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-api/src/main/resources/META-INF/spring/org.springframework.cloud.openfeign.FeignClient.imports @@ -0,0 +1,2 @@ +com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowTaskService +com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowEngineService diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/Dockerfile b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/Dockerfile new file mode 100644 index 000000000..80582bf1b --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/Dockerfile @@ -0,0 +1,18 @@ +FROM pig4cloud/java:8-jre + +MAINTAINER wangiegie@gmail.com + +ENV TZ=Asia/Shanghai +ENV JAVA_OPTS="-Xms512m -Xmx1024m -Djava.security.egd=file:/dev/./urandom" + +RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN mkdir -p /pigx-flow-task + +WORKDIR /pigx-flow-task + +EXPOSE 4000 + +ADD ./target/pigx-flow-task-biz.jar ./ + +CMD sleep 60;java $JAVA_OPTS -jar pigx-flow-task-biz.jar diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/pom.xml b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/pom.xml new file mode 100644 index 000000000..4307e2c50 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/pom.xml @@ -0,0 +1,114 @@ + + + 4.0.0 + + com.pig4cloud + pigx-flow-task + 5.2.0-snapshot + + + pigx-flow-task-biz + + + + + org.springframework.boot + spring-boot-starter-undertow + + + + org.springframework.boot + spring-boot-starter-web + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + com.pig4cloud + pigx-common-data + + + + com.pig4cloud + pigx-common-sequence + + + + com.pig4cloud + pigx-common-security + + + + com.pig4cloud + pigx-common-xss + + + + com.pig4cloud + pigx-common-sentinel + + + + com.pig4cloud + pigx-common-feign + + + + com.pig4cloud + pigx-flow-task-api + + + + com.pig4cloud + pigx-common-log + + + + com.baomidou + mybatis-plus-boot-starter + + + + com.alibaba + druid-spring-boot-starter + + + + com.mysql + mysql-connector-j + + + + com.pig4cloud + pigx-common-swagger + + + + org.springframework.boot + spring-boot-starter-test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + io.fabric8 + docker-maven-plugin + + + + diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/PigxFlowTaskApplication.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/PigxFlowTaskApplication.java new file mode 100644 index 000000000..ce820a08e --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/PigxFlowTaskApplication.java @@ -0,0 +1,26 @@ +package com.pig4cloud.pigx.flow.task; + +import com.pig4cloud.pigx.common.feign.annotation.EnablePigxFeignClients; +import com.pig4cloud.pigx.common.security.annotation.EnablePigxResourceServer; +import com.pig4cloud.pigx.common.swagger.annotation.EnableOpenApi; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +/** + * @author pigx archetype + *

+ * 项目启动类 + */ +@EnableOpenApi("task") +@EnablePigxFeignClients +@EnableDiscoveryClient +@EnablePigxResourceServer +@SpringBootApplication +public class PigxFlowTaskApplication { + + public static void main(String[] args) { + SpringApplication.run(PigxFlowTaskApplication.class, args); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/config/SequenceConfiguration.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/config/SequenceConfiguration.java new file mode 100644 index 000000000..eee64532d --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/config/SequenceConfiguration.java @@ -0,0 +1,39 @@ +package com.pig4cloud.pigx.flow.task.config; + +import cn.hutool.core.date.DateUtil; +import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder; +import com.pig4cloud.pigx.common.sequence.builder.DbSeqBuilder; +import com.pig4cloud.pigx.common.sequence.properties.SequenceDbProperties; +import com.pig4cloud.pigx.common.sequence.sequence.Sequence; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.sql.DataSource; + +/** + * @author lengleng + * @date 2023-07-19 + *

+ * 设置发号器生成规则 + */ +@Configuration +public class SequenceConfiguration { + + /** + * 工作流发号器 + * @param dataSource + * @param properties + * @return + */ + @Bean + public Sequence flowSequence(DataSource dataSource, SequenceDbProperties properties) { + return DbSeqBuilder.create() + .bizName(() -> String.format("flow_%s_%s", TenantContextHolder.getTenantId(), DateUtil.today())) + .dataSource(dataSource) + .step(properties.getStep()) + .retryTimes(properties.getRetryTimes()) + .tableName(properties.getTableName()) + .build(); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/CombinationGroupController.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/CombinationGroupController.java new file mode 100644 index 000000000..2c5ba3e1c --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/CombinationGroupController.java @@ -0,0 +1,39 @@ +package com.pig4cloud.pigx.flow.task.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.service.ICombinationGroupService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 组聚合接口控制器 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/combination/group") +public class CombinationGroupController { + + private final ICombinationGroupService combinationGroupService; + + /** + * 查询表单组包含流程 + * @return 表单组数据 + */ + @GetMapping("listGroupWithProcess") + public R listGroupWithProcess(Page page, Long groupId) { + return combinationGroupService.listGroupWithProcess(page, groupId); + } + + /** + * 查询所有我可以发起的表单组 + * @return + */ + @GetMapping("listCurrentUserStartGroup") + public R listCurrentUserStartGroup() { + return combinationGroupService.listCurrentUserStartGroup(); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/OrgController.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/OrgController.java new file mode 100644 index 000000000..2d58bc84c --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/OrgController.java @@ -0,0 +1,44 @@ +package com.pig4cloud.pigx.flow.task.controller; + +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.service.IOrgService; +import lombok.RequiredArgsConstructor; +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; + +/** + * @author : willian fu + * @date : 2022/6/27 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/org") +public class OrgController { + + private final IOrgService orgService; + + /** + * 查询组织架构树 + * @param deptId 部门id + * @param showLeave 是否显示离职员工 + * @return 组织架构树数据 + */ + @GetMapping("tree") + public R getOrgTreeData(@RequestParam(defaultValue = "0") Long deptId, String type, + @RequestParam(defaultValue = "false") Boolean showLeave) { + return orgService.getOrgTreeData(deptId, type, showLeave); + } + + /** + * 模糊搜索用户 + * @param userName 用户名/拼音/首字母 + * @return 匹配到的用户 + */ + @GetMapping("tree/user/search") + public R getOrgTreeUser(@RequestParam String userName) { + return orgService.getOrgTreeUser(userName.trim()); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessController.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessController.java new file mode 100644 index 000000000..644522bcc --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessController.java @@ -0,0 +1,49 @@ +package com.pig4cloud.pigx.flow.task.controller; + +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.entity.Process; +import com.pig4cloud.pigx.flow.task.service.IProcessService; +import com.pig4cloud.pigx.flow.task.vo.ProcessVO; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/process") +public class ProcessController { + + private final IProcessService processService; + + /** + * 获取详细数据 + * @param flowId + * @return + */ + @GetMapping("getDetail") + public R getDetail(String flowId) { + return processService.getDetail(flowId); + } + + /** + * 创建流程 + * @param process + * @return + */ + @PostMapping("create") + public R create(@RequestBody Process process) { + return processService.create(process); + } + + /** + * 编辑表单 + * @param flowId 摸板ID + * @param type 类型 stop using delete + * @return 操作结果 + */ + @PutMapping("update/{flowId}") + public R update(@PathVariable String flowId, @RequestParam String type, + @RequestParam(required = false) Long groupId) { + return processService.update(flowId, type, groupId); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessCopyController.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessCopyController.java new file mode 100644 index 000000000..19d2362a0 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessCopyController.java @@ -0,0 +1,35 @@ +package com.pig4cloud.pigx.flow.task.controller; + +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.service.IProcessCopyService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 流程抄送数据 前端控制器 + *

+ * + * @author Vincent + * @since 2023-05-20 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/processCopy") +public class ProcessCopyController { + + private final IProcessCopyService processCopyService; + + /** + * 查询单个抄送详细信息 + * @param id + * @return + */ + @GetMapping("querySingleDetail") + public R querySingleDetail(long id) { + return processCopyService.querySingleDetail(id); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessGroupController.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessGroupController.java new file mode 100644 index 000000000..70e0a878f --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessGroupController.java @@ -0,0 +1,47 @@ +package com.pig4cloud.pigx.flow.task.controller; + +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.entity.ProcessGroup; +import com.pig4cloud.pigx.flow.task.service.IProcessGroupService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("processGroup") +public class ProcessGroupController { + + private final IProcessGroupService processGroupService; + + /** + * 组列表 + * @return + */ + @GetMapping("list") + public R> queryList() { + return processGroupService.queryList(); + } + + /** + * 新增流程分组 + * @param processGroup 分组名 + * @return 添加结果 + */ + @PostMapping("create") + public R create(@RequestBody ProcessGroup processGroup) { + return processGroupService.create(processGroup); + } + + /** + * 删除分组 + * @param id + * @return + */ + @DeleteMapping("delete/{id}") + public R delete(@PathVariable long id) { + return processGroupService.delete(id); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessInstanceController.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessInstanceController.java new file mode 100644 index 000000000..feeba5c28 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessInstanceController.java @@ -0,0 +1,97 @@ +package com.pig4cloud.pigx.flow.task.controller; + +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.ProcessInstanceParamDto; +import com.pig4cloud.pigx.flow.task.dto.TaskQueryParamDto; +import com.pig4cloud.pigx.flow.task.service.IProcessInstanceService; +import com.pig4cloud.pigx.flow.task.vo.NodeFormatParamVo; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.web.bind.annotation.*; + +/** + * 流程实例 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/process-instance") +public class ProcessInstanceController { + + private final IProcessInstanceService processInstanceService; + + /** + * 启动流程 + * @param processInstanceParamDto + * @return + */ + @SneakyThrows + @PostMapping("startProcessInstance") + public R startProcessInstance(@RequestBody ProcessInstanceParamDto processInstanceParamDto) { + return processInstanceService.startProcessInstance(processInstanceParamDto); + } + + /** + * 查询当前登录用户的待办任务 + * @param pageDto + * @return + */ + @SneakyThrows + @PostMapping("queryMineTask") + public R queryMineTask(@RequestBody TaskQueryParamDto queryParamDto) { + return processInstanceService.queryMineTask(queryParamDto); + } + + /** + * 查询当前登录用户已办任务 + * @param taskQueryParamDto + * @return + */ + @SneakyThrows + @PostMapping("queryMineEndTask") + public R queryMineEndTask(@RequestBody TaskQueryParamDto taskQueryParamDto) { + return processInstanceService.queryMineEndTask(taskQueryParamDto); + } + + /** + * 查询我发起的 + * @param pageDto + * @return + */ + @SneakyThrows + @PostMapping("queryMineStarted") + public R queryMineStarted(@RequestBody TaskQueryParamDto taskQueryParamDto) { + return processInstanceService.queryMineStarted(taskQueryParamDto); + } + + /** + * 查询抄送我的 + * @param pageDto + * @return + */ + @SneakyThrows + @PostMapping("queryMineCC") + public R queryMineCC(@RequestBody TaskQueryParamDto taskQueryParamDto) { + return processInstanceService.queryMineCC(taskQueryParamDto); + } + + /** + * 格式化流程显示 + * @param nodeFormatParamVo + * @return + */ + @PostMapping("formatStartNodeShow") + public R formatStartNodeShow(@RequestBody NodeFormatParamVo nodeFormatParamVo) { + return processInstanceService.formatStartNodeShow(nodeFormatParamVo); + } + + /** + * 流程详情 + * @param processInstanceId + * @return + */ + @GetMapping("detail") + public R detail(String processInstanceId) { + return processInstanceService.detail(processInstanceId); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessNodeDataController.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessNodeDataController.java new file mode 100644 index 000000000..2a5d6dce9 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/ProcessNodeDataController.java @@ -0,0 +1,46 @@ +package com.pig4cloud.pigx.flow.task.controller; + +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.ProcessNodeDataDto; +import com.pig4cloud.pigx.flow.task.service.IProcessNodeDataService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +/** + *

+ * 流程节点数据 前端控制器 + *

+ * + * @author Vincent + * @since 2023-05-07 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/processNodeData") +public class ProcessNodeDataController { + + private final IProcessNodeDataService processNodeDataService; + + /** + * 保存节点数据 + * @param processNodeDataDto + * @return + */ + @PostMapping("saveNodeData") + public R saveNodeData(@RequestBody ProcessNodeDataDto processNodeDataDto) { + return processNodeDataService.saveNodeData(processNodeDataDto); + } + + /** + * 获取节点数据 + * @param flowId + * @param nodeId + * @return + */ + @GetMapping("getNodeData") + public R getNodeData(String flowId, String nodeId) { + return processNodeDataService.getNodeData(flowId, nodeId); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/RemoteController.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/RemoteController.java new file mode 100644 index 000000000..551a84ea8 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/RemoteController.java @@ -0,0 +1,152 @@ +package com.pig4cloud.pigx.flow.task.controller; + +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.*; +import com.pig4cloud.pigx.flow.task.service.IRemoteService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 远程请求控制器 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/remote") +public class RemoteController { + + private final IRemoteService remoteService; + + /** + * 根据角色id集合查询用户id集合 + * @param roleIdList + * @return + */ + @PostMapping("queryUserIdListByRoleIdList") + public R> queryUserIdListByRoleIdList(@RequestBody List roleIdList) { + return remoteService.queryUserIdListByRoleIdList(roleIdList); + } + + /** + * 保存抄送 + * @param copyDto + * @return + */ + @PostMapping("savecc") + public R saveCC(@RequestBody ProcessCopyDto copyDto) { + return remoteService.saveCC(copyDto); + } + + /** + * 检查是否是所有的父级 + * @param checkParentDto + * @return + */ + @PostMapping("checkIsAllParent") + public R checkIsAllParent(@RequestBody CheckParentDto checkParentDto) { + return remoteService.checkIsAllParent(checkParentDto); + } + + /** + * 根据部门id集合查询用户id集合 + * @param depIdList + * @return + */ + @PostMapping("queryUserIdListByDepIdList") + public R> queryUserIdListByDepIdList(@RequestBody List depIdList) { + return remoteService.queryUserIdListByDepIdList(depIdList); + } + + /** + * 检查是否是所有的子级 + * @param checkChildDto + * @return + */ + @PostMapping("checkIsAllChild") + public R checkIsAllChild(@RequestBody CheckChildDto checkChildDto) { + return remoteService.checkIsAllChild(checkChildDto); + } + + /** + * 获取用户的信息-包括扩展字段 + * @param userId + * @return + */ + @GetMapping("queryUserAllInfo") + public R> queryUserAllInfo(long userId) { + return remoteService.queryUserAllInfo(userId); + } + + /** + * 开始节点事件 + * @param recordParamDto + * @return + */ + @PostMapping("startNodeEvent") + public R startNodeEvent(@RequestBody ProcessNodeRecordParamDto recordParamDto) { + return remoteService.startNodeEvent(recordParamDto); + } + + /** + * 流程创建了 + * @param processInstanceRecordParamDto + * @return + */ + @PostMapping("createProcessEvent") + public R createProcessEvent(@RequestBody ProcessInstanceRecordParamDto processInstanceRecordParamDto) { + return remoteService.createProcessEvent(processInstanceRecordParamDto); + } + + /** + * 结束节点事件 + * @param recordParamDto + * @return + */ + @PostMapping("endNodeEvent") + public R endNodeEvent(@RequestBody ProcessNodeRecordParamDto recordParamDto) { + return remoteService.endNodeEvent(recordParamDto); + } + + /** + * 开始设置执行人 + * @param processNodeRecordAssignUserParamDto + * @return + */ + @PostMapping("startAssignUser") + public R startAssignUser(@RequestBody ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto) { + return remoteService.startAssignUser(processNodeRecordAssignUserParamDto); + } + + /** + * 任务结束事件 + * @param processNodeRecordAssignUserParamDto + * @return + */ + @PostMapping("taskEndEvent") + public R taskEndEvent(@RequestBody ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto) { + return remoteService.taskEndEvent(processNodeRecordAssignUserParamDto); + } + + /** + * 实例结束 + * @param processInstanceParamDto + * @return + */ + @PostMapping("endProcess") + public R endProcess(@RequestBody ProcessInstanceParamDto processInstanceParamDto) { + return remoteService.endProcess(processInstanceParamDto.getProcessInstanceId()); + } + + /** + * 查询流程管理员 + * @param flowId + * @return + */ + @GetMapping("queryProcessAdmin") + public R queryProcessAdmin(String flowId) { + return remoteService.queryProcessAdmin(flowId); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/TaskController.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/TaskController.java new file mode 100644 index 000000000..665743f92 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/controller/TaskController.java @@ -0,0 +1,118 @@ +package com.pig4cloud.pigx.flow.task.controller; + +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.TaskParamDto; +import com.pig4cloud.pigx.flow.task.service.ITaskService; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.web.bind.annotation.*; + +/** + * 任务实例 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/task") +public class TaskController { + + private final ITaskService taskService; + + /** + * 查询首页数据看板 + * @return + */ + @SneakyThrows + @GetMapping("queryTaskData") + public R queryTaskData() { + return taskService.queryTaskData(); + } + + /** + * 查询任务 + * @param taskId + * @return + */ + @SneakyThrows + @GetMapping("queryTask") + public R queryTask(String taskId, boolean view) { + + return taskService.queryTask(taskId, view); + + } + + /** + * 完成任务 + * @param completeParamDto + * @return + */ + @SneakyThrows + @PostMapping("completeTask") + public R completeTask(@RequestBody TaskParamDto completeParamDto) { + + return taskService.completeTask(completeParamDto); + + } + + /** + * 前加签 + * @param completeParamDto + * @return + */ + @SneakyThrows + @PostMapping("delegateTask") + public R delegateTask(@RequestBody TaskParamDto completeParamDto) { + + return taskService.delegateTask(completeParamDto); + + } + + /** + * 加签完成任务 + * @param completeParamDto + * @return + */ + @SneakyThrows + @PostMapping("resolveTask") + public R resolveTask(@RequestBody TaskParamDto completeParamDto) { + + return taskService.resolveTask(completeParamDto); + + } + + /** + * 设置执行人 + * @param completeParamDto + * @return + */ + @SneakyThrows + @PostMapping("setAssignee") + public R setAssignee(@RequestBody TaskParamDto completeParamDto) { + + return taskService.setAssignee(completeParamDto); + + } + + /** + * 结束流程 + * @param completeParamDto + * @return + */ + @SneakyThrows + @PostMapping("stopProcessInstance") + public R stopProcessInstance(@RequestBody TaskParamDto completeParamDto) { + + return taskService.stopProcessInstance(completeParamDto); + + } + + /** + * 退回 + * @param taskParamDto + * @return + */ + @PostMapping("back") + public R back(@RequestBody TaskParamDto taskParamDto) { + return taskService.back(taskParamDto); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessCopyMapper.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessCopyMapper.java new file mode 100644 index 000000000..db6b2d223 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessCopyMapper.java @@ -0,0 +1,18 @@ +package com.pig4cloud.pigx.flow.task.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pig4cloud.pigx.flow.task.entity.ProcessCopy; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 流程抄送数据 Mapper 接口 + *

+ * + * @author Vincent + * @since 2023-05-20 + */ +@Mapper +public interface ProcessCopyMapper extends BaseMapper { + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessGroupMapper.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessGroupMapper.java new file mode 100644 index 000000000..3b949c0e5 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessGroupMapper.java @@ -0,0 +1,18 @@ +package com.pig4cloud.pigx.flow.task.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pig4cloud.pigx.flow.task.entity.ProcessGroup; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author Vincent + * @since 2023-05-25 + */ +@Mapper +public interface ProcessGroupMapper extends BaseMapper { + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessInstanceRecordMapper.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessInstanceRecordMapper.java new file mode 100644 index 000000000..b54d1fece --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessInstanceRecordMapper.java @@ -0,0 +1,18 @@ +package com.pig4cloud.pigx.flow.task.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pig4cloud.pigx.flow.task.entity.ProcessInstanceRecord; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 流程记录 Mapper 接口 + *

+ * + * @author Vincent + * @since 2023-05-07 + */ +@Mapper +public interface ProcessInstanceRecordMapper extends BaseMapper { + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessMapper.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessMapper.java new file mode 100644 index 000000000..b469ce726 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessMapper.java @@ -0,0 +1,18 @@ +package com.pig4cloud.pigx.flow.task.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pig4cloud.pigx.flow.task.entity.Process; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author cxygzl + * @since 2023-05-25 + */ +@Mapper +public interface ProcessMapper extends BaseMapper { + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessNodeDataMapper.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessNodeDataMapper.java new file mode 100644 index 000000000..817977561 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessNodeDataMapper.java @@ -0,0 +1,18 @@ +package com.pig4cloud.pigx.flow.task.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pig4cloud.pigx.flow.task.entity.ProcessNodeData; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 流程节点数据 Mapper 接口 + *

+ * + * @author Vincent + * @since 2023-05-07 + */ +@Mapper +public interface ProcessNodeDataMapper extends BaseMapper { + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessNodeRecordAssignUserMapper.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessNodeRecordAssignUserMapper.java new file mode 100644 index 000000000..73cfbf26d --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessNodeRecordAssignUserMapper.java @@ -0,0 +1,18 @@ +package com.pig4cloud.pigx.flow.task.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pig4cloud.pigx.flow.task.entity.ProcessNodeRecordAssignUser; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 流程节点记录-执行人 Mapper 接口 + *

+ * + * @author cxygzl + * @since 2023-05-10 + */ +@Mapper +public interface ProcessNodeRecordAssignUserMapper extends BaseMapper { + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessNodeRecordMapper.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessNodeRecordMapper.java new file mode 100644 index 000000000..35acc48ff --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessNodeRecordMapper.java @@ -0,0 +1,18 @@ +package com.pig4cloud.pigx.flow.task.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pig4cloud.pigx.flow.task.entity.ProcessNodeRecord; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 流程节点记录 Mapper 接口 + *

+ * + * @author cxygzl + * @since 2023-05-10 + */ +@Mapper +public interface ProcessNodeRecordMapper extends BaseMapper { + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessStarterMapper.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessStarterMapper.java new file mode 100644 index 000000000..005e208a9 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/mapper/ProcessStarterMapper.java @@ -0,0 +1,18 @@ +package com.pig4cloud.pigx.flow.task.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pig4cloud.pigx.flow.task.entity.ProcessStarter; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 流程发起人 Mapper 接口 + *

+ * + * @author Vincent + * @since 2023-05-30 + */ +@Mapper +public interface ProcessStarterMapper extends BaseMapper { + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/ICombinationGroupService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/ICombinationGroupService.java new file mode 100644 index 000000000..41155c026 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/ICombinationGroupService.java @@ -0,0 +1,24 @@ +package com.pig4cloud.pigx.flow.task.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.pig4cloud.pigx.common.core.util.R; + +/** + * 聚合接口 + */ +public interface ICombinationGroupService { + + /** + * 查询表单组包含流程 + * @param groupId 表单ID + * @return 表单组数据 + */ + R listGroupWithProcess(Page page, Long groupId); + + /** + * 查询所有我可以发起的表单组 + * @return + */ + R listCurrentUserStartGroup(); + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IOrgService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IOrgService.java new file mode 100644 index 000000000..81cf342a7 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IOrgService.java @@ -0,0 +1,27 @@ +package com.pig4cloud.pigx.flow.task.service; + +import com.pig4cloud.pigx.common.core.util.R; + +/** + * @author : willian fu + * @version : 1.0 + */ +public interface IOrgService { + + /** + * 查询组织架构树 + * @param deptId 部门id + * @param type 只查询部门架构 + * @param showLeave 是否显示离职员工 + * @return 组织架构树数据 + */ + R getOrgTreeData(Long deptId, String type, Boolean showLeave); + + /** + * 模糊搜索用户 + * @param userName 用户名/拼音/首字母 + * @return 匹配到的用户 + */ + R getOrgTreeUser(String userName); + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessCopyService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessCopyService.java new file mode 100644 index 000000000..ac181cb80 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessCopyService.java @@ -0,0 +1,24 @@ +package com.pig4cloud.pigx.flow.task.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.entity.ProcessCopy; + +/** + *

+ * 流程抄送数据 服务类 + *

+ * + * @author Vincent + * @since 2023-05-20 + */ +public interface IProcessCopyService extends IService { + + /** + * 查询单个抄送详细信息 + * @param id + * @return + */ + R querySingleDetail(long id); + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessGroupService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessGroupService.java new file mode 100644 index 000000000..1737f2a50 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessGroupService.java @@ -0,0 +1,39 @@ +package com.pig4cloud.pigx.flow.task.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.entity.ProcessGroup; + +import java.util.List; + +/** + *

+ * 服务类 + *

+ * + * @author Vincent + * @since 2023-05-25 + */ +public interface IProcessGroupService extends IService { + + /** + * 组列表 + * @return + */ + R> queryList(); + + /** + * 新增流程分组 + * @param processGroup 分组名 + * @return 添加结果 + */ + R create(ProcessGroup processGroup); + + /** + * 删除分组 + * @param id + * @return + */ + R delete(long id); + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessInstanceRecordService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessInstanceRecordService.java new file mode 100644 index 000000000..8e08978d8 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessInstanceRecordService.java @@ -0,0 +1,16 @@ +package com.pig4cloud.pigx.flow.task.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pig4cloud.pigx.flow.task.entity.ProcessInstanceRecord; + +/** + *

+ * 流程记录 服务类 + *

+ * + * @author Vincent + * @since 2023-05-07 + */ +public interface IProcessInstanceRecordService extends IService { + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessInstanceService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessInstanceService.java new file mode 100644 index 000000000..96685c1d3 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessInstanceService.java @@ -0,0 +1,70 @@ +package com.pig4cloud.pigx.flow.task.service; + +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.ProcessInstanceParamDto; +import com.pig4cloud.pigx.flow.task.dto.TaskQueryParamDto; +import com.pig4cloud.pigx.flow.task.vo.NodeFormatParamVo; + +/** + * 流程实例进程 + */ +public interface IProcessInstanceService { + + /** + * 启动流程 + * @param processInstanceParamDto + * @return + */ + R startProcessInstance(ProcessInstanceParamDto processInstanceParamDto); + + /** + * 查询当前登录用户的待办任务 + * @param taskQueryParamDto taskQueryParamDto + * @return + */ + R queryMineTask(TaskQueryParamDto taskQueryParamDto); + + /** + * 查询已办任务 + * @param taskQueryParamDto + * @return + */ + R queryMineEndTask(TaskQueryParamDto taskQueryParamDto); + + /** + * 流程结束 + * @param processsInstanceId + * @return + */ + R end(String processsInstanceId); + + /** + * 查询我发起的 + * @param taskQueryParamDto + * @return + */ + R queryMineStarted(TaskQueryParamDto taskQueryParamDto); + + /** + * 查询抄送给我的 + * @param taskQueryParamDto + * @return + */ + R queryMineCC(TaskQueryParamDto taskQueryParamDto); + + + /** + * 格式化流程显示 + * @param nodeFormatParamVo + * @return + */ + R formatStartNodeShow(NodeFormatParamVo nodeFormatParamVo); + + /** + * 流程详情 + * @param processInstanceId + * @return + */ + R detail(String processInstanceId); + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessNodeDataService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessNodeDataService.java new file mode 100644 index 000000000..76d1d62d8 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessNodeDataService.java @@ -0,0 +1,34 @@ +package com.pig4cloud.pigx.flow.task.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.ProcessNodeDataDto; +import com.pig4cloud.pigx.flow.task.entity.ProcessNodeData; + +/** + *

+ * 流程节点数据 服务类 + *

+ * + * @author Vincent + * @since 2023-05-07 + */ +public interface IProcessNodeDataService extends IService { + + /** + * 保存流程节点数据 + * @param processNodeDataDto + * @return + */ + R saveNodeData(ProcessNodeDataDto processNodeDataDto); + + /*** + * 获取节点数据 + * @param flowId + * @param nodeId + * @return + */ + R getNodeData(String flowId, String nodeId); + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessNodeRecordAssignUserService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessNodeRecordAssignUserService.java new file mode 100644 index 000000000..eb718587e --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessNodeRecordAssignUserService.java @@ -0,0 +1,32 @@ +package com.pig4cloud.pigx.flow.task.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.ProcessNodeRecordAssignUserParamDto; +import com.pig4cloud.pigx.flow.task.entity.ProcessNodeRecordAssignUser; + +/** + *

+ * 流程节点记录-执行人 服务类 + *

+ * + * @author cxygzl + * @since 2023-05-10 + */ +public interface IProcessNodeRecordAssignUserService extends IService { + + /** + * 设置执行人 + * @param processNodeRecordAssignUserParamDto + * @return + */ + R addAssignUser(ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto); + + /** + * 任务完成通知 + * @param processNodeRecordAssignUserParamDto + * @return + */ + R completeTaskEvent(ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto); + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessNodeRecordService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessNodeRecordService.java new file mode 100644 index 000000000..e27652fb9 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessNodeRecordService.java @@ -0,0 +1,32 @@ +package com.pig4cloud.pigx.flow.task.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.ProcessNodeRecordParamDto; +import com.pig4cloud.pigx.flow.task.entity.ProcessNodeRecord; + +/** + *

+ * 流程节点记录 服务类 + *

+ * + * @author cxygzl + * @since 2023-05-10 + */ +public interface IProcessNodeRecordService extends IService { + + /** + * 节点开始 + * @param processNodeRecordParamDto + * @return + */ + R start(ProcessNodeRecordParamDto processNodeRecordParamDto); + + /** + * 节点结束 + * @param processNodeRecordParamDto + * @return + */ + R complete(ProcessNodeRecordParamDto processNodeRecordParamDto); + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessService.java new file mode 100644 index 000000000..aa828dfae --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessService.java @@ -0,0 +1,46 @@ +package com.pig4cloud.pigx.flow.task.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.entity.Process; +import com.pig4cloud.pigx.flow.task.vo.ProcessVO; + +/** + *

+ * 服务类 + *

+ * + * @author cxygzl + * @since 2023-05-25 + */ +public interface IProcessService extends IService { + + /** + * 获取详细数据 + * @param flowId + * @return + */ + R getDetail(String flowId); + + Process getByFlowId(String flowId); + + void updateByFlowId(Process process); + + void hide(String flowId); + + /** + * 创建流程 + * @param process + * @return + */ + R create(Process process); + + /** + * 编辑表单 + * @param flowId 摸板ID + * @param type 类型 stop using delete + * @return 操作结果 + */ + R update(String flowId, String type, Long groupId); + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessStarterService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessStarterService.java new file mode 100644 index 000000000..45f021b24 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IProcessStarterService.java @@ -0,0 +1,16 @@ +package com.pig4cloud.pigx.flow.task.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.pig4cloud.pigx.flow.task.entity.ProcessStarter; + +/** + *

+ * 流程发起人 服务类 + *

+ * + * @author Vincent + * @since 2023-05-30 + */ +public interface IProcessStarterService extends IService { + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IRemoteService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IRemoteService.java new file mode 100644 index 000000000..1de7852bc --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/IRemoteService.java @@ -0,0 +1,106 @@ + +package com.pig4cloud.pigx.flow.task.service; + +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.*; + +import java.util.List; +import java.util.Map; + +/** + * 远程调用的接口 + */ +public interface IRemoteService { + + /** + * 根据角色id集合查询用户id集合 + * @param roleIdList + * @return + */ + R> queryUserIdListByRoleIdList(List roleIdList); + + /** + * 保存抄送 + * @param copyDto + * @return + */ + R saveCC(ProcessCopyDto copyDto); + + /** + * 检查是否是所有的父级 + * @param checkParentDto + * @return + */ + R checkIsAllParent(CheckParentDto checkParentDto); + + /** + * 根据部门id集合查询用户id集合 + * @param depIdList + * @return + */ + R> queryUserIdListByDepIdList(List depIdList); + + /** + * 检查是否是所有的子级 + * @param checkChildDto + * @return + */ + R checkIsAllChild(CheckChildDto checkChildDto); + + /** + * 获取用户的信息-包括扩展字段 + * @param userId + * @return + */ + R> queryUserAllInfo(long userId); + + /** + * 开始节点事件 + * @param recordParamDto + * @return + */ + R startNodeEvent(ProcessNodeRecordParamDto recordParamDto); + + /** + * 流程创建了 + * @param processInstanceRecordParamDto + * @return + */ + R createProcessEvent(ProcessInstanceRecordParamDto processInstanceRecordParamDto); + + /** + * 完成节点事件 + * @param recordParamDto + * @return + */ + R endNodeEvent(ProcessNodeRecordParamDto recordParamDto); + + /** + * 开始设置执行人 + * @param processNodeRecordAssignUserParamDto + * @return + */ + R startAssignUser(ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto); + + /** + * 任务结束事件 + * @param processNodeRecordAssignUserParamDto + * @return + */ + R taskEndEvent(ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto); + + /** + * 实例结束 + * @param processInstanceId + * @return + */ + R endProcess(String processInstanceId); + + /** + * 查询流程管理员 + * @param flowId + * @return + */ + R queryProcessAdmin(String flowId); + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/ITaskService.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/ITaskService.java new file mode 100644 index 000000000..82698ecde --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/ITaskService.java @@ -0,0 +1,67 @@ +package com.pig4cloud.pigx.flow.task.service; + +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.TaskParamDto; + +/** + * 任务处理 + */ +public interface ITaskService { + + /** + * 查询任务 + * @param taskId + * @param view + * @return + */ + R queryTask(String taskId, boolean view); + + /** + * 完成任务 + * @param taskParamDto + * @return + */ + R completeTask(TaskParamDto taskParamDto); + + /** + * 前加签 + * @param taskParamDto + * @return + */ + R delegateTask(TaskParamDto taskParamDto); + + /** + * 加签完成任务 + * @param taskParamDto + * @return + */ + R resolveTask(TaskParamDto taskParamDto); + + /** + * 设置执行人 + * @param taskParamDto + * @return + */ + R setAssignee(TaskParamDto taskParamDto); + + /** + * 结束流程 + * @param taskParamDto + * @return + */ + R stopProcessInstance(TaskParamDto taskParamDto); + + /** + * 退回 + * @param taskParamDto + * @return + */ + R back(TaskParamDto taskParamDto); + + /** + * 查询首页数据看板 + * @return + */ + R queryTaskData(); + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/CombinationGroupServiceImpl.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/CombinationGroupServiceImpl.java new file mode 100644 index 000000000..50e0a9b15 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/CombinationGroupServiceImpl.java @@ -0,0 +1,133 @@ +package com.pig4cloud.pigx.flow.task.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.common.security.service.PigxUser; +import com.pig4cloud.pigx.common.security.util.SecurityUtils; +import com.pig4cloud.pigx.flow.task.constant.NodeUserTypeEnum; +import com.pig4cloud.pigx.flow.task.entity.Process; +import com.pig4cloud.pigx.flow.task.entity.ProcessGroup; +import com.pig4cloud.pigx.flow.task.entity.ProcessStarter; +import com.pig4cloud.pigx.flow.task.service.ICombinationGroupService; +import com.pig4cloud.pigx.flow.task.service.IProcessGroupService; +import com.pig4cloud.pigx.flow.task.service.IProcessService; +import com.pig4cloud.pigx.flow.task.service.IProcessStarterService; +import com.pig4cloud.pigx.flow.task.vo.FormGroupVo; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +@Service +@Slf4j +@RequiredArgsConstructor +public class CombinationGroupServiceImpl implements ICombinationGroupService { + + private final IProcessGroupService processGroupService; + + private final IProcessService processService; + + private final IProcessStarterService processStarterService; + + /** + * 查询表单组包含流程 + * @param hidden + * @return 表单组数据 + */ + @Override + public R listGroupWithProcess(Page page, Long groupId) { + return R.ok(processService.lambdaQuery() + .eq(Process::getGroupId, groupId) + .eq(Process::getHidden, false) + .orderByAsc(Process::getSort) + .orderByDesc(Process::getCreateTime) + .page(page)); + } + + /** + * 查询所有我可以发起的表单组 + * @return + */ + @Override + public R listCurrentUserStartGroup() { + PigxUser user = SecurityUtils.getUser(); + + List formGroupVos = new LinkedList<>(); + + List processGroupList = processGroupService.lambdaQuery() + .orderByAsc(ProcessGroup::getSort) + .list(); + + processGroupList.forEach(group -> { + FormGroupVo formGroupVo = FormGroupVo.builder() + .id(group.getId()) + .name(group.getGroupName()) + .items(new LinkedList<>()) + .build(); + formGroupVos.add(formGroupVo); + + List processList = processService.lambdaQuery() + .eq(Process::getGroupId, group.getId()) + .eq(Process::getHidden, false) + .eq(Process::getStop, false) + .orderByAsc(Process::getSort) + .list(); + + Map existMap = new HashMap<>(); + + if (!processList.isEmpty()) { + List idList = processList.stream().map(Process::getId).collect(Collectors.toList()); + // 查询发起人集合 + List processStarterList = processStarterService.lambdaQuery() + .in(ProcessStarter::getProcessId, idList) + .list(); + Map> groupmap = processStarterList.stream() + .collect(Collectors.groupingBy(ProcessStarter::getProcessId)); + + for (Process process : processList) { + List processStarters = groupmap.get(process.getId()); + if (processStarters == null) { + existMap.put(process.getId(), true); + continue; + } + boolean match = processStarters.stream() + .anyMatch(w -> w.getTypeId().longValue() == user.getId() + && w.getType().equals(NodeUserTypeEnum.USER.getKey())); + if (match) { + existMap.put(process.getId(), true); + continue; + } + Set deptIdSet = processStarters.stream() + .filter(w -> w.getType().equals(NodeUserTypeEnum.DEPT.getKey())) + .map(w -> w.getTypeId()) + .collect(Collectors.toSet()); + + existMap.put(process.getId(), deptIdSet.contains(user.getDeptId())); + + } + + } + + processList.forEach(process -> { + + if (!existMap.get(process.getId())) { + return; + } + + formGroupVo.getItems() + .add(FormGroupVo.FlowVo.builder() + .flowId(process.getFlowId()) + .name(process.getName()) + .logo(process.getLogo()) + .remark(process.getRemark()) + .stop(process.getStop()) + .updated(process.getUpdateTime()) + .build()); + }); + }); + return R.ok(formGroupVos); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/OrgServiceImpl.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/OrgServiceImpl.java new file mode 100644 index 000000000..08b5c4352 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/OrgServiceImpl.java @@ -0,0 +1,139 @@ +package com.pig4cloud.pigx.flow.task.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pigx.admin.api.entity.SysDept; +import com.pig4cloud.pigx.admin.api.entity.SysRole; +import com.pig4cloud.pigx.admin.api.entity.SysUser; +import com.pig4cloud.pigx.admin.api.feign.RemoteDeptService; +import com.pig4cloud.pigx.admin.api.feign.RemoteRoleService; +import com.pig4cloud.pigx.admin.api.feign.RemoteUserService; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.common.core.util.RetOps; +import com.pig4cloud.pigx.flow.task.constant.NodeUserTypeEnum; +import com.pig4cloud.pigx.flow.task.service.IOrgService; +import com.pig4cloud.pigx.flow.task.utils.DataUtil; +import com.pig4cloud.pigx.flow.task.vo.OrgTreeVo; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class OrgServiceImpl implements IOrgService { + + private final RemoteUserService remoteUserService; + + private final RemoteRoleService remoteRoleService; + + private final RemoteDeptService remoteDeptService; + + /** + * 查询组织架构树 + * @param deptId 部门id + * @param type 只查询部门架构 + * @param showLeave 是否显示离职员工 + * @return 组织架构树数据 + */ + @Override + public R getOrgTreeData(Long deptId, String type, Boolean showLeave) { + List orgs = new LinkedList<>(); + + if (StrUtil.equals(type, NodeUserTypeEnum.ROLE.getKey())) { + // 角色 + + List roleList = remoteRoleService.getAllRole().getData(); + + for (SysRole role : roleList) { + OrgTreeVo orgTreeVo = new OrgTreeVo(); + orgTreeVo.setId(role.getRoleId()); + orgTreeVo.setName(role.getRoleName()); + orgTreeVo.setType(NodeUserTypeEnum.ROLE.getKey()); + orgTreeVo.setSelected(false); + orgs.add(orgTreeVo); + } + + Dict dict = Dict.create() + .set("roleList", orgs) + .set("childDepartments", orgs) + .set("employees", new ArrayList<>()); + return R.ok(dict); + + } + + Dict dict = Dict.create() + .set("titleDepartments", new ArrayList<>()) + .set("roleList", new ArrayList<>()) + .set("employees", new ArrayList<>()); + + List deptList = remoteDeptService.getAllDept().getData(); + + // 查询所有部门及员工 + { + List deptVoList = new ArrayList(); + for (SysDept dept : deptList) { + OrgTreeVo orgTreeVo = new OrgTreeVo(); + orgTreeVo.setId(dept.getDeptId()); + orgTreeVo.setName(dept.getName()); + orgTreeVo.setType(NodeUserTypeEnum.DEPT.getKey()); + orgTreeVo.setSelected(false); + deptVoList.add(orgTreeVo); + } + dict.set("childDepartments", deptVoList); + } + if (!StrUtil.equals(type, NodeUserTypeEnum.DEPT.getKey())) { + + List userVoList = new ArrayList(); + + List userList = remoteUserService.getUserIdListByDeptIdList(CollUtil.toList(deptId)).getData(); + + for (SysUser user : userList) { + OrgTreeVo orgTreeVo = new OrgTreeVo(); + orgTreeVo.setId(user.getUserId()); + orgTreeVo.setName(user.getUsername()); + orgTreeVo.setType(NodeUserTypeEnum.USER.getKey()); + orgTreeVo.setSelected(false); + orgTreeVo.setAvatar(user.getAvatar()); + userVoList.add(orgTreeVo); + } + dict.set("employees", userVoList); + } + + if (deptId > 0) { + List allDept = remoteDeptService.getAllDept().getData(); + List depts = DataUtil.selectParentByDept(deptId, allDept); + dict.set("titleDepartments", CollUtil.reverse(depts)); + } + + return R.ok(dict); + } + + /** + * 模糊搜索用户 + * @param userName 用户名/拼音/首字母 + * @return 匹配到的用户 + */ + @Override + public R getOrgTreeUser(String userName) { + return R.ok(RetOps.of(remoteUserService.getUserListByUserName(userName)) + .getData() + .orElseGet(ArrayList::new) + .stream() + .map(user -> { + OrgTreeVo orgTreeVo = new OrgTreeVo(); + orgTreeVo.setId(user.getUserId()); + orgTreeVo.setName(user.getUsername()); + orgTreeVo.setType(NodeUserTypeEnum.USER.getKey()); + orgTreeVo.setSelected(false); + orgTreeVo.setAvatar(user.getAvatar()); + return orgTreeVo; + }) + .collect(Collectors.toList())); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessCopyServiceImpl.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessCopyServiceImpl.java new file mode 100644 index 000000000..3c246a47c --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessCopyServiceImpl.java @@ -0,0 +1,122 @@ +package com.pig4cloud.pigx.flow.task.service.impl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.constant.FormTypeEnum; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.entity.Process; +import com.pig4cloud.pigx.flow.task.entity.ProcessCopy; +import com.pig4cloud.pigx.flow.task.mapper.ProcessCopyMapper; +import com.pig4cloud.pigx.flow.task.service.IProcessCopyService; +import com.pig4cloud.pigx.flow.task.service.IProcessNodeDataService; +import com.pig4cloud.pigx.flow.task.service.IProcessService; +import com.pig4cloud.pigx.flow.task.vo.FormItemVO; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + *

+ * 流程抄送数据 服务实现类 + *

+ * + * @author Vincent + * @since 2023-05-20 + */ +@Service +@RequiredArgsConstructor +public class ProcessCopyServiceImpl extends ServiceImpl implements IProcessCopyService { + + private final ObjectMapper objectMapper; + + private final IProcessService processService; + + private final IProcessNodeDataService nodeDataService; + + /** + * 查询单个抄送详细信息 + * @param id + * @return + */ + @SneakyThrows + @Override + public R querySingleDetail(long id) { + ProcessCopy processCopy = this.getById(id); + String flowId = processCopy.getFlowId(); + Process oaForms = processService.getByFlowId(flowId); + if (oaForms == null) { + return R.failed("流程不存在"); + } + String formData = processCopy.getFormData(); + + Map variableMap = objectMapper.readValue(formData, new TypeReference>() { + }); + + String nodeId = processCopy.getNodeId(); + + Node node = nodeDataService.getNodeData(flowId, nodeId).getData(); + Map formPerms = node.getFormPerms(); + + List jsonObjectList = objectMapper.readValue(oaForms.getFormItems(), new TypeReference>() { + }); + for (FormItemVO formItemVO : jsonObjectList) { + String fid = formItemVO.getId(); + String perm = formPerms.get(fid); + formItemVO.setPerm(StrUtil.isBlankIfStr(perm) ? ProcessInstanceConstant.FormPermClass.HIDE : perm); + + if (formItemVO.getType().equals(FormTypeEnum.LAYOUT.getType())) { + // 明细 + + List> subParamList = MapUtil.get(variableMap, fid, + new cn.hutool.core.lang.TypeReference>>() { + }); + + Object value = formItemVO.getProps().getValue(); + + List> l = new ArrayList<>(); + for (Map map : subParamList) { + List subItemList = Convert.toList(FormItemVO.class, value); + for (FormItemVO itemVO : subItemList) { + itemVO.getProps().setValue(map.get(itemVO.getId())); + + String permSub = formPerms.get(itemVO.getId()); + if (StrUtil.isNotBlank(permSub)) { + itemVO.setPerm(ProcessInstanceConstant.FormPermClass.EDIT.equals(permSub) + ? ProcessInstanceConstant.FormPermClass.READ : permSub + + ); + + } + else { + itemVO.setPerm(ProcessInstanceConstant.FormPermClass.HIDE); + } + + } + l.add(subItemList); + } + formItemVO.getProps().setValue(l); + + } + else { + formItemVO.getProps().setValue(variableMap.get(fid)); + + } + + } + Dict set = Dict.create().set("formItems", jsonObjectList); + + return R.ok(set); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessGroupServiceImpl.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessGroupServiceImpl.java new file mode 100644 index 000000000..b8e48d8de --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessGroupServiceImpl.java @@ -0,0 +1,61 @@ +package com.pig4cloud.pigx.flow.task.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.entity.ProcessGroup; +import com.pig4cloud.pigx.flow.task.mapper.ProcessGroupMapper; +import com.pig4cloud.pigx.flow.task.service.IProcessGroupService; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + *

+ * 服务实现类 + *

+ * + * @author Vincent + * @since 2023-05-25 + */ +@Service +public class ProcessGroupServiceImpl extends ServiceImpl + implements IProcessGroupService { + + /** + * 组列表 + * @return + */ + @Override + public R> queryList() { + List processGroupList = this.lambdaQuery().orderByAsc(ProcessGroup::getSort).list(); + + return R.ok(processGroupList); + } + + /** + * 新增表单分组 + * @param processGroup 分组名 + * @return 添加结果 + */ + @Override + public R create(ProcessGroup processGroup) { + ProcessGroup pg = new ProcessGroup(); + pg.setSort(0); + pg.setGroupName(processGroup.getGroupName()); + + this.save(pg); + return R.ok(); + } + + /** + * 删除分组 + * @param id + * @return + */ + @Override + public R delete(long id) { + this.removeById(id); + return R.ok(); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessInstanceRecordServiceImpl.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessInstanceRecordServiceImpl.java new file mode 100644 index 000000000..9cc962709 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessInstanceRecordServiceImpl.java @@ -0,0 +1,21 @@ +package com.pig4cloud.pigx.flow.task.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pig4cloud.pigx.flow.task.entity.ProcessInstanceRecord; +import com.pig4cloud.pigx.flow.task.mapper.ProcessInstanceRecordMapper; +import com.pig4cloud.pigx.flow.task.service.IProcessInstanceRecordService; +import org.springframework.stereotype.Service; + +/** + *

+ * 流程记录 服务实现类 + *

+ * + * @author Vincent + * @since 2023-05-07 + */ +@Service +public class ProcessInstanceRecordServiceImpl extends ServiceImpl + implements IProcessInstanceRecordService { + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessInstanceServiceImpl.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessInstanceServiceImpl.java new file mode 100644 index 000000000..40aa624cd --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessInstanceServiceImpl.java @@ -0,0 +1,445 @@ +package com.pig4cloud.pigx.flow.task.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.admin.api.entity.SysUser; +import com.pig4cloud.pigx.admin.api.feign.RemoteUserService; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.common.security.service.PigxUser; +import com.pig4cloud.pigx.common.security.util.SecurityUtils; +import com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowEngineService; +import com.pig4cloud.pigx.flow.task.constant.FormTypeEnum; +import com.pig4cloud.pigx.flow.task.constant.NodeStatusEnum; +import com.pig4cloud.pigx.flow.task.constant.NodeUserTypeEnum; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.*; +import com.pig4cloud.pigx.flow.task.entity.Process; +import com.pig4cloud.pigx.flow.task.entity.*; +import com.pig4cloud.pigx.flow.task.service.*; +import com.pig4cloud.pigx.flow.task.utils.NodeFormatUtil; +import com.pig4cloud.pigx.flow.task.vo.FormItemVO; +import com.pig4cloud.pigx.flow.task.vo.NodeFormatParamVo; +import com.pig4cloud.pigx.flow.task.vo.NodeVo; +import com.pig4cloud.pigx.flow.task.vo.ProcessCopyVo; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 实例进程服务 + */ +@Service +@Slf4j +@RequiredArgsConstructor +public class ProcessInstanceServiceImpl implements IProcessInstanceService { + + private final RemoteFlowEngineService flowEngineService; + + private final RemoteUserService userService; + + private final IProcessInstanceRecordService processInstanceRecordService; + + private final IProcessCopyService processCopyService; + + private final IProcessService processService; + + private final IProcessNodeRecordService processNodeRecordService; + + private final IProcessNodeRecordAssignUserService processNodeRecordAssignUserService; + + private final ObjectMapper objectMapper; + + /** + * 启动流程 + * @param processInstanceParamDto + * @return + */ + @Override + public R startProcessInstance(ProcessInstanceParamDto processInstanceParamDto) { + + PigxUser user = SecurityUtils.getUser(); + + processInstanceParamDto.setStartUserId(String.valueOf(user.getId())); + Map paramMap = processInstanceParamDto.getParamMap(); + Dict rootUser = Dict.create() + .set("id", user.getId()) + .set("name", user.getUsername()) + .set("type", NodeUserTypeEnum.USER.getKey()); + paramMap.put("root", CollUtil.newArrayList(rootUser)); + return flowEngineService.startProcess(processInstanceParamDto); + } + + /** + * 查询当前登录用户的待办任务 + * @param pageVO + * @param taskDto + * @return + */ + @Override + public R queryMineTask(TaskQueryParamDto taskQueryParamDto) { + taskQueryParamDto.setAssign(SecurityUtils.getUser().getId().toString()); + R> r = flowEngineService.queryAssignTask(taskQueryParamDto); + + if (CollUtil.isEmpty(r.getData().getRecords())) { + return r; + } + + Set processInstanceIdSet = r.getData() + .getRecords() + .stream() + .map(TaskDto::getProcessInstanceId) + .collect(Collectors.toSet()); + + // 流程实例记录 + List processInstanceRecordList = processInstanceRecordService.lambdaQuery() + .in(ProcessInstanceRecord::getProcessInstanceId, processInstanceIdSet) + .list(); + + // 发起人 + List startUserList = processInstanceRecordList.stream() + .map(ProcessInstanceRecord::getUserId) + .map(userId -> userService.getUserById(userId).getData()) + .collect(Collectors.toList()); + + Page pageResultDto = r.getData(); + List taskDtoList = new ArrayList<>(); + for (TaskDto record : r.getData().getRecords()) { + ProcessInstanceRecord processInstanceRecord = processInstanceRecordList.stream() + .filter(w -> StrUtil.equals(w.getProcessInstanceId(), record.getProcessInstanceId())) + .findAny() + .orElse(null); + + if (processInstanceRecord != null) { + record.setProcessName(processInstanceRecord.getName()); + SysUser startUser = startUserList.stream() + .filter(w -> w.getUserId().longValue() == processInstanceRecord.getUserId()) + .findAny() + .orElse(null); + + record.setRootUserId(processInstanceRecord.getUserId()); + record.setGroupName(processInstanceRecord.getGroupName()); + record.setRootUserName(startUser.getUsername()); + record.setRootUserAvatarUrl(startUser.getAvatar()); + record.setStartTime(processInstanceRecord.getCreateTime()); + + taskDtoList.add(record); + } + } + + pageResultDto.setRecords(taskDtoList); + + return R.ok(pageResultDto); + } + + /** + * 查询已办任务 + * @param taskQueryParamDto + * @return + */ + @Override + public R queryMineEndTask(TaskQueryParamDto taskQueryParamDto) { + taskQueryParamDto.setAssign(SecurityUtils.getUser().getId().toString()); + R> r = flowEngineService.queryCompletedTask(taskQueryParamDto); + + Page pageResultDto = r.getData(); + List records = pageResultDto.getRecords(); + if (CollUtil.isEmpty(records)) { + return R.ok(pageResultDto); + + } + + Set processInstanceIdSet = records.stream() + .map(TaskDto::getProcessInstanceId) + .collect(Collectors.toSet()); + + // 流程实例记录 + List processInstanceRecordList = processInstanceRecordService.lambdaQuery() + .in(ProcessInstanceRecord::getProcessInstanceId, processInstanceIdSet) + .list(); + + // 发起人 + List startUserList = processInstanceRecordList.stream() + .map(ProcessInstanceRecord::getUserId) + .map(userId -> userService.getUserById(userId).getData()) + .collect(Collectors.toList()); + + List taskDtoList = new ArrayList<>(); + for (TaskDto record : records) { + + ProcessInstanceRecord processInstanceRecord = processInstanceRecordList.stream() + .filter(w -> StrUtil.equals(w.getProcessInstanceId(), record.getProcessInstanceId())) + .findAny() + .orElse(null); + + if (processInstanceRecord != null) { + + record.setProcessName(processInstanceRecord.getName()); + + SysUser startUser = startUserList.stream() + .filter(w -> w.getUserId().longValue() == processInstanceRecord.getUserId()) + .findAny() + .orElse(null); + + record.setRootUserId(processInstanceRecord.getUserId()); + record.setGroupName(processInstanceRecord.getGroupName()); + record.setRootUserName(startUser.getName()); + record.setRootUserAvatarUrl(startUser.getAvatar()); + record.setTaskId(record.getTaskId()); + record.setStartTime(processInstanceRecord.getCreateTime()); + taskDtoList.add(record); + } + } + + pageResultDto.setRecords(taskDtoList); + + return R.ok(pageResultDto); + } + + /** + * 流程结束 + * @param processsInstanceId + * @return + */ + @Override + public R end(String processsInstanceId) { + processInstanceRecordService.lambdaUpdate() + .set(ProcessInstanceRecord::getEndTime, new Date()) + .set(ProcessInstanceRecord::getStatus, NodeStatusEnum.YJS.getCode()) + .eq(ProcessInstanceRecord::getProcessInstanceId, processsInstanceId) + .update(new ProcessInstanceRecord()); + return R.ok(); + } + + /** + * 查询我发起的 + * @param pageDto + * @return + */ + @Override + public R queryMineStarted(TaskQueryParamDto taskQueryParamDto) { + + long userId = SecurityUtils.getUser().getId(); + + Page instanceRecordPage = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getUserId, userId) + .eq(ProcessInstanceRecord::getStatus, taskQueryParamDto.getStatus()) + .between(ArrayUtil.isNotEmpty(taskQueryParamDto.getTaskTime()), ProcessInstanceRecord::getCreateTime, + ArrayUtil.isNotEmpty(taskQueryParamDto.getTaskTime()) ? taskQueryParamDto.getTaskTime()[0] : null, + ArrayUtil.isNotEmpty(taskQueryParamDto.getTaskTime()) ? taskQueryParamDto.getTaskTime()[1] : null) + .orderByDesc(ProcessInstanceRecord::getCreateTime) + .page(new Page<>(taskQueryParamDto.getPageNum(), taskQueryParamDto.getPageSize())); + + return R.ok(instanceRecordPage); + } + + /** + * 查询抄送给我的 + * @param pageDto + * @return + */ + @Override + public R queryMineCC(TaskQueryParamDto taskQueryParamDto) { + + long userId = SecurityUtils.getUser().getId(); + + Page page = processCopyService.lambdaQuery() + .eq(ProcessCopy::getUserId, userId) + .between(ArrayUtil.isNotEmpty(taskQueryParamDto.getTaskTime()), ProcessCopy::getCreateTime, + ArrayUtil.isNotEmpty(taskQueryParamDto.getTaskTime()) ? taskQueryParamDto.getTaskTime()[0] : null, + ArrayUtil.isNotEmpty(taskQueryParamDto.getTaskTime()) ? taskQueryParamDto.getTaskTime()[1] : null) + .orderByDesc(ProcessCopy::getNodeTime) + .page(new Page<>(taskQueryParamDto.getPageNum(), taskQueryParamDto.getPageSize())); + + List records = page.getRecords(); + + List processCopyVoList = BeanUtil.copyToList(records, ProcessCopyVo.class); + + if (CollUtil.isNotEmpty(records)) { + // 发起人 + List startUserList = records.stream() + .map(ProcessCopy::getStartUserId) + .map(id -> userService.getUserById(id).getData()) + .collect(Collectors.toList()); + + for (ProcessCopyVo record : processCopyVoList) { + SysUser startUser = startUserList.stream() + .filter(w -> w.getUserId().longValue() == record.getStartUserId()) + .findAny() + .orElse(null); + record.setStartUserName(startUser.getUsername()); + } + } + + Page p = BeanUtil.copyProperties(page, Page.class); + + p.setRecords(processCopyVoList); + + return R.ok(p); + } + + + /** + * 格式化流程显示 + * @param nodeFormatParamVo + * @return + */ + @SneakyThrows + @Override + public R formatStartNodeShow(NodeFormatParamVo nodeFormatParamVo) { + String flowId = nodeFormatParamVo.getFlowId(); + String processInstanceId = nodeFormatParamVo.getProcessInstanceId(); + if (StrUtil.isAllBlank(flowId, processInstanceId)) { + return R.ok(new ArrayList<>()); + } + + if (StrUtil.isBlankIfStr(flowId) && StrUtil.isNotBlank(processInstanceId)) { + ProcessInstanceRecord processInstanceRecord = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId) + .one(); + flowId = processInstanceRecord.getFlowId(); + + } + Map paramMap = nodeFormatParamVo.getParamMap(); + if (StrUtil.isNotBlank(nodeFormatParamVo.getTaskId())) { + VariableQueryParamDto variableQueryParamDto = new VariableQueryParamDto(); + variableQueryParamDto.setTaskId(nodeFormatParamVo.getTaskId()); + R> r = flowEngineService.queryTaskVariables(variableQueryParamDto); + if (!r.isOk()) { + ProcessNodeRecordAssignUser processNodeRecordAssignUser = processNodeRecordAssignUserService + .lambdaQuery() + .eq(ProcessNodeRecordAssignUser::getTaskId, nodeFormatParamVo.getTaskId()) + .eq(ProcessNodeRecordAssignUser::getStatus, NodeStatusEnum.YJS.getCode()) + .last("limit 1") + .orderByDesc(ProcessNodeRecordAssignUser::getEndTime) + .one(); + + String data = processNodeRecordAssignUser.getData(); + + Map variableMap = objectMapper.readValue(data, new TypeReference>() { + }); + variableMap.putAll(paramMap); + paramMap.putAll(variableMap); + } + else { + Map variableMap = r.getData(); + variableMap.putAll(paramMap); + paramMap.putAll(variableMap); + } + + } + + Set completeNodeSet = new HashSet<>(); + + if (StrUtil.isNotBlank(processInstanceId)) { + List processNodeRecordList = processNodeRecordService.lambdaQuery() + .eq(ProcessNodeRecord::getProcessInstanceId, processInstanceId) + .eq(ProcessNodeRecord::getStatus, NodeStatusEnum.YJS.getCode()) + .list(); + Set collect = processNodeRecordList.stream() + .map(ProcessNodeRecord::getNodeId) + .collect(Collectors.toSet()); + completeNodeSet.addAll(collect); + } + + Process oaForms = processService.getByFlowId(flowId); + String process = oaForms.getProcess(); + Node nodeDto = objectMapper.readValue(process, new TypeReference() { + }); + + List processNodeShowDtos = NodeFormatUtil.formatProcessNodeShow(nodeDto, completeNodeSet, + new HashSet<>(), processInstanceId, paramMap); + + return R.ok(processNodeShowDtos); + } + + /** + * 流程详情 + * @param processInstanceId + * @return + */ + @SneakyThrows + @Override + public R detail(String processInstanceId) { + ProcessInstanceRecord processInstanceRecord = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId) + .one(); + + Process oaForms = processService.getByFlowId(processInstanceRecord.getFlowId()); + if (oaForms == null) { + return R.failed("流程不存在"); + } + + // 发起人变量数据 + String formData = processInstanceRecord.getFormData(); + Map variableMap = objectMapper.readValue(formData, new TypeReference>() { + }); + // 发起人表单权限 + String process = oaForms.getProcess(); + + Node nodeDto = objectMapper.readValue(process, Node.class); + Map formPerms1 = nodeDto.getFormPerms(); + + List jsonObjectList = objectMapper.readValue(oaForms.getFormItems(), new TypeReference>() { + }); + for (FormItemVO formItemVO : jsonObjectList) { + String id = formItemVO.getId(); + String perm = formPerms1.get(id); + + formItemVO.setPerm(StrUtil.isBlankIfStr(perm) ? ProcessInstanceConstant.FormPermClass.READ + : (StrUtil.equals(perm, ProcessInstanceConstant.FormPermClass.HIDE) ? perm + : ProcessInstanceConstant.FormPermClass.READ)); + + if (formItemVO.getType().equals(FormTypeEnum.LAYOUT.getType())) { + // 明细 + List> subParamList = MapUtil.get(variableMap, id, + new cn.hutool.core.lang.TypeReference>>() { + }); + + Object value = formItemVO.getProps().getValue(); + + List> l = new ArrayList<>(); + for (Map map : subParamList) { + List subItemList = Convert.toList(FormItemVO.class, value); + for (FormItemVO itemVO : subItemList) { + itemVO.getProps().setValue(map.get(itemVO.getId())); + + String permSub = formPerms1.get(itemVO.getId()); + + itemVO.setPerm(StrUtil.isBlankIfStr(permSub) ? ProcessInstanceConstant.FormPermClass.READ + : (StrUtil.equals(permSub, ProcessInstanceConstant.FormPermClass.HIDE) ? permSub + : ProcessInstanceConstant.FormPermClass.READ)); + + } + l.add(subItemList); + } + formItemVO.getProps().setValue(l); + + } + else { + formItemVO.getProps().setValue(variableMap.get(id)); + + } + + } + Dict set = Dict.create() + .set("processInstanceId", processInstanceId) + .set("process", oaForms.getProcess()) + + .set("formItems", jsonObjectList); + + return R.ok(set); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessNodeDataServiceImpl.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessNodeDataServiceImpl.java new file mode 100644 index 000000000..8691e7b0b --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessNodeDataServiceImpl.java @@ -0,0 +1,60 @@ +package com.pig4cloud.pigx.flow.task.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.ProcessNodeDataDto; +import com.pig4cloud.pigx.flow.task.entity.ProcessNodeData; +import com.pig4cloud.pigx.flow.task.mapper.ProcessNodeDataMapper; +import com.pig4cloud.pigx.flow.task.service.IProcessNodeDataService; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +/** + *

+ * 流程节点数据 服务实现类 + *

+ * + * @author Vincent + * @since 2023-05-07 + */ +@Service +public class ProcessNodeDataServiceImpl extends ServiceImpl + implements IProcessNodeDataService { + + /** + * 保存流程节点数据。 + * @param processNodeDataDto 流程节点数据DTO + * @return 保存结果 + */ + @Override + public R saveNodeData(ProcessNodeDataDto processNodeDataDto) { + ProcessNodeData processNodeData = BeanUtil.copyProperties(processNodeDataDto, ProcessNodeData.class); + this.save(processNodeData); + return R.ok(); + } + + /** + * 获取节点数据。 + * @param flowId 流程ID + * @param nodeId 节点ID + * @return 节点数据 + */ + @Override + public R getNodeData(String flowId, String nodeId) { + Optional processNodeDataOptional = this.lambdaQuery() + .eq(ProcessNodeData::getFlowId, flowId) + .eq(ProcessNodeData::getNodeId, nodeId) + .oneOpt(); + + Node node = processNodeDataOptional + .map(processNodeData -> JSONUtil.toBean(processNodeData.getData(), Node.class)) + .orElse(null); + + return R.ok(node); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessNodeRecordAssignUserServiceImpl.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessNodeRecordAssignUserServiceImpl.java new file mode 100644 index 000000000..3356dfd7b --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessNodeRecordAssignUserServiceImpl.java @@ -0,0 +1,89 @@ +package com.pig4cloud.pigx.flow.task.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.constant.NodeStatusEnum; +import com.pig4cloud.pigx.flow.task.dto.ProcessNodeRecordAssignUserParamDto; +import com.pig4cloud.pigx.flow.task.entity.ProcessNodeRecordAssignUser; +import com.pig4cloud.pigx.flow.task.mapper.ProcessNodeRecordAssignUserMapper; +import com.pig4cloud.pigx.flow.task.service.IProcessNodeRecordAssignUserService; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; + +/** + *

+ * 流程节点记录-执行人 服务实现类 + *

+ * + * @author cxygzl + * @since 2023-05-10 + */ +@Service +public class ProcessNodeRecordAssignUserServiceImpl + extends ServiceImpl + implements IProcessNodeRecordAssignUserService { + + /** + * 设置执行人 + * @param processNodeRecordAssignUserParamDto + * @return + */ + @Override + public R addAssignUser(ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto) { + if (StrUtil.isNotBlank(processNodeRecordAssignUserParamDto.getApproveDesc())) { + ProcessNodeRecordAssignUser processNodeRecordAssignUser = this.lambdaQuery() + .eq(ProcessNodeRecordAssignUser::getTaskId, processNodeRecordAssignUserParamDto.getTaskId()) + .orderByDesc(ProcessNodeRecordAssignUser::getId) + .last("limit 1") + .one(); + if (processNodeRecordAssignUser != null) { + processNodeRecordAssignUser.setApproveDesc(processNodeRecordAssignUserParamDto.getApproveDesc()); + processNodeRecordAssignUser.setTaskType(processNodeRecordAssignUserParamDto.getTaskType()); + processNodeRecordAssignUser.setStatus(NodeStatusEnum.YJS.getCode()); + processNodeRecordAssignUser.setEndTime(LocalDateTime.now()); + this.updateById(processNodeRecordAssignUser); + } + + } + + ProcessNodeRecordAssignUser processNodeRecordAssignUser = BeanUtil + .copyProperties(processNodeRecordAssignUserParamDto, ProcessNodeRecordAssignUser.class); + processNodeRecordAssignUser.setStartTime(LocalDateTime.now()); + processNodeRecordAssignUser.setStatus(NodeStatusEnum.JXZ.getCode()); + processNodeRecordAssignUser.setApproveDesc(""); + processNodeRecordAssignUser.setTaskType(""); + this.save(processNodeRecordAssignUser); + + return R.ok(); + } + + /** + * 任务完成通知 + * @param processNodeRecordAssignUserParamDto + * @return + */ + @Override + public R completeTaskEvent(ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto) { + ProcessNodeRecordAssignUser processNodeRecordAssignUser = this.lambdaQuery() + .eq(ProcessNodeRecordAssignUser::getTaskId, processNodeRecordAssignUserParamDto.getTaskId()) + .eq(ProcessNodeRecordAssignUser::getUserId, processNodeRecordAssignUserParamDto.getUserId()) + .eq(ProcessNodeRecordAssignUser::getProcessInstanceId, + processNodeRecordAssignUserParamDto.getProcessInstanceId()) + .eq(ProcessNodeRecordAssignUser::getStatus, NodeStatusEnum.JXZ.getCode()) + .orderByDesc(ProcessNodeRecordAssignUser::getId) + .last("limit 1") + .one(); + processNodeRecordAssignUser.setStatus(NodeStatusEnum.YJS.getCode()); + processNodeRecordAssignUser.setApproveDesc(processNodeRecordAssignUserParamDto.getApproveDesc()); + processNodeRecordAssignUser.setEndTime(LocalDateTime.now()); + processNodeRecordAssignUser.setData(processNodeRecordAssignUserParamDto.getData()); + processNodeRecordAssignUser.setLocalData(processNodeRecordAssignUserParamDto.getLocalData()); + processNodeRecordAssignUser.setTaskType("COMPLETE"); + this.updateById(processNodeRecordAssignUser); + return R.ok(); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessNodeRecordServiceImpl.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessNodeRecordServiceImpl.java new file mode 100644 index 000000000..827d47507 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessNodeRecordServiceImpl.java @@ -0,0 +1,66 @@ +package com.pig4cloud.pigx.flow.task.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.constant.NodeStatusEnum; +import com.pig4cloud.pigx.flow.task.dto.ProcessNodeRecordParamDto; +import com.pig4cloud.pigx.flow.task.entity.ProcessNodeRecord; +import com.pig4cloud.pigx.flow.task.mapper.ProcessNodeRecordMapper; +import com.pig4cloud.pigx.flow.task.service.IProcessNodeRecordService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Date; + +/** + *

+ * 流程节点记录 服务实现类 + *

+ * + * @author cxygzl + * @since 2023-05-10 + */ +@Slf4j +@Service +public class ProcessNodeRecordServiceImpl extends ServiceImpl + implements IProcessNodeRecordService { + + /** + * 节点开始 + * @param processNodeRecordParamDto + * @return + */ + @Override + public R start(ProcessNodeRecordParamDto processNodeRecordParamDto) { + + ProcessNodeRecord processNodeRecord = BeanUtil.copyProperties(processNodeRecordParamDto, + ProcessNodeRecord.class); + processNodeRecord.setStartTime(new Date()); + processNodeRecord.setStatus(NodeStatusEnum.JXZ.getCode()); + + this.save(processNodeRecord); + return R.ok(); + } + + /** + * 节点结束 + * @param processNodeRecordParamDto + * @return + */ + @Override + public R complete(ProcessNodeRecordParamDto processNodeRecordParamDto) { + + log.info("节点结束---{}", processNodeRecordParamDto); + + // TODO 完成节点和完成任务要区分下 + this.lambdaUpdate() + .set(ProcessNodeRecord::getStatus, NodeStatusEnum.YJS.getCode()) + .set(ProcessNodeRecord::getEndTime, new Date()) + .eq(ProcessNodeRecord::getProcessInstanceId, processNodeRecordParamDto.getProcessInstanceId()) + .eq(ProcessNodeRecord::getNodeId, processNodeRecordParamDto.getNodeId()) + .update(new ProcessNodeRecord()); + return R.ok(); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessServiceImpl.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessServiceImpl.java new file mode 100644 index 000000000..151186607 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessServiceImpl.java @@ -0,0 +1,256 @@ +package com.pig4cloud.pigx.flow.task.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.common.security.util.SecurityUtils; +import com.pig4cloud.pigx.common.sequence.sequence.Sequence; +import com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowEngineService; +import com.pig4cloud.pigx.flow.task.constant.FormTypeEnum; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.NodeUser; +import com.pig4cloud.pigx.flow.task.entity.Process; +import com.pig4cloud.pigx.flow.task.entity.ProcessStarter; +import com.pig4cloud.pigx.flow.task.mapper.ProcessMapper; +import com.pig4cloud.pigx.flow.task.service.IProcessService; +import com.pig4cloud.pigx.flow.task.service.IProcessStarterService; +import com.pig4cloud.pigx.flow.task.utils.NodeUtil; +import com.pig4cloud.pigx.flow.task.vo.FormItemVO; +import com.pig4cloud.pigx.flow.task.vo.ProcessVO; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *

+ * 服务实现类 + *

+ * + * @author cxygzl + * @since 2023-05-25 + */ +@Service +@RequiredArgsConstructor +public class ProcessServiceImpl extends ServiceImpl implements IProcessService { + + private final IProcessStarterService processStarterService; + + private final RemoteFlowEngineService flowEngineService; + + private final Sequence flowSequence; + + private final ObjectMapper objectMapper; + + /** + * 获取详细数据 + * @param flowId + * @return + */ + @Override + public R getDetail(String flowId) { + // 获取流程详情 + ProcessVO processVO = this.getProcessVO(flowId); + return R.ok(processVO); + } + + /** + * 根据流程ID获取流程详情 + * @param flowId 流程ID + * @return 流程详情 + */ + @SneakyThrows + private ProcessVO getProcessVO(String flowId) { + + Process oaForms = getByFlowId(flowId); + String process = oaForms.getProcess(); + String formItems = oaForms.getFormItems(); + Node startNode = objectMapper.readValue(process, new TypeReference() { + }); + + Map formPerms = startNode.getFormPerms(); + + List formItemVOList = objectMapper.readValue(formItems, new TypeReference>() { + }); + for (FormItemVO formItemVO : formItemVOList) { + String perm = MapUtil.getStr(formPerms, formItemVO.getId(), ProcessInstanceConstant.FormPermClass.EDIT); + formItemVO.setPerm(perm); + + // 发号器赋值 + if (StrUtil.equals(formItemVO.getType(), FormTypeEnum.SEQUENCE.getType())) { + FormItemVO.Props props = formItemVO.getProps(); + String prefix = props.getPrefix(); + String nextNo = flowSequence.nextNo(); + formItemVO.setPlaceholder(StrUtil.isNotBlank(prefix) ? prefix + nextNo : nextNo); + props.setValue(StrUtil.isNotBlank(prefix) ? prefix + nextNo : nextNo); + formItemVO.setProps(props); + } + + if (StrUtil.equals(formItemVO.getType(), FormTypeEnum.LAYOUT.getType())) { + // 明细 + Object value = formItemVO.getProps().getValue(); + List subList = Convert.toList(FormItemVO.class, value); + for (FormItemVO itemVO : subList) { + String perm1 = MapUtil.getStr(formPerms, itemVO.getId(), + ProcessInstanceConstant.FormPermClass.EDIT); + itemVO.setPerm(perm1); + } + + formItemVO.getProps().setValue(subList); + formItemVO.getProps(); + } + + } + oaForms.setFormItems(objectMapper.writeValueAsString(formItemVOList)); + + List selectUserNodeId = NodeUtil.selectUserNodeId(startNode); + + ProcessVO processVO = BeanUtil.copyProperties(oaForms, ProcessVO.class); + processVO.setSelectUserNodeId(selectUserNodeId); + + return processVO; + } + + /** + * 根据流程ID获取流程 + * @param flowId 流程ID + * @return 流程 + */ + @Override + public Process getByFlowId(String flowId) { + return this.lambdaQuery().eq(Process::getFlowId, flowId).one(); + } + + /** + * 更新流程 + * @param process 流程 + */ + @Override + public void updateByFlowId(Process process) { + this.lambdaUpdate().eq(Process::getFlowId, process.getFlowId()).update(process); + } + + /** + * 隐藏流程 + * @param flowId 流程ID + */ + @Override + public void hide(String flowId) { + this.lambdaUpdate().set(Process::getHidden, true).eq(Process::getFlowId, flowId).update(new Process()); + } + + /** + * 创建流程 + * @param process 流程 + * @return 操作结果 + */ + @SneakyThrows + @Override + public R create(Process process) { + Map map = new HashMap<>(); + map.put("process", process); + map.put("userId", SecurityUtils.getUser().getId()); + R r = flowEngineService.createFlow(map); + if (!r.isOk()) { + return R.failed(r.getMsg()); + } + String flowId = r.getData(); + NodeUser nodeUser = objectMapper.readValue(process.getAdmin(), new TypeReference>() { + }).get(0); + + // 更新流程 + if (StrUtil.isNotBlank(process.getFlowId())) { + + Process oldProcess = this.getByFlowId(process.getFlowId()); + this.hide(process.getFlowId()); + // 修改所有的管理员 + this.lambdaUpdate() + .set(Process::getAdminId, nodeUser.getId()) + .eq(Process::getUniqueId, oldProcess.getUniqueId()) + .update(new Process()); + + } + + Node startNode = objectMapper.readValue(process.getProcess(), Node.class); + + List nodeUserList = startNode.getNodeUserList(); + + StringBuilder stringBuilder = new StringBuilder(); + if (CollUtil.isNotEmpty(nodeUserList)) { + int index = 0; + + for (NodeUser user : nodeUserList) { + if (index > 0) { + stringBuilder.append(","); + } + stringBuilder.append(user.getName()); + index++; + if (index > 5) { + break; + } + + } + } + + Process p = new Process(); + p.setFlowId(flowId); + p.setName(process.getName()); + p.setLogo(process.getLogo()); + p.setSettings(process.getSettings()); + p.setGroupId(process.getGroupId()); + p.setFormItems(process.getFormItems()); + p.setProcess(process.getProcess()); + p.setRemark(process.getRemark()); + p.setSort(0); + p.setHidden(false); + p.setStop(false); + p.setAdminId(nodeUser.getId()); + p.setUniqueId(IdUtil.fastSimpleUUID()); + p.setAdmin(process.getAdmin()); + p.setRangeShow(stringBuilder.toString()); + + this.save(p); + + // 保存范围 + for (NodeUser nodeUserDto : nodeUserList) { + ProcessStarter processStarter = new ProcessStarter(); + + processStarter.setProcessId(p.getId()); + processStarter.setTypeId(nodeUserDto.getId()); + processStarter.setType(nodeUserDto.getType()); + processStarterService.save(processStarter); + } + + return R.ok(); + } + + /** + * 编辑表单 + * @param flowId 流程ID + * @param type 类型 stop using delete + * @param groupId 分组ID + * @return 操作结果 + */ + @Override + public R update(String flowId, String type, Long groupId) { + Process process = new Process(); + process.setFlowId(flowId); + process.setStop("stop".equals(type)); + process.setHidden("delete".equals(type)); + process.setGroupId(groupId); + this.updateByFlowId(process); + return R.ok(); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessStarterServiceImpl.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessStarterServiceImpl.java new file mode 100644 index 000000000..5e4f74efe --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/ProcessStarterServiceImpl.java @@ -0,0 +1,21 @@ +package com.pig4cloud.pigx.flow.task.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pig4cloud.pigx.flow.task.entity.ProcessStarter; +import com.pig4cloud.pigx.flow.task.mapper.ProcessStarterMapper; +import com.pig4cloud.pigx.flow.task.service.IProcessStarterService; +import org.springframework.stereotype.Service; + +/** + *

+ * 流程发起人 服务实现类 + *

+ * + * @author Vincent + * @since 2023-05-30 + */ +@Service +public class ProcessStarterServiceImpl extends ServiceImpl + implements IProcessStarterService { + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/RemoteServiceImpl.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/RemoteServiceImpl.java new file mode 100644 index 000000000..8848c8c76 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/RemoteServiceImpl.java @@ -0,0 +1,262 @@ +package com.pig4cloud.pigx.flow.task.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.ArrayUtil; +import com.pig4cloud.pigx.admin.api.entity.SysDept; +import com.pig4cloud.pigx.admin.api.entity.SysUser; +import com.pig4cloud.pigx.admin.api.feign.RemoteDeptService; +import com.pig4cloud.pigx.admin.api.feign.RemoteUserService; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.flow.task.constant.NodeStatusEnum; +import com.pig4cloud.pigx.flow.task.dto.*; +import com.pig4cloud.pigx.flow.task.entity.Process; +import com.pig4cloud.pigx.flow.task.entity.ProcessCopy; +import com.pig4cloud.pigx.flow.task.entity.ProcessGroup; +import com.pig4cloud.pigx.flow.task.entity.ProcessInstanceRecord; +import com.pig4cloud.pigx.flow.task.service.*; +import com.pig4cloud.pigx.flow.task.utils.DataUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Service +@Slf4j +@RequiredArgsConstructor +public class RemoteServiceImpl implements IRemoteService { + + private final IProcessInstanceRecordService processInstanceRecordService; + + private final IProcessNodeRecordService processNodeRecordService; + + private final IProcessNodeRecordAssignUserService processNodeRecordAssignUserService; + + private final IProcessInstanceService processInstanceService; + + private final IProcessCopyService processCopyService; + + private final IProcessService processService; + + private final IProcessGroupService processGroupService; + + private final RemoteUserService userService; + + private final RemoteDeptService deptService; + + /** + * 根据角色id集合查询用户id集合 + * @param roleIdList + * @return + */ + @Override + public R> queryUserIdListByRoleIdList(List roleIdList) { + return userService.getUserIdListByRoleIdList(roleIdList); + } + + /** + * 保存抄送 + * @param copyDto + * @return + */ + @Override + public R saveCC(ProcessCopyDto copyDto) { + + String processInstanceId = copyDto.getProcessInstanceId(); + + // 如果抄送是第一个节点 会出现查询不到的情况 + ThreadUtil.execute(() -> { + try { + ProcessInstanceRecord processInstanceRecord = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId) + .one(); + + int index = 10; + while (index > 0 && processInstanceRecord == null) { + TimeUnit.SECONDS.sleep(5); + processInstanceRecord = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId) + .one(); + index--; + } + + ProcessCopy processCopy = BeanUtil.copyProperties(copyDto, ProcessCopy.class); + processCopy.setGroupId(processInstanceRecord.getGroupId()); + processCopy.setGroupName(processInstanceRecord.getGroupName()); + processCopy.setProcessName(processInstanceRecord.getName()); + processCopy.setStartTime(processInstanceRecord.getCreateTime()); + + processCopyService.save(processCopy); + } + catch (Exception e) { + log.error("Error:", e); + } + }); + + return R.ok(); + } + + /** + * 检查是否是所有的父级 + * @param checkParentDto + * @return + */ + @Override + public R checkIsAllParent(CheckParentDto checkParentDto) { + + Long parentId = checkParentDto.getParentId(); + List deptIdList = checkParentDto.getDeptIdList(); + // 查询子级包括自己 + List allDept = deptService.getAllDept().getData(); + List childrenDeptList = DataUtil.selectChildrenByDept(parentId, allDept); + + List childrenDeptIdList = childrenDeptList.stream().map(SysDept::getDeptId).collect(Collectors.toList()); + childrenDeptIdList.remove(parentId); + + List remainIdList = CollUtil.removeAny(deptIdList, ArrayUtil.toArray(childrenDeptIdList, Long.class)); + + return R.ok(remainIdList.isEmpty()); + } + + /** + * 根据部门id集合查询用户id集合 + * @param depIdList + * @return + */ + @Override + public R> queryUserIdListByDepIdList(List depIdList) { + List list = userService.getUserIdListByDeptIdList(depIdList) + .getData() + .stream() + .map(SysUser::getUserId) + .collect(Collectors.toList()); + return R.ok(list); + } + + /** + * 检查是否是所有的子级 + * @param checkChildDto + * @return + */ + @Override + public R checkIsAllChild(CheckChildDto checkChildDto) { + Long childId = checkChildDto.getChildId(); + List deptIdList = checkChildDto.getDeptIdList(); + // 查询父级包括自己 + List allDept = deptService.getAllDept().getData(); + List parentDeptList = DataUtil.selectParentByDept(childId, allDept); + + List parentDeptIdList = parentDeptList.stream().map(SysDept::getDeptId).collect(Collectors.toList()); + parentDeptIdList.remove(childId); + + List remainIdList = CollUtil.removeAny(deptIdList, ArrayUtil.toArray(parentDeptIdList, Long.class)); + + return R.ok(remainIdList.isEmpty()); + } + + /** + * 获取用户的信息-包括扩展字段 + * @param userId + * @return + */ + @Override + public R> queryUserAllInfo(long userId) { + SysUser sysUser = userService.getUserById(userId).getData(); + return R.ok(BeanUtil.beanToMap(sysUser)); + } + + /** + * 开始节点事件 + * @param recordParamDto + * @return + */ + @Override + public R startNodeEvent(ProcessNodeRecordParamDto recordParamDto) { + return processNodeRecordService.start(recordParamDto); + } + + /** + * 流程创建了 + * @param processInstanceRecordParamDto + * @return + */ + @Override + public R createProcessEvent(ProcessInstanceRecordParamDto processInstanceRecordParamDto) { + ProcessInstanceRecord entity = BeanUtil.copyProperties(processInstanceRecordParamDto, + ProcessInstanceRecord.class); + + Process oaForms = processService.getByFlowId(processInstanceRecordParamDto.getFlowId()); + + ProcessGroup oaFormGroups = processGroupService.getById(oaForms.getGroupId()); + + entity.setName(oaForms.getName()); + entity.setLogo(oaForms.getLogo()); + entity.setUserId(processInstanceRecordParamDto.getUserId()); + entity.setFlowId(processInstanceRecordParamDto.getFlowId()); + entity.setProcessInstanceId(processInstanceRecordParamDto.getProcessInstanceId()); + entity.setGroupId(oaFormGroups.getId()); + entity.setGroupName(oaFormGroups.getGroupName()); + entity.setStatus(NodeStatusEnum.JXZ.getCode()); + + processInstanceRecordService.save(entity); + + return R.ok(); + } + + /** + * 完成节点事件 + * @param recordParamDto + * @return + */ + @Override + public R endNodeEvent(ProcessNodeRecordParamDto recordParamDto) { + return processNodeRecordService.complete(recordParamDto); + } + + /** + * 开始设置执行人 + * @param processNodeRecordAssignUserParamDto + * @return + */ + @Override + public R startAssignUser(ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto) { + return processNodeRecordAssignUserService.addAssignUser(processNodeRecordAssignUserParamDto); + } + + /** + * 任务结束事件 + * @param processNodeRecordAssignUserParamDto + * @return + */ + @Override + public R taskEndEvent(ProcessNodeRecordAssignUserParamDto processNodeRecordAssignUserParamDto) { + return processNodeRecordAssignUserService.completeTaskEvent(processNodeRecordAssignUserParamDto); + } + + /** + * 实例结束 + * @param processInstanceId + * @return + */ + @Override + public R endProcess(String processInstanceId) { + return processInstanceService.end(processInstanceId); + } + + /** + * 查询流程管理员 + * @param flowId + * @return + */ + @Override + public R queryProcessAdmin(String flowId) { + Process process = processService.getByFlowId(flowId); + return R.ok(process.getAdminId()); + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/TaskServiceImpl.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/TaskServiceImpl.java new file mode 100644 index 000000000..3fec4ae1c --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/service/impl/TaskServiceImpl.java @@ -0,0 +1,339 @@ +package com.pig4cloud.pigx.flow.task.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.common.security.util.SecurityUtils; +import com.pig4cloud.pigx.flow.task.api.feign.RemoteFlowEngineService; +import com.pig4cloud.pigx.flow.task.constant.FormTypeEnum; +import com.pig4cloud.pigx.flow.task.constant.NodeStatusEnum; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.IndexPageStatistics; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.TaskParamDto; +import com.pig4cloud.pigx.flow.task.dto.TaskResultDto; +import com.pig4cloud.pigx.flow.task.entity.Process; +import com.pig4cloud.pigx.flow.task.entity.ProcessCopy; +import com.pig4cloud.pigx.flow.task.entity.ProcessInstanceRecord; +import com.pig4cloud.pigx.flow.task.entity.ProcessNodeRecordAssignUser; +import com.pig4cloud.pigx.flow.task.service.*; +import com.pig4cloud.pigx.flow.task.vo.FormItemVO; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +@Slf4j +@RequiredArgsConstructor +public class TaskServiceImpl implements ITaskService { + + private final RemoteFlowEngineService flowEngineService; + + private final IProcessService processService; + + private final IProcessCopyService processCopyService; + + private final IProcessNodeDataService nodeDataService; + + private final IProcessNodeRecordAssignUserService processNodeRecordAssignUserService; + + private final IProcessInstanceRecordService processInstanceRecordService; + + private final ObjectMapper objectMapper; + + /** + * 查询首页数据看板 + * @return + */ + @Override + public R queryTaskData() { + R indexPageStatisticsR = flowEngineService + .querySimpleData(SecurityUtils.getUser().getId()); + + // 获取抄送任务 + Long copyCount = processCopyService.lambdaQuery() + .eq(ProcessCopy::getUserId, SecurityUtils.getUser().getId()) + .count(); + indexPageStatisticsR.getData().setCopyNum(copyCount); + return indexPageStatisticsR; + } + + /** + * 查询任务 + * @param taskId + * @param view + * @return + */ + @SneakyThrows + @Override + public R queryTask(String taskId, boolean view) { + + long userId = SecurityUtils.getUser().getId(); + + R r = flowEngineService.queryTask(taskId, userId); + + if (!r.isOk()) { + return R.failed(r.getMsg()); + } + + TaskResultDto taskResultDto = r.getData(); + + // 变量 + Map paramMap = taskResultDto.getVariableAll(); + // 是否是当前活动任务 + Boolean currentTask = taskResultDto.getCurrentTask(); + if (!currentTask) { + ProcessNodeRecordAssignUser processNodeRecordAssignUser = processNodeRecordAssignUserService.lambdaQuery() + .eq(ProcessNodeRecordAssignUser::getTaskId, taskId) + .eq(ProcessNodeRecordAssignUser::getUserId, userId) + .eq(ProcessNodeRecordAssignUser::getStatus, NodeStatusEnum.YJS.getCode()) + .last("limit 1") + .orderByDesc(ProcessNodeRecordAssignUser::getEndTime) + .one(); + + if (processNodeRecordAssignUser != null) { + String data = processNodeRecordAssignUser.getData(); + if (StrUtil.isNotBlank(data)) { + Map collect = objectMapper.readValue(data, new TypeReference>() { + }); + paramMap.putAll(collect); + + } + } + + } + + // 当前节点数据 + Node node = nodeDataService.getNodeData(taskResultDto.getFlowId(), taskResultDto.getNodeId()).getData(); + Map formPerms = node.getFormPerms(); + + Process oaForms = processService.getByFlowId(taskResultDto.getFlowId()); + if (oaForms == null) { + return R.failed("流程不存在"); + } + + List formItemVOList = JSONUtil.toList(oaForms.getFormItems(), FormItemVO.class); + for (FormItemVO formItemVO : formItemVOList) { + + String id = formItemVO.getId(); + + String perm = formPerms.get(id); + + if (StrUtil.isNotBlank(perm)) { + + formItemVO.setPerm(view ? (ProcessInstanceConstant.FormPermClass.EDIT.equals(perm) + ? ProcessInstanceConstant.FormPermClass.READ : perm) : perm); + + } + else { + formItemVO.setPerm(ProcessInstanceConstant.FormPermClass.HIDE); + } + + if (formItemVO.getType().equals(FormTypeEnum.LAYOUT.getType())) { + // 明细 + + List> subParamList = MapUtil.get(paramMap, id, + new cn.hutool.core.lang.TypeReference>>() { + }); + + Object value = formItemVO.getProps().getValue(); + + List> l = new ArrayList<>(); + for (Map map : subParamList) { + List subItemList = Convert.toList(FormItemVO.class, value); + for (FormItemVO itemVO : subItemList) { + itemVO.getProps().setValue(map.get(itemVO.getId())); + + String permSub = formPerms.get(itemVO.getId()); + if (StrUtil.isNotBlank(permSub)) { + itemVO.setPerm(view ? (ProcessInstanceConstant.FormPermClass.EDIT.equals(permSub) + ? ProcessInstanceConstant.FormPermClass.READ : permSub) : permSub); + + } + else { + itemVO.setPerm(ProcessInstanceConstant.FormPermClass.HIDE); + } + + } + l.add(subItemList); + } + formItemVO.getProps().setValue(l); + { + List subItemList = Convert.toList(FormItemVO.class, value); + for (FormItemVO itemVO : subItemList) { + + String permSub = formPerms.get(itemVO.getId()); + if (StrUtil.isNotBlank(permSub)) { + + itemVO.setPerm(permSub); + + } + else { + itemVO.setPerm(ProcessInstanceConstant.FormPermClass.HIDE); + } + + } + formItemVO.getProps().setOriForm(subItemList); + + } + + } + else { + formItemVO.getProps().setValue(paramMap.get(id)); + + } + + } + Dict set = Dict.create() + .set("processInstanceId", taskResultDto.getProcessInstanceId()) + .set("node", taskResultDto.getTaskNode()) + .set("process", oaForms.getProcess()) + .set("delegateAgain", taskResultDto.getDelegate()) + .set("delegationTask", StrUtil.equals(taskResultDto.getDelegationState(), "PENDING")) + + .set("formItems", formItemVOList); + + return R.ok(set); + } + + /** + * 完成任务 + * @param taskParamDto + * @return + */ + @Override + public R completeTask(TaskParamDto taskParamDto) { + long userId = SecurityUtils.getUser().getId(); + taskParamDto.setUserId(String.valueOf(userId)); + + R r = flowEngineService.completeTask(taskParamDto); + + if (!r.isOk()) { + return R.failed(r.getMsg()); + } + + return R.ok(); + } + + /** + * 前加签 + * @param taskParamDto + * @return + */ + @Transactional + @Override + public R delegateTask(TaskParamDto taskParamDto) { + + taskParamDto.setUserId(SecurityUtils.getUser().getId().toString()); + + R r = flowEngineService.delegateTask(taskParamDto); + if (!r.isOk()) { + return R.failed(r.getMsg()); + } + + return R.ok(); + } + + /** + * 加签完成任务 + * @param taskParamDto + * @return + */ + @Override + public R resolveTask(TaskParamDto taskParamDto) { + R r = flowEngineService.resolveTask(taskParamDto); + if (!r.isOk()) { + return R.failed(r.getMsg()); + } + + return R.ok(); + } + + /** + * 设置执行人 + * @param taskParamDto + * @return + */ + @Override + public R setAssignee(TaskParamDto taskParamDto) { + taskParamDto.setUserId(SecurityUtils.getUser().getId().toString()); + R r = flowEngineService.setAssignee(taskParamDto); + if (!r.isOk()) { + return R.failed(r.getMsg()); + } + + return R.ok(); + } + + /** + * 结束流程 + * @param taskParamDto + * @return + */ + @Override + public R stopProcessInstance(TaskParamDto taskParamDto) { + + String processInstanceId = taskParamDto.getProcessInstanceId(); + + List allStopProcessInstanceIdList = getAllStopProcessInstanceIdList(processInstanceId); + CollUtil.reverse(allStopProcessInstanceIdList); + allStopProcessInstanceIdList.add(processInstanceId); + + taskParamDto.setProcessInstanceIdList(allStopProcessInstanceIdList); + taskParamDto.setUserId(SecurityUtils.getUser().getId().toString()); + R r = flowEngineService.stopProcessInstance(taskParamDto); + + if (!r.isOk()) { + return R.failed(r.getMsg()); + } + + return R.ok(); + } + + /** + * 退回 + * @param taskParamDto + * @return + */ + @Override + public R back(TaskParamDto taskParamDto) { + taskParamDto.setUserId(SecurityUtils.getUser().getId().toString()); + R r = flowEngineService.back(taskParamDto); + if (!r.isOk()) { + return R.failed(r.getMsg()); + } + + return R.ok(); + } + + private List getAllStopProcessInstanceIdList(String processInstanceId) { + List list = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getParentProcessInstanceId, processInstanceId) + .list(); + + List collect = list.stream().map(w -> w.getProcessInstanceId()).collect(Collectors.toList()); + + for (ProcessInstanceRecord processInstanceRecord : list) { + List allStopProcessInstanceIdList = getAllStopProcessInstanceIdList( + processInstanceRecord.getProcessInstanceId()); + + collect.addAll(allStopProcessInstanceIdList); + + } + return collect; + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/utils/DataUtil.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/utils/DataUtil.java new file mode 100644 index 000000000..57d3adaed --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/utils/DataUtil.java @@ -0,0 +1,81 @@ +package com.pig4cloud.pigx.flow.task.utils; + +import cn.hutool.core.collection.CollUtil; +import com.pig4cloud.pigx.admin.api.entity.SysDept; +import com.pig4cloud.pigx.admin.api.entity.SysMenu; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class DataUtil { + + /** + * 根据当前部门id,向上查找所有的部门 + * @param deptId + * @param allDeptList + * @return 返回包括自己部门在内的所有父级部门 + */ + public static List selectParentByDept(long deptId, List allDeptList) { + List list = new ArrayList<>(); + SysDept dept = allDeptList.stream().filter(w -> w.getDeptId() == deptId).findAny().orElse(null); + if (dept == null || dept.getParentId() == null) { + return list; + } + Long parentId = dept.getParentId(); + List depts = selectParentByDept(parentId, allDeptList); + list.add(dept); + list.addAll(depts); + return list; + + } + + /** + * 根据当前部门id,向下查找所有的部门 + * @param deptId + * @param allDeptList + * @return 返回包括自己部门在内的所有子级部门 + */ + public static List selectChildrenByDept(long deptId, List allDeptList) { + List list = new ArrayList<>(); + list.add(allDeptList.stream().filter(w -> w.getDeptId() == deptId).findFirst().get()); + + List collect = allDeptList.stream() + .filter(w -> w.getParentId() == deptId) + .collect(Collectors.toList()); + if (CollUtil.isEmpty(collect)) { + return list; + } + for (SysDept dept : collect) { + List depts = selectChildrenByDept(dept.getDeptId(), allDeptList); + list.addAll(depts); + } + return list; + + } + + /** + * 根据当前菜单id,向下查找所有的菜单 + * @param menuId + * @param allMenuList + * @return 返回包括自己在内的所有子级菜单 + */ + public static List selectChildrenByMenu(long menuId, List allMenuList) { + List list = new ArrayList<>(); + list.add(allMenuList.stream().filter(w -> w.getMenuId() == menuId).findFirst().get()); + + List collect = allMenuList.stream() + .filter(w -> w.getParentId() == menuId) + .collect(Collectors.toList()); + if (CollUtil.isEmpty(collect)) { + return list; + } + for (SysMenu dept : collect) { + List depts = selectChildrenByMenu(dept.getMenuId(), allMenuList); + list.addAll(depts); + } + return list; + + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/utils/NodeFormatUtil.java b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/utils/NodeFormatUtil.java new file mode 100644 index 000000000..3de5ac969 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/java/com/pig4cloud/pigx/flow/task/utils/NodeFormatUtil.java @@ -0,0 +1,299 @@ +package com.pig4cloud.pigx.flow.task.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.admin.api.entity.SysUser; +import com.pig4cloud.pigx.admin.api.feign.RemoteUserService; +import com.pig4cloud.pigx.common.security.util.SecurityUtils; +import com.pig4cloud.pigx.flow.task.constant.NodeStatusEnum; +import com.pig4cloud.pigx.flow.task.constant.NodeTypeEnum; +import com.pig4cloud.pigx.flow.task.constant.NodeUserTypeEnum; +import com.pig4cloud.pigx.flow.task.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.task.dto.Node; +import com.pig4cloud.pigx.flow.task.dto.NodeUser; +import com.pig4cloud.pigx.flow.task.entity.ProcessInstanceRecord; +import com.pig4cloud.pigx.flow.task.entity.ProcessNodeRecordAssignUser; +import com.pig4cloud.pigx.flow.task.service.IProcessInstanceRecordService; +import com.pig4cloud.pigx.flow.task.service.IProcessNodeRecordAssignUserService; +import com.pig4cloud.pigx.flow.task.service.IRemoteService; +import com.pig4cloud.pigx.flow.task.vo.NodeVo; +import com.pig4cloud.pigx.flow.task.vo.UserVo; +import lombok.SneakyThrows; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 节点格式化显示工具 + */ +public class NodeFormatUtil { + + /** + * 格式化流程节点显示 + * @param node + * @param completeNodeSet + * @param continueNodeSet + * @param processInstanceId + * @param paramMap + */ + @SneakyThrows + public static List formatProcessNodeShow(Node node, Set completeNodeSet, + Set continueNodeSet, String processInstanceId, Map paramMap) { + List list = new ArrayList<>(); + + if (!NodeUtil.isNode(node)) { + return list; + } + + String name = node.getName(); + Integer type = node.getType(); + + // SELF_SELECT + + NodeVo nodeVo = new NodeVo(); + nodeVo.setId(node.getId()); + nodeVo.setName(name); + nodeVo.setType(type); + nodeVo.setStatus(NodeStatusEnum.WKS.getCode()); + if (completeNodeSet.contains(node.getId())) { + nodeVo.setStatus(NodeStatusEnum.YJS.getCode()); + + } + if (continueNodeSet.contains(node.getId())) { + nodeVo.setStatus(NodeStatusEnum.JXZ.getCode()); + + } + + { + + nodeVo.setPlaceholder(node.getPlaceHolder()); + + } + + List userVoList = new ArrayList<>(); + if (type == NodeTypeEnum.APPROVAL.getValue().intValue()) { + + Integer assignedType = node.getAssignedType(); + + boolean selfSelect = assignedType == ProcessInstanceConstant.AssignedTypeClass.SELF_SELECT; + nodeVo.setSelectUser(selfSelect); + if (selfSelect) { + nodeVo.setMultiple(node.getMultiple()); + } + + // 用户列表 + if (StrUtil.isNotBlank(processInstanceId)) { + IProcessNodeRecordAssignUserService processNodeRecordAssignUserService = SpringUtil + .getBean(IProcessNodeRecordAssignUserService.class); + List processNodeRecordAssignUserList = processNodeRecordAssignUserService + .lambdaQuery() + .eq(ProcessNodeRecordAssignUser::getNodeId, node.getId()) + .eq(ProcessNodeRecordAssignUser::getProcessInstanceId, processInstanceId) + .orderByAsc(ProcessNodeRecordAssignUser::getCreateTime) + .list(); + Map> map = processNodeRecordAssignUserList.stream() + .collect(Collectors.groupingBy(ProcessNodeRecordAssignUser::getTaskId)); + + for (Map.Entry> entry : map.entrySet()) { + List value = entry.getValue(); + List collect = value.stream().map(w -> { + UserVo userVo = buildUser(Long.parseLong(w.getUserId())); + userVo.setShowTime(w.getEndTime()); + userVo.setApproveDesc(w.getApproveDesc()); + userVo.setStatus(w.getStatus()); + userVo.setOperType(w.getTaskType()); + return userVo; + }).collect(Collectors.toList()); + userVoList.addAll(collect); + } + + if (processNodeRecordAssignUserList.isEmpty()) { + if (assignedType == ProcessInstanceConstant.AssignedTypeClass.SELF) { + // 发起人自己 + userVoList.addAll(CollUtil.newArrayList(buildRootUser(processInstanceId))); + } + if (assignedType == ProcessInstanceConstant.AssignedTypeClass.SELF_SELECT) { + // 发起人自选 + Object variable = paramMap.get(StrUtil.format("{}_assignee_select", node.getId())); + + ObjectMapper objectMapper = SpringUtil.getBean(ObjectMapper.class); + List nodeUserDtos = objectMapper.readValue(objectMapper.writeValueAsString(variable), + new TypeReference>() { + }); + + List collect = nodeUserDtos.stream().map(w -> Long.valueOf(w.getId())).collect(Collectors.toList()); + for (Long aLong : collect) { + UserVo userVo = buildUser(aLong); + userVoList.addAll(CollUtil.newArrayList(userVo)); + } + } + } + + } + else if (assignedType == ProcessInstanceConstant.AssignedTypeClass.USER) { + // 指定用户 + + List nodeUserList = node.getNodeUserList(); + List tempList = buildUser(nodeUserList); + userVoList.addAll(tempList); + + } + else if (assignedType == ProcessInstanceConstant.AssignedTypeClass.FORM_USER) { + // 表单人员 + String formUser = node.getFormUserId(); + + Object o = paramMap.get(formUser); + if (o != null) { + ObjectMapper objectMapper = SpringUtil.getBean(ObjectMapper.class); + String jsonString = objectMapper.writeValueAsString(o); + if (StrUtil.isNotBlank(jsonString)) { + List nodeUserDtoList = objectMapper.readValue(jsonString, new TypeReference>() { + }); + List userIdList = nodeUserDtoList.stream().map(NodeUser::getId).collect(Collectors.toList()); + for (Long aLong : userIdList) { + userVoList.addAll(CollUtil.newArrayList(buildUser(aLong))); + } + } + } + + } + else if (assignedType == ProcessInstanceConstant.AssignedTypeClass.SELF) { + // 发起人自己 + userVoList.addAll(CollUtil.newArrayList(buildUser(SecurityUtils.getUser().getId()))); + } + + } + else if (Objects.equals(node.getType(), NodeTypeEnum.ROOT.getValue())) { + // 发起节点 + if (StrUtil.isBlank(processInstanceId)) { + UserVo userVo = buildUser(SecurityUtils.getUser().getId()); + userVoList.addAll(CollUtil.newArrayList(userVo)); + } + else { + + IProcessInstanceRecordService processInstanceRecordService = SpringUtil + .getBean(IProcessInstanceRecordService.class); + ProcessInstanceRecord processInstanceRecord = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId) + .one(); + + UserVo userVo = buildRootUser(processInstanceId); + userVo.setShowTime(processInstanceRecord.getCreateTime()); + userVo.setStatus(NodeStatusEnum.YJS.getCode()); + userVoList.addAll(CollUtil.newArrayList(userVo)); + + } + } + else if (node.getType() == NodeTypeEnum.CC.getValue()) { + // 抄送节点 + + List nodeUserList = node.getNodeUserList(); + + List tempList = buildUser(nodeUserList); + userVoList.addAll(tempList); + + } + nodeVo.setUserVoList(userVoList); + + List branchList = new ArrayList<>(); + + if (type == NodeTypeEnum.EXCLUSIVE_GATEWAY.getValue().intValue() + || type == NodeTypeEnum.PARALLEL_GATEWAY.getValue().intValue()) { + // 条件分支 + List branchs = node.getConditionNodes(); + + for (Node branch : branchs) { + Node children = branch.getChildren(); + List processNodeShowDtos = formatProcessNodeShow(children, completeNodeSet, continueNodeSet, + processInstanceId, paramMap); + + NodeVo p = new NodeVo(); + p.setChildren(processNodeShowDtos); + + p.setPlaceholder(branch.getPlaceHolder()); + branchList.add(p); + } + } + nodeVo.setBranch(branchList); + + list.add(nodeVo); + + List next = formatProcessNodeShow(node.getChildren(), completeNodeSet, continueNodeSet, + processInstanceId, paramMap); + list.addAll(next); + + return list; + } + + /** + * 根据实例id + * @param processInstanceId + * @return + */ + private static UserVo buildRootUser(String processInstanceId) { + + IProcessInstanceRecordService processInstanceRecordService = SpringUtil + .getBean(IProcessInstanceRecordService.class); + ProcessInstanceRecord processInstanceRecord = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId) + .one(); + Long userId = processInstanceRecord.getUserId(); + UserVo userVo = buildUser(userId); + return userVo; + } + + /** + * 根据用户id + * @param userId + * @return + */ + private static UserVo buildUser(long userId) { + RemoteUserService userService = SpringUtil.getBean(RemoteUserService.class); + SysUser user = userService.getUserById(userId).getData(); + if (user == null) { + return null; + } + + return UserVo.builder().id(userId).name(user.getName()).avatar(user.getAvatar()).build(); + } + + private static List buildUser(List nodeUserList) { + List userVoList = new ArrayList<>(); + // 用户id + List userIdList = nodeUserList.stream() + .filter(w -> StrUtil.equals(w.getType(), NodeUserTypeEnum.USER.getKey())) + .map(w -> Convert.toLong(w.getId())) + .collect(Collectors.toList()); + // 部门id + List deptIdList = nodeUserList.stream() + .filter(w -> StrUtil.equals(w.getType(), NodeUserTypeEnum.DEPT.getKey())) + .map(w -> Convert.toLong(w.getId())) + .collect(Collectors.toList()); + + if (CollUtil.isNotEmpty(deptIdList)) { + + IRemoteService iRemoteService = SpringUtil.getBean(IRemoteService.class); + + List data = iRemoteService.queryUserIdListByDepIdList(deptIdList).getData(); + + if (CollUtil.isNotEmpty(data)) { + for (long datum : data) { + if (!userIdList.contains(datum)) { + userIdList.add(datum); + } + } + } + } + { + for (Long aLong : userIdList) { + userVoList.addAll(CollUtil.newArrayList(buildUser(aLong))); + } + } + return userVoList; + } + +} diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/resources/application.yml b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/resources/application.yml new file mode 100644 index 000000000..4373cafe4 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/resources/application.yml @@ -0,0 +1,18 @@ +server: + port: 9030 + +spring: + application: + name: @artifactId@ + cloud: + nacos: + username: @nacos.username@ + password: @nacos.password@ + discovery: + server-addr: ${NACOS_HOST:pigx-register}:${NACOS_PORT:8848} + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + config: + import: + - optional:nacos:application-@profiles.active@.yml + - optional:nacos:${spring.application.name}-@profiles.active@.yml diff --git a/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/resources/logback-spring.xml b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/resources/logback-spring.xml new file mode 100644 index 000000000..0cd6c2983 --- /dev/null +++ b/pigx-flow/pigx-flow-task/pigx-flow-task-biz/src/main/resources/logback-spring.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + + + + + + ${log.path}/debug.log + + ${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz + 50MB + 30 + + + %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n + + + + + + ${log.path}/error.log + + ${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz + 50MB + 30 + + + %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n + + + ERROR + + + + + + + + + + + + + + + + + diff --git a/pigx-flow/pigx-flow-task/pom.xml b/pigx-flow/pigx-flow-task/pom.xml new file mode 100644 index 000000000..0add965fd --- /dev/null +++ b/pigx-flow/pigx-flow-task/pom.xml @@ -0,0 +1,22 @@ + + + + 4.0.0 + + + com.pig4cloud + pigx-flow + 5.2.0-snapshot + + + pigx-flow-task + flowable 工作流业务处理模块 + pom + + + + pigx-flow-task-api + pigx-flow-task-biz + + diff --git a/pigx-flow/pom.xml b/pigx-flow/pom.xml new file mode 100644 index 000000000..83df8de50 --- /dev/null +++ b/pigx-flow/pom.xml @@ -0,0 +1,38 @@ + + + + + 4.0.0 + + com.pig4cloud + pigx + 5.2.0-snapshot + + + pigx-flow + pigx 工作流 + pom + + + pigx-flow-engine + pigx-flow-task + + diff --git a/pigx-visual/pigx-codegen/pom.xml b/pigx-visual/pigx-codegen/pom.xml index 4cb7fbb97..d4bc77e6d 100755 --- a/pigx-visual/pigx-codegen/pom.xml +++ b/pigx-visual/pigx-codegen/pom.xml @@ -16,7 +16,7 @@ 2.0.2 - 0.0.2 + 0.0.1 11.5.0.0 1.10 diff --git a/pigx-visual/pigx-report-platform/pom.xml b/pigx-visual/pigx-report-platform/pom.xml index 54dd5b33d..56a404121 100644 --- a/pigx-visual/pigx-report-platform/pom.xml +++ b/pigx-visual/pigx-report-platform/pom.xml @@ -15,7 +15,7 @@ 基于 GoView 的大屏设计系统 - 0.0.6 + 0.0.4 diff --git a/pom.xml b/pom.xml index c6950a472..097fee248 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 1.5.6 1.11.543 2.4.0 - 5.22.0 + 6.8.0 172.17.0.111 http://172.17.0.111:2375 library @@ -119,6 +119,7 @@ pigx-auth pigx-upms pigx-common + pigx-flow pigx-visual pigx-app-server