From f6e01abe0eee8de2a1c243e8894ddaf4cce6af04 Mon Sep 17 00:00:00 2001 From: zt Date: Mon, 24 Nov 2025 10:51:49 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=81=E7=A8=8B=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- xinnengyuan/pom.xml | 4 +- .../src/main/resources/application-dev.yml | 6 +- .../domain/dto/FlowInstanceBizExtDTO.java | 45 ++ .../core/domain/dto/StartProcessDTO.java | 18 + .../core/domain/dto/TaskAssigneeDTO.java | 10 +- .../core/domain/event/ProcessEvent.java | 6 + .../core/domain/event/ProcessTaskEvent.java | 11 + .../core/exception/ServiceException.java | 5 + .../common/core/service/WorkflowService.java | 10 + .../common/core/utils/StreamUtils.java | 133 ++++-- .../common/core/utils/StringUtils.java | 10 + .../ruoyi-common/ruoyi-common-doc/pom.xml | 10 + .../impl/DesTechnicalStandardServiceImpl.java | 4 +- .../service/impl/BusProjectServiceImpl.java | 2 +- .../impl/QltKnowledgeDocumentServiceImpl.java | 4 +- .../impl/HseKnowledgeDocumentServiceImpl.java | 4 +- .../dromara/system/domain/bo/SysDeptBo.java | 5 + .../dromara/system/mapper/SysDeptMapper.java | 8 + .../system/service/ISysDeptService.java | 12 + .../service/impl/SysDeptServiceImpl.java | 30 +- .../impl/SysTaskAssigneeServiceImpl.java | 221 ++++++--- .../common/constant/FlowConstant.java | 14 + .../common/enums/ButtonPermissionEnum.java | 2 +- .../common/enums/CopySettingEnum.java | 20 + .../common/enums/TaskAssigneeEnum.java | 33 +- .../workflow/common/enums/TaskStatusEnum.java | 10 + .../workflow/common/enums/VariablesEnum.java | 20 + .../controller/FlwCategoryController.java | 2 - .../controller/FlwDefinitionController.java | 70 +-- .../controller/FlwInstanceController.java | 22 + .../controller/FlwSpelController.java | 93 ++++ .../controller/FlwTaskController.java | 26 +- .../controller/TestLeaveController.java | 11 + .../workflow/domain/FlowInstanceBizExt.java | 59 +++ .../org/dromara/workflow/domain/FlowSpel.java | 69 +++ .../dromara/workflow/domain/TestLeave.java | 5 + .../workflow/domain/bo/CompleteTaskBo.java | 5 + .../workflow/domain/bo/FlowSpelBo.java | 60 +++ .../workflow/domain/bo/FlowTaskBo.java | 3 +- .../workflow/domain/bo/FlowUrgeTaskBo.java | 38 ++ .../workflow/domain/bo/FlowVariableBo.java | 39 ++ .../workflow/domain/bo/StartProcessBo.java | 19 + .../workflow/domain/bo/TestLeaveBo.java | 10 + .../workflow/domain/vo/FlowCategoryVo.java | 1 + .../workflow/domain/vo/FlowCopyVo.java | 36 ++ .../workflow/domain/vo/FlowHisTaskVo.java | 18 +- .../workflow/domain/vo/FlowInstanceVo.java | 15 +- .../workflow/domain/vo/FlowSpelVo.java | 80 ++++ .../workflow/domain/vo/FlowTaskVo.java | 32 +- .../dromara/workflow/domain/vo/NodeExtVo.java | 45 ++ .../workflow/domain/vo/TestLeaveVo.java | 7 + .../handler/FlowProcessEventHandler.java | 10 +- .../handler/WorkflowPermissionHandler.java | 16 +- .../listener/WorkflowGlobalListener.java | 151 +++--- .../workflow/mapper/FlwCategoryMapper.java | 15 - .../mapper/FlwInstanceBizExtMapper.java | 61 +++ .../workflow/mapper/FlwSpelMapper.java | 15 + .../workflow/mapper/FlwTaskMapper.java | 34 +- .../workflow/rule/SpelRuleComponent.java | 38 ++ .../workflow/service/IFlwCategoryService.java | 7 - .../workflow/service/IFlwCommonService.java | 32 +- .../service/IFlwDefinitionService.java | 21 +- .../workflow/service/IFlwInstanceService.java | 25 +- .../workflow/service/IFlwNodeExtService.java | 25 +- .../workflow/service/IFlwSpelService.java | 88 ++++ .../workflow/service/IFlwTaskService.java | 59 ++- .../workflow/service/ITestLeaveService.java | 5 + .../service/impl/FlwCategoryServiceImpl.java | 48 +- .../service/impl/FlwChartExtServiceImpl.java | 52 ++- .../service/impl/FlwCommonServiceImpl.java | 101 ++-- .../impl/FlwDefinitionServiceImpl.java | 62 +-- .../service/impl/FlwInstanceServiceImpl.java | 208 ++++++--- .../service/impl/FlwNodeExtServiceImpl.java | 297 ++++++++---- .../service/impl/FlwSpelServiceImpl.java | 190 ++++++++ .../impl/FlwTaskAssigneeServiceImpl.java | 109 +++-- .../service/impl/FlwTaskServiceImpl.java | 436 ++++++++++-------- .../service/impl/TestLeaveServiceImpl.java | 46 +- .../service/impl/WorkflowServiceImpl.java | 37 ++ .../workflow/FlwInstanceBizExtMapper.xml | 7 + .../mapper/workflow/FlwInstanceMapper.xml | 10 +- .../mapper/workflow/FlwSpelMapper.xml | 7 + .../mapper/workflow/FlwTaskMapper.xml | 197 ++++---- xinnengyuan/script/sql/flowUpdate.sql | 93 ++++ 83 files changed, 2903 insertions(+), 1021 deletions(-) create mode 100644 xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/FlowInstanceBizExtDTO.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/CopySettingEnum.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/VariablesEnum.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwSpelController.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/FlowInstanceBizExt.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/FlowSpel.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowSpelBo.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowUrgeTaskBo.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowVariableBo.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCopyVo.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowSpelVo.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/NodeExtVo.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwInstanceBizExtMapper.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwSpelMapper.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/rule/SpelRuleComponent.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwSpelService.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwInstanceBizExtMapper.xml create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwSpelMapper.xml create mode 100644 xinnengyuan/script/sql/flowUpdate.sql diff --git a/xinnengyuan/pom.xml b/xinnengyuan/pom.xml index 56b6c6d4..110f2286 100644 --- a/xinnengyuan/pom.xml +++ b/xinnengyuan/pom.xml @@ -18,7 +18,7 @@ UTF-8 21 3.5.16 - 2.8.4 + 2.3.0 0.15.0 4.0.3 2.3 @@ -49,7 +49,7 @@ 8.7.2-20250101 - 1.7.4 + 1.8.2 3.2.2 diff --git a/xinnengyuan/ruoyi-admin/src/main/resources/application-dev.yml b/xinnengyuan/ruoyi-admin/src/main/resources/application-dev.yml index d563a3a7..d0c61cf1 100644 --- a/xinnengyuan/ruoyi-admin/src/main/resources/application-dev.yml +++ b/xinnengyuan/ruoyi-admin/src/main/resources/application-dev.yml @@ -59,9 +59,9 @@ spring: driverClassName: com.mysql.cj.jdbc.Driver # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) - url: jdbc:mysql://192.168.110.2:13386/xinnengyuandev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 - username: xinnengyuandev - password: StRWCZdZirysNSs2 + url: jdbc:mysql://192.168.110.2:13386/xinnengyuandev-update?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: xinnengyuandev-update + password: cp7cAbWLZnRc6wyp # url: jdbc:mysql://192.168.110.2:13386/xinnengyuan?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 # username: xinnengyuan # password: mEZPC5Sdf3r2HENi diff --git a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/FlowInstanceBizExtDTO.java b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/FlowInstanceBizExtDTO.java new file mode 100644 index 00000000..d22937bc --- /dev/null +++ b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/FlowInstanceBizExtDTO.java @@ -0,0 +1,45 @@ +package org.dromara.common.core.domain.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 流程实例业务扩展对象 + * + * @author may + * @date 2025-08-05 + */ +@Data +public class FlowInstanceBizExtDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long id; + + /** + * 流程实例ID + */ + private Long instanceId; + + /** + * 业务ID + */ + private String businessId; + + /** + * 业务编码 + */ + private String businessCode; + + /** + * 业务标题 + */ + private String businessTitle; + +} diff --git a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/StartProcessDTO.java b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/StartProcessDTO.java index 3934ada5..fa356578 100644 --- a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/StartProcessDTO.java +++ b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/StartProcessDTO.java @@ -1,6 +1,7 @@ package org.dromara.common.core.domain.dto; +import cn.hutool.core.util.ObjectUtil; import lombok.Data; import java.io.Serial; @@ -30,11 +31,21 @@ public class StartProcessDTO implements Serializable { */ private String flowCode; + /** + * 办理人(可不填 用于覆盖当前节点办理人) + */ + private String handler; + /** * 流程变量,前端会提交一个元素{'entity': {业务详情数据对象}} */ private Map variables; + /** + * 流程业务扩展信息 + */ + private FlowInstanceBizExtDTO bizExt; + public Map getVariables() { if (variables == null) { return new HashMap<>(16); @@ -42,4 +53,11 @@ public class StartProcessDTO implements Serializable { variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); return variables; } + + public FlowInstanceBizExtDTO getBizExt() { + if (ObjectUtil.isNull(bizExt)) { + bizExt = new FlowInstanceBizExtDTO(); + } + return bizExt; + } } diff --git a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/TaskAssigneeDTO.java b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/TaskAssigneeDTO.java index 85893e1d..e7174118 100644 --- a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/TaskAssigneeDTO.java +++ b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/TaskAssigneeDTO.java @@ -52,21 +52,23 @@ public class TaskAssigneeDTO implements Serializable { */ public static List convertToHandlerList( List sourceList, - Function storageId, + Function storageId, Function handlerCode, Function handlerName, - Function groupName, + Function groupName, Function createTimeMapper) { return sourceList.stream() .map(item -> new TaskHandler( - String.valueOf(storageId.apply(item)), + storageId.apply(item), handlerCode.apply(item), handlerName.apply(item), - groupName != null ? String.valueOf(groupName.apply(item)) : null, + groupName.apply(item), createTimeMapper.apply(item) )).collect(Collectors.toList()); } + + @Data @NoArgsConstructor @AllArgsConstructor diff --git a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java index 7b15b85a..4c3c4b55 100644 --- a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java +++ b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java @@ -62,4 +62,10 @@ public class ProcessEvent implements Serializable { */ private Boolean submit; + /** + * 实例id + */ + private Long instanceId; + + } diff --git a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessTaskEvent.java b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessTaskEvent.java index 67cf7385..7a83fcd8 100644 --- a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessTaskEvent.java +++ b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessTaskEvent.java @@ -4,6 +4,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; +import java.util.Map; /** * 流程办理监听 @@ -56,4 +57,14 @@ public class ProcessTaskEvent implements Serializable { */ private String status; + /** + * 实例id + */ + private Long instanceId; + + + /** + * 办理参数 + */ + private Map params; } diff --git a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java index e9dc6ec9..df581ed7 100644 --- a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java +++ b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java @@ -1,5 +1,6 @@ package org.dromara.common.core.exception; +import cn.hutool.core.text.StrFormatter; import lombok.*; import java.io.Serial; @@ -56,4 +57,8 @@ public final class ServiceException extends RuntimeException { this.detailMessage = detailMessage; return this; } + + public ServiceException(String message, Object... args) { + this.message = StrFormatter.format(message, args); + } } diff --git a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/WorkflowService.java b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/WorkflowService.java index 9d1a9019..706d357e 100644 --- a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/WorkflowService.java +++ b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/WorkflowService.java @@ -82,6 +82,7 @@ public interface WorkflowService { * completeTask.getVariables().put("ignore", true); * * @param completeTask 参数 + * @return 结果 */ boolean completeTask(CompleteTaskDTO completeTask); @@ -90,6 +91,15 @@ public interface WorkflowService { * * @param taskId 任务ID * @param message 办理意见 + * @return 结果 */ boolean completeTask(Long taskId, String message); + + /** + * 启动流程并办理第一个任务 + * + * @param startProcess 参数 + * @return 结果 + */ + boolean startCompleteTask(StartProcessDTO startProcess); } diff --git a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StreamUtils.java b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StreamUtils.java index 1342deb7..c5487c0b 100644 --- a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StreamUtils.java +++ b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StreamUtils.java @@ -7,7 +7,6 @@ import lombok.NoArgsConstructor; import java.util.*; import java.util.function.BiFunction; -import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -31,8 +30,10 @@ public class StreamUtils { if (CollUtil.isEmpty(collection)) { return CollUtil.newArrayList(); } - // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题 - return collection.stream().filter(function).collect(Collectors.toList()); + return collection.stream() + .filter(function) + // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题 + .collect(Collectors.toList()); } /** @@ -40,13 +41,26 @@ public class StreamUtils { * * @param collection 需要查询的集合 * @param function 过滤方法 - * @return 找到符合条件的第一个元素,没有则返回null + * @return 找到符合条件的第一个元素,没有则返回 Optional.empty() */ - public static E findFirst(Collection collection, Predicate function) { + public static Optional findFirst(Collection collection, Predicate function) { if (CollUtil.isEmpty(collection)) { - return null; + return Optional.empty(); } - return collection.stream().filter(function).findFirst().orElse(null); + return collection.stream() + .filter(function) + .findFirst(); + } + + /** + * 找到流中满足条件的第一个元素值 + * + * @param collection 需要查询的集合 + * @param function 过滤方法 + * @return 找到符合条件的第一个元素,没有则返回 null + */ + public static E findFirstValue(Collection collection, Predicate function) { + return findFirst(collection,function).orElse(null); } /** @@ -54,13 +68,26 @@ public class StreamUtils { * * @param collection 需要查询的集合 * @param function 过滤方法 - * @return 找到符合条件的任意一个元素,没有则返回null + * @return 找到符合条件的任意一个元素,没有则返回 Optional.empty() */ public static Optional findAny(Collection collection, Predicate function) { if (CollUtil.isEmpty(collection)) { return Optional.empty(); } - return collection.stream().filter(function).findAny(); + return collection.stream() + .filter(function) + .findAny(); + } + + /** + * 找到流中任意一个满足条件的元素值 + * + * @param collection 需要查询的集合 + * @param function 过滤方法 + * @return 找到符合条件的任意一个元素,没有则返回null + */ + public static E findAnyValue(Collection collection, Predicate function) { + return findAny(collection,function).orElse(null); } /** @@ -86,7 +113,10 @@ public class StreamUtils { if (CollUtil.isEmpty(collection)) { return StringUtils.EMPTY; } - return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter)); + return collection.stream() + .map(function) + .filter(Objects::nonNull) + .collect(Collectors.joining(delimiter)); } /** @@ -100,8 +130,11 @@ public class StreamUtils { if (CollUtil.isEmpty(collection)) { return CollUtil.newArrayList(); } - // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题 - return collection.stream().filter(Objects::nonNull).sorted(comparing).collect(Collectors.toList()); + return collection.stream() + .filter(Objects::nonNull) + .sorted(comparing) + // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题 + .collect(Collectors.toList()); } /** @@ -118,7 +151,9 @@ public class StreamUtils { if (CollUtil.isEmpty(collection)) { return MapUtil.newHashMap(); } - return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); + return collection.stream() + .filter(Objects::nonNull) + .collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); } /** @@ -137,7 +172,25 @@ public class StreamUtils { if (CollUtil.isEmpty(collection)) { return MapUtil.newHashMap(); } - return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, value, (l, r) -> l)); + return collection.stream() + .filter(Objects::nonNull) + .collect(Collectors.toMap(key, value, (l, r) -> l)); + } + + /** + * 获取 map 中的数据作为新 Map 的 value ,key 不变 + * @param map 需要处理的map + * @param take 取值函数 + * @param map中的key类型 + * @param map中的value类型 + * @param 新map中的value类型 + * @return 新的map + */ + public static Map toMap(Map map, BiFunction take) { + if (CollUtil.isEmpty(map)) { + return MapUtil.newHashMap(); + } + return toMap(map.entrySet(), Map.Entry::getKey, entry -> take.apply(entry.getKey(), entry.getValue())); } /** @@ -154,8 +207,8 @@ public class StreamUtils { if (CollUtil.isEmpty(collection)) { return MapUtil.newHashMap(); } - return collection - .stream().filter(Objects::nonNull) + return collection.stream() + .filter(Objects::nonNull) .collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList())); } @@ -175,8 +228,8 @@ public class StreamUtils { if (CollUtil.isEmpty(collection)) { return MapUtil.newHashMap(); } - return collection - .stream().filter(Objects::nonNull) + return collection.stream() + .filter(Objects::nonNull) .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList()))); } @@ -193,11 +246,11 @@ public class StreamUtils { * @return 分类后的map */ public static Map> group2Map(Collection collection, Function key1, Function key2) { - if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) { + if (CollUtil.isEmpty(collection)) { return MapUtil.newHashMap(); } - return collection - .stream().filter(Objects::nonNull) + return collection.stream() + .filter(Objects::nonNull) .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l))); } @@ -215,8 +268,7 @@ public class StreamUtils { if (CollUtil.isEmpty(collection)) { return CollUtil.newArrayList(); } - return collection - .stream() + return collection.stream() .map(function) .filter(Objects::nonNull) // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题 @@ -234,11 +286,10 @@ public class StreamUtils { * @return 转化后的Set */ public static Set toSet(Collection collection, Function function) { - if (CollUtil.isEmpty(collection) || function == null) { + if (CollUtil.isEmpty(collection)) { return CollUtil.newHashSet(); } - return collection - .stream() + return collection.stream() .map(function) .filter(Objects::nonNull) .collect(Collectors.toSet()); @@ -258,26 +309,20 @@ public class StreamUtils { * @return 合并后的map */ public static Map merge(Map map1, Map map2, BiFunction merge) { - if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) { + if (CollUtil.isEmpty(map1) && CollUtil.isEmpty(map2)) { + // 如果两个 map 都为空,则直接返回空的 map return MapUtil.newHashMap(); - } else if (MapUtil.isEmpty(map1)) { - map1 = MapUtil.newHashMap(); - } else if (MapUtil.isEmpty(map2)) { - map2 = MapUtil.newHashMap(); + } else if (CollUtil.isEmpty(map1)) { + // 如果 map1 为空,则直接处理返回 map2 + return toMap(map2.entrySet(), Map.Entry::getKey, entry -> merge.apply(null, entry.getValue())); + } else if (CollUtil.isEmpty(map2)) { + // 如果 map2 为空,则直接处理返回 map1 + return toMap(map1.entrySet(), Map.Entry::getKey, entry -> merge.apply(entry.getValue(), null)); } - Set key = new HashSet<>(); - key.addAll(map1.keySet()); - key.addAll(map2.keySet()); - Map map = new HashMap<>(); - for (K t : key) { - X x = map1.get(t); - Y y = map2.get(t); - V z = merge.apply(x, y); - if (z != null) { - map.put(t, z); - } - } - return map; + Set keySet = new HashSet<>(); + keySet.addAll(map1.keySet()); + keySet.addAll(map2.keySet()); + return toMap(keySet, key -> key, key -> merge.apply(map1.get(key), map2.get(key))); } } diff --git a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java index 21546b8b..e69222bf 100644 --- a/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java +++ b/xinnengyuan/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java @@ -351,4 +351,14 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils { return noSpaces.matches("\\d+"); } + /** + * 将可迭代对象中的元素使用逗号拼接成字符串 + * + * @param iterable 可迭代对象,如 List、Set 等 + * @return 拼接后的字符串 + */ + public static String joinComma(Iterable iterable) { + return StringUtils.join(iterable, SEPARATOR); + } + } diff --git a/xinnengyuan/ruoyi-common/ruoyi-common-doc/pom.xml b/xinnengyuan/ruoyi-common/ruoyi-common-doc/pom.xml index 7d207ae9..d40248a8 100644 --- a/xinnengyuan/ruoyi-common/ruoyi-common-doc/pom.xml +++ b/xinnengyuan/ruoyi-common/ruoyi-common-doc/pom.xml @@ -45,6 +45,16 @@ com.github.xiaoymin knife4j-openapi3-jakarta-spring-boot-starter 4.5.0 + + + org.springdoc + springdoc-openapi-starter-webmvc-api + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/service/impl/DesTechnicalStandardServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/service/impl/DesTechnicalStandardServiceImpl.java index 850cac81..fd870fd9 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/service/impl/DesTechnicalStandardServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/service/impl/DesTechnicalStandardServiceImpl.java @@ -384,13 +384,13 @@ public class DesTechnicalStandardServiceImpl extends ServiceImpl> treeList = CollUtil.newArrayList(); for (DesTechnicalStandard d : documentList) { Long parentId = d.getPid(); - DesTechnicalStandard document = StreamUtils.findFirst(documentList, it -> Objects.equals(it.getId(), parentId)); + DesTechnicalStandard document = StreamUtils.findFirst(documentList, it -> Objects.equals(it.getId(), parentId)).orElse(null); if (ObjectUtil.isNull(document)) { List> trees = TreeBuildUtils.build(documentList, parentId, (desTechnicalStandard, tree) -> tree.setId(desTechnicalStandard.getId()) .setParentId(desTechnicalStandard.getPid()) .setName(desTechnicalStandard.getFileName())); - Tree tree = StreamUtils.findFirst(trees, it -> Objects.equals(it.getId(), d.getId())); + Tree tree = StreamUtils.findFirst(trees, it -> Objects.equals(it.getId(), d.getId())).orElse(null); treeList.add(tree); } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectServiceImpl.java index ed1342ca..e42b35c2 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectServiceImpl.java @@ -524,7 +524,7 @@ public class BusProjectServiceImpl extends ServiceImpl> treeList = CollUtil.newArrayList(); for (QltKnowledgeDocument d : documentList) { Long parentId = d.getPid(); - QltKnowledgeDocument document = StreamUtils.findFirst(documentList, it -> Objects.equals(it.getId(), parentId)); + QltKnowledgeDocument document = StreamUtils.findFirst(documentList, it -> Objects.equals(it.getId(), parentId)).orElse(null); if (ObjectUtil.isNull(document)) { List> trees = TreeBuildUtils.build(documentList, parentId, (knowledgeDocument, tree) -> tree.setId(knowledgeDocument.getId()) .setParentId(knowledgeDocument.getPid()) .setName(knowledgeDocument.getFileName())); - Tree tree = StreamUtils.findFirst(trees, it -> Objects.equals(it.getId(), d.getId())); + Tree tree = StreamUtils.findFirst(trees, it -> Objects.equals(it.getId(), d.getId())).orElse(null); treeList.add(tree); } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/HseKnowledgeDocumentServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/HseKnowledgeDocumentServiceImpl.java index 8ae62026..6befe51e 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/HseKnowledgeDocumentServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/HseKnowledgeDocumentServiceImpl.java @@ -377,13 +377,13 @@ public class HseKnowledgeDocumentServiceImpl extends ServiceImpl> treeList = CollUtil.newArrayList(); for (HseKnowledgeDocument d : documentList) { Long parentId = d.getPid(); - HseKnowledgeDocument document = StreamUtils.findFirst(documentList, it -> Objects.equals(it.getId(), parentId)); + HseKnowledgeDocument document = StreamUtils.findFirst(documentList, it -> Objects.equals(it.getId(), parentId)).orElse(null); if (ObjectUtil.isNull(document)) { List> trees = TreeBuildUtils.build(documentList, parentId, (knowledgeDocument, tree) -> tree.setId(knowledgeDocument.getId()) .setParentId(knowledgeDocument.getPid()) .setName(knowledgeDocument.getFileName())); - Tree tree = StreamUtils.findFirst(trees, it -> Objects.equals(it.getId(), d.getId())); + Tree tree = StreamUtils.findFirst(trees, it -> Objects.equals(it.getId(), d.getId())).orElse(null); treeList.add(tree); } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDeptBo.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDeptBo.java index ca47c6c3..e4f64a4a 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDeptBo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDeptBo.java @@ -91,4 +91,9 @@ public class SysDeptBo extends BaseEntity { private List deptTypes; + + /** + * 归属部门id(部门树) + */ + private Long belongDeptId; } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java index a2d32532..f9f1ef12 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java @@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; +import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; @@ -123,4 +124,11 @@ public interface SysDeptMapper extends BaseMapperPlus { */ SysDeptBo selectDeptByIdBo(@Param("deptId") Long deptId); + + default List selectDeptAndChildById(Long parentId) { + List deptList = this.selectListByParentId(parentId); + List deptIds = StreamUtils.toList(deptList, SysDept::getDeptId); + deptIds.add(parentId); + return deptIds; + } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java index 82e1b0ee..263b3a13 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java @@ -1,6 +1,8 @@ package org.dromara.system.service; import cn.hutool.core.lang.tree.Tree; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.system.domain.SysDept; import org.dromara.system.domain.bo.SysDeptBo; import org.dromara.system.domain.vo.SysDeptVo; @@ -167,4 +169,14 @@ public interface ISysDeptService { List querListDept(); String selectDeptNameById(Long id); + + + /** + * 分页查询部门管理数据 + * + * @param dept 部门信息 + * @param pageQuery 分页对象 + * @return 部门信息集合 + */ + TableDataInfo selectPageDeptList(SysDeptBo dept, PageQuery pageQuery); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java index 2153b337..ab1ef256 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java @@ -9,6 +9,7 @@ import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.CacheNames; @@ -18,6 +19,8 @@ import org.dromara.common.core.domain.dto.DeptDTO; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.service.DeptService; import org.dromara.common.core.utils.*; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.helper.DataBaseHelper; import org.dromara.common.redis.utils.CacheUtils; import org.dromara.common.satoken.utils.LoginHelper; @@ -88,6 +91,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { } private LambdaQueryWrapper buildQueryWrapper(SysDeptBo bo) { + Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(SysDept::getDelFlag, SystemConstants.NORMAL); lqw.eq(ObjectUtil.isNotNull(bo.getDeptId()), SysDept::getDeptId, bo.getDeptId()); @@ -95,14 +99,19 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { lqw.like(StringUtils.isNotBlank(bo.getDeptName()), SysDept::getDeptName, bo.getDeptName()); lqw.like(StringUtils.isNotBlank(bo.getDeptCategory()), SysDept::getDeptCategory, bo.getDeptCategory()); lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysDept::getStatus, bo.getStatus()); - lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysDept::getStatus, bo.getStatus()); - lqw.eq(StringUtils.isNotBlank(bo.getIsShow()), SysDept::getIsShow, bo.getIsShow()); - lqw.eq(StringUtils.isNotBlank(bo.getDeptType()), SysDept::getDeptType, bo.getDeptType()); - lqw.in(CollectionUtil.isNotEmpty(bo.getDeptTypes()), SysDept::getDeptType, bo.getDeptTypes()); + lqw.between(params.get("beginTime") != null && params.get("endTime") != null, + SysDept::getCreateTime, params.get("beginTime"), params.get("endTime")); lqw.orderByAsc(SysDept::getAncestors); lqw.orderByAsc(SysDept::getParentId); lqw.orderByAsc(SysDept::getOrderNum); lqw.orderByAsc(SysDept::getDeptId); + if (ObjectUtil.isNotNull(bo.getBelongDeptId())) { + //部门树搜索 + lqw.and(x -> { + List deptIds = baseMapper.selectDeptAndChildById(bo.getBelongDeptId()); + x.in(SysDept::getDeptId, deptIds); + }); + } return lqw; } @@ -121,7 +130,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { List> treeList = CollUtil.newArrayList(); for (SysDeptVo d : depts) { Long parentId = d.getParentId(); - SysDeptVo sysDeptVo = StreamUtils.findFirst(depts, it -> it.getDeptId().longValue() == parentId); + SysDeptVo sysDeptVo = StreamUtils.findFirst(depts, it -> it.getDeptId().longValue() == parentId).orElse(null); if (ObjectUtil.isNull(sysDeptVo)) { List> trees = TreeBuildUtils.build(depts, parentId, (dept, tree) -> { tree.setId(dept.getDeptId()) @@ -132,7 +141,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { tree.putExtra("deptType", dept.getDeptType()); } ); - Tree tree = StreamUtils.findFirst(trees, it -> it.getId().longValue() == d.getDeptId()); + Tree tree = StreamUtils.findFirst(trees, it -> it.getId().longValue() == d.getDeptId()).orElse(null); treeList.add(tree); } } @@ -167,7 +176,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { List> treeList = CollUtil.newArrayList(); for (SysDeptVo d : deptVoList) { Long parentId = d.getParentId(); - SysDeptVo sysDeptVo = StreamUtils.findFirst(deptVoList, it -> it.getDeptId().longValue() == parentId); + SysDeptVo sysDeptVo = StreamUtils.findFirst(deptVoList, it -> it.getDeptId().longValue() == parentId).orElse(null); if (ObjectUtil.isNull(sysDeptVo)) { List> trees = TreeBuildUtils.build(deptVoList, parentId, (deptVo, tree) -> { Long deptId = deptVo.getDeptId(); @@ -181,7 +190,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { tree.putExtra("roleVoList", roleVoMap.get(deptId)); } ); - Tree tree = StreamUtils.findFirst(trees, it -> it.getId().longValue() == d.getDeptId()); + Tree tree = StreamUtils.findFirst(trees, it -> it.getId().longValue() == d.getDeptId()).orElse(null); treeList.add(tree); } } @@ -595,4 +604,9 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { return new ArrayList<>(deptIds); } + @Override + public TableDataInfo selectPageDeptList(SysDeptBo dept, PageQuery pageQuery) { + Page page = baseMapper.selectPageDeptList(pageQuery.build(), buildQueryWrapper(dept)); + return TableDataInfo.build(page); + } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaskAssigneeServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaskAssigneeServiceImpl.java index 23dd0520..0c5ee700 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaskAssigneeServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaskAssigneeServiceImpl.java @@ -1,5 +1,6 @@ package org.dromara.system.service.impl; +import cn.hutool.core.convert.Convert; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; @@ -12,10 +13,15 @@ import org.dromara.common.core.service.TaskAssigneeService; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.system.domain.SysDept; import org.dromara.system.domain.SysPost; import org.dromara.system.domain.SysRole; import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.bo.SysDeptBo; +import org.dromara.system.domain.bo.SysPostBo; +import org.dromara.system.domain.bo.SysRoleBo; +import org.dromara.system.domain.bo.SysUserBo; import org.dromara.system.domain.vo.SysDeptVo; import org.dromara.system.domain.vo.SysPostVo; import org.dromara.system.domain.vo.SysRoleVo; @@ -24,9 +30,14 @@ import org.dromara.system.mapper.SysDeptMapper; import org.dromara.system.mapper.SysPostMapper; import org.dromara.system.mapper.SysRoleMapper; import org.dromara.system.mapper.SysUserMapper; +import org.dromara.system.service.ISysDeptService; +import org.dromara.system.service.ISysPostService; +import org.dromara.system.service.ISysRoleService; +import org.dromara.system.service.ISysUserService; import org.springframework.stereotype.Service; import java.util.List; +import java.util.Map; /** * 工作流设计器获取任务执行人 @@ -37,10 +48,15 @@ import java.util.List; @Service public class SysTaskAssigneeServiceImpl implements TaskAssigneeService { - private final SysPostMapper postMapper; - private final SysDeptMapper deptMapper; - private final SysUserMapper userMapper; - private final SysRoleMapper roleMapper; +// private final SysPostMapper postMapper; +// private final SysDeptMapper deptMapper; +// private final SysUserMapper userMapper; +// private final SysRoleMapper roleMapper; + private final ISysPostService postService; + private final ISysDeptService deptService; + private final ISysUserService userService; + private final ISysRoleService roleService; + /** * 查询角色并返回任务指派的列表,支持分页 @@ -50,18 +66,31 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService { */ @Override public TaskAssigneeDTO selectRolesByTaskAssigneeList(TaskAssigneeBody taskQuery) { +// PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); +// QueryWrapper wrapper = Wrappers.query(); +// wrapper.eq("r.del_flag", SystemConstants.NORMAL) +// .like(StringUtils.isNotBlank(taskQuery.getHandlerCode()), "r.role_name", taskQuery.getHandlerCode()) +// .like(StringUtils.isNotBlank(taskQuery.getHandlerName()), "r.role_key", taskQuery.getHandlerName()) +// .between(StringUtils.isNotBlank(taskQuery.getBeginTime()) && StringUtils.isNotBlank(taskQuery.getEndTime()), +// "r.create_time", taskQuery.getBeginTime(), taskQuery.getEndTime()) +// .orderByAsc("r.role_sort").orderByAsc("r.create_time"); +// Page page = roleMapper.selectPageRoleList(pageQuery.build(), wrapper); +// // 使用封装的字段映射方法进行转换 +// List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRecords(), +// item -> Convert.toStr(item.getPostId()), SysRoleVo::getRoleKey, SysRoleVo::getRoleName, SysPostVo::getPostName, item -> Convert.toStr(item.getDeptId()), SysRoleVo::getCreateTime); +// return new TaskAssigneeDTO(page.getTotal(), handlers); PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); - QueryWrapper wrapper = Wrappers.query(); - wrapper.eq("r.del_flag", SystemConstants.NORMAL) - .like(StringUtils.isNotBlank(taskQuery.getHandlerCode()), "r.role_name", taskQuery.getHandlerCode()) - .like(StringUtils.isNotBlank(taskQuery.getHandlerName()), "r.role_key", taskQuery.getHandlerName()) - .between(StringUtils.isNotBlank(taskQuery.getBeginTime()) && StringUtils.isNotBlank(taskQuery.getEndTime()), - "r.create_time", taskQuery.getBeginTime(), taskQuery.getEndTime()) - .orderByAsc("r.role_sort").orderByAsc("r.create_time"); - Page page = roleMapper.selectPageRoleList(pageQuery.build(), wrapper); + SysRoleBo bo = new SysRoleBo(); + bo.setRoleKey(taskQuery.getHandlerCode()); + bo.setRoleName(taskQuery.getHandlerName()); + bo.setStatus(SystemConstants.NORMAL); + Map params = bo.getParams(); + params.put("beginTime", taskQuery.getBeginTime()); + params.put("endTime", taskQuery.getEndTime()); + TableDataInfo page = roleService.selectPageRoleList(bo, pageQuery); // 使用封装的字段映射方法进行转换 - List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRecords(), - SysRoleVo::getRoleId, SysRoleVo::getRoleKey, SysRoleVo::getRoleName, null, SysRoleVo::getCreateTime); + List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRows(), + item -> Convert.toStr(item.getRoleId()), SysRoleVo::getRoleKey, SysRoleVo::getRoleName, item -> "", SysRoleVo::getCreateTime); return new TaskAssigneeDTO(page.getTotal(), handlers); } @@ -73,25 +102,40 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService { */ @Override public TaskAssigneeDTO selectPostsByTaskAssigneeList(TaskAssigneeBody taskQuery) { +// PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); +// LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() +// .like(StringUtils.isNotBlank(taskQuery.getHandlerCode()), SysPost::getPostCategory, taskQuery.getHandlerCode()) +// .like(StringUtils.isNotBlank(taskQuery.getHandlerName()), SysPost::getPostName, taskQuery.getHandlerName()) +// .between(StringUtils.isNotBlank(taskQuery.getBeginTime()) && StringUtils.isNotBlank(taskQuery.getEndTime()), +// SysPost::getCreateTime, taskQuery.getBeginTime(), taskQuery.getEndTime()); +// if (StringUtils.isNotBlank(taskQuery.getGroupId())) { +// Long belongDeptId = Long.valueOf(taskQuery.getGroupId()); +// wrapper.and(x -> { +// List deptList = deptMapper.selectListByParentId(belongDeptId); +// List deptIds = StreamUtils.toList(deptList, SysDept::getDeptId); +// deptIds.add(belongDeptId); +// x.in(SysPost::getDeptId, deptIds); +// }); +// } +// Page page = postMapper.selectPagePostList(pageQuery.build(), wrapper); +// // 使用封装的字段映射方法进行转换 +// List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRecords(), +// SysPostVo::getPostId, SysPostVo::getPostCategory, SysPostVo::getPostName, SysPostVo::getDeptId, SysPostVo::getCreateTime); +// return new TaskAssigneeDTO(page.getTotal(), handlers); + PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); - LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() - .like(StringUtils.isNotBlank(taskQuery.getHandlerCode()), SysPost::getPostCategory, taskQuery.getHandlerCode()) - .like(StringUtils.isNotBlank(taskQuery.getHandlerName()), SysPost::getPostName, taskQuery.getHandlerName()) - .between(StringUtils.isNotBlank(taskQuery.getBeginTime()) && StringUtils.isNotBlank(taskQuery.getEndTime()), - SysPost::getCreateTime, taskQuery.getBeginTime(), taskQuery.getEndTime()); - if (StringUtils.isNotBlank(taskQuery.getGroupId())) { - Long belongDeptId = Long.valueOf(taskQuery.getGroupId()); - wrapper.and(x -> { - List deptList = deptMapper.selectListByParentId(belongDeptId); - List deptIds = StreamUtils.toList(deptList, SysDept::getDeptId); - deptIds.add(belongDeptId); - x.in(SysPost::getDeptId, deptIds); - }); - } - Page page = postMapper.selectPagePostList(pageQuery.build(), wrapper); + SysPostBo bo = new SysPostBo(); + bo.setPostCategory(taskQuery.getHandlerCode()); + bo.setPostName(taskQuery.getHandlerName()); + bo.setStatus(SystemConstants.NORMAL); + Map params = bo.getParams(); + params.put("beginTime", taskQuery.getBeginTime()); + params.put("endTime", taskQuery.getEndTime()); + bo.setBelongDeptId(Convert.toLong(taskQuery.getGroupId())); + TableDataInfo page = postService.selectPagePostList(bo, pageQuery); // 使用封装的字段映射方法进行转换 - List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRecords(), - SysPostVo::getPostId, SysPostVo::getPostCategory, SysPostVo::getPostName, SysPostVo::getDeptId, SysPostVo::getCreateTime); + List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRows(), + item -> Convert.toStr(item.getPostId()), SysPostVo::getPostCategory, SysPostVo::getPostName, item -> Convert.toStr(item.getDeptId()), SysPostVo::getCreateTime); return new TaskAssigneeDTO(page.getTotal(), handlers); } @@ -103,31 +147,45 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService { */ @Override public TaskAssigneeDTO selectDeptsByTaskAssigneeList(TaskAssigneeBody taskQuery) { +// PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); +// LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() +// .eq(SysDept::getDelFlag, SystemConstants.NORMAL) +// .like(StringUtils.isNotBlank(taskQuery.getHandlerCode()), SysDept::getDeptCategory, taskQuery.getHandlerCode()) +// .like(StringUtils.isNotBlank(taskQuery.getHandlerName()), SysDept::getDeptName, taskQuery.getHandlerName()) +// .between(StringUtils.isNotBlank(taskQuery.getBeginTime()) && StringUtils.isNotBlank(taskQuery.getEndTime()), +// SysDept::getCreateTime, taskQuery.getBeginTime(), taskQuery.getEndTime()) +// .orderByAsc(SysDept::getAncestors) +// .orderByAsc(SysDept::getParentId) +// .orderByAsc(SysDept::getOrderNum) +// .orderByAsc(SysDept::getDeptId); +// if (StringUtils.isNotBlank(taskQuery.getGroupId())) { +// //部门树搜索 +// wrapper.and(x -> { +// Long parentId = Long.valueOf(taskQuery.getGroupId()); +// List deptList = deptMapper.selectListByParentId(parentId); +// List deptIds = StreamUtils.toList(deptList, SysDept::getDeptId); +// deptIds.add(parentId); +// x.in(SysDept::getDeptId, deptIds); +// }); +// } +// Page page = deptMapper.selectPageDeptList(pageQuery.build(), wrapper); +// // 使用封装的字段映射方法进行转换 +// List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRecords(), +// SysDeptVo::getDeptId, SysDeptVo::getDeptCategory, SysDeptVo::getDeptName, SysDeptVo::getParentId, SysDeptVo::getCreateTime); +// return new TaskAssigneeDTO(page.getTotal(), handlers); PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); - LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() - .eq(SysDept::getDelFlag, SystemConstants.NORMAL) - .like(StringUtils.isNotBlank(taskQuery.getHandlerCode()), SysDept::getDeptCategory, taskQuery.getHandlerCode()) - .like(StringUtils.isNotBlank(taskQuery.getHandlerName()), SysDept::getDeptName, taskQuery.getHandlerName()) - .between(StringUtils.isNotBlank(taskQuery.getBeginTime()) && StringUtils.isNotBlank(taskQuery.getEndTime()), - SysDept::getCreateTime, taskQuery.getBeginTime(), taskQuery.getEndTime()) - .orderByAsc(SysDept::getAncestors) - .orderByAsc(SysDept::getParentId) - .orderByAsc(SysDept::getOrderNum) - .orderByAsc(SysDept::getDeptId); - if (StringUtils.isNotBlank(taskQuery.getGroupId())) { - //部门树搜索 - wrapper.and(x -> { - Long parentId = Long.valueOf(taskQuery.getGroupId()); - List deptList = deptMapper.selectListByParentId(parentId); - List deptIds = StreamUtils.toList(deptList, SysDept::getDeptId); - deptIds.add(parentId); - x.in(SysDept::getDeptId, deptIds); - }); - } - Page page = deptMapper.selectPageDeptList(pageQuery.build(), wrapper); + SysDeptBo bo = new SysDeptBo(); + bo.setDeptCategory(taskQuery.getHandlerCode()); + bo.setDeptName(taskQuery.getHandlerName()); + bo.setStatus(SystemConstants.NORMAL); + Map params = bo.getParams(); + params.put("beginTime", taskQuery.getBeginTime()); + params.put("endTime", taskQuery.getEndTime()); + bo.setBelongDeptId(Convert.toLong(taskQuery.getGroupId())); + TableDataInfo page = deptService.selectPageDeptList(bo, pageQuery); // 使用封装的字段映射方法进行转换 - List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRecords(), - SysDeptVo::getDeptId, SysDeptVo::getDeptCategory, SysDeptVo::getDeptName, SysDeptVo::getParentId, SysDeptVo::getCreateTime); + List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRows(), + item -> Convert.toStr(item.getDeptId()), SysDeptVo::getDeptCategory, SysDeptVo::getDeptName, item -> Convert.toStr(item.getParentId()), SysDeptVo::getCreateTime); return new TaskAssigneeDTO(page.getTotal(), handlers); } @@ -140,28 +198,43 @@ public class SysTaskAssigneeServiceImpl implements TaskAssigneeService { */ @Override public TaskAssigneeDTO selectUsersByTaskAssigneeList(TaskAssigneeBody taskQuery) { +// PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); +// QueryWrapper wrapper = Wrappers.query(); +// wrapper.eq("u.del_flag", SystemConstants.NORMAL) +// .like(StringUtils.isNotBlank(taskQuery.getHandlerCode()), "u.user_name", taskQuery.getHandlerCode()) +// .like(StringUtils.isNotBlank(taskQuery.getHandlerName()), "u.nick_name", taskQuery.getHandlerName()) +// .between(taskQuery.getBeginTime() != null && taskQuery.getEndTime() != null, +// "u.create_time", taskQuery.getBeginTime(), taskQuery.getEndTime()) +// .orderByAsc("u.user_id"); +// if (StringUtils.isNotBlank(taskQuery.getGroupId())) { +// //部门树搜索 +// wrapper.and(x -> { +// Long parentId = Long.valueOf(taskQuery.getGroupId()); +// List deptList = deptMapper.selectListByParentId(parentId); +// List deptIds = StreamUtils.toList(deptList, SysDept::getDeptId); +// deptIds.add(parentId); +// x.in("u.dept_id", deptIds); +// }); +// } +// Page page = userMapper.selectPageUserList(pageQuery.build(), wrapper); +// // 使用封装的字段映射方法进行转换 +// List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRecords(), +// SysUserVo::getUserId, SysUserVo::getUserName, SysUserVo::getNickName, SysUserVo::getDeptId, SysUserVo::getCreateTime); +// return new TaskAssigneeDTO(page.getTotal(), handlers); + PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); - QueryWrapper wrapper = Wrappers.query(); - wrapper.eq("u.del_flag", SystemConstants.NORMAL) - .like(StringUtils.isNotBlank(taskQuery.getHandlerCode()), "u.user_name", taskQuery.getHandlerCode()) - .like(StringUtils.isNotBlank(taskQuery.getHandlerName()), "u.nick_name", taskQuery.getHandlerName()) - .between(taskQuery.getBeginTime() != null && taskQuery.getEndTime() != null, - "u.create_time", taskQuery.getBeginTime(), taskQuery.getEndTime()) - .orderByAsc("u.user_id"); - if (StringUtils.isNotBlank(taskQuery.getGroupId())) { - //部门树搜索 - wrapper.and(x -> { - Long parentId = Long.valueOf(taskQuery.getGroupId()); - List deptList = deptMapper.selectListByParentId(parentId); - List deptIds = StreamUtils.toList(deptList, SysDept::getDeptId); - deptIds.add(parentId); - x.in("u.dept_id", deptIds); - }); - } - Page page = userMapper.selectPageUserList(pageQuery.build(), wrapper); + SysUserBo bo = new SysUserBo(); + bo.setUserName(taskQuery.getHandlerCode()); + bo.setNickName(taskQuery.getHandlerName()); + bo.setStatus(SystemConstants.NORMAL); + Map params = bo.getParams(); + params.put("beginTime", taskQuery.getBeginTime()); + params.put("endTime", taskQuery.getEndTime()); + bo.setDeptId(Convert.toLong(taskQuery.getGroupId())); + TableDataInfo page = userService.selectPageUserList(bo, pageQuery); // 使用封装的字段映射方法进行转换 - List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRecords(), - SysUserVo::getUserId, SysUserVo::getUserName, SysUserVo::getNickName, SysUserVo::getDeptId, SysUserVo::getCreateTime); + List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRows(), + item -> Convert.toStr(item.getUserId()), SysUserVo::getUserName, SysUserVo::getNickName, item -> Convert.toStr(item.getDeptId()), SysUserVo::getCreateTime); return new TaskAssigneeDTO(page.getTotal(), handlers); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java index aaa640bf..cacb2a25 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java @@ -18,6 +18,11 @@ public interface FlowConstant { */ String BUSINESS_ID = "businessId"; + /** + * 部门id + */ + String INITIATOR_DEPT_ID = "initiatorDeptId"; + /** * 委托 */ @@ -78,4 +83,13 @@ public interface FlowConstant { */ String WF_TASK_STATUS = "wf_task_status"; + /** + * 自动通过 + */ + String AUTO_PASS = "autoPass"; + + /** + * 业务编码 + */ + String BUSINESS_CODE = "businessCode"; } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/ButtonPermissionEnum.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/ButtonPermissionEnum.java index 598cd05c..3ad9cf9e 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/ButtonPermissionEnum.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/ButtonPermissionEnum.java @@ -30,7 +30,7 @@ public enum ButtonPermissionEnum implements NodeExtEnum { /** * 是否能抄送 */ - COPY("是否能抄送", "copy", false), + COPY("是否能抄送", "copy", true), /** * 是否显示退回 diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/CopySettingEnum.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/CopySettingEnum.java new file mode 100644 index 00000000..a74af3b2 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/CopySettingEnum.java @@ -0,0 +1,20 @@ +package org.dromara.workflow.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 抄送设置枚举 + * + * @author AprilWind + */ +@Getter +@AllArgsConstructor +public enum CopySettingEnum implements NodeExtEnum { + ; + private final String label; + private final String value; + private final boolean selected; + +} + diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskAssigneeEnum.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskAssigneeEnum.java index 60be92fe..fff26883 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskAssigneeEnum.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskAssigneeEnum.java @@ -3,6 +3,7 @@ package org.dromara.workflow.common.enums; import lombok.AllArgsConstructor; import lombok.Getter; import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; import java.util.Arrays; import java.util.List; @@ -35,7 +36,12 @@ public enum TaskAssigneeEnum { /** * 岗位 */ - POST("岗位", "post:"); + POST("岗位", "post:"), + + /** + * SPEL表达式 + */ + SPEL("SpEL表达式", ""); private final String desc; private final String code; @@ -105,5 +111,30 @@ public enum TaskAssigneeEnum { .map(TaskAssigneeEnum::getCode) .collect(Collectors.toList()); } + + /** + * 判断当前办理人类型是否需要调用部门服务(deptService) + * + * @return 如果类型是 USER、DEPT 或 POST,则返回 true;否则返回 false + */ + public boolean needsDeptService() { + return this == USER || this == DEPT || this == POST; + } + + /** + * 判断给定字符串是否符合 SPEL 表达式格式(以 $ 或 # 开头) + * + * @param value 待判断字符串 + * @return 是否为 SPEL 表达式 + */ + public static boolean isSpelExpression(String value) { + if (value == null) { + return false; + } + // $前缀表示默认办理人变量策略 + // #前缀表示spel办理人变量策略 + return StringUtils.startsWith(value, "$") || StringUtils.startsWith(value, "#"); + } + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java index d18ebb01..c7bced89 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java @@ -100,5 +100,15 @@ public enum TaskStatusEnum { return STATUS_DESC_MAP.getOrDefault(status, StrUtil.EMPTY); } + /** + * 判断状态是否为通过或退回 + * + * @param status 状态值 + * @return true 表示是通过或退回状态 + */ + public static boolean isPassOrBack(String status) { + return PASS.getStatus().equals(status) || BACK.getStatus().equals(status); + } + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/VariablesEnum.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/VariablesEnum.java new file mode 100644 index 00000000..dbd54ed5 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/VariablesEnum.java @@ -0,0 +1,20 @@ +package org.dromara.workflow.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 变量枚举 + * + * @author AprilWind + */ +@Getter +@AllArgsConstructor +public enum VariablesEnum implements NodeExtEnum { + ; + private final String label; + private final String value; + private final boolean selected; + +} + diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwCategoryController.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwCategoryController.java index 3018b084..3007921d 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwCategoryController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwCategoryController.java @@ -66,7 +66,6 @@ public class FlwCategoryController extends BaseController { @SaCheckPermission("workflow:category:query") @GetMapping("/{categoryId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long categoryId) { - flwCategoryService.checkCategoryDataScope(categoryId); return R.ok(flwCategoryService.queryById(categoryId)); } @@ -93,7 +92,6 @@ public class FlwCategoryController extends BaseController { @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody FlowCategoryBo category) { Long categoryId = category.getCategoryId(); - flwCategoryService.checkCategoryDataScope(categoryId); if (!flwCategoryService.checkCategoryNameUnique(category)) { return R.fail("修改流程分类'" + category.getCategoryName() + "'失败,流程分类名称已存在"); } else if (category.getParentId().equals(categoryId)) { diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwDefinitionController.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwDefinitionController.java index 8b9afe49..10d9de83 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwDefinitionController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwDefinitionController.java @@ -9,15 +9,10 @@ import org.dromara.common.log.enums.BusinessType; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.web.core.BaseController; -import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.entity.Definition; -import org.dromara.warm.flow.core.entity.Node; -import org.dromara.warm.flow.core.entity.Skip; import org.dromara.warm.flow.core.service.DefService; -import org.dromara.warm.flow.core.utils.AssertUtil; import org.dromara.warm.flow.orm.entity.FlowDefinition; import org.dromara.workflow.common.ConditionalOnEnable; -import org.dromara.workflow.domain.bo.FlowDefinitionBo; import org.dromara.workflow.domain.vo.FlowDefinitionVo; import org.dromara.workflow.service.IFlwDefinitionService; import org.springframework.transaction.annotation.Transactional; @@ -27,7 +22,6 @@ import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.util.List; -import java.util.stream.Collectors; /** * 流程定义管理 控制层 @@ -47,23 +41,23 @@ public class FlwDefinitionController extends BaseController { /** * 查询流程定义列表 * - * @param flowDefinitionBo 参数 - * @param pageQuery 分页 + * @param flowDefinition 参数 + * @param pageQuery 分页 */ @GetMapping("/list") - public TableDataInfo list(FlowDefinitionBo flowDefinitionBo, PageQuery pageQuery) { - return flwDefinitionService.queryList(flowDefinitionBo, pageQuery); + public TableDataInfo list(FlowDefinition flowDefinition, PageQuery pageQuery) { + return flwDefinitionService.queryList(flowDefinition, pageQuery); } /** * 查询未发布的流程定义列表 * - * @param flowDefinitionBo 参数 - * @param pageQuery 分页 + * @param flowDefinition 参数 + * @param pageQuery 分页 */ @GetMapping("/unPublishList") - public TableDataInfo unPublishList(FlowDefinitionBo flowDefinitionBo, PageQuery pageQuery) { - return flwDefinitionService.unPublishList(flowDefinitionBo, pageQuery); + public TableDataInfo unPublishList(FlowDefinition flowDefinition, PageQuery pageQuery) { + return flwDefinitionService.unPublishList(flowDefinition, pageQuery); } /** @@ -78,6 +72,8 @@ public class FlwDefinitionController extends BaseController { /** * 新增流程定义 + * + * @param flowDefinition 参数 */ @Log(title = "流程定义", businessType = BusinessType.INSERT) @PostMapping @@ -195,50 +191,4 @@ public class FlwDefinitionController extends BaseController { return R.ok(active ? defService.active(id) : defService.unActive(id)); } - /** - * 复制流程定义到另一项目 - * - * @param id 流程定义id - */ - @Log(title = "复制流程定义到另一项目", businessType = BusinessType.INSERT) - @PostMapping("/copyToProject/{id}/{projectId}") - @RepeatSubmit() - @Transactional(rollbackFor = Exception.class) - public R copyToProjectById(@PathVariable Long id, @PathVariable Long projectId) { - Definition definition = FlowEngine.defService().getById(id).copy(); - definition.setVersion("1"); - AssertUtil.isNull(definition, "流程定义不存在!"); - String[] split = definition.getFlowCode().split("_"); - definition.setFlowCode(projectId + "_" + split[1]); - List nodeList = (List) FlowEngine.nodeService().getByDefId(id).stream().map(Node::copy).collect(Collectors.toList()); - List skipList = (List)FlowEngine.skipService().getByDefId(id).stream().map(Skip::copy).collect(Collectors.toList()); - FlowEngine.dataFillHandler().idFill(definition); - nodeList.forEach((node) -> { - node.setDefinitionId(definition.getId()).setVersion(definition.getVersion()); - }); - FlowEngine.nodeService().saveBatch(nodeList); - skipList.forEach((skip) -> { - skip.setDefinitionId(definition.getId()); - }); - FlowEngine.skipService().saveBatch(skipList); - boolean save = FlowEngine.defService().save(definition); - return R.ok(save); - } - - - @PostMapping("/copyAllProject/{projectId}/{toProjectId}") - @Transactional(rollbackFor = Exception.class) - @RepeatSubmit() - public R copyToProject(@PathVariable Long projectId,@PathVariable Long toProjectId) { - FlowDefinitionBo flowDefinitionBo = new FlowDefinitionBo(); - flowDefinitionBo.setProjectId(projectId); - PageQuery pageQuery = new PageQuery(9999,1); - TableDataInfo flowDefinitionVoTableDataInfo = flwDefinitionService.queryList(flowDefinitionBo, pageQuery); - List list = flowDefinitionVoTableDataInfo.getRows(); - for (FlowDefinitionVo flowDefinitionVo : list) { - copyToProjectById(flowDefinitionVo.getId(),toProjectId); - } - return R.ok(); - } - } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java index e7ea5e4c..c798dc5c 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java @@ -13,6 +13,7 @@ import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.domain.bo.FlowCancelBo; import org.dromara.workflow.domain.bo.FlowInstanceBo; import org.dromara.workflow.domain.bo.FlowInvalidBo; +import org.dromara.workflow.domain.bo.FlowVariableBo; import org.dromara.workflow.domain.vo.FlowInstanceVo; import org.dromara.workflow.service.IFlwInstanceService; import org.springframework.validation.annotation.Validated; @@ -88,6 +89,16 @@ public class FlwInstanceController extends BaseController { return toAjax(flwInstanceService.deleteByInstanceIds(instanceIds)); } + /** + * 按照实例id删除已完成得流程实例 + * + * @param instanceIds 实例id + */ + @DeleteMapping("/deleteHisByInstanceIds/{instanceIds}") + public R deleteHisByInstanceIds(@PathVariable List instanceIds) { + return toAjax(flwInstanceService.deleteHisByInstanceIds(instanceIds)); + } + /** * 撤销流程 * @@ -142,6 +153,17 @@ public class FlwInstanceController extends BaseController { return R.ok(flwInstanceService.instanceVariable(instanceId)); } + /** + * 修改流程变量 + * + * @param bo 参数 + */ + @RepeatSubmit() + @PutMapping("/updateVariable") + public R updateVariable(@Validated @RequestBody FlowVariableBo bo) { + return toAjax(flwInstanceService.updateVariable(bo)); + } + /** * 作废流程 * diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwSpelController.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwSpelController.java new file mode 100644 index 00000000..212b6592 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwSpelController.java @@ -0,0 +1,93 @@ +package org.dromara.workflow.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.common.ConditionalOnEnable; +import org.dromara.workflow.domain.bo.FlowSpelBo; +import org.dromara.workflow.domain.vo.FlowSpelVo; +import org.dromara.workflow.service.IFlwSpelService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 流程spel达式定义 + * + * @author Michelle.Chung + * @date 2025-07-04 + */ +@ConditionalOnEnable +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/spel") +public class FlwSpelController extends BaseController { + + private final IFlwSpelService flwSpelService; + + /** + * 查询流程spel达式定义列表 + */ + @SaCheckPermission("workflow:spel:list") + @GetMapping("/list") + public TableDataInfo list(FlowSpelBo bo, PageQuery pageQuery) { + return flwSpelService.queryPageList(bo, pageQuery); + } + + /** + * 获取流程spel达式定义详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("workflow:spel:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return R.ok(flwSpelService.queryById(id)); + } + + /** + * 新增流程spel达式定义 + */ + @SaCheckPermission("workflow:spel:add") + @Log(title = "流程spel达式定义", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody FlowSpelBo bo) { + return toAjax(flwSpelService.insertByBo(bo)); + } + + /** + * 修改流程spel达式定义 + */ + @SaCheckPermission("workflow:spel:edit") + @Log(title = "流程spel达式定义", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody FlowSpelBo bo) { + return toAjax(flwSpelService.updateByBo(bo)); + } + + /** + * 删除流程spel达式定义 + * + * @param ids 主键串 + */ + @SaCheckPermission("workflow:spel:remove") + @Log(title = "流程spel达式定义", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { + return toAjax(flwSpelService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwTaskController.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwTaskController.java index 0d914936..4370ff82 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwTaskController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwTaskController.java @@ -191,12 +191,12 @@ public class FlwTaskController extends BaseController { /** * 获取可驳回的前置节点 * - * @param definitionId 流程定义id + * @param taskId 任务id * @param nowNodeCode 当前节点 */ - @GetMapping("/getBackTaskNode/{definitionId}/{nowNodeCode}") - public R> getBackTaskNode(@PathVariable Long definitionId, @PathVariable String nowNodeCode) { - return R.ok(flwTaskService.getBackTaskNode(definitionId, nowNodeCode)); + @GetMapping("/getBackTaskNode/{taskId}/{nowNodeCode}") + public R> getBackTaskNode(@PathVariable Long taskId, @PathVariable String nowNodeCode) { + return R.ok(flwTaskService.getBackTaskNode(taskId, nowNodeCode)); } /** @@ -206,13 +206,19 @@ public class FlwTaskController extends BaseController { */ @GetMapping("/currentTaskAllUser/{taskId}") public R> currentTaskAllUser(@PathVariable Long taskId) { - return R.ok(flwTaskService.currentTaskAllUser(taskId)); + return R.ok(flwTaskService.currentTaskAllUser(List.of(taskId))); + } + + /** + * 催办任务 + * + * @param bo 参数 + * @return 结果 + */ + @PostMapping("/urgeTask") + public R urgeTask(@RequestBody FlowUrgeTaskBo bo) { + return toAjax(flwTaskService.urgeTask(bo)); } - @PutMapping("/copyRead/{flowUserId}") - public R copyRead(@PathVariable Long flowUserId) { - flwTaskService.copyRead(flowUserId); - return R.ok(); - } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java index 98825d92..39bb41f2 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java @@ -82,6 +82,17 @@ public class TestLeaveController extends BaseController { return R.ok(testLeaveService.insertByBo(bo)); } + /** + * 提交请假并提交流程 + */ + @SaCheckPermission("workflow:leave:add") + @Log(title = "请假", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/submitAndFlowStart") + public R submitAndFlowStart(@Validated(AddGroup.class) @RequestBody TestLeaveBo bo) { + return R.ok(testLeaveService.submitAndFlowStart(bo)); + } + /** * 修改请假 */ diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/FlowInstanceBizExt.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/FlowInstanceBizExt.java new file mode 100644 index 00000000..932b0380 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/FlowInstanceBizExt.java @@ -0,0 +1,59 @@ +package org.dromara.workflow.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.tenant.core.TenantEntity; + +import java.io.Serial; + +/** + * 流程实例业务扩展对象 flow_instance_biz_ext + * + * @author may + * @date 2025-08-05 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("flow_instance_biz_ext") +public class FlowInstanceBizExt extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 流程实例ID + */ + private Long instanceId; + + /** + * 业务ID + */ + private String businessId; + + /** + * 业务编码 + */ + private String businessCode; + + /** + * 业务标题 + */ + private String businessTitle; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/FlowSpel.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/FlowSpel.java new file mode 100644 index 00000000..be74ccb9 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/FlowSpel.java @@ -0,0 +1,69 @@ +package org.dromara.workflow.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 流程spel达式定义对象 flow_spel + * + * @author Michelle.Chung + * @date 2025-07-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("flow_spel") +public class FlowSpel extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + @TableId(value = "id") + private Long id; + + /** + * 组件名称 + */ + private String componentName; + + /** + * 方法名 + */ + private String methodName; + + /** + * 参数 + */ + private String methodParams; + + /** + * 预览spel表达式 + */ + private String viewSpel; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + + /** + * 删除标志 + */ + @TableLogic + private String delFlag; + + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/TestLeave.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/TestLeave.java index 7d42a9b5..b54873ca 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/TestLeave.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/TestLeave.java @@ -29,6 +29,11 @@ public class TestLeave extends BaseEntity { @TableId(value = "id") private Long id; + /** + * 申请编号 + */ + private String applyCode; + /** * 请假类型 */ diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java index 360fc3b8..e2462935 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java @@ -53,6 +53,11 @@ public class CompleteTaskBo implements Serializable { */ private String notice; + /** + * 办理人(可不填 用于覆盖当前节点办理人) + */ + private String handler; + /** * 流程变量 */ diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowSpelBo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowSpelBo.java new file mode 100644 index 00000000..e2044999 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowSpelBo.java @@ -0,0 +1,60 @@ +package org.dromara.workflow.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.workflow.domain.FlowSpel; + +/** + * 流程spel达式定义业务对象 flow_spel + * + * @author Michelle.Chung + * @date 2025-07-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = FlowSpel.class, reverseConvertGenerate = false) +public class FlowSpelBo extends BaseEntity { + + /** + * 主键id + */ + private Long id; + + /** + * 组件名称 + */ + private String componentName; + + /** + * 方法名 + */ + private String methodName; + + /** + * 参数 + */ + private String methodParams; + + /** + * 预览spel值 + */ + @NotBlank(message = "预览spel值不能为空", groups = { AddGroup.class, EditGroup.class }) + private String viewSpel; + + /** + * 状态(0正常 1停用) + */ + @NotBlank(message = "状态(0正常 1停用)不能为空", groups = { AddGroup.class, EditGroup.class }) + private String status; + + /** + * 备注 + */ + private String remark; + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowTaskBo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowTaskBo.java index a60ef4c3..3f4ababd 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowTaskBo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowTaskBo.java @@ -32,8 +32,6 @@ public class FlowTaskBo implements Serializable { */ private String flowCode; - private String projectId; - /** * 流程分类id */ @@ -60,4 +58,5 @@ public class FlowTaskBo implements Serializable { */ private String isRead; + private String projectId; } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowUrgeTaskBo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowUrgeTaskBo.java new file mode 100644 index 00000000..8e51b12e --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowUrgeTaskBo.java @@ -0,0 +1,38 @@ +package org.dromara.workflow.domain.bo; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.dromara.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 流程变量参数 + * + * @author may + */ +@Data +public class FlowUrgeTaskBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务id + */ + @NotNull(message = "任务id为空", groups = AddGroup.class) + private List taskIdList; + + /** + * 消息类型 + */ + private List messageType; + + /** + * 催办内容 + */ + @NotNull(message = "催办内容为空", groups = AddGroup.class) + private String message; +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowVariableBo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowVariableBo.java new file mode 100644 index 00000000..9361299f --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowVariableBo.java @@ -0,0 +1,39 @@ +package org.dromara.workflow.domain.bo; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.dromara.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 流程变量参数 + * + * @author may + */ +@Data +public class FlowVariableBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程实例id + */ + @NotNull(message = "流程实例id为空", groups = AddGroup.class) + private Long instanceId; + + /** + * 流程变量key + */ + @NotNull(message = "流程变量key为空", groups = AddGroup.class) + private String key; + + /** + * 流程变量value + */ + @NotNull(message = "流程变量value为空", groups = AddGroup.class) + private String value; + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java index ea21a81e..b31f4fa1 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java @@ -1,9 +1,11 @@ package org.dromara.workflow.domain.bo; +import cn.hutool.core.util.ObjectUtil; import jakarta.validation.constraints.NotBlank; import lombok.Data; import org.dromara.common.core.validate.AddGroup; +import org.dromara.workflow.domain.FlowInstanceBizExt; import java.io.Serial; import java.io.Serializable; @@ -34,11 +36,21 @@ public class StartProcessBo implements Serializable { @NotBlank(message = "流程定义编码不能为空", groups = {AddGroup.class}) private String flowCode; + /** + * 办理人(可不填 用于覆盖当前节点办理人) + */ + private String handler; + /** * 流程变量,前端会提交一个元素{'entity': {业务详情数据对象}} */ private Map variables; + /** + * 流程业务扩展信息 + */ + private FlowInstanceBizExt bizExt; + public Map getVariables() { if (variables == null) { return new HashMap<>(16); @@ -46,4 +58,11 @@ public class StartProcessBo implements Serializable { variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); return variables; } + + public FlowInstanceBizExt getBizExt() { + if (ObjectUtil.isNull(bizExt)) { + bizExt = new FlowInstanceBizExt(); + } + return bizExt; + } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java index 395f71d9..2463569b 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java @@ -31,6 +31,16 @@ public class TestLeaveBo extends BaseEntity { @NotNull(message = "主键不能为空", groups = {EditGroup.class}) private Long id; + /** + * 流程code + */ + private String flowCode; + + /** + * 申请编号 + */ + private String applyCode; + /** * 请假类型 */ diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCategoryVo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCategoryVo.java index 37d1bc80..b31ac28f 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCategoryVo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCategoryVo.java @@ -1,5 +1,6 @@ package org.dromara.workflow.domain.vo; + import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.github.linpeilie.annotations.AutoMapper; diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCopyVo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCopyVo.java new file mode 100644 index 00000000..67ef9e2c --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCopyVo.java @@ -0,0 +1,36 @@ +package org.dromara.workflow.domain.vo; + +import lombok.Data; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 抄送对象 + * + * @author AprilWind + */ +@Data +public class FlowCopyVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + + /** + * 用户名称 + */ + @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "userId") + private String userName; + + public FlowCopyVo(Long userId) { + this.userId = userId; + } + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowHisTaskVo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowHisTaskVo.java index 6f8f0535..a19d96ad 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowHisTaskVo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowHisTaskVo.java @@ -9,7 +9,6 @@ import org.dromara.workflow.common.constant.FlowConstant; import java.io.Serial; import java.io.Serializable; -import java.time.temporal.ChronoUnit; import java.util.Date; import java.util.List; @@ -51,9 +50,6 @@ public class FlowHisTaskVo implements Serializable { */ private Long definitionId; - - private String projectName; - /** * 流程定义名称 */ @@ -207,10 +203,19 @@ public class FlowHisTaskVo implements Serializable { */ private String runDuration; + //业务扩展信息开始 /** - * 运行时长数字 + * 业务编码 */ - private Long runDurationNum; + private String businessCode; + + private String projectName; + + /** + * 业务标题 + */ + private String businessTitle; + //业务扩展信息结束 /** * 设置创建时间并计算任务运行时长 @@ -239,7 +244,6 @@ public class FlowHisTaskVo implements Serializable { // 如果创建时间和更新时间均不为空,计算它们之间的时长 if (this.updateTime != null && this.createTime != null) { this.runDuration = DateUtils.getTimeDifference(this.updateTime, this.createTime); - this.runDurationNum = DateUtils.difference(this.createTime, this.updateTime, ChronoUnit.SECONDS); } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowInstanceVo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowInstanceVo.java index f5eb63fb..ab248020 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowInstanceVo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowInstanceVo.java @@ -42,8 +42,6 @@ public class FlowInstanceVo { */ private Long definitionId; - private String projectName; - /** * 流程定义名称 */ @@ -136,4 +134,17 @@ public class FlowInstanceVo { @Translation(type = FlowConstant.CATEGORY_ID_TO_NAME, mapper = "category") private String categoryName; + //业务扩展信息开始 + /** + * 业务编码 + */ + private String businessCode; + + /** + * 业务标题 + */ + private String businessTitle; + //业务扩展信息结束 + + private String projectName; } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowSpelVo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowSpelVo.java new file mode 100644 index 00000000..7bec4d5c --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowSpelVo.java @@ -0,0 +1,80 @@ +package org.dromara.workflow.domain.vo; + + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import org.dromara.workflow.domain.FlowSpel; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 流程spel达式定义视图对象 flow_spel + * + * @author Michelle.Chung + * @date 2025-07-04 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = FlowSpel.class) +public class FlowSpelVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + @ExcelProperty(value = "主键id") + private Long id; + + /** + * 组件名称 + */ + @ExcelProperty(value = "组件名称") + private String componentName; + + /** + * 方法名 + */ + @ExcelProperty(value = "方法名") + private String methodName; + + /** + * 参数 + */ + @ExcelProperty(value = "参数") + private String methodParams; + + /** + * 预览spel值 + */ + @ExcelProperty(value = "预览spel值") + private String viewSpel; + + /** + * 状态(0正常 1停用) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=正常,1=停用") + private String status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java index 77e41014..d3906693 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java @@ -11,6 +11,7 @@ import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; import java.util.List; +import java.util.Map; /** * 任务视图 @@ -50,9 +51,6 @@ public class FlowTaskVo implements Serializable { */ private Long definitionId; - - private String projectName; - /** * 流程实例表id */ @@ -148,6 +146,7 @@ public class FlowTaskVo implements Serializable { /** * 办理人名称 */ + @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "assigneeIds") private String assigneeNames; /** @@ -187,8 +186,31 @@ public class FlowTaskVo implements Serializable { */ private List buttonList; - private Long flowUserId; + /** + * 抄送对象 ID 集合 + *

+ * 根据扩展属性中 CopySettingEnum 类型的数据生成,存储需要抄送的对象 ID + */ + private List copyList; - private String isRead; + /** + * 自定义参数 Map + *

+ * 根据扩展属性中 VariablesEnum 类型的数据生成,存储 key=value 格式的自定义参数 + */ + private Map varList; + //业务扩展信息开始 + /** + * 业务编码 + */ + private String businessCode; + + /** + * 业务标题 + */ + private String businessTitle; + //业务扩展信息结束 + + private String projectName; } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/NodeExtVo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/NodeExtVo.java new file mode 100644 index 00000000..5fb3380b --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/NodeExtVo.java @@ -0,0 +1,45 @@ +package org.dromara.workflow.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Node 扩展属性解析结果 VO + *

+ * 用于封装从扩展属性 JSON 中解析出的各类信息,包括按钮权限、抄送对象和自定义参数。 + * + * @author AprilWind + */ +@Data +public class NodeExtVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 按钮权限列表 + *

+ * 根据扩展属性中 ButtonPermissionEnum 类型的数据生成,每个元素表示一个按钮及其是否勾选。 + */ + private List buttonPermissions; + + /** + * 抄送对象 ID 集合 + *

+ * 根据扩展属性中 CopySettingEnum 类型的数据生成,存储需要抄送的对象 ID + */ + private Set copySettings; + + /** + * 自定义参数 Map + *

+ * 根据扩展属性中 VariablesEnum 类型的数据生成,存储 key=value 格式的自定义参数 + */ + private Map variables; + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java index 47886d72..84cbda68 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java @@ -1,5 +1,6 @@ package org.dromara.workflow.domain.vo; + import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.github.linpeilie.annotations.AutoMapper; @@ -31,6 +32,12 @@ public class TestLeaveVo implements Serializable { @ExcelProperty(value = "主键") private Long id; + /** + * 申请编号 + */ + @ExcelProperty(value = "申请编号") + private String applyCode; + /** * 请假类型 */ diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java index c465271c..f36fed22 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java @@ -1,9 +1,9 @@ package org.dromara.workflow.handler; import lombok.extern.slf4j.Slf4j; -import org.dromara.common.core.domain.event.ProcessTaskEvent; import org.dromara.common.core.domain.event.ProcessDeleteEvent; import org.dromara.common.core.domain.event.ProcessEvent; +import org.dromara.common.core.domain.event.ProcessTaskEvent; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.warm.flow.core.entity.Instance; @@ -24,7 +24,7 @@ import java.util.Map; public class FlowProcessEventHandler { /** - * 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等) + * 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成等) * * @param flowCode 流程定义编码 * @param instance 实例数据 @@ -39,6 +39,7 @@ public class FlowProcessEventHandler { ProcessEvent processEvent = new ProcessEvent(); processEvent.setTenantId(tenantId); processEvent.setFlowCode(flowCode); + processEvent.setInstanceId(instance.getId()); processEvent.setBusinessId(instance.getBusinessId()); processEvent.setNodeType(instance.getNodeType()); processEvent.setNodeCode(instance.getNodeCode()); @@ -55,20 +56,23 @@ public class FlowProcessEventHandler { * @param flowCode 流程定义编码 * @param instance 实例数据 * @param taskId 任务id + * @param params 上一个任务的办理参数 */ - public void processTaskHandler(String flowCode, Instance instance, Long taskId) { + public void processTaskHandler(String flowCode, Instance instance, Long taskId, Map params) { String tenantId = TenantHelper.getTenantId(); log.info("【流程任务事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 任务ID: {}", tenantId, flowCode, instance.getBusinessId(), instance.getNodeType(), instance.getNodeCode(), instance.getNodeName(), taskId); ProcessTaskEvent processTaskEvent = new ProcessTaskEvent(); processTaskEvent.setTenantId(tenantId); processTaskEvent.setFlowCode(flowCode); + processTaskEvent.setInstanceId(instance.getId()); processTaskEvent.setBusinessId(instance.getBusinessId()); processTaskEvent.setNodeType(instance.getNodeType()); processTaskEvent.setNodeCode(instance.getNodeCode()); processTaskEvent.setNodeName(instance.getNodeName()); processTaskEvent.setTaskId(taskId); processTaskEvent.setStatus(instance.getFlowStatus()); + processTaskEvent.setParams(params); SpringUtils.context().publishEvent(processTaskEvent); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java index f9ede15c..b5aec3de 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java @@ -1,13 +1,17 @@ package org.dromara.workflow.handler; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.dto.UserDTO; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.handler.PermissionHandler; import org.dromara.workflow.common.ConditionalOnEnable; -import org.dromara.workflow.service.IFlwCommonService; +import org.dromara.workflow.service.IFlwTaskAssigneeService; import org.springframework.stereotype.Component; import java.util.Collections; @@ -24,7 +28,7 @@ import java.util.List; @Slf4j public class WorkflowPermissionHandler implements PermissionHandler { - private final IFlwCommonService flwCommonService; + private final IFlwTaskAssigneeService flwTaskAssigneeService; /** * 办理人权限标识,比如用户,角色,部门等,用于校验是否有权限办理任务 @@ -51,9 +55,11 @@ public class WorkflowPermissionHandler implements PermissionHandler { */ @Override public List convertPermissions(List permissions) { - if (CollUtil.isNotEmpty(permissions)) { - permissions = flwCommonService.buildUser(permissions); + if (CollUtil.isEmpty(permissions)) { + return permissions; } - return permissions; + String storageIds = CollUtil.join(permissions, StringUtils.SEPARATOR); + List users = flwTaskAssigneeService.fetchUsersByStorageIds(storageIds,null); + return StreamUtils.toList(users, userDTO -> Convert.toStr(userDTO.getUserId())); } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java index 7aa19ce1..f0855331 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java @@ -2,6 +2,8 @@ package org.dromara.workflow.listener; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.TypeReference; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.json.JSONUtil; @@ -11,6 +13,7 @@ import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.enums.BusinessStatusEnum; import org.dromara.common.core.service.UserService; +import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.dto.FlowParams; @@ -28,11 +31,9 @@ import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.common.enums.TaskStatusEnum; import org.dromara.workflow.domain.bo.FlowCopyBo; +import org.dromara.workflow.domain.vo.NodeExtVo; import org.dromara.workflow.handler.FlowProcessEventHandler; -import org.dromara.workflow.service.IFlwCommonService; -import org.dromara.workflow.service.IFlwInstanceService; -import org.dromara.workflow.service.IFlwTaskAssigneeService; -import org.dromara.workflow.service.IFlwTaskService; +import org.dromara.workflow.service.*; import org.springframework.stereotype.Component; import java.util.*; @@ -56,7 +57,8 @@ public class WorkflowGlobalListener implements GlobalListener { private final InsService insService; private final IFlwTaskAssigneeService flwTaskAssigneeService; private final UserService userService; - + private final IFlwInstanceService flwInstanceService; + private final IFlwNodeExtService nodeExtService; // 需要分专业的角色 @@ -76,42 +78,6 @@ public class WorkflowGlobalListener implements GlobalListener { */ @Override public void create(ListenerVariable listenerVariable) { - log.info("全局创建监听器开始执行......"); - - // 获取相关变量 -// List nextTasks = listenerVariable.getNextTasks(); -// Definition definition = listenerVariable.getDefinition(); -// Map variable = listenerVariable.getVariable(); -// -// for (Task flowTask : nextTasks) { -// String nodeCode = flowTask.getNodeCode(); -// -// -// // 查询节点信息 -// FlowNode byNodeCode = flwTaskService.getByNodeCode(nodeCode, definition.getId()); -// -// // 获取原来的办理人信息 -// List originalPermissionList = flowTask.getPermissionList(); -// log.info("节点 {} 原来的办理人信息: {}", nodeCode, originalPermissionList); -// String permissionFlag = byNodeCode.getPermissionFlag(); -// try { -// if(permissionFlag.contains("role")){ -// permissionFlag = permissionFlag.replace("@@", ","); -// -// String flowCode = definition.getFlowCode(); -// String projectId = flowCode.split("_")[0]; -// -// List userDTOS = flwTaskAssigneeService.fetchUsersByStorageIds(permissionFlag, Long.valueOf(projectId)); -// variable.put("userList", userDTOS.stream().map(UserDTO::getUserId).map(String::valueOf).collect(Collectors.toList())); -// flowTask.setPermissionList(userDTOS.stream().map(UserDTO::getUserId).map(String::valueOf).collect(Collectors.toList())); -// } -// -// }catch (Exception e) { -// log.error("设置失败: {}", e.getMessage()); -// } -// } - - log.info("全局创建监听器执行结束......"); } @@ -123,7 +89,25 @@ public class WorkflowGlobalListener implements GlobalListener { */ @Override public void start(ListenerVariable listenerVariable) { - + String ext = listenerVariable.getNode().getExt(); + if (StringUtils.isNotBlank(ext)) { + Map variable = listenerVariable.getVariable(); + NodeExtVo nodeExt = nodeExtService.parseNodeExt(ext, variable); + Set copyList = nodeExt.getCopySettings(); + if (CollUtil.isNotEmpty(copyList)) { + List list = StreamUtils.toList(copyList, x -> { + FlowCopyBo bo = new FlowCopyBo(); + Long id = Convert.toLong(x); + bo.setUserId(id); + bo.setUserName(userService.selectUserNameById(id)); + return bo; + }); + variable.put(FlowConstant.FLOW_COPY_LIST, list); + } + if (CollUtil.isNotEmpty(nodeExt.getVariables())) { + variable.putAll(nodeExt.getVariables()); + } + } } /** @@ -223,6 +207,7 @@ public class WorkflowGlobalListener implements GlobalListener { Instance instance = listenerVariable.getInstance(); Definition definition = listenerVariable.getDefinition(); Task task = listenerVariable.getTask(); + List nextTasks = listenerVariable.getNextTasks(); Map params = new HashMap<>(); FlowParams flowParams = listenerVariable.getFlowParams(); Map variable = new HashMap<>(); @@ -245,52 +230,61 @@ public class WorkflowGlobalListener implements GlobalListener { if (StringUtils.isNotBlank(status)) { flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, status, params, false); } + if (!BusinessStatusEnum.initialState(instance.getFlowStatus())) { + if (task != null && CollUtil.isNotEmpty(nextTasks) && nextTasks.size() == 1 + && flwCommonService.applyNodeCode(definition.getId()).equals(nextTasks.get(0).getNodeCode())) { + // 如果为画线指定驳回 线条指定为驳回 驳回得节点为申请人节点 则修改流程状态为退回 + flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, BusinessStatusEnum.BACK.getStatus(), params, false); + // 修改流程实例状态 + instance.setFlowStatus(BusinessStatusEnum.BACK.getStatus()); + FlowEngine.insService().updateById(instance); + } + } } //发布任务事件 - if (task != null) { - flowProcessEventHandler.processTaskHandler(definition.getFlowCode(), instance, task.getId()); + if (CollUtil.isNotEmpty(nextTasks)) { + for (Task nextTask : nextTasks) { + flowProcessEventHandler.processTaskHandler(definition.getFlowCode(), instance, nextTask.getId(), params); + } } if (ObjectUtil.isNull(flowParams)) { return; } - //给处理人发消息重新统计数据 + // 只有办理或者退回的时候才执行消息通知和抄送 + if (!TaskStatusEnum.isPassOrBack(flowParams.getHisStatus())) { + return; + } + if (ObjectUtil.isNull(variable)) { + return; + } + if (variable.containsKey(FlowConstant.FLOW_COPY_LIST)) { + List flowCopyList = MapUtil.get(variable, FlowConstant.FLOW_COPY_LIST, new TypeReference<>() { + }); + // 添加抄送人 + flwTaskService.setCopy(task, flowCopyList); + //发送抄送消息 + flwCommonService.sendCopyMessage(definition.getFlowName(), definition.getFlowCode(), flowCopyList); + } + if (variable.containsKey(FlowConstant.MESSAGE_TYPE)) { + List messageType = MapUtil.get(variable, FlowConstant.MESSAGE_TYPE, new TypeReference<>() { + }); + String notice = MapUtil.getStr(variable, FlowConstant.MESSAGE_NOTICE); + flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice); + } + FlowEngine.insService().removeVariables(instance.getId(), + FlowConstant.FLOW_COPY_LIST, + FlowConstant.MESSAGE_TYPE, + FlowConstant.MESSAGE_NOTICE, + FlowConstant.SUBMIT + ); + //给处理人发消息重新统计数据 if (task != null) { flwCommonService.sendCountMessage(task); } - // 只有办理或者退回的时候才执行消息通知和抄送 - if (TaskStatusEnum.PASS.getStatus().equals(flowParams.getHisStatus()) - || TaskStatusEnum.BACK.getStatus().equals(flowParams.getHisStatus())) { - if (variable != null) { - if (variable.containsKey(FlowConstant.FLOW_COPY_LIST)) { - List flowCopyList = (List) variable.get(FlowConstant.FLOW_COPY_LIST); - // 添加抄送人 - flwTaskService.setCopy(task, flowCopyList); - //发送抄送消息 - flwCommonService.sendCopyMessage(definition.getFlowName(), definition.getFlowCode(), flowCopyList); - } - if (variable.containsKey(FlowConstant.MESSAGE_TYPE)) { - List messageType = (List) variable.get(FlowConstant.MESSAGE_TYPE); - String notice = (String) variable.get(FlowConstant.MESSAGE_NOTICE); - // 消息通知 - if (CollUtil.isNotEmpty(messageType)) { - flwCommonService.sendMessage(definition.getFlowName(),definition.getFlowCode(), instance.getId(), messageType, notice); - } - } - FlowInstance ins = new FlowInstance(); - Map variableMap = instance.getVariableMap(); - variableMap.remove(FlowConstant.FLOW_COPY_LIST); - variableMap.remove(FlowConstant.MESSAGE_TYPE); - variableMap.remove(FlowConstant.MESSAGE_NOTICE); - variableMap.remove(FlowConstant.SUBMIT); - ins.setId(instance.getId()); - ins.setVariable(FlowEngine.jsonConvert.objToStr(variableMap)); - insService.updateById(ins); - } - } - } + } /** * 根据流程实例确定最终状态 * @@ -304,11 +298,10 @@ public class WorkflowGlobalListener implements GlobalListener { return flowStatus; } else { Long instanceId = instance.getId(); - List flowTasks = flwTaskService.selectByInstId(instanceId); - if (CollUtil.isEmpty(flowTasks)) { + if (flwTaskService.isTaskEnd(instanceId)) { String status = BusinessStatusEnum.FINISH.getStatus(); // 更新流程状态为已完成 - instanceService.updateStatus(instanceId, status); + flwInstanceService.updateStatus(instanceId, status); log.info("流程已结束,状态更新为: {}", status); return status; } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwCategoryMapper.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwCategoryMapper.java index 4a59f258..c7a3d264 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwCategoryMapper.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwCategoryMapper.java @@ -1,8 +1,6 @@ package org.dromara.workflow.mapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import org.dromara.common.mybatis.annotation.DataColumn; -import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.common.mybatis.helper.DataBaseHelper; import org.dromara.workflow.domain.FlowCategory; @@ -20,19 +18,6 @@ import java.util.stream.Stream; */ public interface FlwCategoryMapper extends BaseMapperPlus { - /** - * 统计指定流程分类ID的分类数量 - * - * @param categoryId 流程分类ID - * @return 该流程分类ID的分类数量 - */ - @DataPermission({ - @DataColumn(key = "deptName", value = "createDept") - }) - default long countCategoryById(Long categoryId) { - return this.selectCount(new LambdaQueryWrapper().eq(FlowCategory::getCategoryId, categoryId)); - } - /** * 根据父流程分类ID查询其所有子流程分类的列表 * diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwInstanceBizExtMapper.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwInstanceBizExtMapper.java new file mode 100644 index 00000000..e11613cf --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwInstanceBizExtMapper.java @@ -0,0 +1,61 @@ +package org.dromara.workflow.mapper; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.workflow.domain.FlowInstanceBizExt; + +import java.util.List; + +/** + * 流程实例业务扩展Mapper接口 + * + * @author may + * @date 2025-08-05 + */ +public interface FlwInstanceBizExtMapper extends BaseMapperPlus { + + /** + * 根据 instanceId 保存或更新流程实例业务扩展 + * + * @param entity 流程实例业务扩展实体 + * @return 操作是否成功 + */ + default int saveOrUpdateByInstanceId(FlowInstanceBizExt entity) { + // 查询是否存在 + FlowInstanceBizExt exist = this.selectOne(new LambdaQueryWrapper() + .eq(FlowInstanceBizExt::getInstanceId, entity.getInstanceId())); + + if (ObjectUtil.isNotNull(exist)) { + // 存在就带上主键更新 + entity.setId(exist.getId()); + return updateById(entity); + } else { + // 不存在就插入 + return insert(entity); + } + } + + /** + * 按照流程实例ID删除单个流程实例业务扩展 + * + * @param instanceId 流程实例ID + * @return 删除的行数 + */ + default int deleteByInstId(Long instanceId) { + return this.delete(new LambdaQueryWrapper() + .eq(FlowInstanceBizExt::getInstanceId, instanceId)); + } + + /** + * 按照流程实例ID批量删除流程实例业务扩展 + * + * @param instanceIds 流程实例ID列表 + * @return 删除的行数 + */ + default int deleteByInstIds(List instanceIds) { + return this.delete(new LambdaQueryWrapper() + .in(FlowInstanceBizExt::getInstanceId, instanceIds)); + } + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwSpelMapper.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwSpelMapper.java new file mode 100644 index 00000000..4cba7eeb --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwSpelMapper.java @@ -0,0 +1,15 @@ +package org.dromara.workflow.mapper; + +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.workflow.domain.FlowSpel; +import org.dromara.workflow.domain.vo.FlowSpelVo; + +/** + * 流程spel达式定义Mapper接口 + * + * @author Michelle.Chung + * @date 2025-07-04 + */ +public interface FlwSpelMapper extends BaseMapperPlus { + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwTaskMapper.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwTaskMapper.java index 8068e4c8..2b28c056 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwTaskMapper.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwTaskMapper.java @@ -19,7 +19,6 @@ import java.util.List; * @date 2024-03-02 */ public interface FlwTaskMapper { - /** * 获取待办信息 * @@ -28,23 +27,6 @@ public interface FlwTaskMapper { * @return 结果 */ Page getListRunTask(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); - Page getListRunTaskDefinitionIds(@Param("page") Page page, @Param("definitionIds") List definitionIds, @Param(Constants.WRAPPER) Wrapper queryWrapper); - - /** - * 不分页版 - * @param definitionIds - * @param queryWrapper - * @return - */ - List getListRunTaskDefinitionInfo(@Param("definitionIds") List definitionIds, @Param(Constants.WRAPPER) Wrapper queryWrapper); - - /** - * 获取待办信息 - * - * @param queryWrapper 条件 - * @return 结果 - */ - List getListRunTask(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 获取已办 @@ -54,7 +36,6 @@ public interface FlwTaskMapper { * @return 结果 */ Page getListFinishTask(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); - Page getListFinishDefinitionIdsTask(@Param("page") Page page,@Param("definitionIds") List definitionIds, @Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 查询当前用户的抄送 @@ -65,9 +46,24 @@ public interface FlwTaskMapper { */ Page getTaskCopyByPage(@Param("page") Page page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); + + + + + Page getTaskCopyDefinitionIdsByPage(@Param("page") Page page,@Param("definitionIds") List definitionIds, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); void copyRead(@Param("flowUserId") Long flowUserId); + /** + * 不分页版 + * @param definitionIds + * @param queryWrapper + * @return + */ + List getListRunTaskDefinitionInfo(@Param("definitionIds") List definitionIds, @Param(Constants.WRAPPER) Wrapper queryWrapper); + Page getListFinishDefinitionIdsTask(@Param("page") Page page,@Param("definitionIds") List definitionIds, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + Page getListRunTaskDefinitionIds(@Param("page") Page page, @Param("definitionIds") List definitionIds, @Param(Constants.WRAPPER) Wrapper queryWrapper); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/rule/SpelRuleComponent.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/rule/SpelRuleComponent.java new file mode 100644 index 00000000..7498db56 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/rule/SpelRuleComponent.java @@ -0,0 +1,38 @@ +package org.dromara.workflow.rule; + +import cn.hutool.core.util.ObjectUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.service.DeptService; +import org.dromara.workflow.common.ConditionalOnEnable; +import org.springframework.stereotype.Component; + +/** + * spel表达式规则组件 + *

+ * 通过该组件统一管理流程定义中的spel表达式 + *

+ * + * @author Michelle.Chung + */ +@ConditionalOnEnable +@Slf4j +@Component +@RequiredArgsConstructor +public class SpelRuleComponent { + + private final DeptService deptService; + + /** + * 通过发起人部门id获取部门负责人 + */ + public Long selectDeptLeaderById(Long initiatorDeptId) { + Long leaderId = deptService.selectDeptLeaderById(initiatorDeptId); + if (ObjectUtil.isNull(leaderId)) { + throw new ServiceException("当前部门未设置负责人,请联系管理员操作。"); + } + return leaderId; + } + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCategoryService.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCategoryService.java index 91f173d4..f66882b8 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCategoryService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCategoryService.java @@ -45,13 +45,6 @@ public interface IFlwCategoryService { */ List> selectCategoryTreeList(FlowCategoryBo category); - /** - * 校验流程分类是否有数据权限 - * - * @param categoryId 流程分类ID - */ - void checkCategoryDataScope(Long categoryId); - /** * 校验流程分类名称是否唯一 * diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java index e9df5cf6..2f8c6957 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java @@ -1,5 +1,6 @@ package org.dromara.workflow.service; +import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.warm.flow.core.entity.Task; import org.dromara.workflow.domain.bo.FlowCopyBo; @@ -12,14 +13,6 @@ import java.util.List; */ public interface IFlwCommonService { - /** - * 构建工作流用户 - * - * @param permissionList 办理用户 - * @return 用户 - */ - List buildUser(List permissionList); - /** * 发送消息 * @@ -47,4 +40,27 @@ public interface IFlwCommonService { * @return 申请人节点编码 */ String applyNodeCode(Long definitionId); + + + /** + * 发送消息 + * + * @param messageType 消息类型 + * @param message 消息内容 + * @param subject 邮件标题 + * @param userList 接收用户 + */ + void sendMessage(List messageType, String message, String subject, List userList); + + + /** + * 发送消息 + * + * @param flowName 流程定义名称 + * @param instId 实例id + * @param messageType 消息类型 + * @param message 消息内容,为空则发送默认配置的消息内容 + */ + void sendMessage(String flowName, Long instId, List messageType, String message); + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwDefinitionService.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwDefinitionService.java index 6076260c..54743b72 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwDefinitionService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwDefinitionService.java @@ -3,7 +3,7 @@ package org.dromara.workflow.service; import jakarta.servlet.http.HttpServletResponse; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; -import org.dromara.workflow.domain.bo.FlowDefinitionBo; +import org.dromara.warm.flow.orm.entity.FlowDefinition; import org.dromara.workflow.domain.vo.FlowDefinitionVo; import org.springframework.web.multipart.MultipartFile; @@ -20,20 +20,20 @@ public interface IFlwDefinitionService { /** * 查询流程定义列表 * - * @param flowDefinitionBo 参数 - * @param pageQuery 分页 + * @param flowDefinition 参数 + * @param pageQuery 分页 * @return 返回分页列表 */ - TableDataInfo queryList(FlowDefinitionBo flowDefinitionBo, PageQuery pageQuery); + TableDataInfo queryList(FlowDefinition flowDefinition, PageQuery pageQuery); /** * 查询未发布的流程定义列表 * - * @param flowDefinitionBo 参数 - * @param pageQuery 分页 + * @param flowDefinition 参数 + * @param pageQuery 分页 * @return 返回分页列表 */ - TableDataInfo unPublishList(FlowDefinitionBo flowDefinitionBo, PageQuery pageQuery); + TableDataInfo unPublishList(FlowDefinition flowDefinition, PageQuery pageQuery); /** * 发布流程定义 @@ -69,13 +69,6 @@ public interface IFlwDefinitionService { */ boolean removeDef(List ids); - /** - * 新增流程定义 - * - * @param projectId 项目id - */ - void insertDefByProjectId(Long projectId); - /** * 新增租户流程定义 * diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwInstanceService.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwInstanceService.java index 01e5124b..54ffe977 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwInstanceService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwInstanceService.java @@ -6,6 +6,7 @@ import org.dromara.warm.flow.orm.entity.FlowInstance; import org.dromara.workflow.domain.bo.FlowCancelBo; import org.dromara.workflow.domain.bo.FlowInstanceBo; import org.dromara.workflow.domain.bo.FlowInvalidBo; +import org.dromara.workflow.domain.bo.FlowVariableBo; import org.dromara.workflow.domain.vo.FlowInstanceVo; import java.util.List; @@ -84,6 +85,14 @@ public interface IFlwInstanceService { */ boolean deleteByInstanceIds(List instanceIds); + /** + * 按照实例id删除已完成得流程实例 + * + * @param instanceIds 删除的实例id + * @return 删除结果 + */ + boolean deleteHisByInstanceIds(List instanceIds); + /** * 撤销流程 * @@ -125,6 +134,14 @@ public interface IFlwInstanceService { */ Map instanceVariable(Long instanceId); + /** + * 更新流程变量 + * + * @param bo 参数 + * @return 结果 + */ + boolean updateVariable(FlowVariableBo bo); + /** * 设置流程变量 * @@ -141,14 +158,6 @@ public interface IFlwInstanceService { */ FlowInstance selectByTaskId(Long taskId); - /** - * 按任务id查询实例 - * - * @param taskIdList 任务id - * @return 结果 - */ - List selectByTaskIdList(List taskIdList); - /** * 作废流程 * diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwNodeExtService.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwNodeExtService.java index 95951653..a94a225a 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwNodeExtService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwNodeExtService.java @@ -1,8 +1,8 @@ package org.dromara.workflow.service; -import org.dromara.workflow.domain.vo.ButtonPermissionVo; +import org.dromara.workflow.domain.vo.NodeExtVo; -import java.util.List; +import java.util.Map; /** * 流程节点扩展属性 服务层 @@ -12,11 +12,24 @@ import java.util.List; public interface IFlwNodeExtService { /** - * 从扩展属性构建按钮权限列表:根据 ext 中记录的权限值,标记每个按钮是否勾选 + * 解析扩展属性 JSON 并构建 Node 扩展属性对象 + *

+ * 根据传入的 JSON 字符串,将扩展属性分为三类: + * 1. ButtonPermissionEnum:解析为按钮权限列表,标记每个按钮是否勾选 + * 2. CopySettingEnum:解析为抄送对象 ID 集合 + * 3. VariablesEnum:解析为自定义参数 Map * - * @param ext 扩展属性 JSON 字符串 - * @return 按钮权限 VO 列表 + *

示例 JSON: + * [ + * {"code": "ButtonPermissionEnum", "value": "back,termination"}, + * {"code": "CopySettingEnum", "value": "1"}, + * {"code": "VariablesEnum", "value": "key1=value1,key2=value2"} + * ] + * + * @param ext 扩展属性 JSON 字符串 + * @param variable 流程变量 + * @return NodeExtVo 对象,封装按钮权限列表、抄送对象集合和自定义参数 Map */ - List buildButtonPermissionsFromExt(String ext); + NodeExtVo parseNodeExt(String ext, Map variable); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwSpelService.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwSpelService.java new file mode 100644 index 00000000..02781682 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwSpelService.java @@ -0,0 +1,88 @@ +package org.dromara.workflow.service; + +import org.dromara.common.core.domain.dto.TaskAssigneeDTO; +import org.dromara.common.core.domain.model.TaskAssigneeBody; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.workflow.domain.bo.FlowSpelBo; +import org.dromara.workflow.domain.vo.FlowSpelVo; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 流程spel达式定义Service接口 + * + * @author Michelle.Chung + * @date 2025-07-04 + */ +public interface IFlwSpelService { + + /** + * 查询流程spel达式定义 + * + * @param id 主键 + * @return 流程spel达式定义 + */ + FlowSpelVo queryById(Long id); + + /** + * 分页查询流程spel达式定义列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 流程spel达式定义分页列表 + */ + TableDataInfo queryPageList(FlowSpelBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的流程spel达式定义列表 + * + * @param bo 查询条件 + * @return 流程spel达式定义列表 + */ + List queryList(FlowSpelBo bo); + + /** + * 新增流程spel达式定义 + * + * @param bo 流程spel达式定义 + * @return 是否新增成功 + */ + Boolean insertByBo(FlowSpelBo bo); + + /** + * 修改流程spel达式定义 + * + * @param bo 流程spel达式定义 + * @return 是否修改成功 + */ + Boolean updateByBo(FlowSpelBo bo); + + /** + * 校验并批量删除流程spel达式定义信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 查询spel并返回任务指派的列表,支持分页 + * + * @param taskQuery 查询条件 + * @return 办理人 + */ + TaskAssigneeDTO selectSpelByTaskAssigneeList(TaskAssigneeBody taskQuery); + + /** + * 根据视图 SpEL 表达式列表,查询对应的备注信息 + * + * @param viewSpels SpEL 表达式列表 + * @return 映射表:key 为 SpEL 表达式,value 为对应备注;若为空则返回空 Map + */ + Map selectRemarksBySpels(List viewSpels); + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java index c83885f1..f9dc45e1 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java @@ -12,10 +12,8 @@ import org.dromara.warm.flow.orm.entity.FlowTask; import org.dromara.workflow.domain.bo.*; import org.dromara.workflow.domain.vo.FlowHisTaskVo; import org.dromara.workflow.domain.vo.FlowTaskVo; -import org.springframework.web.bind.annotation.PathVariable; import java.util.List; -import java.util.Map; /** * 任务 服务层 @@ -113,11 +111,11 @@ public interface IFlwTaskService { /** * 获取可驳回的前置节点 * - * @param definitionId 流程定义id + * @param taskId 任务id * @param nowNodeCode 当前节点 * @return 结果 */ - List getBackTaskNode(Long definitionId, String nowNodeCode); + List getBackTaskNode(Long taskId, String nowNodeCode); /** * 终止任务 @@ -151,14 +149,6 @@ public interface IFlwTaskService { */ List getNextNodeList(FlowNextNodeBo bo); - /** - * 按照任务id查询任务 - * - * @param taskIdList 任务id - * @return 结果 - */ - List selectHisTaskByIdList(List taskIdList); - /** * 按照任务id查询任务 * @@ -167,14 +157,6 @@ public interface IFlwTaskService { */ FlowHisTask selectHisTaskById(Long taskId); - /** - * 按照实例id查询任务 - * - * @param instanceIdList 流程实例id - * @return 结果 - */ - List selectByInstIdList(List instanceIdList); - /** * 按照实例id查询任务 * @@ -183,6 +165,22 @@ public interface IFlwTaskService { */ List selectByInstId(Long instanceId); + /** + * 按照实例id查询任务 + * + * @param instanceIds 列表 + * @return 结果 + */ + List selectByInstIds(List instanceIds); + + /** + * 判断流程是否已结束(即该流程实例下是否还有未完成的任务) + * + * @param instanceId 流程实例ID + * @return true 表示任务已全部结束;false 表示仍有任务存在 + */ + boolean isTaskEnd(Long instanceId); + /** * 任务操作 * @@ -192,21 +190,13 @@ public interface IFlwTaskService { */ boolean taskOperation(TaskOperationBo bo, String taskOperation); - /** - * 获取任务所有办理人 - * - * @param taskIdList 任务id - * @return 结果 - */ - Map> currentTaskAllUser(List taskIdList); - /** * 获取当前任务的所有办理人 * - * @param taskId 任务id + * @param taskIds 任务id * @return 结果 */ - List currentTaskAllUser(Long taskId); + List currentTaskAllUser(List taskIds); /** * 按照节点编码查询节点 @@ -217,6 +207,11 @@ public interface IFlwTaskService { */ FlowNode getByNodeCode(String nodeCode, Long definitionId); - - void copyRead(@PathVariable Long flowUserId); + /** + * 催办任务 + * + * @param bo 参数 + * @return 结果 + */ + boolean urgeTask(FlowUrgeTaskBo bo); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/ITestLeaveService.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/ITestLeaveService.java index 67b50baf..748a2b17 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/ITestLeaveService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/ITestLeaveService.java @@ -35,6 +35,11 @@ public interface ITestLeaveService { */ TestLeaveVo insertByBo(TestLeaveBo bo); + /** + * 提交请假并发起流程 + */ + TestLeaveVo submitAndFlowStart(TestLeaveBo bo); + /** * 修改请假 */ diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCategoryServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCategoryServiceImpl.java index d42a48a2..1e2f6c26 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCategoryServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCategoryServiceImpl.java @@ -1,6 +1,7 @@ package org.dromara.workflow.service.impl; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -8,14 +9,11 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.exception.ServiceException; -import org.dromara.common.core.utils.MapstructUtils; -import org.dromara.common.core.utils.ObjectUtils; -import org.dromara.common.core.utils.StringUtils; -import org.dromara.common.core.utils.TreeBuildUtils; +import org.dromara.common.core.utils.*; import org.dromara.common.mybatis.helper.DataBaseHelper; -import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.warm.flow.core.service.DefService; import org.dromara.warm.flow.orm.entity.FlowDefinition; +import org.dromara.warm.flow.ui.service.CategoryService; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.domain.FlowCategory; @@ -26,6 +24,7 @@ import org.dromara.workflow.service.IFlwCategoryService; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.List; @@ -38,7 +37,7 @@ import java.util.List; @ConditionalOnEnable @RequiredArgsConstructor @Service -public class FlwCategoryServiceImpl implements IFlwCategoryService { +public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryService { private final DefService defService; private final FlwCategoryMapper baseMapper; @@ -97,32 +96,29 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService { } return TreeBuildUtils.buildMultiRoot( categoryList, - node -> String.valueOf(node.getCategoryId()), - node -> String.valueOf(node.getParentId()), + node -> Convert.toStr(node.getCategoryId()), + node -> Convert.toStr(node.getParentId()), (node, treeNode) -> treeNode - .setId(String.valueOf(node.getCategoryId())) - .setParentId(String.valueOf(node.getParentId())) + .setId(Convert.toStr(node.getCategoryId())) + .setParentId(Convert.toStr(node.getParentId())) .setName(node.getCategoryName()) .setWeight(node.getOrderNum()) ); } /** - * 校验流程分类是否有数据权限 + * 工作流查询分类 * - * @param categoryId 流程分类ID + * @return 分类树结构列表 */ @Override - public void checkCategoryDataScope(Long categoryId) { - if (ObjectUtil.isNull(categoryId)) { - return; - } - if (LoginHelper.isSuperAdmin()) { - return; - } - if (baseMapper.countCategoryById(categoryId) == 0) { - throw new ServiceException("没有权限访问流程分类数据!"); - } + public List queryCategory() { + List list = this.queryList(new FlowCategoryBo()); + return StreamUtils.toList(list, category -> new org.dromara.warm.flow.core.dto.Tree() + .setId(Convert.toStr(category.getCategoryId())) + .setName(category.getCategoryName()) + .setParentId(Convert.toStr(category.getParentId())) + ); } /** @@ -187,6 +183,9 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService { @Override public int insertByBo(FlowCategoryBo bo) { FlowCategory info = baseMapper.selectById(bo.getParentId()); + if (ObjectUtil.isNull(info)) { + throw new ServiceException("父级流程分类不存在!"); + } FlowCategory category = MapstructUtils.convert(bo, FlowCategory.class); category.setAncestors(info.getAncestors() + StringUtils.SEPARATOR + category.getParentId()); return baseMapper.insert(category); @@ -200,6 +199,7 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService { */ @CacheEvict(cacheNames = FlowConstant.FLOW_CATEGORY_NAME, key = "#bo.categoryId") @Override + @Transactional(rollbackFor = Exception.class) public int updateByBo(FlowCategoryBo bo) { FlowCategory category = MapstructUtils.convert(bo, FlowCategory.class); FlowCategory oldCategory = baseMapper.selectById(category.getCategoryId()); @@ -207,14 +207,14 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService { throw new ServiceException("流程分类不存在,无法修改"); } if (!oldCategory.getParentId().equals(category.getParentId())) { - // 如果是新父流程分类 则校验是否具有新父流程分类权限 避免越权 - this.checkCategoryDataScope(category.getParentId()); FlowCategory newParentCategory = baseMapper.selectById(category.getParentId()); if (ObjectUtil.isNotNull(newParentCategory)) { String newAncestors = newParentCategory.getAncestors() + StringUtils.SEPARATOR + newParentCategory.getCategoryId(); String oldAncestors = oldCategory.getAncestors(); category.setAncestors(newAncestors); updateCategoryChildren(category.getCategoryId(), newAncestors, oldAncestors); + } else { + throw new ServiceException("父级流程分类不存在!"); } } else { category.setAncestors(oldCategory.getAncestors()); diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwChartExtServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwChartExtServiceImpl.java index d7c6f77e..cb82cbed 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwChartExtServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwChartExtServiceImpl.java @@ -12,7 +12,6 @@ import org.dromara.common.core.service.DeptService; import org.dromara.common.core.service.DictService; import org.dromara.common.core.service.UserService; import org.dromara.common.core.utils.DateUtils; -import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.warm.flow.core.dto.DefJson; @@ -25,10 +24,15 @@ import org.dromara.warm.flow.orm.mapper.FlowHisTaskMapper; import org.dromara.warm.flow.ui.service.ChartExtService; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.constant.FlowConstant; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; /** * 流程图提示信息 @@ -45,6 +49,8 @@ public class FlwChartExtServiceImpl implements ChartExtService { private final DeptService deptService; private final FlowHisTaskMapper flowHisTaskMapper; private final DictService dictService; + @Value("${warm-flow.node-tooltip:true}") + private boolean nodeTooltip; /** * 设置流程图提示信息 @@ -53,12 +59,13 @@ public class FlwChartExtServiceImpl implements ChartExtService { */ @Override public void execute(DefJson defJson) { - // 临时修复 后续版本将通过defjson获取流程实例ID - String[] parts = ServletUtils.getRequest().getRequestURI().split("/"); - Long instanceId = Convert.toLong(parts[parts.length - 1]); + // 配置关闭,直接返回,不渲染悬浮窗 + if (!nodeTooltip) { + return; + } // 根据流程实例ID查询所有相关的历史任务列表 - List flowHisTasks = this.getHisTaskGroupedByNode(instanceId); + List flowHisTasks = this.getHisTaskGroupedByNode(defJson.getInstance().getId()); if (CollUtil.isEmpty(flowHisTasks)) { return; } @@ -74,15 +81,26 @@ public class FlwChartExtServiceImpl implements ChartExtService { Map dictType = dictService.getAllDictByDictType(FlowConstant.WF_TASK_STATUS); - // 遍历流程定义中的每个节点,调用处理方法,将对应节点的任务列表及用户信息传入,生成扩展提示内容 for (NodeJson nodeJson : defJson.getNodeList()) { - // 获取当前节点对应的历史任务列表,如果没有则返回空列表避免空指针 List taskList = groupedByNode.get(nodeJson.getNodeCode()); if (CollUtil.isEmpty(taskList)) { continue; } - // 处理当前节点的扩展信息,包括构建审批人提示内容等 - this.processNodeExtInfo(nodeJson, taskList, userMap, dictType); + + // 按审批人分组去重,保留最新处理记录,最终转换成 List + List latestPerApprover = taskList.stream() + .collect(Collectors.collectingAndThen( + Collectors.toMap( + FlowHisTask::getApprover, + Function.identity(), + (oldTask, newTask) -> newTask.getUpdateTime().after(oldTask.getUpdateTime()) ? newTask : oldTask, + LinkedHashMap::new + ), + map -> new ArrayList<>(map.values()) + )); + + // 处理当前节点的扩展信息 + this.processNodeExtInfo(nodeJson, latestPerApprover, userMap, dictType); } } @@ -93,6 +111,11 @@ public class FlwChartExtServiceImpl implements ChartExtService { */ @Override public void initPromptContent(DefJson defJson) { + // 配置关闭,直接返回,不渲染悬浮窗 + if (!nodeTooltip) { + return; + } + defJson.setTopText("流程名称: " + defJson.getFlowName()); defJson.getNodeList().forEach(nodeJson -> { nodeJson.setPromptContent( @@ -128,7 +151,8 @@ public class FlwChartExtServiceImpl implements ChartExtService { "fontSize", "14px", "zIndex", "1000", "maxWidth", "500px", - "overflowY", "visible", + "maxHeight", "300px", + "overflowY", "auto", "overflowX", "hidden", "color", "#333", "pointerEvents", "auto", @@ -141,8 +165,10 @@ public class FlwChartExtServiceImpl implements ChartExtService { /** * 处理节点的扩展信息,构建用于流程图悬浮提示的内容 * - * @param nodeJson 当前节点对象 - * @param taskList 当前节点对应的历史审批任务列表 + * @param nodeJson 当前流程节点对象,包含节点基础信息和提示内容容器 + * @param taskList 当前节点关联的历史审批任务列表,用于生成提示信息 + * @param userMap 用户信息映射表,key 为用户ID,value 为用户DTO对象,用于获取审批人信息 + * @param dictType 数据字典映射表,key 为字典项编码,value 为对应显示值,用于翻译审批状态等 */ private void processNodeExtInfo(NodeJson nodeJson, List taskList, Map userMap, Map dictType) { @@ -240,7 +266,7 @@ public class FlwChartExtServiceImpl implements ChartExtService { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.eq(FlowHisTask::getInstanceId, instanceId) .eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey()) - .orderByDesc(FlowHisTask::getCreateTime, FlowHisTask::getUpdateTime); + .orderByDesc(FlowHisTask::getUpdateTime); return flowHisTaskMapper.selectList(wrapper); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java index d313e088..46648ffa 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java @@ -35,10 +35,7 @@ import org.dromara.workflow.service.IFlwTaskAssigneeService; import org.dromara.workflow.service.IFlwTaskService; import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; @@ -56,25 +53,6 @@ public class FlwCommonServiceImpl implements IFlwCommonService { private final SseProperties properties; - /** - * 构建工作流用户 - * - * @param permissionList 办理用户 - * @return 用户 - */ - @Override - public List buildUser(List permissionList) { - if (CollUtil.isEmpty(permissionList)) { - return List.of(); - } - IFlwTaskAssigneeService taskAssigneeService = SpringUtils.getBean(IFlwTaskAssigneeService.class); - String processedBys = CollUtil.join(permissionList, StringUtils.SEPARATOR); - // 根据 processedBy 前缀判断处理人类型,分别获取用户列表 - List users = taskAssigneeService.fetchUsersByStorageIds(processedBys, null); - - return StreamUtils.toList(users, userDTO -> String.valueOf(userDTO.getUserId())); - } - /** * 发送消息 @@ -93,7 +71,7 @@ public class FlwCommonServiceImpl implements IFlwCommonService { } for (Task task : list) { - List users = flwTaskService.currentTaskAllUser(task.getId()); + List users = flwTaskService.currentTaskAllUser(StreamUtils.toList(list, FlowTask::getId)); if (CollUtil.isNotEmpty(users)) { userList.addAll(users); } @@ -194,6 +172,76 @@ public class FlwCommonServiceImpl implements IFlwCommonService { SseMessageUtils.publishMessage(dto); } + + + private static final String DEFAULT_SUBJECT = "单据审批提醒"; + + /** + * 根据流程实例发送消息给当前处理人 + * + * @param flowName 流程定义名称 + * @param instId 流程实例ID + * @param messageType 消息类型列表 + * @param message 消息内容,为空则使用默认消息 + */ + @Override + public void sendMessage(String flowName, Long instId, List messageType, String message) { + if (CollUtil.isEmpty(messageType)) { + return; + } + IFlwTaskService flwTaskService = SpringUtils.getBean(IFlwTaskService.class); + List list = flwTaskService.selectByInstId(instId); + if (CollUtil.isEmpty(list)) { + return; + } + if (StringUtils.isBlank(message)) { + message = "有新的【" + flowName + "】单据已经提交至您,请您及时处理。"; + } + List userList = flwTaskService.currentTaskAllUser(StreamUtils.toList(list, FlowTask::getId)); + if (CollUtil.isEmpty(userList)) { + return; + } + sendMessage(messageType, message, DEFAULT_SUBJECT, userList); + } + + /** + * 发送消息给指定用户列表 + * + * @param messageType 消息类型列表 + * @param message 消息内容 + * @param subject 邮件标题 + * @param userList 接收用户列表 + */ + @Override + public void sendMessage(List messageType, String message, String subject, List userList) { + if (CollUtil.isEmpty(messageType) || CollUtil.isEmpty(userList)) { + return; + } + List userIds = new ArrayList<>(StreamUtils.toSet(userList, UserDTO::getUserId)); + Set emails = StreamUtils.toSet(userList, UserDTO::getEmail); + + for (String code : messageType) { + MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(code); + if (ObjectUtil.isEmpty(messageTypeEnum)) { + continue; + } + switch (messageTypeEnum) { + case SYSTEM_MESSAGE -> { + SseMessageDto dto = new SseMessageDto(); + dto.setUserIds(userIds); + dto.setMessage(message); + SseMessageUtils.publishMessage(dto); + } + case EMAIL_MESSAGE -> MailUtils.sendText(emails, subject, message); + case SMS_MESSAGE -> { + //todo 短信发送 + } + default -> throw new IllegalStateException("Unexpected value: " + messageTypeEnum); + } + } + } + + /** * 申请人节点编码 * @@ -202,8 +250,7 @@ public class FlwCommonServiceImpl implements IFlwCommonService { */ @Override public String applyNodeCode(Long definitionId) { - Node startNode = nodeService.getStartNode(definitionId); - Node nextNode = nodeService.getNextNode(definitionId, startNode.getNodeCode(), null, SkipType.PASS.getKey()); - return nextNode.getNodeCode(); + List firstBetweenNode = FlowEngine.nodeService().getFirstBetweenNode(definitionId, new HashMap<>()); + return firstBetweenNode.get(0).getNodeCode(); } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java index 10e753e8..b89a369c 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java @@ -30,9 +30,7 @@ import org.dromara.warm.flow.orm.mapper.FlowNodeMapper; import org.dromara.warm.flow.orm.mapper.FlowSkipMapper; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.constant.FlowConstant; -import org.dromara.workflow.common.enums.FlwDefinitionSuffixTemplateEnum; import org.dromara.workflow.domain.FlowCategory; -import org.dromara.workflow.domain.bo.FlowDefinitionBo; import org.dromara.workflow.domain.vo.FlowDefinitionVo; import org.dromara.workflow.mapper.FlwCategoryMapper; import org.dromara.workflow.service.IFlwCommonService; @@ -71,13 +69,13 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService { /** * 查询流程定义列表 * - * @param flowDefinitionBo 流程定义信息 - * @param pageQuery 分页 + * @param flowDefinition 流程定义信息 + * @param pageQuery 分页 * @return 返回分页列表 */ @Override - public TableDataInfo queryList(FlowDefinitionBo flowDefinitionBo, PageQuery pageQuery) { - LambdaQueryWrapper wrapper = buildQueryWrapper(flowDefinitionBo); + public TableDataInfo queryList(FlowDefinition flowDefinition, PageQuery pageQuery) { + LambdaQueryWrapper wrapper = buildQueryWrapper(flowDefinition); wrapper.eq(FlowDefinition::getIsPublish, PublishStatus.PUBLISHED.getKey()); Page page = flowDefinitionMapper.selectPage(pageQuery.build(), wrapper); List list = BeanUtil.copyToList(page.getRecords(), FlowDefinitionVo.class); @@ -87,28 +85,27 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService { /** * 查询未发布的流程定义列表 * - * @param flowDefinitionBo 流程定义信息 - * @param pageQuery 分页 + * @param flowDefinition 流程定义信息 + * @param pageQuery 分页 * @return 返回分页列表 */ @Override - public TableDataInfo unPublishList(FlowDefinitionBo flowDefinitionBo, PageQuery pageQuery) { - LambdaQueryWrapper wrapper = buildQueryWrapper(flowDefinitionBo); + public TableDataInfo unPublishList(FlowDefinition flowDefinition, PageQuery pageQuery) { + LambdaQueryWrapper wrapper = buildQueryWrapper(flowDefinition); wrapper.in(FlowDefinition::getIsPublish, Arrays.asList(PublishStatus.UNPUBLISHED.getKey(), PublishStatus.EXPIRED.getKey())); Page page = flowDefinitionMapper.selectPage(pageQuery.build(), wrapper); List list = BeanUtil.copyToList(page.getRecords(), FlowDefinitionVo.class); return new TableDataInfo<>(list, page.getTotal()); } - private LambdaQueryWrapper buildQueryWrapper(FlowDefinitionBo flowDefinitionBo) { + private LambdaQueryWrapper buildQueryWrapper(FlowDefinition flowDefinition) { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); - wrapper.like(StringUtils.isNotBlank(flowDefinitionBo.getFlowCode()), FlowDefinition::getFlowCode, flowDefinitionBo.getFlowCode()); - wrapper.like(StringUtils.isNotBlank(flowDefinitionBo.getFlowName()), FlowDefinition::getFlowName, flowDefinitionBo.getFlowName()); - if (StringUtils.isNotBlank(flowDefinitionBo.getCategory())) { - List categoryIds = flwCategoryMapper.selectCategoryIdsByParentId(Convert.toLong(flowDefinitionBo.getCategory())); + wrapper.like(StringUtils.isNotBlank(flowDefinition.getFlowCode()), FlowDefinition::getFlowCode, flowDefinition.getFlowCode()); + wrapper.like(StringUtils.isNotBlank(flowDefinition.getFlowName()), FlowDefinition::getFlowName, flowDefinition.getFlowName()); + if (StringUtils.isNotBlank(flowDefinition.getCategory())) { + List categoryIds = flwCategoryMapper.selectCategoryIdsByParentId(Convert.toLong(flowDefinition.getCategory())); wrapper.in(FlowDefinition::getCategory, StreamUtils.toList(categoryIds, Convert::toStr)); } - wrapper.likeRight(FlowDefinition::getFlowCode, flowDefinitionBo.getProjectId() + "_"); wrapper.orderByDesc(FlowDefinition::getCreateTime); return wrapper; } @@ -124,14 +121,14 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService { List flowNodes = flowNodeMapper.selectList(new LambdaQueryWrapper().eq(FlowNode::getDefinitionId, id)); List errorMsg = new ArrayList<>(); if (CollUtil.isNotEmpty(flowNodes)) { + String applyNodeCode = flwCommonService.applyNodeCode(id); for (FlowNode flowNode : flowNodes) { - String applyNodeCode = flwCommonService.applyNodeCode(id); if (StringUtils.isBlank(flowNode.getPermissionFlag()) && !applyNodeCode.equals(flowNode.getNodeCode()) && NodeType.BETWEEN.getKey().equals(flowNode.getNodeType())) { errorMsg.add(flowNode.getNodeName()); } } if (CollUtil.isNotEmpty(errorMsg)) { - throw new ServiceException("节点【" + StringUtils.join(errorMsg, ",") + "】未配置办理人!"); + throw new ServiceException("节点【{}】未配置办理人!", StringUtils.joinComma(errorMsg)); } } return defService.publish(id); @@ -191,7 +188,7 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService { if (CollUtil.isNotEmpty(flowDefinitions)) { String join = StreamUtils.join(flowDefinitions, FlowDefinition::getFlowCode); log.info("流程定义【{}】已被使用不可被删除!", join); - throw new ServiceException("流程定义【" + join + "】已被使用不可被删除!"); + throw new ServiceException("流程定义【{}】已被使用不可被删除!", join); } } try { @@ -203,27 +200,6 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService { return true; } - /** - * 新增流程定义 - * - * @param projectId 项目id - */ - @Override - public void insertDefByProjectId(Long projectId) { - FlwDefinitionSuffixTemplateEnum[] suffixList = FlwDefinitionSuffixTemplateEnum.values(); - for (FlwDefinitionSuffixTemplateEnum suffix : suffixList) { - FlowDefinition definition = new FlowDefinition(); - definition.setCategory("100"); - definition.setFlowCode(projectId + suffix.getCode()); - definition.setFlowName(suffix.getName()); - definition.setFormPath(suffix.getPath()); - boolean result = defService.checkAndSave(definition); - if (!result) { - log.info("流程定义[{}-{}]失败!", suffix.getName(), projectId + suffix.getCode()); - } - } - } - /** * 新增租户流程定义 * @@ -237,9 +213,11 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService { return; } FlowCategory flowCategory = flwCategoryMapper.selectOne(new LambdaQueryWrapper() - .eq(FlowCategory::getTenantId, DEFAULT_TENANT_ID).eq(FlowCategory::getCategoryId, FlowConstant.FLOW_CATEGORY_ID)); + .eq(FlowCategory::getTenantId, DEFAULT_TENANT_ID) + .eq(FlowCategory::getCategoryId, FlowConstant.FLOW_CATEGORY_ID)); flowCategory.setCategoryId(null); flowCategory.setTenantId(tenantId); + flowCategory.setCreateDept(null); flowCategory.setCreateBy(null); flowCategory.setCreateTime(null); flowCategory.setUpdateBy(null); @@ -253,7 +231,7 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService { flowDefinition.setId(null); flowDefinition.setTenantId(tenantId); flowDefinition.setIsPublish(0); - flowDefinition.setCategory(String.valueOf(flowCategory.getCategoryId())); + flowDefinition.setCategory(Convert.toStr(flowCategory.getCategoryId())); int insert = flowDefinitionMapper.insert(flowDefinition); if (insert <= 0) { log.info("同步流程定义【{}】失败!", definition.getFlowCode()); diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java index 6e6434d9..501d070c 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java @@ -11,7 +11,6 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.core.enums.BusinessStatusEnum; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.StreamUtils; @@ -19,11 +18,13 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.constant.ExceptionCons; import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.entity.Definition; import org.dromara.warm.flow.core.entity.Instance; import org.dromara.warm.flow.core.entity.Task; +import org.dromara.warm.flow.core.entity.User; import org.dromara.warm.flow.core.enums.NodeType; import org.dromara.warm.flow.core.service.DefService; import org.dromara.warm.flow.core.service.InsService; @@ -40,6 +41,7 @@ import org.dromara.workflow.common.enums.TaskStatusEnum; import org.dromara.workflow.domain.bo.FlowCancelBo; import org.dromara.workflow.domain.bo.FlowInstanceBo; import org.dromara.workflow.domain.bo.FlowInvalidBo; +import org.dromara.workflow.domain.bo.FlowVariableBo; import org.dromara.workflow.domain.vo.FlowHisTaskVo; import org.dromara.workflow.domain.vo.FlowInstanceVo; import org.dromara.workflow.handler.FlowProcessEventHandler; @@ -51,7 +53,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; -import java.util.stream.Collectors; +import java.util.function.Function; /** * 流程实例 服务层实现 @@ -111,7 +113,7 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { */ @Override public FlowInstanceVo queryByBusinessId(Long businessId) { - FlowInstance instance = this.selectInstByBusinessId(String.valueOf(businessId)); + FlowInstance instance = this.selectInstByBusinessId(Convert.toStr(businessId)); FlowInstanceVo instanceVo = BeanUtil.toBean(instance, FlowInstanceVo.class); Definition definition = defService.getById(instanceVo.getDefinitionId()); instanceVo.setFlowName(definition.getFlowName()); @@ -206,22 +208,77 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { return false; } // 获取定义信息 - Map definitionMap = defService.getByIds( - StreamUtils.toList(instances, Instance::getDefinitionId) - ).stream().collect(Collectors.toMap(Definition::getId, definition -> definition)); + Map definitionMap = StreamUtils.toMap( + defService.getByIds(StreamUtils.toList(instances, Instance::getDefinitionId)), + Definition::getId, + Function.identity() + ); - // 逐一触发删除事件 - instances.forEach(instance -> { - Definition definition = definitionMap.get(instance.getDefinitionId()); - if (ObjectUtil.isNull(definition)) { - log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId()); - return; + try { + // 逐一触发删除事件 + instances.forEach(instance -> { + Definition definition = definitionMap.get(instance.getDefinitionId()); + if (ObjectUtil.isNull(definition)) { + log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId()); + return; + } + flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId()); + }); + // 删除实例 + boolean remove = insService.remove(instanceIds); + if (!remove) { + log.warn("删除流程实例失败!"); + throw new ServiceException("删除流程实例失败"); } - flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId()); - }); + } catch (Exception e) { + log.warn("操作失败!{}", e.getMessage()); + throw new ServiceException(e.getMessage()); + } + return true; + } - // 删除实例 - return insService.remove(instanceIds); + /** + * 按照实例id删除已完成的流程实例 + * + * @param instanceIds 实例id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteHisByInstanceIds(List instanceIds) { + // 获取实例信息 + List instances = insService.getByIds(instanceIds); + if (CollUtil.isEmpty(instances)) { + log.warn("未找到对应的流程实例信息,无法执行删除操作。"); + return false; + } + // 获取定义信息 + Map definitionMap = StreamUtils.toMap( + defService.getByIds(StreamUtils.toList(instances, Instance::getDefinitionId)), + Definition::getId, + Function.identity() + ); + try { + // 逐一触发删除事件 + instances.forEach(instance -> { + Definition definition = definitionMap.get(instance.getDefinitionId()); + if (ObjectUtil.isNull(definition)) { + log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId()); + return; + } + flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId()); + }); + List flowTaskList = flwTaskService.selectByInstIds(instanceIds); + if (CollUtil.isNotEmpty(flowTaskList)) { + FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTaskList, FlowTask::getId)); + } + FlowEngine.taskService().deleteByInsIds(instanceIds); + FlowEngine.hisTaskService().deleteByInsIds(instanceIds); + FlowEngine.insService().removeByIds(instanceIds); + } catch (Exception e) { + log.warn("操作失败!{}", e.getMessage()); + throw new ServiceException(e.getMessage()); + } + return true; } /** @@ -267,6 +324,7 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { @Override public TableDataInfo selectCurrentInstanceList(FlowInstanceBo instanceBo, PageQuery pageQuery) { QueryWrapper queryWrapper = buildQueryWrapper(instanceBo); + queryWrapper.eq("fi.create_by", LoginHelper.getUserIdStr()); if (!"0".equals(instanceBo.getProjectId())){ List flowDefinitions = flowDefinitionMapper.selectList(new LambdaQueryWrapper() .select(FlowDefinition::getId) @@ -282,7 +340,6 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { } queryWrapper.in("fi.definition_id",definitionIds); } - queryWrapper.eq("fi.create_by", LoginHelper.getUserIdStr()); Page page = flwInstanceMapper.selectInstanceList(pageQuery.build(), queryWrapper); return TableDataInfo.build(page); } @@ -299,37 +356,47 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE); } Long instanceId = flowInstance.getId(); - //运行中的任务 - List list = new ArrayList<>(); - List flowTaskList = flwTaskService.selectByInstId(instanceId); - if (CollUtil.isNotEmpty(flowTaskList)) { - List flowHisTaskVos = BeanUtil.copyToList(flowTaskList, FlowHisTaskVo.class); - for (FlowHisTaskVo flowHisTaskVo : flowHisTaskVos) { - flowHisTaskVo.setFlowStatus(TaskStatusEnum.WAITING.getStatus()); - flowHisTaskVo.setUpdateTime(null); - flowHisTaskVo.setRunDuration(null); - List allUser = flwTaskService.currentTaskAllUser(flowHisTaskVo.getId()); - if (CollUtil.isNotEmpty(allUser)) { - String join = StreamUtils.join(allUser, e -> String.valueOf(e.getUserId())); - flowHisTaskVo.setApprover(join); - } - if (BusinessStatusEnum.isDraftOrCancelOrBack(flowInstance.getFlowStatus())) { - flowHisTaskVo.setApprover(LoginHelper.getUserIdStr()); - flowHisTaskVo.setApproveName(LoginHelper.getLoginUser().getNickname()); + + // 先组装待审批任务(运行中的任务) + List runningTaskVos = new ArrayList<>(); + List runningTasks = flwTaskService.selectByInstId(instanceId); + if (CollUtil.isNotEmpty(runningTasks)) { + runningTaskVos = BeanUtil.copyToList(runningTasks, FlowHisTaskVo.class); + + List associatedUsers = FlowEngine.userService() + .getByAssociateds(StreamUtils.toList(runningTasks, FlowTask::getId)); + Map> taskUserMap = StreamUtils.groupByKey(associatedUsers, User::getAssociated); + + for (FlowHisTaskVo vo : runningTaskVos) { + vo.setFlowStatus(TaskStatusEnum.WAITING.getStatus()); + vo.setUpdateTime(null); + vo.setRunDuration(null); + + List users = taskUserMap.get(vo.getId()); + if (CollUtil.isNotEmpty(users)) { + vo.setApprover(StreamUtils.join(users, User::getProcessedBy)); } } - list.addAll(flowHisTaskVos); } - //历史任务 - LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); - wrapper.eq(FlowHisTask::getInstanceId, instanceId) - .eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey()) - .orderByDesc(FlowHisTask::getCreateTime, FlowHisTask::getUpdateTime); - List flowHisTasks = flowHisTaskMapper.selectList(wrapper); - if (CollUtil.isNotEmpty(flowHisTasks)) { - list.addAll(BeanUtil.copyToList(flowHisTasks, FlowHisTaskVo.class)); + + // 再组装历史任务(已处理任务) + List hisTaskVos = new ArrayList<>(); + List hisTasks = flowHisTaskMapper.selectList( + new LambdaQueryWrapper() + .eq(FlowHisTask::getInstanceId, instanceId) + .eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey()) + .orderByDesc(FlowHisTask::getUpdateTime) + ); + if (CollUtil.isNotEmpty(hisTasks)) { + hisTaskVos = BeanUtil.copyToList(hisTasks, FlowHisTaskVo.class); } - return Map.of("list", list, "instanceId", instanceId); + + // 结果列表,待审批任务在前,历史任务在后 + List combinedList = new ArrayList<>(); + combinedList.addAll(runningTaskVos); + combinedList.addAll(hisTaskVos); + + return Map.of("list", combinedList, "instanceId", instanceId); } /** @@ -361,6 +428,34 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { return Map.of("variableList", variableList, "variable", flowInstance.getVariable()); } + /** + * 设置流程变量 + * + * @param bo 参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateVariable(FlowVariableBo bo) { + FlowInstance flowInstance = flowInstanceMapper.selectById(bo.getInstanceId()); + if (flowInstance == null) { + throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE); + } + try { + Map variableMap = new HashMap<>(Optional.ofNullable(flowInstance.getVariableMap()).orElse(Collections.emptyMap())); + if (!variableMap.containsKey(bo.getKey())) { + log.error("变量不存在: {}", bo.getKey()); + return false; + } + variableMap.put(bo.getKey(), bo.getValue()); + flowInstance.setVariable(FlowEngine.jsonConvert.objToStr(variableMap)); + flowInstanceMapper.updateById(flowInstance); + } catch (Exception e) { + log.error("设置流程变量失败: {}", e.getMessage(), e); + throw new ServiceException(e.getMessage()); + } + return true; + } + /** * 设置流程变量 * @@ -395,31 +490,6 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { return null; } - /** - * 按任务id查询实例 - * - * @param taskIdList 任务id - */ - @Override - public List selectByTaskIdList(List taskIdList) { - if (CollUtil.isEmpty(taskIdList)) { - return Collections.emptyList(); - } - Set instanceIds = new HashSet<>(); - List flowTaskList = flwTaskService.selectByIdList(taskIdList); - for (FlowTask flowTask : flowTaskList) { - instanceIds.add(flowTask.getInstanceId()); - } - List flowHisTaskList = flwTaskService.selectHisTaskByIdList(taskIdList); - for (FlowHisTask flowHisTask : flowHisTaskList) { - instanceIds.add(flowHisTask.getInstanceId()); - } - if (!instanceIds.isEmpty()) { - return this.selectInstListByIdList(new ArrayList<>(instanceIds)); - } - return Collections.emptyList(); - } - /** * 作废流程 * diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java index fca21a36..ce845dc4 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java @@ -1,26 +1,31 @@ package org.dromara.workflow.service.impl; import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.domain.dto.DictTypeDTO; import org.dromara.common.core.service.DictService; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.json.utils.JsonUtils; +import org.dromara.warm.flow.core.FlowEngine; +import org.dromara.warm.flow.core.utils.CollUtil; +import org.dromara.warm.flow.core.utils.ExpressionUtil; import org.dromara.warm.flow.ui.service.NodeExtService; import org.dromara.warm.flow.ui.vo.NodeExt; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.enums.ButtonPermissionEnum; +import org.dromara.workflow.common.enums.CopySettingEnum; import org.dromara.workflow.common.enums.NodeExtEnum; +import org.dromara.workflow.common.enums.VariablesEnum; import org.dromara.workflow.domain.vo.ButtonPermissionVo; +import org.dromara.workflow.domain.vo.NodeExtVo; import org.dromara.workflow.service.IFlwNodeExtService; import org.springframework.stereotype.Service; import java.util.*; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * 流程设计器-节点扩展属性 @@ -36,14 +41,35 @@ public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService /** * 存储不同 dictType 对应的配置信息 */ - private static final Map CHILD_NODE_MAP = new HashMap<>(); - - record ButtonPermission(String label, Integer type, Boolean must, Boolean multiple) { - } + private static final Map> CHILD_NODE_MAP; static { - CHILD_NODE_MAP.put(ButtonPermissionEnum.class.getSimpleName(), - new ButtonPermission("权限按钮", 4, false, true)); + CHILD_NODE_MAP = Map.of( + CopySettingEnum.class.getSimpleName(), + Map.of( + "label", "抄送对象", + "type", 5, + "must", false, + "multiple", false, + "desc", "设置该节点的抄送办理人" + ), + VariablesEnum.class.getSimpleName(), + Map.of( + "label", "自定义参数", + "type", 2, + "must", false, + "multiple", false, + "desc", "节点执行时可设置自定义参数,多个参数以逗号分隔,如:key1=value1,key2=value2" + ), + ButtonPermissionEnum.class.getSimpleName(), + Map.of( + "label", "权限按钮", + "type", 4, + "must", false, + "multiple", true, + "desc", "控制该节点的按钮权限" + ) + ); } private final DictService dictService; @@ -56,6 +82,9 @@ public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService @Override public List getNodeExt() { List nodeExtList = new ArrayList<>(); + // 构建基础设置页面 + nodeExtList.add(buildNodeExt("wf_basic_tab", "基础设置", 1, + List.of(CopySettingEnum.class, VariablesEnum.class))); // 构建按钮权限页面 nodeExtList.add(buildNodeExt("wf_button_tab", "权限", 2, List.of(ButtonPermissionEnum.class))); @@ -105,9 +134,20 @@ public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService return null; } String simpleName = enumClass.getSimpleName(); - NodeExt.ChildNode childNode = buildChildNodeMap(simpleName); + NodeExt.ChildNode childNode = new NodeExt.ChildNode(); + Map map = CHILD_NODE_MAP.get(simpleName); // 编码,此json中唯 childNode.setCode(simpleName); + // label名称 + childNode.setLabel(Convert.toStr(map.get("label"))); + // 1:输入框 2:文本域 3:下拉框 4:选择框 5:用户选择器 + childNode.setType(Convert.toInt(map.get("type"), 1)); + // 是否必填 + childNode.setMust(Convert.toBool(map.get("must"), false)); + // 是否多选 + childNode.setMultiple(Convert.toBool(map.get("multiple"), true)); + // 描述 + childNode.setDesc(Convert.toStr(map.get("desc"), null)); // 字典,下拉框和复选框时用到 childNode.setDict(Arrays.stream(enumClass.getEnumConstants()) .map(NodeExtEnum.class::cast) @@ -128,12 +168,18 @@ public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService if (ObjectUtil.isNull(dictTypeDTO)) { return null; } - NodeExt.ChildNode childNode = buildChildNodeMap(dictType); + NodeExt.ChildNode childNode = new NodeExt.ChildNode(); // 编码,此json中唯一 childNode.setCode(dictType); // label名称 childNode.setLabel(dictTypeDTO.getDictName()); - // 描述 + // 1:输入框 2:文本域 3:下拉框 4:选择框 5:用户选择器 + childNode.setType(3); + // 是否必填 + childNode.setMust(false); + // 是否多选 + childNode.setMultiple(true); + // 描述 (可根据描述参数解析更多配置,如type,must,multiple等) childNode.setDesc(dictTypeDTO.getRemark()); // 字典,下拉框和复选框时用到 childNode.setDict(dictService.getDictData(dictType) @@ -144,100 +190,165 @@ public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService } /** - * 根据 CHILD_NODE_MAP 中的配置信息,构建一个基本的 ChildNode 对象 - * 该方法用于设置 ChildNode 的常规属性,例如 label、type、是否必填、是否多选等 + * 解析扩展属性 JSON 并构建 Node 扩展属性对象 + *

+ * 根据传入的 JSON 字符串,将扩展属性分为三类: + * 1. ButtonPermissionEnum:解析为按钮权限列表,标记每个按钮是否勾选 + * 2. CopySettingEnum:解析为抄送对象 ID 集合 + * 3. VariablesEnum:解析为自定义参数 Map * - * @param key CHILD_NODE_MAP 的 key - * @return 返回构建好的 ChildNode 对象 - */ - private NodeExt.ChildNode buildChildNodeMap(String key) { - NodeExt.ChildNode childNode = new NodeExt.ChildNode(); - ButtonPermission bp = CHILD_NODE_MAP.get(key); - if (bp == null) { - childNode.setType(1); - childNode.setMust(false); - childNode.setMultiple(true); - return childNode; - } - // label名称 - childNode.setLabel(bp.label()); - // 1:输入框 2:输入框 3:下拉框 4:选择框 - childNode.setType(bp.type()); - // 是否必填 - childNode.setMust(bp.must()); - // 是否多选 - childNode.setMultiple(bp.multiple()); - return childNode; - } - - /** - * 从扩展属性构建按钮权限列表:根据 ext 中记录的权限值,标记每个按钮是否勾选 + *

示例 JSON: + * [ + * {"code": "ButtonPermissionEnum", "value": "back,termination"}, + * {"code": "CopySettingEnum", "value": "1,3,4,#{@spelRuleComponent.selectDeptLeaderById(#deptId", "#roleId)}"}, + * {"code": "VariablesEnum", "value": "key1=value1,key2=value2"} + * ] * - * @param ext 扩展属性 JSON 字符串 - * @return 按钮权限 VO 列表 + * @param ext 扩展属性 JSON 字符串 + * @param variable 流程变量 + * @return NodeExtVo 对象,封装按钮权限列表、抄送对象集合和自定义参数 Map */ @Override - public List buildButtonPermissionsFromExt(String ext) { - // 解析 ext 为 Map>,用于标记权限 - Map> permissionMap = JsonUtils.parseArray(ext, ButtonPermissionVo.class) - .stream() - .collect(Collectors.toMap( - ButtonPermissionVo::getCode, - item -> StringUtils.splitList(item.getValue()).stream() - .map(String::trim) - .filter(StrUtil::isNotBlank) - .collect(Collectors.toSet()), - (a, b) -> b, - HashMap::new - )); + public NodeExtVo parseNodeExt(String ext, Map variable) { + NodeExtVo nodeExtVo = new NodeExtVo(); - // 构建按钮权限列表,标记哪些按钮在 permissionMap 中出现(表示已勾选) - return buildPermissionsFromSources(permissionMap, List.of(ButtonPermissionEnum.class)); + // 解析 JSON 为 Dict 列表 + List nodeExtMap = JsonUtils.parseArrayMap(ext); + if (ObjectUtil.isEmpty(nodeExtMap)) { + return nodeExtVo; + } + + for (Dict nodeExt : nodeExtMap) { + String code = nodeExt.getStr("code"); + String value = nodeExt.getStr("value"); + + if (ButtonPermissionEnum.class.getSimpleName().equals(code)) { + // 解析按钮权限 + // 将 value 拆分为 Set,便于精确匹配 + Set buttonSet = StringUtils.str2Set(value, StringUtils.SEPARATOR); + + // 获取按钮字典配置 + NodeExt.ChildNode childNode = buildChildNode(ButtonPermissionEnum.class); + + // 构建 ButtonPermissionVo 列表 + List buttonList = Optional.ofNullable(childNode) + .map(NodeExt.ChildNode::getDict) + .orElse(List.of()) + .stream() + .map(dict -> new ButtonPermissionVo(dict.getValue(), buttonSet.contains(dict.getValue()))) + .toList(); + + nodeExtVo.setButtonPermissions(buttonList); + + } else if (CopySettingEnum.class.getSimpleName().equals(code)) { + List permissions = spelSmartSplit(value).stream() + .map(s -> { + List result = ExpressionUtil.evalVariable(s, variable); + if (CollUtil.isNotEmpty(result)) { + return result; + } + return Collections.singletonList(s); + }).filter(Objects::nonNull) + .flatMap(List::stream) + .distinct() + .collect(Collectors.toList()); + List copySettings = FlowEngine.permissionHandler().convertPermissions(permissions); + // 解析抄送对象 ID 集合 + nodeExtVo.setCopySettings(new HashSet<>(copySettings)); + + } else if (VariablesEnum.class.getSimpleName().equals(code)) { + // 解析自定义参数 + // 将 key=value 字符串拆分为 Map + Map variables = Arrays.stream(StringUtils.split(value, StringUtils.SEPARATOR)) + .map(s -> StringUtils.split(s, "=")) + .filter(arr -> arr.length == 2) + .collect(Collectors.toMap(arr -> arr[0], arr -> arr[1])); + + nodeExtVo.setVariables(variables); + } else { + // 未知扩展类型,记录日志 + log.warn("未知扩展类型:code={}, value={}", code, value); + } + } + return nodeExtVo; } /** - * 将权限映射与按钮权限来源(枚举类或字典类型)进行匹配,生成权限视图列表 - *

- * 使用说明: - * - sources 支持传入多个来源类型,支持 NodeExtEnum 枚举类 或 字典类型字符串(dictType) - * - 若需要扩展更多按钮权限,只需在 sources 中新增对应的枚举类或字典类型 - *

- * 示例: - * buildPermissionsFromSources(permissionMap, List.of(ButtonPermissionEnum.class, "custom_button_dict")); - * - * @param permissionMap 权限映射 - * @param sources 枚举类或字典类型列表 - * @return 按钮权限视图对象列表 + * 按逗号分割字符串,但保留 #{...} 表达式和字符串常量中的逗号 */ - @SuppressWarnings("unchecked cast") - private List buildPermissionsFromSources(Map> permissionMap, List sources) { - return sources.stream() - .flatMap(source -> { - if (source instanceof Class clazz && NodeExtEnum.class.isAssignableFrom(clazz)) { - Set selectedSet = permissionMap.getOrDefault(clazz.getSimpleName(), Collections.emptySet()); - return extractDictItems(this.buildChildNode((Class) clazz), selectedSet).stream(); - } else if (source instanceof String dictType) { - Set selectedSet = permissionMap.getOrDefault(dictType, Collections.emptySet()); - return extractDictItems(this.buildChildNode(dictType), selectedSet).stream(); + private static List spelSmartSplit(String str) { + List result = new ArrayList<>(); + if (str == null || str.trim().isEmpty()) { + return result; + } + + StringBuilder token = new StringBuilder(); + // #{...} 的嵌套深度 + int depth = 0; + // 是否在字符串常量中(" 或 ') + boolean inString = false; + // 当前字符串引号类型 + char stringQuote = 0; + + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + + // 检测进入 SpEL 表达式 #{...} + if (!inString && c == '#' && depth == 0 && checkNext(str, i, '{')) { + depth++; + token.append("#{"); + // 跳过 { + i++; + continue; + } + + // 在表达式中遇到 { 或 } 改变嵌套深度 + if (!inString && depth > 0) { + if (c == '{') { + depth++; + } else if (c == '}') { + depth--; } - return Stream.empty(); - }).toList(); + token.append(c); + continue; + } + + // 检测字符串开始/结束 + if (depth > 0 && (c == '"' || c == '\'')) { + if (!inString) { + inString = true; + stringQuote = c; + } else if (stringQuote == c) { + inString = false; + } + token.append(c); + continue; + } + + // 外层逗号才分割 + if (c == ',' && depth == 0 && !inString) { + String part = token.toString().trim(); + if (!part.isEmpty()) { + result.add(part); + } + token.setLength(0); + continue; + } + + token.append(c); + } + + // 添加最后一个 + String part = token.toString().trim(); + if (!part.isEmpty()) { + result.add(part); + } + + return result; } - /** - * 从节点子项中提取字典项,并构建按钮权限视图对象列表 - * - * @param childNode 子节点 - * @param selectedSet 已选中的值集 - * @return 按钮权限视图对象列表 - */ - private List extractDictItems(NodeExt.ChildNode childNode, Set selectedSet) { - return Optional.ofNullable(childNode) - .map(NodeExt.ChildNode::getDict) - .orElse(List.of()) - .stream() - .map(dict -> new ButtonPermissionVo(dict.getValue(), selectedSet.contains(dict.getValue()))) - .toList(); + private static boolean checkNext(String str, int index, char expected) { + return index + 1 < str.length() && str.charAt(index + 1) == expected; } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java new file mode 100644 index 00000000..682803c8 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java @@ -0,0 +1,190 @@ +package org.dromara.workflow.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.constant.SystemConstants; +import org.dromara.common.core.domain.dto.TaskAssigneeDTO; +import org.dromara.common.core.domain.model.TaskAssigneeBody; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.workflow.common.ConditionalOnEnable; +import org.dromara.workflow.domain.FlowSpel; +import org.dromara.workflow.domain.bo.FlowSpelBo; +import org.dromara.workflow.domain.vo.FlowSpelVo; +import org.dromara.workflow.mapper.FlwSpelMapper; +import org.dromara.workflow.service.IFlwSpelService; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * 流程spel达式定义Service业务层处理 + * + * @author Michelle.Chung + * @date 2025-07-04 + */ +@ConditionalOnEnable +@Slf4j +@RequiredArgsConstructor +@Service +public class FlwSpelServiceImpl implements IFlwSpelService { + + private final FlwSpelMapper baseMapper; + + /** + * 查询流程spel达式定义 + * + * @param id 主键 + * @return 流程spel达式定义 + */ + @Override + public FlowSpelVo queryById(Long id){ + return baseMapper.selectVoById(id); + } + + /** + * 分页查询流程spel达式定义列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 流程spel达式定义分页列表 + */ + @Override + public TableDataInfo queryPageList(FlowSpelBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的流程spel达式定义列表 + * + * @param bo 查询条件 + * @return 流程spel达式定义列表 + */ + @Override + public List queryList(FlowSpelBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(FlowSpelBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(FlowSpel::getId); + lqw.like(StringUtils.isNotBlank(bo.getComponentName()), FlowSpel::getComponentName, bo.getComponentName()); + lqw.like(StringUtils.isNotBlank(bo.getMethodName()), FlowSpel::getMethodName, bo.getMethodName()); + lqw.eq(StringUtils.isNotBlank(bo.getMethodParams()), FlowSpel::getMethodParams, bo.getMethodParams()); + lqw.eq(StringUtils.isNotBlank(bo.getViewSpel()), FlowSpel::getViewSpel, bo.getViewSpel()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), FlowSpel::getStatus, bo.getStatus()); + lqw.like(StringUtils.isNotBlank(bo.getRemark()), FlowSpel::getRemark, bo.getRemark()); + return lqw; + } + + /** + * 新增流程spel达式定义 + * + * @param bo 流程spel达式定义 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(FlowSpelBo bo) { + FlowSpel add = MapstructUtils.convert(bo, FlowSpel.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改流程spel达式定义 + * + * @param bo 流程spel达式定义 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(FlowSpelBo bo) { + FlowSpel update = MapstructUtils.convert(bo, FlowSpel.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(FlowSpel entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除流程spel达式定义信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 查询spel并返回任务指派的列表,支持分页 + * + * @param taskQuery 查询条件 + * @return 办理人 + */ + @Override + public TaskAssigneeDTO selectSpelByTaskAssigneeList(TaskAssigneeBody taskQuery) { + PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); + FlowSpelBo bo = new FlowSpelBo(); + bo.setViewSpel(taskQuery.getHandlerCode()); + bo.setRemark(taskQuery.getHandlerName()); + bo.setStatus(SystemConstants.NORMAL); + Map params = bo.getParams(); + params.put("beginTime", taskQuery.getBeginTime()); + params.put("endTime", taskQuery.getEndTime()); + TableDataInfo page = this.queryPageList(bo, pageQuery); + // 使用封装的字段映射方法进行转换 + List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRows(), + FlowSpelVo::getViewSpel, item -> "", FlowSpelVo::getRemark, item -> "null", FlowSpelVo::getCreateTime); + return new TaskAssigneeDTO(page.getTotal(), handlers); + } + + /** + * 根据视图 SpEL 表达式列表,查询对应的备注信息 + * + * @param viewSpels SpEL 表达式列表 + * @return 映射表:key 为 SpEL 表达式,value 为对应备注;若为空则返回空 Map + */ + @Override + public Map selectRemarksBySpels(List viewSpels) { + if (CollUtil.isEmpty(viewSpels)) { + return Collections.emptyMap(); + } + List list = baseMapper.selectList( + new LambdaQueryWrapper() + .select(FlowSpel::getViewSpel, FlowSpel::getRemark) + .in(FlowSpel::getViewSpel, viewSpels) + ); + return StreamUtils.toMap(list, FlowSpel::getViewSpel, x -> + StringUtils.isEmpty(x.getRemark()) ? "" : x.getRemark() + ); + } + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java index 91e53b25..c36dc47b 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java @@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Pair; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -12,10 +13,9 @@ import org.dromara.common.core.domain.dto.TaskAssigneeDTO; import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.core.domain.model.TaskAssigneeBody; import org.dromara.common.core.enums.FormatsType; -import org.dromara.common.core.service.DeptService; -import org.dromara.common.core.service.TaskAssigneeService; -import org.dromara.common.core.service.UserService; +import org.dromara.common.core.service.*; import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.warm.flow.ui.dto.HandlerFunDto; import org.dromara.warm.flow.ui.dto.HandlerQuery; @@ -25,10 +25,12 @@ import org.dromara.warm.flow.ui.vo.HandlerFeedBackVo; import org.dromara.warm.flow.ui.vo.HandlerSelectVo; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.enums.TaskAssigneeEnum; +import org.dromara.workflow.service.IFlwSpelService; import org.dromara.workflow.service.IFlwTaskAssigneeService; import org.springframework.stereotype.Service; import java.util.*; +import java.util.stream.Collectors; /** * 流程设计器-获取办理人权限设置列表 @@ -45,6 +47,9 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand private final TaskAssigneeService taskAssigneeService; private final UserService userService; private final DeptService deptService; + private final RoleService roleService; + private final PostService postService; + private final IFlwSpelService spelService; /** * 获取办理人权限设置列表tabs页签 @@ -88,10 +93,10 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand return Collections.emptyList(); } // 解析并归类 ID,同时记录原始顺序和对应解析结果 - Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); - Map> parsedMap = new LinkedHashMap<>(); + Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); + Map> parsedMap = new LinkedHashMap<>(); for (String storageId : storageIds) { - Pair parsed = this.parseStorageId(storageId); + Pair parsed = this.parseStorageId(storageId); parsedMap.put(storageId, parsed); if (parsed != null) { typeIdMap.computeIfAbsent(parsed.getKey(), k -> new ArrayList<>()).add(parsed.getValue()); @@ -99,14 +104,13 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand } // 查询所有类型对应的 ID 名称映射 - Map> nameMap = new EnumMap<>(TaskAssigneeEnum.class); + Map> nameMap = new EnumMap<>(TaskAssigneeEnum.class); typeIdMap.forEach((type, ids) -> nameMap.put(type, this.getNamesByType(type, ids))); - // 组装返回结果,保持原始顺序 return parsedMap.entrySet().stream() .map(entry -> { String storageId = entry.getKey(); - Pair parsed = entry.getValue(); + Pair parsed = entry.getValue(); String handlerName = (parsed == null) ? null : nameMap.getOrDefault(parsed.getKey(), Collections.emptyMap()) .get(parsed.getValue()); @@ -123,6 +127,7 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand case ROLE -> taskAssigneeService.selectRolesByTaskAssigneeList(taskQuery); case DEPT -> taskAssigneeService.selectDeptsByTaskAssigneeList(taskQuery); case POST -> taskAssigneeService.selectPostsByTaskAssigneeList(taskQuery); + case SPEL -> spelService.selectSpelByTaskAssigneeList(taskQuery); }; } @@ -130,20 +135,37 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand * 根据任务办理类型获取部门数据 */ private List fetchDeptData(TaskAssigneeEnum type) { - if (type == TaskAssigneeEnum.USER || type == TaskAssigneeEnum.DEPT || type == TaskAssigneeEnum.POST) { + if (type.needsDeptService()) { return deptService.selectDeptsByList(); } return new ArrayList<>(); } + /** + * 获取权限分组名称 + * + * @param type 任务分配人枚举 + * @param groupName 权限分组 + * @return 权限分组名称 + */ + private String getGroupName(TaskAssigneeEnum type, String groupName) { + if (StringUtils.isEmpty(groupName)) { + return DEFAULT_GROUP_NAME; + } + if (type.needsDeptService()) { + return deptService.selectDeptNameByIds(groupName); + } + return DEFAULT_GROUP_NAME; + } + /** * 构建部门树状结构 */ private TreeFunDto buildDeptTree(List depts) { return new TreeFunDto<>(depts) - .setId(dept -> String.valueOf(dept.getDeptId())) + .setId(dept -> Convert.toStr(dept.getDeptId())) .setName(DeptDTO::getDeptName) - .setParentId(dept -> String.valueOf(dept.getParentId())); + .setParentId(dept -> Convert.toStr(dept.getParentId())); } /** @@ -154,10 +176,7 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand .setStorageId(assignee -> type.getCode() + assignee.getStorageId()) .setHandlerCode(assignee -> StringUtils.blankToDefault(assignee.getHandlerCode(), "无")) .setHandlerName(assignee -> StringUtils.blankToDefault(assignee.getHandlerName(), "无")) - .setGroupName(assignee -> StringUtils.defaultIfBlank( - Optional.ofNullable(assignee.getGroupName()) - .map(deptService::selectDeptNameByIds) - .orElse(DEFAULT_GROUP_NAME), DEFAULT_GROUP_NAME)) + .setGroupName(assignee -> this.getGroupName(type, assignee.getGroupName())) .setCreateTime(assignee -> DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, assignee.getCreateTime())); } @@ -174,9 +193,9 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand if (StringUtils.isEmpty(storageIds)) { return List.of(); } - Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); + Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); for (String storageId : storageIds.split(StringUtils.SEPARATOR)) { - Pair parsed = this.parseStorageId(storageId); + Pair parsed = this.parseStorageId(storageId); if (parsed != null) { typeIdMap.computeIfAbsent(parsed.getKey(), k -> new ArrayList<>()).add(parsed.getValue()); } @@ -197,12 +216,17 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand * 如果类型为部门(DEPT),则通过部门ID列表查询; * 如果类型为岗位(POST)或无法识别的类型,则返回空列表 */ - private List getUsersByType(TaskAssigneeEnum type, List ids,Long projectId) { + private List getUsersByType(TaskAssigneeEnum type, List ids,Long projectId) { + if (type == TaskAssigneeEnum.SPEL) { + return new ArrayList<>(); + } + List longIds = StreamUtils.toList(ids, Convert::toLong); return switch (type) { - case USER -> userService.selectListByIds(ids); - case ROLE -> userService.selectUsersByRoleIds(ids,projectId); - case DEPT -> userService.selectUsersByDeptIds(ids); - case POST -> userService.selectUsersByPostIds(ids); + case USER -> userService.selectListByIds(longIds); + case ROLE -> userService.selectUsersByRoleIds(longIds,projectId); + case DEPT -> userService.selectUsersByDeptIds(longIds); + case POST -> userService.selectUsersByPostIds(longIds); + default -> new ArrayList<>(); }; } @@ -213,13 +237,28 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand * @param ids ID 列表(如用户ID、角色ID等) * @return 返回 Map,其中 key 为 ID,value 为对应的名称 */ - private Map getNamesByType(TaskAssigneeEnum type, List ids) { - return switch (type) { - case USER -> userService.selectUserNamesByIds(ids); - case ROLE -> userService.selectRoleNamesByIds(ids); - case DEPT -> userService.selectDeptNamesByIds(ids); - case POST -> userService.selectPostNamesByIds(ids); + private Map getNamesByType(TaskAssigneeEnum type, List ids) { + if (type == TaskAssigneeEnum.SPEL) { + return spelService.selectRemarksBySpels(ids); + } + + List longIds = StreamUtils.toList(ids, Convert::toLong); + Map rawMap = switch (type) { + case USER -> userService.selectUserNamesByIds(longIds); + case ROLE -> userService.selectRoleNamesByIds(longIds); + case DEPT -> userService.selectDeptNamesByIds(longIds); + case POST -> userService.selectPostNamesByIds(longIds); + default -> Collections.emptyMap(); }; + if (MapUtil.isEmpty(rawMap)) { + return Collections.emptyMap(); + } + return rawMap.entrySet() + .stream() + .collect(Collectors.toMap( + e -> Convert.toStr(e.getKey()), + Map.Entry::getValue + )); } /** @@ -228,22 +267,20 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand * @param storageId 例如 "user:123" 或 "456" * @return Pair(TaskAssigneeEnum, Long),如果格式非法返回 null */ - private Pair parseStorageId(String storageId) { + private Pair parseStorageId(String storageId) { if (StringUtils.isBlank(storageId)) { return null; } - // 跳过以 $ 或 # 开头的字符串 - if (StringUtils.startsWith(storageId, "$") || StringUtils.startsWith(storageId, "#")) { - log.debug("跳过 storageId 解析,检测到内置变量表达式:{}", storageId); - return null; + if (TaskAssigneeEnum.isSpelExpression(storageId)) { + return Pair.of(TaskAssigneeEnum.SPEL, storageId); } try { String[] parts = storageId.split(StrUtil.COLON, 2); if (parts.length < 2) { - return Pair.of(TaskAssigneeEnum.USER, Convert.toLong(parts[0])); + return Pair.of(TaskAssigneeEnum.USER, parts[0]); } else { TaskAssigneeEnum type = TaskAssigneeEnum.fromCode(parts[0] + StrUtil.COLON); - return Pair.of(type, Convert.toLong(parts[1])); + return Pair.of(type, parts[1]); } } catch (Exception e) { log.warn("解析 storageId 失败,格式非法:{},错误信息:{}", storageId, e.getMessage()); diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index 22864fc2..d9a762fa 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -3,8 +3,8 @@ package org.dromara.workflow.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.util.ObjectUtil; -import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; @@ -22,17 +22,16 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.satoken.utils.LoginHelper; -import org.dromara.common.sse.dto.SeeMessageContentDto; -import org.dromara.common.sse.dto.SseMessageDto; -import org.dromara.common.sse.utils.SseMessageUtils; import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.entity.*; import org.dromara.warm.flow.core.enums.NodeType; import org.dromara.warm.flow.core.enums.SkipType; +import org.dromara.warm.flow.core.enums.UserType; import org.dromara.warm.flow.core.service.*; import org.dromara.warm.flow.core.utils.ExpressionUtil; import org.dromara.warm.flow.core.utils.MapUtil; @@ -42,10 +41,14 @@ import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.common.enums.TaskAssigneeType; import org.dromara.workflow.common.enums.TaskStatusEnum; +import org.dromara.workflow.domain.FlowInstanceBizExt; import org.dromara.workflow.domain.bo.*; +import org.dromara.workflow.domain.vo.FlowCopyVo; import org.dromara.workflow.domain.vo.FlowHisTaskVo; import org.dromara.workflow.domain.vo.FlowTaskVo; +import org.dromara.workflow.domain.vo.NodeExtVo; import org.dromara.workflow.mapper.FlwCategoryMapper; +import org.dromara.workflow.mapper.FlwInstanceBizExtMapper; import org.dromara.workflow.mapper.FlwTaskMapper; import org.dromara.workflow.service.IFlwCommonService; import org.dromara.workflow.service.IFlwNodeExtService; @@ -56,7 +59,6 @@ import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.*; -import java.util.stream.Collectors; import static org.dromara.workflow.common.constant.FlowConstant.*; @@ -87,6 +89,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { private final IFlwTaskAssigneeService flwTaskAssigneeService; private final IFlwCommonService flwCommonService; private final IFlwNodeExtService flwNodeExtService; + private final FlwInstanceBizExtMapper flwInstanceBizExtMapper; private final FlowDefinitionMapper flowDefinitionMapper; /** @@ -101,15 +104,23 @@ public class FlwTaskServiceImpl implements IFlwTaskService { if (StringUtils.isBlank(businessId)) { throw new ServiceException("启动工作流时必须包含业务ID"); } + // 启动流程实例(提交申请) Map variables = startProcessBo.getVariables(); // 流程发起人 variables.put(INITIATOR, LoginHelper.getUserIdStr()); + // 发起人部门id + variables.put(INITIATOR_DEPT_ID, LoginHelper.getDeptId()); // 业务id variables.put(BUSINESS_ID, businessId); + FlowInstanceBizExt bizExt = startProcessBo.getBizExt(); + + // 获取已有流程实例 FlowInstance flowInstance = flowInstanceMapper.selectOne(new LambdaQueryWrapper<>(FlowInstance.class) .eq(FlowInstance::getBusinessId, businessId)); + if (ObjectUtil.isNotNull(flowInstance)) { + // 已存在流程 BusinessStatusEnum.checkStartStatus(flowInstance.getFlowStatus()); List taskList = taskService.list(new FlowTask().setInstanceId(flowInstance.getId())); taskService.mergeVariable(flowInstance, variables); @@ -117,9 +128,19 @@ public class FlwTaskServiceImpl implements IFlwTaskService { StartProcessReturnDTO dto = new StartProcessReturnDTO(); dto.setProcessInstanceId(taskList.get(0).getInstanceId()); dto.setTaskId(taskList.get(0).getId()); + // 保存流程实例业务信息 + this.buildFlowInstanceBizExt(flowInstance, bizExt); return dto; } + + // 将流程定义内的扩展参数设置到变量中 + Definition definition = FlowEngine.defService().getPublishByFlowCode(startProcessBo.getFlowCode()); + Dict dict = JsonUtils.parseMap(definition.getExt()); + boolean autoPass = !ObjectUtil.isNull(dict) && dict.getBool(FlowConstant.AUTO_PASS); + variables.put(FlowConstant.AUTO_PASS, autoPass); + variables.put(FlowConstant.BUSINESS_CODE, this.generateBusinessCode(bizExt)); FlowParams flowParams = FlowParams.build() + .handler(startProcessBo.getHandler()) .flowCode(startProcessBo.getFlowCode()) .variable(startProcessBo.getVariables()) .flowStatus(BusinessStatusEnum.DRAFT.getStatus()); @@ -129,6 +150,8 @@ public class FlwTaskServiceImpl implements IFlwTaskService { } catch (Exception e) { throw new ServiceException(e.getMessage()); } + // 保存流程实例业务信息 + this.buildFlowInstanceBizExt(instance, bizExt); // 申请人执行流程 List taskList = taskService.list(new FlowTask().setInstanceId(instance.getId())); if (taskList.size() > 1) { @@ -140,6 +163,31 @@ public class FlwTaskServiceImpl implements IFlwTaskService { return dto; } + /** + * 生成业务编号,如果已有则直接返回已有值 + */ + private String generateBusinessCode(FlowInstanceBizExt bizExt) { + if (StringUtils.isBlank(bizExt.getBusinessCode())) { + // TODO: 按照自己业务规则生成编号 + String businessCode = Convert.toStr(System.currentTimeMillis()); + bizExt.setBusinessCode(businessCode); + return businessCode; + } + return bizExt.getBusinessCode(); + } + + /** + * 构建流程实例业务信息 + * + * @param instance 流程实例 + * @param bizExt 流程业务扩展信息 + */ + private void buildFlowInstanceBizExt(Instance instance, FlowInstanceBizExt bizExt) { + bizExt.setInstanceId(instance.getId()); + bizExt.setBusinessId(instance.getBusinessId()); + flwInstanceBizExtMapper.saveOrUpdateByInstanceId(bizExt); + } + /** * 办理任务 * @@ -156,11 +204,12 @@ public class FlwTaskServiceImpl implements IFlwTaskService { // 获取抄送人 List flowCopyList = completeTaskBo.getFlowCopyList(); // 设置抄送人 - completeTaskBo.getVariables().put(FlowConstant.FLOW_COPY_LIST, flowCopyList); + Map variables = completeTaskBo.getVariables(); + variables.put(FlowConstant.FLOW_COPY_LIST, flowCopyList); // 消息类型 - completeTaskBo.getVariables().put(FlowConstant.MESSAGE_TYPE, messageType); + variables.put(FlowConstant.MESSAGE_TYPE, messageType); // 消息通知 - completeTaskBo.getVariables().put(FlowConstant.MESSAGE_NOTICE, notice); + variables.put(FlowConstant.MESSAGE_NOTICE, notice); FlowTask flowTask = flowTaskMapper.selectById(taskId); @@ -170,23 +219,24 @@ public class FlwTaskServiceImpl implements IFlwTaskService { Instance ins = insService.getById(flowTask.getInstanceId()); // 检查流程状态是否为草稿、已撤销或已退回状态,若是则执行流程提交监听 if (BusinessStatusEnum.isDraftOrCancelOrBack(ins.getFlowStatus())) { - completeTaskBo.getVariables().put(FlowConstant.SUBMIT, true); + variables.put(FlowConstant.SUBMIT, true); } // 设置弹窗处理人 Map assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), ins.getVariableMap()); if (CollUtil.isNotEmpty(assigneeMap)) { - completeTaskBo.getVariables().putAll(assigneeMap); + variables.putAll(assigneeMap); } // 构建流程参数,包括变量、跳转类型、消息、处理人、权限等信息 FlowParams flowParams = FlowParams.build() - .variable(completeTaskBo.getVariables()) + .handler(completeTaskBo.getHandler()) + .variable(variables) .skipType(SkipType.PASS.getKey()) .message(completeTaskBo.getMessage()) .flowStatus(BusinessStatusEnum.WAITING.getStatus()) .hisStatus(TaskStatusEnum.PASS.getStatus()) .hisTaskExt(completeTaskBo.getFileId()); - // 执行任务跳转,并根据返回的处理人设置下一步处理人 - taskService.skip(taskId, flowParams); + Boolean autoPass = Convert.toBool(variables.getOrDefault(AUTO_PASS, false)); + skipTask(taskId, flowParams, flowTask.getInstanceId(), autoPass); return true; } catch (Exception e) { log.error(e.getMessage(), e); @@ -194,6 +244,43 @@ public class FlwTaskServiceImpl implements IFlwTaskService { } } + /** + * 流程办理 + * + * @param taskId 任务ID + * @param flowParams 参数 + * @param instanceId 实例ID + * @param autoPass 自动审批 + */ + private void skipTask(Long taskId, FlowParams flowParams, Long instanceId, Boolean autoPass) { + // 执行任务跳转,并根据返回的处理人设置下一步处理人 + taskService.skip(taskId, flowParams); + List flowTaskList = selectByInstId(instanceId); + if (CollUtil.isEmpty(flowTaskList)) { + return; + } + List userList = FlowEngine.userService() + .getByAssociateds(StreamUtils.toList(flowTaskList, FlowTask::getId)); + if (CollUtil.isEmpty(userList)) { + return; + } + for (FlowTask task : flowTaskList) { + if (!task.getId().equals(taskId) && autoPass) { + List users = StreamUtils.filter(userList, e -> ObjectUtil.equals(task.getId(), e.getAssociated()) && ObjectUtil.equal(e.getProcessedBy(), LoginHelper.getUserIdStr())); + if (CollUtil.isEmpty(users)) { + continue; + } + flowParams. + message("流程引擎自动审批!"). + variable(Map.of( + FlowConstant.SUBMIT, false, + FlowConstant.FLOW_COPY_LIST, Collections.emptyList(), + FlowConstant.MESSAGE_NOTICE, StringUtils.EMPTY)); + skipTask(task.getId(), flowParams, instanceId, true); + } + } + } + /** * 设置弹窗处理人 * @@ -216,7 +303,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { List variableUserIds = Arrays.asList(userIds.split(StringUtils.SEPARATOR)); hashSet.addAll(popUserIds); hashSet.addAll(variableUserIds); - map.put(entry.getKey(), String.join(StringUtils.SEPARATOR, hashSet)); + map.put(entry.getKey(), StringUtils.joinComma(hashSet)); } } else { map.put(entry.getKey(), entry.getValue()); @@ -237,7 +324,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { return; } // 添加抄送人记录 - FlowHisTask flowHisTask = flowHisTaskMapper.selectList(new LambdaQueryWrapper<>(FlowHisTask.class).eq(FlowHisTask::getTaskId, task.getId())).get(0); + FlowHisTask flowHisTask = flowHisTaskMapper.selectList( + new LambdaQueryWrapper<>(FlowHisTask.class) + .eq(FlowHisTask::getTaskId, task.getId())).get(0); FlowNode flowNode = new FlowNode(); flowNode.setNodeCode(flowHisTask.getTargetNodeCode()); flowNode.setNodeName(flowHisTask.getTargetNodeName()); @@ -254,14 +343,11 @@ public class FlwTaskServiceImpl implements IFlwTaskService { hisTask.setCreateTime(updateTime); hisTask.setUpdateTime(updateTime); hisTaskService.save(hisTask); - List userList = flowCopyList.stream() - .map(flowCopy -> { - FlowUser flowUser = new FlowUser(); - flowUser.setType(TaskAssigneeType.COPY.getCode()); - flowUser.setProcessedBy(String.valueOf(flowCopy.getUserId())); - flowUser.setAssociated(taskId); - return flowUser; - }).collect(Collectors.toList()); + List userList = StreamUtils.toList(flowCopyList, x -> + new FlowUser() + .setType(TaskAssigneeType.COPY.getCode()) + .setProcessedBy(Convert.toStr(x.getUserId())) + .setAssociated(taskId)); // 批量保存抄送人员 FlowEngine.userService().saveBatch(userList); } @@ -275,6 +361,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { @Override public TableDataInfo pageByTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) { QueryWrapper queryWrapper = buildQueryWrapper(flowTaskBo); + queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey()); + queryWrapper.in("t.processed_by", LoginHelper.getUserIdStr()); + queryWrapper.in("t.flow_status", BusinessStatusEnum.WAITING.getStatus()); List definitionIds = new ArrayList<>(); if (!"0".equals(flowTaskBo.getProjectId())){ List flowDefinitions = flowDefinitionMapper.selectList(new LambdaQueryWrapper() @@ -289,13 +378,28 @@ public class FlwTaskServiceImpl implements IFlwTaskService { return null; } } - queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey()); - queryWrapper.in("t.processed_by", LoginHelper.getUserIdStr()); - queryWrapper.in("t.flow_status", BusinessStatusEnum.WAITING.getStatus()); - Page page = this.getFlowTaskDefinitionIdsVoPage(pageQuery,definitionIds, queryWrapper); + Page page = flwTaskMapper.getListRunTask(pageQuery.build(), queryWrapper); + this.wrapAssigneeInfo(page.getRecords()); return TableDataInfo.build(page); } + + private Page getFlowTaskDefinitionIdsVoPage(PageQuery pageQuery, List definitionIds, QueryWrapper queryWrapper) { + Page page = flwTaskMapper.getListRunTaskDefinitionIds(pageQuery.build(),definitionIds, queryWrapper); + List records = page.getRecords(); + if (CollUtil.isNotEmpty(records)) { + List taskIds = StreamUtils.toList(records, FlowTaskVo::getId); + List userList = currentTaskAllUser(taskIds); + records.forEach(t -> { + if (CollUtil.isNotEmpty(userList)) { + t.setAssigneeIds(StreamUtils.join(userList, e -> String.valueOf(e.getUserId()))); + t.setAssigneeNames(StreamUtils.join(userList, UserDTO::getNickName)); + } + }); + } + return page; + } + /** * 查询当前用户的已办任务 * @@ -305,24 +409,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { @Override public TableDataInfo pageByTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery) { QueryWrapper queryWrapper = buildQueryWrapper(flowTaskBo); - List definitionIds = new ArrayList<>(); - if (!"0".equals(flowTaskBo.getProjectId())){ - List flowDefinitions = flowDefinitionMapper.selectList(new LambdaQueryWrapper() - .select(FlowDefinition::getId) - .like(FlowDefinition::getFlowCode, flowTaskBo.getProjectId())); - if (flowDefinitions != null && !flowDefinitions.isEmpty()) { - flowDefinitions.forEach(flowDefinition -> { - definitionIds.add(flowDefinition.getId()); - }); - } - if (definitionIds.isEmpty()) { - return null; - } - } queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey()); queryWrapper.in("t.approver", LoginHelper.getUserIdStr()); - queryWrapper.orderByDesc("t.create_time").orderByDesc("t.update_time"); - Page page = flwTaskMapper.getListFinishDefinitionIdsTask(pageQuery.build(),definitionIds, queryWrapper); + Page page = flwTaskMapper.getListFinishTask(pageQuery.build(), queryWrapper); return TableDataInfo.build(page); } @@ -336,41 +425,27 @@ public class FlwTaskServiceImpl implements IFlwTaskService { public TableDataInfo pageByAllTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) { QueryWrapper queryWrapper = buildQueryWrapper(flowTaskBo); queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey()); - Page page = getFlowTaskVoPage(pageQuery, queryWrapper); + Page page = flwTaskMapper.getListRunTask(pageQuery.build(), queryWrapper); + this.wrapAssigneeInfo(page.getRecords()); return TableDataInfo.build(page); } - private Page getFlowTaskVoPage(PageQuery pageQuery, QueryWrapper queryWrapper) { - Page page = flwTaskMapper.getListRunTask(pageQuery.build(), queryWrapper); - List records = page.getRecords(); - if (CollUtil.isNotEmpty(records)) { - List taskIds = StreamUtils.toList(records, FlowTaskVo::getId); - Map> listMap = currentTaskAllUser(taskIds); - records.forEach(t -> { - List userList = listMap.getOrDefault(t.getId(), Collections.emptyList()); - if (CollUtil.isNotEmpty(userList)) { - t.setAssigneeIds(StreamUtils.join(userList, e -> String.valueOf(e.getUserId()))); - t.setAssigneeNames(StreamUtils.join(userList, UserDTO::getNickName)); - } - }); + /** + * 为流程任务列表封装处理人 ID(assigneeIds) + * + * @param taskList 流程任务列表 + */ + private void wrapAssigneeInfo(List taskList) { + if (CollUtil.isEmpty(taskList)) { + return; } - return page; - } - private Page getFlowTaskDefinitionIdsVoPage(PageQuery pageQuery, List definitionIds, QueryWrapper queryWrapper) { - Page page = flwTaskMapper.getListRunTaskDefinitionIds(pageQuery.build(),definitionIds, queryWrapper); - List records = page.getRecords(); - if (CollUtil.isNotEmpty(records)) { - List taskIds = StreamUtils.toList(records, FlowTaskVo::getId); - Map> listMap = currentTaskAllUser(taskIds); - records.forEach(t -> { - List userList = listMap.getOrDefault(t.getId(), Collections.emptyList()); - if (CollUtil.isNotEmpty(userList)) { - t.setAssigneeIds(StreamUtils.join(userList, e -> String.valueOf(e.getUserId()))); - t.setAssigneeNames(StreamUtils.join(userList, UserDTO::getNickName)); - } - }); + List associatedUsers = FlowEngine.userService().getByAssociateds(StreamUtils.toList(taskList, FlowTaskVo::getId)); + Map> taskUserMap = StreamUtils.groupByKey(associatedUsers, User::getAssociated); + // 组装用户数据回任务列表 + for (FlowTaskVo task : taskList) { + List users = taskUserMap.get(task.getId()); + task.setAssigneeIds(StreamUtils.join(users, User::getProcessedBy)); } - return page; } /** @@ -395,23 +470,8 @@ public class FlwTaskServiceImpl implements IFlwTaskService { @Override public TableDataInfo pageByTaskCopy(FlowTaskBo flowTaskBo, PageQuery pageQuery) { QueryWrapper queryWrapper = buildQueryWrapper(flowTaskBo); - List definitionIds = new ArrayList<>(); - if (!"0".equals(flowTaskBo.getProjectId())){ - List flowDefinitions = flowDefinitionMapper.selectList(new LambdaQueryWrapper() - .select(FlowDefinition::getId) - .like(FlowDefinition::getFlowCode, flowTaskBo.getProjectId())); - if (flowDefinitions != null && !flowDefinitions.isEmpty()) { - flowDefinitions.forEach(flowDefinition -> { - definitionIds.add(flowDefinition.getId()); - }); - } - if (definitionIds.isEmpty()) { - return null; - } - } - queryWrapper.eq(StringUtils.isNotBlank(flowTaskBo.getIsRead()),"t.is_read", flowTaskBo.getIsRead()); queryWrapper.in("t.processed_by", LoginHelper.getUserIdStr()); - Page page = flwTaskMapper.getTaskCopyDefinitionIdsByPage(pageQuery.build(),definitionIds, queryWrapper); + Page page = flwTaskMapper.getTaskCopyByPage(pageQuery.build(), queryWrapper); return TableDataInfo.build(page); } @@ -425,7 +485,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { List categoryIds = flwCategoryMapper.selectCategoryIdsByParentId(Convert.toLong(flowTaskBo.getCategory())); wrapper.in("t.category", StreamUtils.toList(categoryIds, Convert::toStr)); } - wrapper.orderByDesc("t.create_time"); + wrapper.orderByDesc("t.create_time").orderByDesc("t.update_time"); return wrapper; } @@ -453,9 +513,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { Map variable = new HashMap<>(); // 消息类型 - variable.put("messageType", messageType); + variable.put(FlowConstant.MESSAGE_TYPE, messageType); // 消息通知 - variable.put("notice", notice); + variable.put(FlowConstant.MESSAGE_NOTICE, notice); FlowParams flowParams = FlowParams.build() .nodeCode(bo.getNodeCode()) @@ -476,22 +536,28 @@ public class FlwTaskServiceImpl implements IFlwTaskService { /** * 获取可驳回的前置节点 * - * @param definitionId 流程定义id - * @param nowNodeCode 当前节点 + * @param taskId 任务id + * @param nowNodeCode 当前节点 */ @Override - public List getBackTaskNode(Long definitionId, String nowNodeCode) { - List nodeCodes = nodeService.getByNodeCodes(Collections.singletonList(nowNodeCode), definitionId); + public List getBackTaskNode(Long taskId, String nowNodeCode) { + FlowTask task = flowTaskMapper.selectById(taskId); + List nodeCodes = nodeService.getByNodeCodes(Collections.singletonList(nowNodeCode), task.getDefinitionId()); if (!CollUtil.isNotEmpty(nodeCodes)) { return nodeCodes; } + List userList = FlowEngine.userService() + .getByAssociateds(Collections.singletonList(task.getId()), UserType.DEPUTE.getKey()); + if (CollUtil.isNotEmpty(userList)) { + return nodeCodes; + } //判断是否配置了固定驳回节点 Node node = nodeCodes.get(0); if (StringUtils.isNotBlank(node.getAnyNodeSkip())) { - return nodeService.getByNodeCodes(Collections.singletonList(node.getAnyNodeSkip()), definitionId); + return nodeService.getByNodeCodes(Collections.singletonList(node.getAnyNodeSkip()), task.getDefinitionId()); } //获取可驳回的前置节点 - List nodes = nodeService.previousNodeList(definitionId, nowNodeCode); + List nodes = nodeService.previousNodeList(task.getDefinitionId(), nowNodeCode); if (CollUtil.isNotEmpty(nodes)) { return StreamUtils.filter(nodes, e -> NodeType.BETWEEN.getKey().equals(e.getNodeType())); } @@ -535,8 +601,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { */ @Override public List selectByIdList(List taskIdList) { - return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class) - .in(FlowTask::getId, taskIdList)); + return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class).in(FlowTask::getId, taskIdList)); } /** @@ -562,8 +627,24 @@ public class FlwTaskServiceImpl implements IFlwTaskService { if (ObjectUtil.isNull(flowNode)) { throw new NullPointerException("当前【" + flowTaskVo.getNodeCode() + "】节点编码不存在"); } + NodeExtVo nodeExtVo = flwNodeExtService.parseNodeExt(flowNode.getExt(), instance.getVariableMap()); //设置按钮权限 - flowTaskVo.setButtonList(flwNodeExtService.buildButtonPermissionsFromExt(flowNode.getExt())); + if (CollUtil.isNotEmpty(nodeExtVo.getButtonPermissions())) { + flowTaskVo.setButtonList(nodeExtVo.getButtonPermissions()); + } else { + flowTaskVo.setButtonList(new ArrayList<>()); + } + if (CollUtil.isNotEmpty(nodeExtVo.getCopySettings())) { + List list = StreamUtils.toList(nodeExtVo.getCopySettings(), x -> new FlowCopyVo(Convert.toLong(x))); + flowTaskVo.setCopyList(list); + } else { + flowTaskVo.setCopyList(new ArrayList<>()); + } + if (CollUtil.isNotEmpty(nodeExtVo.getVariables())) { + flowTaskVo.setVarList(nodeExtVo.getVariables()); + } else { + flowTaskVo.setVarList(new HashMap<>()); + } flowTaskVo.setNodeRatio(flowNode.getNodeRatio()); flowTaskVo.setApplyNode(flowNode.getNodeCode().equals(flwCommonService.applyNodeCode(task.getDefinitionId()))); return flowTaskVo; @@ -588,39 +669,24 @@ public class FlwTaskServiceImpl implements IFlwTaskService { // 只获取中间节点 nextFlowNodes = StreamUtils.filter(nextFlowNodes, node -> NodeType.BETWEEN.getKey().equals(node.getNodeType())); if (CollUtil.isNotEmpty(nextNodeList)) { - // 构建以下节点数据 + //构建以下节点数据 List buildNextTaskList = StreamUtils.toList(nextNodeList, node -> taskService.addTask(node, instance, definition, FlowParams.build())); - // 办理人变量替换 - ExpressionUtil.evalVariable(buildNextTaskList, - FlowParams.build() - .variable(mergeVariable) - ); + //办理人变量替换 + ExpressionUtil.evalVariable(buildNextTaskList, FlowParams.build().variable(mergeVariable)); for (FlowNode flowNode : nextFlowNodes) { - buildNextTaskList.stream().filter(t -> t.getNodeCode().equals(flowNode.getNodeCode())).findFirst().ifPresent(t -> { - if (CollUtil.isNotEmpty(t.getPermissionList())) { - List users = flwTaskAssigneeService.fetchUsersByStorageIds(String.join(StringUtils.SEPARATOR, t.getPermissionList()),null); - if (CollUtil.isNotEmpty(users)) { - flowNode.setPermissionFlag(StreamUtils.join(users, e -> String.valueOf(e.getUserId()))); + StreamUtils.findFirst(buildNextTaskList, t -> t.getNodeCode().equals(flowNode.getNodeCode())) + .ifPresent(first -> { + List users; + if (CollUtil.isNotEmpty(first.getPermissionList()) + && CollUtil.isNotEmpty(users = flwTaskAssigneeService.fetchUsersByStorageIds(StringUtils.joinComma(first.getPermissionList()),null))) { + flowNode.setPermissionFlag(StreamUtils.join(users, e -> Convert.toStr(e.getUserId()))); } - } - }); + }); } } return nextFlowNodes; } - /** - * 按照任务id查询任务 - * - * @param taskIdList 任务id - * @return 结果 - */ - @Override - public List selectHisTaskByIdList(List taskIdList) { - return flowHisTaskMapper.selectList(new LambdaQueryWrapper<>(FlowHisTask.class) - .in(FlowHisTask::getId, taskIdList)); - } - /** * 按照任务id查询任务 * @@ -629,19 +695,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { */ @Override public FlowHisTask selectHisTaskById(Long taskId) { - return flowHisTaskMapper.selectOne(new LambdaQueryWrapper<>(FlowHisTask.class) - .eq(FlowHisTask::getId, taskId)); - } - - /** - * 按照实例id查询任务 - * - * @param instanceIdList 流程实例id - */ - @Override - public List selectByInstIdList(List instanceIdList) { - return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class) - .in(FlowTask::getInstanceId, instanceIdList)); + return flowHisTaskMapper.selectOne(new LambdaQueryWrapper<>(FlowHisTask.class).eq(FlowHisTask::getId, taskId)); } /** @@ -651,8 +705,29 @@ public class FlwTaskServiceImpl implements IFlwTaskService { */ @Override public List selectByInstId(Long instanceId) { - return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class) - .eq(FlowTask::getInstanceId, instanceId)); + return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class).eq(FlowTask::getInstanceId, instanceId)); + } + + /** + * 按照实例id查询任务 + * + * @param instanceIds 流程实例id + */ + @Override + public List selectByInstIds(List instanceIds) { + return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class).in(FlowTask::getInstanceId, instanceIds)); + } + + /** + * 判断流程是否已结束(即该流程实例下是否还有未完成的任务) + * + * @param instanceId 流程实例ID + * @return true 表示任务已全部结束;false 表示仍有任务存在 + */ + @Override + public boolean isTaskEnd(Long instanceId) { + boolean exists = flowTaskMapper.exists(new LambdaQueryWrapper().eq(FlowTask::getInstanceId, instanceId)); + return !exists; } /** @@ -664,8 +739,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { @Override @Transactional(rollbackFor = Exception.class) public boolean taskOperation(TaskOperationBo bo, String taskOperation) { - FlowParams flowParams = FlowParams.build() - .message(bo.getMessage()); + FlowParams flowParams = FlowParams.build().message(bo.getMessage()); if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) { flowParams.ignore(true); } @@ -693,7 +767,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { Long taskId = bo.getTaskId(); Task task = taskService.getById(taskId); FlowNode flowNode = getByNodeCode(task.getNodeCode(), task.getDefinitionId()); - if ("addSignature".equals(taskOperation) || "reductionSignature".equals(taskOperation)) { + if (ADD_SIGNATURE.equals(taskOperation) || REDUCTION_SIGNATURE.equals(taskOperation)) { if (flowNode.getNodeRatio().compareTo(BigDecimal.ZERO) == 0) { throw new ServiceException(task.getNodeName() + "不是会签节点!"); } @@ -744,15 +818,11 @@ public class FlwTaskServiceImpl implements IFlwTaskService { // 批量删除现有任务的办理人记录 if (CollUtil.isNotEmpty(flowTasks)) { FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTasks, FlowTask::getId)); - List userList = flowTasks.stream() - .map(flowTask -> { - FlowUser flowUser = new FlowUser(); - flowUser.setType(TaskAssigneeType.APPROVER.getCode()); - flowUser.setProcessedBy(userId); - flowUser.setAssociated(flowTask.getId()); - return flowUser; - }) - .collect(Collectors.toList()); + List userList = StreamUtils.toList(flowTasks, flowTask -> + new FlowUser() + .setType(TaskAssigneeType.APPROVER.getCode()) + .setProcessedBy(userId) + .setAssociated(flowTask.getId())); if (CollUtil.isNotEmpty(userList)) { FlowEngine.userService().saveBatch(userList); } @@ -764,36 +834,15 @@ public class FlwTaskServiceImpl implements IFlwTaskService { return true; } - /** - * 获取任务所有办理人 - * - * @param taskIdList 任务id - */ - @Override - public Map> currentTaskAllUser(List taskIdList) { - Map> map = new HashMap<>(); - // 获取与当前任务关联的用户列表 - List associatedUsers = FlowEngine.userService().getByAssociateds(taskIdList); - Map> listMap = StreamUtils.groupByKey(associatedUsers, User::getAssociated); - for (Map.Entry> entry : listMap.entrySet()) { - List value = entry.getValue(); - if (CollUtil.isNotEmpty(value)) { - List userDtoList = userService.selectListByIds(StreamUtils.toList(value, e -> Convert.toLong(e.getProcessedBy()))); - map.put(entry.getKey(), userDtoList); - } - } - return map; - } - /** * 获取当前任务的所有办理人 * - * @param taskId 任务id + * @param taskIds 任务id */ @Override - public List currentTaskAllUser(Long taskId) { + public List currentTaskAllUser(List taskIds) { // 获取与当前任务关联的用户列表 - List userList = FlowEngine.userService().getByAssociateds(Collections.singletonList(taskId)); + List userList = FlowEngine.userService().getByAssociateds(taskIds); if (CollUtil.isEmpty(userList)) { return Collections.emptyList(); } @@ -813,17 +862,28 @@ public class FlwTaskServiceImpl implements IFlwTaskService { .eq(FlowNode::getDefinitionId, definitionId)); } + /** + * 催办任务 + * + * @param bo 参数 + */ @Override - public void copyRead(Long flowUserId) { - Long userId = LoginHelper.getUserId(); - flwTaskMapper.copyRead(flowUserId); - SseMessageDto dto = new SseMessageDto(); - SeeMessageContentDto contentDto = new SeeMessageContentDto(); - contentDto.setType("count"); - contentDto.setContent("统计"); - dto.setMessage(JSONUtil.toJsonStr(contentDto)); - dto.setIsRecord(false); - dto.setUserIds(Collections.singletonList(userId)); - SseMessageUtils.publishMessage(dto); + public boolean urgeTask(FlowUrgeTaskBo bo) { + try { + if (CollUtil.isEmpty(bo.getTaskIdList())) { + return false; + } + List userList = this.currentTaskAllUser(bo.getTaskIdList()); + if (CollUtil.isEmpty(userList)) { + return false; + } + List messageType = bo.getMessageType(); + String message = bo.getMessage(); + flwCommonService.sendMessage(messageType, message, "单据审批提醒", userList); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new ServiceException(e.getMessage()); + } + return true; } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java index 17d1c7ca..ae11d18e 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java @@ -4,15 +4,18 @@ import cn.hutool.core.convert.Convert; import cn.hutool.core.date.DateUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.dromara.common.core.domain.event.ProcessTaskEvent; +import org.dromara.common.core.domain.dto.StartProcessDTO; import org.dromara.common.core.domain.event.ProcessDeleteEvent; import org.dromara.common.core.domain.event.ProcessEvent; +import org.dromara.common.core.domain.event.ProcessTaskEvent; import org.dromara.common.core.enums.BusinessStatusEnum; +import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.service.WorkflowService; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; @@ -20,6 +23,7 @@ import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.workflow.common.ConditionalOnEnable; +import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.domain.TestLeave; import org.dromara.workflow.domain.bo.TestLeaveBo; import org.dromara.workflow.domain.vo.TestLeaveVo; @@ -104,6 +108,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService { long day = DateUtil.betweenDay(bo.getStartDate(), bo.getEndDate(), true); // 截止日期也算一天 bo.setLeaveDays((int) day + 1); + bo.setApplyCode(System.currentTimeMillis() + StrUtil.EMPTY); TestLeave add = MapstructUtils.convert(bo, TestLeave.class); if (StringUtils.isBlank(add.getStatus())) { add.setStatus(BusinessStatusEnum.DRAFT.getStatus()); @@ -115,6 +120,37 @@ public class TestLeaveServiceImpl implements ITestLeaveService { return MapstructUtils.convert(add, TestLeaveVo.class); } + @Transactional(rollbackFor = Exception.class) + @Override + public TestLeaveVo submitAndFlowStart(TestLeaveBo bo) { + long day = DateUtil.betweenDay(bo.getStartDate(), bo.getEndDate(), true); + // 截止日期也算一天 + bo.setLeaveDays((int) day + 1); + if (ObjectUtil.isNull(bo.getId())) { + bo.setApplyCode(System.currentTimeMillis() + StrUtil.EMPTY); + } + TestLeave leave = MapstructUtils.convert(bo, TestLeave.class); + boolean flag = baseMapper.insertOrUpdate(leave); + if (flag) { + bo.setId(leave.getId()); + // 后端发起需要忽略权限 + bo.getParams().put("ignore", true); + + StartProcessDTO startProcess = new StartProcessDTO(); + startProcess.setBusinessId(leave.getId().toString()); + startProcess.setFlowCode(StringUtils.isEmpty(bo.getFlowCode()) ? "leave1" : bo.getFlowCode()); + startProcess.setVariables(bo.getParams()); + // 后端发起 如果没有登录用户 比如定时任务 可以手动设置一个处理人id + // startProcess.setHandler("0"); + + boolean flag1 = workflowService.startCompleteTask(startProcess); + if (!flag1) { + throw new ServiceException("流程发起异常"); + } + } + return MapstructUtils.convert(leave, TestLeaveVo.class); + } + /** * 修改请假 */ @@ -136,7 +172,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService { } /** - * 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等) + * 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成等) * 正常使用只需#processEvent.flowCode=='leave1' * 示例为了方便则使用startsWith匹配了全部示例key * @@ -158,13 +194,17 @@ public class TestLeaveServiceImpl implements ITestLeaveService { String message = Convert.toStr(params.get("message")); } if (processEvent.getSubmit()) { + if (StringUtils.isBlank(testLeave.getApplyCode())) { + String businessCode = MapUtil.getStr(params, FlowConstant.BUSINESS_CODE, StrUtil.EMPTY); + testLeave.setApplyCode(businessCode); + } testLeave.setStatus(BusinessStatusEnum.WAITING.getStatus()); } baseMapper.updateById(testLeave); } /** - * 执行任务创建监听 + * 执行任务创建监听(也代表上一条任务完成事件) * 示例:也可通过 @EventListener(condition = "#processTaskEvent.flowCode=='leave1'")进行判断 * 在方法中判断流程节点key * if ("xxx".equals(processTaskEvent.getNodeCode())) { diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java index 0c022400..8f831406 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java @@ -6,17 +6,22 @@ import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.dto.CompleteTaskDTO; import org.dromara.common.core.domain.dto.StartProcessDTO; import org.dromara.common.core.domain.dto.StartProcessReturnDTO; +import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.service.WorkflowService; import org.dromara.common.core.utils.StringUtils; import org.dromara.warm.flow.orm.entity.FlowInstance; import org.dromara.workflow.common.ConditionalOnEnable; +import org.dromara.workflow.common.enums.MessageTypeEnum; +import org.dromara.workflow.domain.FlowInstanceBizExt; import org.dromara.workflow.domain.bo.CompleteTaskBo; import org.dromara.workflow.domain.bo.StartProcessBo; import org.dromara.workflow.service.IFlwDefinitionService; import org.dromara.workflow.service.IFlwInstanceService; import org.dromara.workflow.service.IFlwTaskService; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -148,4 +153,36 @@ public class WorkflowServiceImpl implements WorkflowService { return flwTaskService.completeTask(completeTask); } + /** + * 启动流程并办理第一个任务 + * + * @param startProcess 参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean startCompleteTask(StartProcessDTO startProcess) { + try { + StartProcessBo processBo = new StartProcessBo(); + processBo.setBusinessId(startProcess.getBusinessId()); + processBo.setFlowCode(startProcess.getFlowCode()); + processBo.setVariables(startProcess.getVariables()); + processBo.setHandler(startProcess.getHandler()); + processBo.setBizExt(BeanUtil.toBean(startProcess.getBizExt(), FlowInstanceBizExt.class)); + + StartProcessReturnDTO result = flwTaskService.startWorkFlow(processBo); + CompleteTaskBo taskBo = new CompleteTaskBo(); + taskBo.setTaskId(result.getTaskId()); + taskBo.setMessageType(Collections.singletonList(MessageTypeEnum.SYSTEM_MESSAGE.getCode())); + taskBo.setVariables(startProcess.getVariables()); + taskBo.setHandler(startProcess.getHandler()); + + boolean flag = flwTaskService.completeTask(taskBo); + if (!flag) { + throw new ServiceException("流程发起异常"); + } + return true; + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwInstanceBizExtMapper.xml b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwInstanceBizExtMapper.xml new file mode 100644 index 00000000..c2cc9c7c --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwInstanceBizExtMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwInstanceMapper.xml b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwInstanceMapper.xml index eae0432f..1288bdfb 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwInstanceMapper.xml +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwInstanceMapper.xml @@ -28,11 +28,13 @@ fd.form_custom, fd.form_path, fd.category, - bp.project_name + bp.project_name, + biz.business_code, + biz.business_title from flow_instance fi left join flow_definition fd on fi.definition_id = fd.id - left join bus_project bp on bp.id = SUBSTRING_INDEX(fd.flow_code, '_', 1) - ${ew.getCustomSqlSegment} + left join flow_instance_biz_ext biz on biz.instance_id = fi.id + left join bus_project bp on bp.id = SUBSTRING_INDEX(fd.flow_code, '_', 1) + ${ew.getCustomSqlSegment} - diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwSpelMapper.xml b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwSpelMapper.xml new file mode 100644 index 00000000..03355f6a --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwSpelMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml index c3196faa..d346ac9b 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml +++ b/xinnengyuan/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml @@ -10,37 +10,40 @@ @@ -132,72 +135,78 @@