feat(pigx-flow-biz): 条件表达式新增角色和岗位选择器条件处理功能

- 新增SelectRoleNodeConditionStrategy类,处理角色选择器类型条件表达式
- 新增SelectPostNodeConditionStrategy类,处理岗位选择器类型条件表达式
- 在ExpressionHandler中新增roleCompare和postCompare方法,支持角色和岗位比较逻辑
- 更新SysUserService接口及实现,新增根据岗位ID列表获取用户ID列表功能
- 在SysUserController中新增根据岗位ID列表获取用户ID列表接口
- 更新RemoteUserService Feign客户端,新增岗位查询方法
- 更新相关文件版权信息和日期注释
- 删除无用的IProcessInstanceStatusEventService接口
- 在数据库中新增bpm_oa_leave请假申请表结构
This commit is contained in:
PIGCLOUD committed 2026-03-08 15:02:15 +08:00
1 parent b7631ddeaf
commit d1858d5844
10 files changed
+306 -30

No files matched your search

+19
View File
@@ -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
-- ----------------------------
+19
View File
@@ -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
-- ----------------------------
@@ -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);
}
@@ -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);
}
/**
* 角色比较
* <p>
* 检查流程变量中的用户是否拥有指定的角色(属于或不属于指定角色列表)。
* 通过角色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<NodeUser> nodeUserDtoList = objectMapper.readValue(jsonString, new TypeReference<>() {
});
if (CollUtil.isEmpty(nodeUserDtoList) || nodeUserDtoList.size() != 1) {
return false;
}
NodeUser nodeUserDto = nodeUserDtoList.get(0);
// 参数 - 解析角色列表
List<NodeUser> paramRoleList = objectMapper.readValue(param, new TypeReference<List<NodeUser>>() {
});
List<Long> roleIdList = paramRoleList.stream().map(NodeUser::getId).toList();
// 根据角色ID列表查询拥有这些角色的所有用户ID
List<Long> userIdList = new java.util.ArrayList<>();
if (CollUtil.isNotEmpty(roleIdList)) {
R<List<Long>> r = remoteUserService.getUserIdListByRoleIdList(roleIdList);
if (r.getData() != null) {
userIdList.addAll(r.getData());
}
}
return inCompare(symbol, Convert.toLong(nodeUserDto.getId()), userIdList);
}
/**
* 岗位比较
* <p>
* 检查流程变量中的用户是否属于指定的岗位(属于或不属于指定岗位列表)。
* 通过岗位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<NodeUser> nodeUserDtoList = objectMapper.readValue(jsonString, new TypeReference<>() {
});
if (CollUtil.isEmpty(nodeUserDtoList) || nodeUserDtoList.size() != 1) {
return false;
}
NodeUser nodeUserDto = nodeUserDtoList.get(0);
// 参数 - 解析岗位列表
List<NodeUser> paramPostList = objectMapper.readValue(param, new TypeReference<List<NodeUser>>() {
});
List<Long> postIdList = paramPostList.stream().map(NodeUser::getId).toList();
// 根据岗位ID列表查询属于这些岗位的所有用户ID
List<Long> userIdList = new java.util.ArrayList<>();
if (CollUtil.isNotEmpty(postIdList)) {
R<List<Long>> 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;
}
@@ -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;
/**
* 岗位选择器类型条件处理器
* <p>
* 处理岗位选择器类型表单控件的条件表达式生成。
* 支持判断发起人是否属于指定的岗位。
* 支持的比较操作:
* 1. 属于(in/==):检查用户是否属于指定岗位中的任意一个
* 2. 不属于(notin/!=):检查用户是否不属于指定岗位中的任意一个
*
* @author pigx
* @date 2026-02-10
*/
@RequiredArgsConstructor
@Component("SelectPostNodeConditionStrategy")
public class SelectPostNodeConditionStrategy implements NodeConditionStrategy {
private final ObjectMapper objectMapper;
/**
* 处理岗位选择器类型的条件表达式
* <p>
* 将岗位选择数据序列化为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);
}
}
@@ -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;
/**
* 角色选择器类型条件处理器
* <p>
* 处理角色选择器类型表单控件的条件表达式生成。
* 支持判断发起人是否拥有指定的角色。
* 支持的比较操作:
* 1. 属于(in/==):检查用户是否拥有指定角色中的任意一个
* 2. 不属于(notin/!=):检查用户是否不拥有指定角色中的任意一个
*
* @author pigx
* @date 2026-02-10
*/
@RequiredArgsConstructor
@Component("SelectRoleNodeConditionStrategy")
public class SelectRoleNodeConditionStrategy implements NodeConditionStrategy {
private final ObjectMapper objectMapper;
/**
* 处理角色选择器类型的条件表达式
* <p>
* 将角色选择数据序列化为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);
}
}
@@ -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<List<SysUser>> getUserIdListByDeptIdList(@RequestParam("deptIdList") List<Long> deptIdList);
/**
* 根据岗位ID列表获取用户ID列表
*
* @param postIdList 岗位ID列表
* @return 用户ID列表
*/
@GetMapping("/user/getUserIdListByPostIdList")
R<List<Long>> getUserIdListByPostIdList(@RequestParam("postIdList") List<Long> postIdList);
/**
* 通过用户名查询用户列表
*
@@ -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<List<Long>> getUserIdListByPostIdList(Long[] postIdList) {
return R.ok(userService.listUserIdByPostIds(CollUtil.toList(postIdList)));
}
/**
* 根据用户名获取用户列表
*
@@ -36,7 +36,7 @@ import java.util.List;
/**
* @author lengleng
* @date 2017/10/31
* @date 2026-02-10
*/
public interface SysUserService extends IService<SysUser> {
@@ -180,6 +180,14 @@ public interface SysUserService extends IService<SysUser> {
*/
List<SysUser> listUserIdByDeptIds(List<Long> deptIdList);
/**
* 根据岗位ID列表获取用户ID列表
*
* @param postIdList 岗位ID列表
* @return List<Long> 用户ID列表
*/
List<Long> listUserIdByPostIds(List<Long> postIdList);
/**
* 重置用户密码
*
@@ -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<SysUserMapper, SysUser> impl
return R.ok();
}
/**
* 根据角色ID列表获取用户ID列表
*
* @param roleIdList 角色ID列表
* @return 用户ID列表
*/
@Override
public List<Long> listUserIdByRoleIds(List<Long> roleIdList) {
return sysUserRoleMapper.selectList(Wrappers.<SysUserRole>lambdaQuery().in(SysUserRole::getRoleId, roleIdList))
@@ -768,6 +773,20 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
return baseMapper.selectList(Wrappers.<SysUser>lambdaQuery().in(SysUser::getUserId, userList));
}
/**
* 根据岗位ID列表获取用户ID列表
*
* @param postIdList 岗位ID列表
* @return 用户ID列表
*/
@Override
public List<Long> listUserIdByPostIds(List<Long> postIdList) {
return sysUserPostMapper.selectList(Wrappers.<SysUserPost>lambdaQuery().in(SysUserPost::getPostId, postIdList))
.stream()
.map(SysUserPost::getUserId)
.toList();
}
/**
* 获取当前用户的部门列表
*