feat(bpm): 添加流程映射配置系统
- 新增 BpmProcessMappingConfigDO 数据对象和相关 Mapper - 实现流程映射配置的查询功能 - 添加日志记录和错误处理 -优化权限控制注解
This commit is contained in:
parent
8ee70f4944
commit
b8339c9056
@ -46,7 +46,7 @@ public class BpmProcessMappingConfigController {
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新流程映射配置")
|
||||
@PreAuthorize("@ss.hasPermission('bmp:process-mapping-config:update')")
|
||||
@PreAuthorize("@ss.hasPermission('bpm:process-mapping-config:update')")
|
||||
public CommonResult<Boolean> updateProcessMappingConfig(@Valid @RequestBody BpmProcessMappingConfigUpdateReqVO updateReqVO) {
|
||||
processMappingConfigService.updateProcessMappingConfig(updateReqVO);
|
||||
return success(true);
|
||||
|
@ -34,6 +34,8 @@ import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@ -59,6 +61,8 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti
|
||||
@Validated
|
||||
public class BpmProcessInstanceController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BpmProcessInstanceController.class);
|
||||
|
||||
@Resource
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
|
||||
@ -265,7 +269,9 @@ public class BpmProcessInstanceController {
|
||||
private List<Class<?>> resolveEntityType(String typeName) {
|
||||
List<Class<?>> entityClazzs = entityTypeMap.get(typeName);
|
||||
if (entityClazzs == null) {
|
||||
throw new ServiceException(500, "不支持的流程类型");
|
||||
// 添加日志记录以便更好地调试
|
||||
log.error("不支持的流程类型: {}", typeName);
|
||||
throw new ServiceException(500, "不支持的流程类型: " + typeName);
|
||||
}
|
||||
return entityClazzs;
|
||||
}
|
||||
|
@ -0,0 +1,60 @@
|
||||
package cn.iocoder.yudao.module.bpm.dal.dataobject.processmapping;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 流程映射配置 DO
|
||||
*
|
||||
* @author 系统
|
||||
*/
|
||||
@TableName("bpm_process_mapping_config")
|
||||
@KeySequence("bpm_process_mapping_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BpmProcessMappingConfigDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 流程唯一标识(如:ORDER,PAYMENT)
|
||||
*/
|
||||
private String processCode;
|
||||
|
||||
/**
|
||||
* 流程显示名称
|
||||
*/
|
||||
private String processName;
|
||||
|
||||
/**
|
||||
* 物理表名(如:order_info)
|
||||
*/
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 实体类全限定名(如:com.example.order.OrderDO)
|
||||
*/
|
||||
private String entityClass;
|
||||
|
||||
/**
|
||||
* Mapper接口全限定名(如:com.example.order.OrderMapper)
|
||||
*/
|
||||
private String mapperClass;
|
||||
|
||||
/**
|
||||
* 流程描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
}
|
@ -29,4 +29,4 @@ public interface BpmProcessMappingConfigMapper extends BaseMapperX<BpmProcessMap
|
||||
return selectOne(BpmProcessMappingConfigDO::getProcessCode, processCode);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,191 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.core.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 反射调用工具类
|
||||
* 用于动态调用Mapper方法,支持流程映射配置系统的动态查询功能
|
||||
*
|
||||
* @author 系统
|
||||
*/
|
||||
@Slf4j
|
||||
public class ReflectionInvoker {
|
||||
|
||||
/**
|
||||
* 动态调用Mapper方法
|
||||
*
|
||||
* @param mapperBean Spring容器中的Mapper实例
|
||||
* @param methodName 方法名(如"selectById")
|
||||
* @param paramType 参数类型
|
||||
* @param args 参数值
|
||||
* @return 方法执行结果
|
||||
*/
|
||||
public static Object invokeMapperMethod(Object mapperBean, String methodName, Class<?> paramType, Object... args) {
|
||||
return invokeMapperMethod(mapperBean, methodName, new Class<?>[]{paramType}, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态调用Mapper方法
|
||||
*
|
||||
* @param mapperBean Spring容器中的Mapper实例
|
||||
* @param methodName 方法名(如"selectById")
|
||||
* @param paramTypes 参数类型数组
|
||||
* @param args 参数值数组
|
||||
* @return 方法执行结果
|
||||
*/
|
||||
public static Object invokeMapperMethod(Object mapperBean, String methodName, Class<?>[] paramTypes, Object... args) {
|
||||
if (mapperBean == null) {
|
||||
throw new IllegalArgumentException("mapperBean cannot be null");
|
||||
}
|
||||
if (methodName == null || methodName.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("methodName cannot be null or empty");
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取Mapper类
|
||||
Class<?> mapperClass = mapperBean.getClass();
|
||||
|
||||
// 处理代理类的情况,获取实际的接口类型
|
||||
Class<?> targetClass = getTargetInterface(mapperClass);
|
||||
|
||||
log.debug("[invokeMapperMethod][开始调用] class={}, method={}, paramTypes={}, args={}",
|
||||
targetClass.getSimpleName(), methodName, Arrays.toString(paramTypes), Arrays.toString(args));
|
||||
|
||||
// 查找匹配的方法
|
||||
Method method = findMatchingMethod(targetClass, methodName, paramTypes);
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(String.format("Method '%s' with parameter types %s not found in class %s",
|
||||
methodName, Arrays.toString(paramTypes), targetClass.getName()));
|
||||
}
|
||||
|
||||
// 设置方法可访问
|
||||
method.setAccessible(true);
|
||||
|
||||
// 调用方法
|
||||
Object result = method.invoke(mapperBean, args);
|
||||
|
||||
log.debug("[invokeMapperMethod][调用成功] class={}, method={}, result={}",
|
||||
targetClass.getSimpleName(), methodName, result);
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
String errorMsg = String.format("Failed to invoke method '%s' on class '%s'",
|
||||
methodName, mapperBean.getClass().getName());
|
||||
log.error("[invokeMapperMethod][调用失败] {}, paramTypes={}, args={}",
|
||||
errorMsg, Arrays.toString(paramTypes), Arrays.toString(args), e);
|
||||
throw new RuntimeException(errorMsg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取目标接口类型
|
||||
* 处理Spring代理类的情况
|
||||
*/
|
||||
private static Class<?> getTargetInterface(Class<?> clazz) {
|
||||
// 如果是代理类,尝试获取实际的接口
|
||||
if (clazz.getName().contains("$Proxy") || clazz.getName().contains("CGLIB")) {
|
||||
Class<?>[] interfaces = clazz.getInterfaces();
|
||||
if (interfaces.length > 0) {
|
||||
// 选择第一个看起来像Mapper的接口
|
||||
for (Class<?> intf : interfaces) {
|
||||
if (intf.getName().contains("Mapper")) {
|
||||
return intf;
|
||||
}
|
||||
}
|
||||
return interfaces[0];
|
||||
}
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找匹配的方法
|
||||
* 支持精确匹配和兼容性匹配
|
||||
*/
|
||||
private static Method findMatchingMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes) {
|
||||
Method[] methods = clazz.getMethods();
|
||||
|
||||
// 先尝试精确匹配
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(methodName) &&
|
||||
Arrays.equals(method.getParameterTypes(), paramTypes)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
// 再尝试兼容性匹配(参数类型可赋值)
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(methodName) &&
|
||||
isParameterTypesAssignable(method.getParameterTypes(), paramTypes)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
// 最后尝试参数数量匹配(用于处理泛型擦除的情况)
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(methodName) &&
|
||||
method.getParameterCount() == paramTypes.length) {
|
||||
log.warn("[findMatchingMethod][使用参数数量匹配] method={}, expected={}, actual={}",
|
||||
method, Arrays.toString(paramTypes), Arrays.toString(method.getParameterTypes()));
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查参数类型是否可赋值
|
||||
*/
|
||||
private static boolean isParameterTypesAssignable(Class<?>[] methodParams, Class<?>[] providedParams) {
|
||||
if (methodParams.length != providedParams.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < methodParams.length; i++) {
|
||||
if (!isAssignable(methodParams[i], providedParams[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查类型是否可赋值(包含基本类型和包装类型的转换)
|
||||
*/
|
||||
private static boolean isAssignable(Class<?> target, Class<?> source) {
|
||||
if (target.isAssignableFrom(source)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 处理基本类型和包装类型
|
||||
if (target.isPrimitive() || source.isPrimitive()) {
|
||||
return isPrimitiveAssignable(target, source);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查基本类型是否可赋值
|
||||
*/
|
||||
private static boolean isPrimitiveAssignable(Class<?> target, Class<?> source) {
|
||||
if (target == source) return true;
|
||||
|
||||
// 常见的基本类型转换
|
||||
if ((target == int.class && source == Integer.class) ||
|
||||
(target == Integer.class && source == int.class) ||
|
||||
(target == long.class && source == Long.class) ||
|
||||
(target == Long.class && source == long.class) ||
|
||||
(target == boolean.class && source == Boolean.class) ||
|
||||
(target == Boolean.class && source == boolean.class)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
package cn.iocoder.yudao.module.bpm.service.processmapping;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程动态查询 Service 接口
|
||||
@ -17,4 +20,13 @@ public interface ProcessQueryService {
|
||||
* @return 对应的实体对象
|
||||
*/
|
||||
Object queryById(String processCode, Serializable id);
|
||||
|
||||
/**
|
||||
* 根据流程代码和条件查询列表
|
||||
*
|
||||
* @param processCode 流程标识
|
||||
* @param queryWrapper 查询条件
|
||||
* @return 实体列表
|
||||
*/
|
||||
List<?> queryList(String processCode, QueryWrapper<?> queryWrapper);
|
||||
}
|
||||
|
@ -8,9 +8,11 @@ import cn.iocoder.yudao.module.bpm.framework.core.util.ReflectionInvoker;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_MAPPING_CONFIG_NOT_EXISTS;
|
||||
@ -40,6 +42,18 @@ public class ProcessQueryServiceImpl implements ProcessQueryService {
|
||||
return ReflectionInvoker.invokeMapperMethod(mapperBean, "selectById", Serializable.class, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<?> queryList(String processCode, QueryWrapper<?> queryWrapper) {
|
||||
// 1. 获取流程配置
|
||||
BpmProcessMappingConfigDO config = getProcessMappingConfig(processCode);
|
||||
|
||||
// 2. 动态获取Mapper实例
|
||||
Object mapperBean = getMapperBean(config.getMapperClass());
|
||||
|
||||
// 3. 调用selectList方法
|
||||
return (List<?>) ReflectionInvoker.invokeMapperMethod(mapperBean, "selectList", QueryWrapper.class, queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程映射配置
|
||||
*/
|
||||
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.yudao.module.bpm.dal.mysql.processmapping.BpmProcessMappingConfigMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="cn.iocoder.yudao.module.bpm.dal.dataobject.processmapping.BpmProcessMappingConfigDO">
|
||||
<id column="id" property="id" jdbcType="BIGINT"/>
|
||||
<result column="process_code" property="processCode" jdbcType="VARCHAR"/>
|
||||
<result column="process_name" property="processName" jdbcType="VARCHAR"/>
|
||||
<result column="table_name" property="tableName" jdbcType="VARCHAR"/>
|
||||
<result column="entity_class" property="entityClass" jdbcType="VARCHAR"/>
|
||||
<result column="mapper_class" property="mapperClass" jdbcType="VARCHAR"/>
|
||||
<result column="description" property="description" jdbcType="VARCHAR"/>
|
||||
<result column="creator" property="creator" jdbcType="VARCHAR"/>
|
||||
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="updater" property="updater" jdbcType="VARCHAR"/>
|
||||
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="deleted" property="deleted" jdbcType="BIT"/>
|
||||
<result column="tenant_id" property="tenantId" jdbcType="BIGINT"/>
|
||||
</resultMap>
|
||||
|
||||
</mapper>
|
Loading…
Reference in New Issue
Block a user