diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/processmapping/BpmProcessMappingConfigController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/processmapping/BpmProcessMappingConfigController.java index c1d9ebdb..2229db25 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/processmapping/BpmProcessMappingConfigController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/processmapping/BpmProcessMappingConfigController.java @@ -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 updateProcessMappingConfig(@Valid @RequestBody BpmProcessMappingConfigUpdateReqVO updateReqVO) { processMappingConfigService.updateProcessMappingConfig(updateReqVO); return success(true); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index b6e2901b..fc3e526a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -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> resolveEntityType(String typeName) { List> entityClazzs = entityTypeMap.get(typeName); if (entityClazzs == null) { - throw new ServiceException(500, "不支持的流程类型"); + // 添加日志记录以便更好地调试 + log.error("不支持的流程类型: {}", typeName); + throw new ServiceException(500, "不支持的流程类型: " + typeName); } return entityClazzs; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/processmapping/BpmProcessMappingConfigDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/processmapping/BpmProcessMappingConfigDO.java new file mode 100644 index 00000000..7e24d05c --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/processmapping/BpmProcessMappingConfigDO.java @@ -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; + +} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/processmapping/BpmProcessMappingConfigMapper.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/processmapping/BpmProcessMappingConfigMapper.java index b88d8569..03011099 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/processmapping/BpmProcessMappingConfigMapper.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/processmapping/BpmProcessMappingConfigMapper.java @@ -29,4 +29,4 @@ public interface BpmProcessMappingConfigMapper extends BaseMapperX 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; + } +} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/processmapping/ProcessQueryService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/processmapping/ProcessQueryService.java index 7efb1aa0..7499c982 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/processmapping/ProcessQueryService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/processmapping/ProcessQueryService.java @@ -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); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/processmapping/ProcessQueryServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/processmapping/ProcessQueryServiceImpl.java index 9ada60ea..20685e30 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/processmapping/ProcessQueryServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/processmapping/ProcessQueryServiceImpl.java @@ -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); + } + /** * 获取流程映射配置 */ diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/mapper/processmapping/BpmProcessMappingConfigMapper.xml b/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/mapper/processmapping/BpmProcessMappingConfigMapper.xml new file mode 100644 index 00000000..8914465f --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/mapper/processmapping/BpmProcessMappingConfigMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file