当前位置: 首页 > news >正文

从‘查不到数据’到‘自动流转’:手把手调试RuoYi-Flowable动态审批人逻辑(附完整测试类写法)

从‘查不到数据’到‘自动流转’:RuoYi-Flowable动态审批人全流程调试指南

1. 问题定位与调试环境搭建

当你在RuoYi-Flowable项目中实现动态审批人功能时,最令人头疼的莫过于流程表达式配置正确但运行时却返回null或报错。这种情况往往源于三个关键环节的配置问题:

典型报错场景分析

  • Unknown property used in expression:服务类未被Spring容器正确识别
  • NullPointerException:MyBatis查询结果封装失败
  • No such method:流程表达式调用方法签名不匹配

提示:调试前请确保已安装Lombok插件,否则部分代码可能无法正常编译

我们先搭建一个最小化的测试环境。在src/test/java下创建测试类结构:

@SpringBootTest @RunWith(SpringRunner.class) public class FlowableDynamicAssigneeTest { @Autowired private ISysUserService userService; @Autowired private RuntimeService runtimeService; }

注意测试类必须与主启动类在相同包路径下,否则需要显式指定启动类:

@SpringBootTest(classes = RuoYiApplication.class)

2. Service层单元测试实战

2.1 解决MyBatis结果封装为null的问题

selectDirectLeaderByPostCode方法始终返回null时,首先检查Mapper层结果映射。以下是两种解决方案的对比:

方案配置方式优点缺点
全局配置在application.yml中添加mybatis.configuration.map-underscore-to-camel-case=true一次性解决所有映射问题无法处理复杂映射关系
XML映射在Mapper.xml中定义<resultMap>可精确控制每个字段映射需要为每个查询单独配置

推荐使用XML映射方案,示例:

<resultMap id="UserResult" type="com.ruoyi.system.domain.SysUser"> <id property="userId" column="user_id" /> <result property="deptId" column="dept_id" /> <result property="userName" column="user_name" /> </resultMap> <select id="selectUsersByDeptId" resultMap="UserResult"> SELECT * FROM sys_user WHERE dept_id = #{deptId} </select>

2.2 循环依赖问题破解

若测试时遇到Circular dependency警告,可通过以下方式解决:

  1. 将测试类移到admin模块(主模块)下
  2. 使用@Lazy注解延迟加载依赖
  3. 重构代码消除循环引用

推荐方案1,这是最彻底的解决方案。

3. 流程表达式深度解析

3.1 两种动态审批人实现方式对比

固定变量方式

variables.put("headTeacherId", 12345);

BPMN配置:

<formalExpression>${headTeacherId}</formalExpression>

动态查询方式

<formalExpression>${userService.getHeadTeacher(execution.getVariable('initiator'))}</formalExpression>

性能与灵活性对比:

维度固定变量动态查询
执行效率★★★★☆★★☆☆☆
实时性★☆☆☆☆★★★★☆
配置复杂度★★☆☆☆★★★★☆
维护成本★★☆☆☆★★★☆☆

3.2 表达式服务注册机制

要使流程引擎识别自定义Service,必须确保:

  1. 类上有@Service注解
  2. 首字母小写的bean名称与表达式中的引用一致
  3. 在Flowable配置中显式注册:
@Configuration public class FlowableConfig { @Bean public SpringProcessEngineConfiguration springProcessEngineConfiguration( UserService userService) { SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration(); config.getBeans().put("userService", userService); return config; } }

常见错误排查清单:

  • 检查@Service注解的value是否与表达式中的名称一致
  • 确认方法访问权限为public
  • 验证方法参数类型与流程变量类型匹配

4. 完整测试类编写指南

4.1 集成测试示例

@Test public void testDynamicApprovalFlow() { // 1. 准备测试数据 Long studentId = 123L; String processDefinitionKey = "leaveApproval"; // 2. 设置流程变量 Map<String, Object> variables = new HashMap<>(); variables.put("initiator", studentId); // 3. 启动流程实例 ProcessInstance instance = runtimeService.startProcessInstanceByKey( processDefinitionKey, variables); // 4. 验证任务分配 Task task = taskService.createTaskQuery() .processInstanceId(instance.getId()) .singleResult(); Long expectedTeacherId = userService.selectDirectLeaderByPostCode( studentId, "class_teacher"); assertEquals(expectedTeacherId.toString(), task.getAssignee()); }

4.2 测试数据准备技巧

使用@Sql注解初始化测试数据:

@Test @Sql(scripts = "/sql/init_teacher_student.sql") public void testWithRealData() { // 测试逻辑 }

init_teacher_student.sql示例:

INSERT INTO sys_dept VALUES(1, '计算机学院', 0, '001'); INSERT INTO sys_user VALUES(100, 1, '张老师', 'teacher'); INSERT INTO sys_user VALUES(101, 1, '李同学', 'student'); INSERT INTO sys_user_post VALUES(100, 1); -- 班主任岗位

5. 生产环境优化建议

  1. 性能优化

    • dept_idpost_code字段添加索引
    • 考虑缓存班主任信息,减少数据库查询
    • 批量查询替代循环中的单条查询
  2. 健壮性增强

    public Long selectDirectLeaderByPostCode(Long userId, String postCode) { List<SysUser> users = this.selectDeptUsersByUserId(userId); if(CollectionUtils.isEmpty(users)) { throw new BusinessException("部门下没有用户"); } return users.stream() .filter(u -> this.checkIncludePostCode( postMapper.selectPostCodeListByUserId(u.getUserId()), postCode)) .findFirst() .orElseThrow(() -> new BusinessException("未找到指定岗位负责人")) .getUserId(); }
  3. 事务管理

    @Transactional(rollbackFor = Exception.class) public Long assignHeadTeacher(Long deptId, Long userId) { // 先解除原班主任关联 postMapper.deleteByDeptAndPost(deptId, "class_teacher"); // 设置新班主任 return postMapper.insertUserPost(userId, "class_teacher"); }

在调试过程中,记得充分利用Flowable的调试日志:

# application.yml logging.level.org.flowable=DEBUG

当看到控制台输出流程变量和SQL语句时,就像拥有了X光透视能力,可以清晰看到整个审批流转的内部运作机制。

http://www.jsqmd.com/news/570977/

相关文章:

  • 终极Obsidian个性化定制指南:从基础到专业的视觉美化完全教程
  • 还在为股票分析头疼?让AI智能体团队帮你做决策
  • 依美妆教育台州校区的美甲培训性价比高吗,价格多少 - 工业设备
  • 蠕虫式XMRig挖矿攻击:盗版软件 + BYOVD + 时间炸弹,新型加密货币劫持威胁来袭
  • 从本地到服务器:EasyExcel导出功能报错NoSuchMethodError的完整解决流程
  • 终极指南:如何用网页时光机浏览器扩展永久保存重要网页内容
  • 如何在Mac上创建Windows启动盘:WinDiskWriter完整指南
  • 2026年全国微型泵厂家实力精选 适配科研环保多工况 兼顾定制与智能 - 深度智识库
  • kkfileview预览Word文档乱码?别急着重启,先检查这3个地方(含字体安装避坑指南)
  • C语言动态内存分配实战:通讯录管理系统设计与优化
  • 黑群晖DSM7.x免全洗白激活AME套件保姆级教程(支持HEVC/HEIC解码)
  • 如何突破艾尔登法环角色培养限制?ER-Save-Editor带来的游戏状态定制新体验
  • lvgl_v8之button之toogle效果代码示例
  • Video2X:AI视频增强的终极解决方案,轻松实现24FPS到120FPS流畅转换
  • 2026年分析台州美甲培训优质机构,哪家性价比高? - 工业品网
  • 青岛OJ(QingdaoU/OnlineJudge)从安装到HTTPS配置:一站式部署指南(含常见问题解决)
  • 基于Simulink的实车数据驱动PMSM参数在线校准
  • 告别复杂配置:通义千问2.5-7B-Instruct一键部署与简单调用
  • 奇妙智能滑轨机器人变电室巡检
  • 如何解决CVE-bin-tool数据库更新失败?3个实用方案
  • 04-自动配置原理
  • 跨端兼容与性能抉择:UniApp安卓项目MQTT接入方案深度对比
  • Sushi扩展开发:基于afterMigrate方法实现自定义表操作
  • 2026年台州美睫培训基地排名,揭秘靠谱美睫培训学校哪家强 - 工业品牌热点
  • 《自然方法》生命科学的GPT时刻:scGPT重新定义单细胞多组学分析
  • GLM-4.1V-9B-Base部署教程:Docker镜像体积精简与启动速度优化
  • SEO优化基础教程_SEO培训班怎么选择
  • Linux作业
  • 轨道巡检机器人如何实现自主充电
  • 2026年小程序定制开发公司推荐:十大权威榜单及甄选测评指南 - 品牌种草官