Flowable 实战:从架构解耦到多状态动态查询的高性能重构方案

基础架构到实战重构:Flowable 工作流"多状态"动态查询方案

1. 项目背景与环境配置

在企业级数智化项目中,工作流引擎(Flowable)是核心组件。本项目采用标准的 Spring Boot 2.7.6 多模块架构进行构建。

  • 父工程 (flow-parent) :负责统一管理 Flowable 6.8.0MyBatis-Plus 3.5.2Druid 等核心依赖版本,确保全项目依赖的一致性。
  • 核心模块 (flow-core) :不仅集成了流程引擎,还包含了 UI 设计器逻辑。通过自定义 build 配置,实现了 Mapper.xml 与 Java 代码同目录打包,提升了开发体验。

2. 需求痛点:如何优雅地支持"多状态"过滤?

原本的"我发起的流程"接口仅支持单一状态过滤。但在实际业务中,用户经常需要在一个页签(如"已结束")中同时看到以下几种状态的数据:

  • BJ (办结)
  • BH (驳回)
  • CH (撤回)
  • ZZ (终止)

2.1 数据表关联逻辑

该查询关联了 6 张表,包括 Flowable 核心历史表(act_hi_procinst)、流程定义表(act_re_procdef)以及业务扩展表(tbl_flow_extend_hisprocinst)。


3. 后端重构:动态 SQL 的演进

3.1 参数模型扩展

InstanceQueryParamsVo 中新增 processStatusList 字段,用于接收前端传来的状态集合。

java 复制代码
@Data
public class InstanceQueryParamsVo implements Serializable {
    // ... 原有字段 ...
    @ApiModelProperty(value = "流程状态集合,如:['BH','CH','BJ','ZZ']")
    private List<String> processStatusList; 
}

3.2 MyBatis 动态 SQL 实现

利用 <foreach> 标签动态构建 IN 子句。这种方式既支持前端自由组合状态,又通过硬编码白名单防御了注入风险。

xml 复制代码
<select id="findMyProcessInstancesPagerModel" ...>
    SELECT DISTINCT t1.PROC_INST_ID_, t1.NAME_, t4.process_status ...
    FROM act_hi_procinst t1
    INNER JOIN tbl_flow_extend_hisprocinst t4 ON t1.PROC_INST_ID_ = t4.process_instance_id
    WHERE 1=1
    
    <if test="params.processStatusList != null and params.processStatusList.size() > 0">
        AND t4.process_status IN
        <foreach collection="params.processStatusList" item="status" open="(" separator="," close=")">
            #{status}
        </foreach>
    </if>
    
    <if test="sqlOrderBy != null">
        ORDER BY
        <foreach collection="sqlOrderBy" item="order" index="field" separator=",">
            <choose>
                <when test="field == 'startTime'">t1.START_TIME_</when>
                <when test="field == 'processStatus'">t4.process_status</when>
                <otherwise>t1.START_TIME_</otherwise>
            </choose>
            ${order.name()}
        </foreach>
    </if>
</select>

4. 前端实践:Tab 驱动与类型挑战

4.1 逻辑适配

前端基于 activeTab 状态,在请求 entity 中动态注入参数。这样可以复用同一个查询接口,极大减轻了维护成本。

typescript 复制代码
const requestData = {
  entity: {
    ...params,
    // 仅在"已完成"页签注入多状态集合
    ...(activeTab === 'completed' && { 
      processStatusList: ['BH', 'CH', 'BJ', 'ZZ'] 
    }),
  },
};

4.2 避坑指南:TypeScript 类型守卫

报错信息This comparison appears to be unintentional because the types 'TabType' and '"completed"' have no overlap. 原因activeTab 定义的联合类型中缺少新状态。 解决 :扩展 TabType 定义,或者在逻辑判断中使用类型断言 (activeTab as string) === 'completed'


5. 进阶:性能与安全深度优化

随着流程数据量增长,简单的实现往往隐藏着性能隐患:

5.1 索引预警:模糊查询的隐患

我们使用了 LIKE CONCAT('%', #{keyword}, '%')

  • 风险 :左模糊查询会导致 B+Tree 索引失效
  • 优化 :在大数据场景下,建议使用右模糊查询 keyword% 或引入 Elasticsearch 进行搜索。

5.2 安全防护:排序字段白名单

ORDER BY 中,必须使用 ${}

  • 对策 :通过 MyBatis 的 <choose> 标签硬编码字段映射,防止攻击者通过字段名进行 SQL 注入。

5.3 架构思考:减少多表 Join

  • 痛点:跨表关联 6 张表在百万级数据下 IO 压力巨大。
  • 建议 :在 tbl_flow_extend_hisprocinst 扩展表中冗余存储高频字段(如应用名、发起人姓名),将"6 表 Join"降级为"单表或双表查询"。

6. 总结

本次重构通过"后端提供能力、前端定义视图"的思路,以最小的代码变动实现了高度灵活的查询。

核心收益:

  1. 高复用性:一个接口支撑了待办、已办、发起、结束等多个业务视图。
  2. 安全性:通过动态 SQL 标签规避了拼接风险。
  3. 可扩展性:未来新增状态,只需前端更新数组,无需后端发布。
相关推荐
子淼8122 小时前
HTML入门指南:构建网页的基石
前端·html
None3212 小时前
【NestJs】Websocket 通关指南:从入门到实战
后端·node.js
yuyu_03042 小时前
Spring Boot在Windows开机自启动
windows·spring boot·后端
农夫山泉不太甜2 小时前
Electron离屏渲染技术详
前端
L0CK2 小时前
高级篇 05. 多级缓存 - JVM 进程缓存之实现业务缓存
后端
深念Y2 小时前
Chrome MCP Server 配置失败全记录:一场历时数小时的“fetch failed”排查之旅
前端·自动化测试·chrome·http·ai·agent·mcp
Oneslide2 小时前
基于Nginx实现目录列表展示与文件下载服务(K8s ConfigMap配置版)
后端
Java编程爱好者2 小时前
对于java工程师(高级)的面试如果只考3道题,就能看出他的真实水平
后端
一个有故事的男同学2 小时前
从零打造专业级前端 SDK (四):错误监控与生产发布
前端