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. 可扩展性:未来新增状态,只需前端更新数组,无需后端发布。
相关推荐
leo_messi947 分钟前
2026版商城项目(三)-- ES+认证服务
后端·python·django
0vvv015 分钟前
2026-NCTF-web-N-RustPICA
前端·ctf
前进的李工16 分钟前
MySQL角色管理:权限控制全攻略
前端·javascript·数据库·mysql
芯智工坊16 分钟前
第13章 Mosquitto监控与日志管理
前端·网络·人工智能·mqtt·开源
洒满阳光的庄园31 分钟前
Electron 桌面端打包流程说明
前端·javascript·electron
Hadoop_Liang1 小时前
构建Spring Boot项目Docker镜像
spring boot·后端·docker
Jagger_1 小时前
模型能力边界外扩时,工作到底在怎样被重做?
前端
SuperEugene1 小时前
前端通用基础组件设计:按钮/输入框/弹窗,统一设计标准|组件化设计基础篇
前端·javascript·vue.js·架构
Jagger_1 小时前
# 模型边界往外推的时候,我最怕的不是学不会,是没人听我解释
前端
OpenTiny社区1 小时前
Chrome 内置「AI 外挂」?NEXTSDK 让浏览器自己调 API、抓数据、填表单!
前端