From d1858d584440493c07678f73bcc6da58c72670f9 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Tue, 10 Feb 2026 10:18:31 +0800 Subject: [PATCH] =?UTF-8?q?feat(pigx-flow-biz):=20=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E8=A1=A8=E8=BE=BE=E5=BC=8F=E6=96=B0=E5=A2=9E=E8=A7=92=E8=89=B2?= =?UTF-8?q?=E5=92=8C=E5=B2=97=E4=BD=8D=E9=80=89=E6=8B=A9=E5=99=A8=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E5=A4=84=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增SelectRoleNodeConditionStrategy类,处理角色选择器类型条件表达式 - 新增SelectPostNodeConditionStrategy类,处理岗位选择器类型条件表达式 - 在ExpressionHandler中新增roleCompare和postCompare方法,支持角色和岗位比较逻辑 - 更新SysUserService接口及实现,新增根据岗位ID列表获取用户ID列表功能 - 在SysUserController中新增根据岗位ID列表获取用户ID列表接口 - 更新RemoteUserService Feign客户端,新增岗位查询方法 - 更新相关文件版权信息和日期注释 - 删除无用的IProcessInstanceStatusEventService接口 - 在数据库中新增bpm_oa_leave请假申请表结构 --- db/0pigxx_boot.sql | 19 +++ db/3pigxx_flow.sql | 19 +++ .../IProcessInstanceStatusEventService.java | 25 ---- .../support/expression/ExpressionHandler.java | 112 ++++++++++++++++++ .../impl/SelectPostNodeConditionStrategy.java | 52 ++++++++ .../impl/SelectRoleNodeConditionStrategy.java | 52 ++++++++ .../admin/api/feign/RemoteUserService.java | 11 +- .../admin/controller/SysUserController.java | 13 +- .../pigx/admin/service/SysUserService.java | 10 +- .../service/impl/SysUserServiceImpl.java | 23 +++- 10 files changed, 306 insertions(+), 30 deletions(-) delete mode 100644 pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/IProcessInstanceStatusEventService.java create mode 100644 pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/expression/condition/impl/SelectPostNodeConditionStrategy.java create mode 100644 pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/expression/condition/impl/SelectRoleNodeConditionStrategy.java diff --git a/db/0pigxx_boot.sql b/db/0pigxx_boot.sql index 028282b29..4d9fa2d92 100644 --- a/db/0pigxx_boot.sql +++ b/db/0pigxx_boot.sql @@ -1492,6 +1492,25 @@ SET FOREIGN_KEY_CHECKS = 1; SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; +CREATE TABLE `bpm_oa_leave` ( + `id` BIGINT NOT NULL COMMENT '主键', + `process_instance_id` VARCHAR(64) DEFAULT NULL COMMENT '流程实例编号', + `username` VARCHAR(255) NOT NULL COMMENT '申请人', + `leave_type` SMALLINT NOT NULL COMMENT '请假类型', + `leave_reason` VARCHAR(255) DEFAULT NULL COMMENT '请假原因', + `start_time` DATETIME(6) NOT NULL COMMENT '开始时间', + `end_time` DATETIME(6) NOT NULL COMMENT '结束时间', + `leave_day` SMALLINT NOT NULL COMMENT '请假天数', + `leave_status` SMALLINT DEFAULT NULL COMMENT '请假结果', + `create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者', + `create_time` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间', + `update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者', + `update_time` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间', + `del_flag` CHAR(1) NOT NULL DEFAULT '0' COMMENT '是否删除', + `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='请假申请表'; + -- ---------------------------- -- Table structure for process -- ---------------------------- diff --git a/db/3pigxx_flow.sql b/db/3pigxx_flow.sql index 2cbf9b3a1..fefcab043 100644 --- a/db/3pigxx_flow.sql +++ b/db/3pigxx_flow.sql @@ -3,6 +3,25 @@ USE pigxx_flow; SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; +CREATE TABLE `bpm_oa_leave` ( + `id` BIGINT NOT NULL COMMENT '主键', + `process_instance_id` VARCHAR(64) DEFAULT NULL COMMENT '流程实例编号', + `username` VARCHAR(255) NOT NULL COMMENT '申请人', + `leave_type` SMALLINT NOT NULL COMMENT '请假类型', + `leave_reason` VARCHAR(255) DEFAULT NULL COMMENT '请假原因', + `start_time` DATETIME(6) NOT NULL COMMENT '开始时间', + `end_time` DATETIME(6) NOT NULL COMMENT '结束时间', + `leave_day` SMALLINT NOT NULL COMMENT '请假天数', + `leave_status` SMALLINT DEFAULT NULL COMMENT '请假结果', + `create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者', + `create_time` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间', + `update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者', + `update_time` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间', + `del_flag` CHAR(1) NOT NULL DEFAULT '0' COMMENT '是否删除', + `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户编号', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='请假申请表'; + -- ---------------------------- -- Table structure for process -- ---------------------------- diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/IProcessInstanceStatusEventService.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/IProcessInstanceStatusEventService.java deleted file mode 100644 index 20f2c4dbc..000000000 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/IProcessInstanceStatusEventService.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.pig4cloud.pigx.flow.service; - -import com.pig4cloud.pigx.flow.dto.ProcessInstanceParamDto; - -/** - * 流程实例状态事件服务接口 - * - * @author lengleng - * @date 2025/08/08 - */ -public interface IProcessInstanceStatusEventService { - - /** - * 获取监听的流程定义Key - * @return 监听的流程定义Key - */ - String getFlowId(); - - /** - * 处理状态事件 - * @param paramDto 流程节点记录分配用户参数DTO - */ - void handleStatusEvent(ProcessInstanceParamDto paramDto); - -} diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/expression/ExpressionHandler.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/expression/ExpressionHandler.java index 628cf5f6f..4092f7a45 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/expression/ExpressionHandler.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/expression/ExpressionHandler.java @@ -35,6 +35,7 @@ import java.util.List; * 使用Aviator表达式引擎进行动态表达式计算,支持在流程定义中配置灵活的条件判断逻辑 * * @author pigx + * @date 2026-02-10 */ @Slf4j @Component("expressionHandler") @@ -376,6 +377,117 @@ public class ExpressionHandler { return inCompare(symbol, Convert.toLong(nodeUserDto.getId()), userIdList); } + /** + * 角色比较 + *

+ * 检查流程变量中的用户是否拥有指定的角色(属于或不属于指定角色列表)。 + * 通过角色ID列表查询出拥有这些角色的所有用户ID,再判断当前用户是否在其中。 + * @param key 表单key,对应流程变量的键名 + * @param param 角色列表参数(JSON格式) + * @param symbol 比较符号(in/==:属于,notin/!=:不属于) + * @param execution 流程执行上下文 + * @return 比较结果 + */ + @SneakyThrows + public boolean roleCompare(String key, String param, String symbol, DelegateExecution execution) { + param = EscapeUtil.unescape(param); + + Object value = execution.getVariable(key); + + String jsonString = objectMapper.writeValueAsString(value); + log.debug("表单值:key={} value={} symbol={}", key, jsonString, symbol); + log.debug("条件 参数:{}", param); + + if (value == null) { + return false; + } + + // 表单值(发起人或表单中的用户) + List nodeUserDtoList = objectMapper.readValue(jsonString, new TypeReference<>() { + }); + + if (CollUtil.isEmpty(nodeUserDtoList) || nodeUserDtoList.size() != 1) { + return false; + } + + NodeUser nodeUserDto = nodeUserDtoList.get(0); + + // 参数 - 解析角色列表 + List paramRoleList = objectMapper.readValue(param, new TypeReference>() { + }); + + List roleIdList = paramRoleList.stream().map(NodeUser::getId).toList(); + + // 根据角色ID列表查询拥有这些角色的所有用户ID + List userIdList = new java.util.ArrayList<>(); + if (CollUtil.isNotEmpty(roleIdList)) { + R> r = remoteUserService.getUserIdListByRoleIdList(roleIdList); + if (r.getData() != null) { + userIdList.addAll(r.getData()); + } + } + + return inCompare(symbol, Convert.toLong(nodeUserDto.getId()), userIdList); + } + + /** + * 岗位比较 + *

+ * 检查流程变量中的用户是否属于指定的岗位(属于或不属于指定岗位列表)。 + * 通过岗位ID列表查询出属于这些岗位的所有用户ID,再判断当前用户是否在其中。 + * @param key 表单key,对应流程变量的键名 + * @param param 岗位列表参数(JSON格式) + * @param symbol 比较符号(in/==:属于,notin/!=:不属于) + * @param execution 流程执行上下文 + * @return 比较结果 + */ + @SneakyThrows + public boolean postCompare(String key, String param, String symbol, DelegateExecution execution) { + param = EscapeUtil.unescape(param); + + Object value = execution.getVariable(key); + + String jsonString = objectMapper.writeValueAsString(value); + log.debug("表单值:key={} value={} symbol={}", key, jsonString, symbol); + log.debug("条件 参数:{}", param); + + if (value == null) { + return false; + } + + // 表单值(发起人或表单中的用户) + List nodeUserDtoList = objectMapper.readValue(jsonString, new TypeReference<>() { + }); + + if (CollUtil.isEmpty(nodeUserDtoList) || nodeUserDtoList.size() != 1) { + return false; + } + + NodeUser nodeUserDto = nodeUserDtoList.get(0); + + // 参数 - 解析岗位列表 + List paramPostList = objectMapper.readValue(param, new TypeReference>() { + }); + + List postIdList = paramPostList.stream().map(NodeUser::getId).toList(); + + // 根据岗位ID列表查询属于这些岗位的所有用户ID + List userIdList = new java.util.ArrayList<>(); + if (CollUtil.isNotEmpty(postIdList)) { + R> r = remoteUserService.getUserIdListByPostIdList(postIdList); + if (r.getData() != null) { + userIdList.addAll(r.getData()); + } + } + + return inCompare(symbol, Convert.toLong(nodeUserDto.getId()), userIdList); + } + + /** + * 天数条件判断 + * @param execution 流程执行上下文 + * @return 始终返回true + */ public boolean days(DelegateExecution execution) { return true; } diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/expression/condition/impl/SelectPostNodeConditionStrategy.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/expression/condition/impl/SelectPostNodeConditionStrategy.java new file mode 100644 index 000000000..1c7a081f3 --- /dev/null +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/expression/condition/impl/SelectPostNodeConditionStrategy.java @@ -0,0 +1,52 @@ +package com.pig4cloud.pigx.flow.support.expression.condition.impl; + +import cn.hutool.core.util.EscapeUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.flow.dto.Condition; +import com.pig4cloud.pigx.flow.support.expression.condition.NodeConditionStrategy; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.stereotype.Component; + +/** + * 岗位选择器类型条件处理器 + *

+ * 处理岗位选择器类型表单控件的条件表达式生成。 + * 支持判断发起人是否属于指定的岗位。 + * 支持的比较操作: + * 1. 属于(in/==):检查用户是否属于指定岗位中的任意一个 + * 2. 不属于(notin/!=):检查用户是否不属于指定岗位中的任意一个 + * + * @author pigx + * @date 2026-02-10 + */ +@RequiredArgsConstructor +@Component("SelectPostNodeConditionStrategy") +public class SelectPostNodeConditionStrategy implements NodeConditionStrategy { + + private final ObjectMapper objectMapper; + + /** + * 处理岗位选择器类型的条件表达式 + *

+ * 将岗位选择数据序列化为JSON格式,并生成调用岗位比较方法的SpEL表达式。 + * 序列化后的JSON会进行转义处理,以确保在表达式中的正确性。 + * + * @param condition 条件配置对象,包含字段名、操作符、岗位列表等信息 + * @return SpEL表达式字符串 + */ + @Override + @SneakyThrows + public String handle(Condition condition) { + + String compare = condition.getExpression(); + String id = condition.getKey(); + Object value = condition.getValue(); + + return StrUtil.format("(expressionHandler.postCompare(\"{}\",\"{}\",\"{}\", execution))", id, + EscapeUtil.escape(objectMapper.writeValueAsString(value)), compare); + + } + +} diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/expression/condition/impl/SelectRoleNodeConditionStrategy.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/expression/condition/impl/SelectRoleNodeConditionStrategy.java new file mode 100644 index 000000000..aecee655f --- /dev/null +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/expression/condition/impl/SelectRoleNodeConditionStrategy.java @@ -0,0 +1,52 @@ +package com.pig4cloud.pigx.flow.support.expression.condition.impl; + +import cn.hutool.core.util.EscapeUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.flow.dto.Condition; +import com.pig4cloud.pigx.flow.support.expression.condition.NodeConditionStrategy; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.stereotype.Component; + +/** + * 角色选择器类型条件处理器 + *

+ * 处理角色选择器类型表单控件的条件表达式生成。 + * 支持判断发起人是否拥有指定的角色。 + * 支持的比较操作: + * 1. 属于(in/==):检查用户是否拥有指定角色中的任意一个 + * 2. 不属于(notin/!=):检查用户是否不拥有指定角色中的任意一个 + * + * @author pigx + * @date 2026-02-10 + */ +@RequiredArgsConstructor +@Component("SelectRoleNodeConditionStrategy") +public class SelectRoleNodeConditionStrategy implements NodeConditionStrategy { + + private final ObjectMapper objectMapper; + + /** + * 处理角色选择器类型的条件表达式 + *

+ * 将角色选择数据序列化为JSON格式,并生成调用角色比较方法的SpEL表达式。 + * 序列化后的JSON会进行转义处理,以确保在表达式中的正确性。 + * + * @param condition 条件配置对象,包含字段名、操作符、角色列表等信息 + * @return SpEL表达式字符串 + */ + @Override + @SneakyThrows + public String handle(Condition condition) { + + String compare = condition.getExpression(); + String id = condition.getKey(); + Object value = condition.getValue(); + + return StrUtil.format("(expressionHandler.roleCompare(\"{}\",\"{}\",\"{}\", execution))", id, + EscapeUtil.escape(objectMapper.writeValueAsString(value)), compare); + + } + +} diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/feign/RemoteUserService.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/feign/RemoteUserService.java index 64d5a9f96..b157e8775 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/feign/RemoteUserService.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/feign/RemoteUserService.java @@ -32,7 +32,7 @@ import java.util.List; /** * @author lengleng - * @date 2018/6/22 + * @date 2026-02-10 */ @FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.UPMS_SERVICE) public interface RemoteUserService { @@ -103,6 +103,15 @@ public interface RemoteUserService { @GetMapping("/user/getUserIdListByDeptIdList") R> getUserIdListByDeptIdList(@RequestParam("deptIdList") List deptIdList); + /** + * 根据岗位ID列表获取用户ID列表 + * + * @param postIdList 岗位ID列表 + * @return 用户ID列表 + */ + @GetMapping("/user/getUserIdListByPostIdList") + R> getUserIdListByPostIdList(@RequestParam("postIdList") List postIdList); + /** * 通过用户名查询用户列表 * diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysUserController.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysUserController.java index cc5deba18..fe4a82eb5 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysUserController.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysUserController.java @@ -53,7 +53,7 @@ import java.util.List; * 用户管理控制器:提供用户相关的REST接口 * * @author lengleng - * @date 2025/07/04 + * @date 2026-02-10 */ @RestController @AllArgsConstructor @@ -291,6 +291,17 @@ public class SysUserController { return R.ok(userService.listUserIdByDeptIds(CollUtil.toList(deptIdList))); } + /** + * 根据岗位ID列表获取用户ID列表接口 + * + * @param postIdList 岗位ID列表 + * @return R 返回结果对象,包含根据岗位ID列表获取到的用户ID列表信息 + */ + @GetMapping("/getUserIdListByPostIdList") + public R> getUserIdListByPostIdList(Long[] postIdList) { + return R.ok(userService.listUserIdByPostIds(CollUtil.toList(postIdList))); + } + /** * 根据用户名获取用户列表 * diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysUserService.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysUserService.java index ccbb74237..d655bff5e 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysUserService.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysUserService.java @@ -36,7 +36,7 @@ import java.util.List; /** * @author lengleng - * @date 2017/10/31 + * @date 2026-02-10 */ public interface SysUserService extends IService { @@ -180,6 +180,14 @@ public interface SysUserService extends IService { */ List listUserIdByDeptIds(List deptIdList); + /** + * 根据岗位ID列表获取用户ID列表 + * + * @param postIdList 岗位ID列表 + * @return List 用户ID列表 + */ + List listUserIdByPostIds(List postIdList); + /** * 重置用户密码 * diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysUserServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysUserServiceImpl.java index 4d9941a5e..f0dc17a7f 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysUserServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysUserServiceImpl.java @@ -21,7 +21,6 @@ package com.pig4cloud.pigx.admin.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.StringPool; @@ -71,7 +70,7 @@ import java.util.stream.Collectors; /** * @author lengleng - * @date 2017/10/31 + * @date 2026-02-10 */ @Slf4j @Service @@ -744,6 +743,12 @@ public class SysUserServiceImpl extends ServiceImpl impl return R.ok(); } + /** + * 根据角色ID列表获取用户ID列表 + * + * @param roleIdList 角色ID列表 + * @return 用户ID列表 + */ @Override public List listUserIdByRoleIds(List roleIdList) { return sysUserRoleMapper.selectList(Wrappers.lambdaQuery().in(SysUserRole::getRoleId, roleIdList)) @@ -768,6 +773,20 @@ public class SysUserServiceImpl extends ServiceImpl impl return baseMapper.selectList(Wrappers.lambdaQuery().in(SysUser::getUserId, userList)); } + /** + * 根据岗位ID列表获取用户ID列表 + * + * @param postIdList 岗位ID列表 + * @return 用户ID列表 + */ + @Override + public List listUserIdByPostIds(List postIdList) { + return sysUserPostMapper.selectList(Wrappers.lambdaQuery().in(SysUserPost::getPostId, postIdList)) + .stream() + .map(SysUserPost::getUserId) + .toList(); + } + /** * 获取当前用户的部门列表 *