报表查询功能在企业级应用中是个高频刚需场景。做过报表开发的同行都深有体会:业务部门今天要按A条件筛选,明天要加B字段过滤,后天又要求支持C和D的组合查询。传统方案要么写死查询条件,要么用大量if-else堆砌,维护起来简直是场灾难。
这个项目通过SpringBoot整合动态SQL与条件编排器,实现了查询条件的自由组合。举个真实案例:某零售企业的销售报表原本有20多个固定筛选条件,每次新增条件都需要改代码上线。改造后,业务人员通过前端勾选就能自由组合查询条件,开发效率提升70%以上。
采用"配置化+解释器"模式:
java复制// 条件描述示例
{
"conditions": [
{
"field": "order_date",
"operator": "BETWEEN",
"value": ["2023-01-01", "2023-12-31"]
},
{
"logic": "OR",
"conditions": [
{"field": "status", "operator": "IN", "value": [1,3,5]},
{"field": "amount", "operator": ">=", "value": 5000}
]
}
]
}
| 方案 | 优点 | 缺点 |
|---|---|---|
| XML |
原生支持,简单易用 | 复杂逻辑可读性差 |
| @Provider注解 | 纯Java代码灵活 | 需要拼接字符串 |
| 条件构造器(Wrapper) | 链式调用直观 | 嵌套逻辑处理困难 |
| 本文方案 | 支持任意条件组合 | 需要额外开发解析器 |
xml复制<!-- 动态WHERE语句生成 -->
<select id="selectByConditions" resultMap="reportResult">
SELECT * FROM sales_report
<where>
<foreach collection="conditionNodes" item="node">
<choose>
<when test="node.type == 'SIMPLE'">
AND ${node.field}
<choose>
<when test="node.operator == 'IN'">
IN <foreach collection="node.value" item="item" open="(" close=")" separator=",">#{item}</foreach>
</when>
<when test="node.operator == 'BETWEEN'">
BETWEEN #{node.value[0]} AND #{node.value[1]}
</when>
<!-- 其他操作符处理 -->
</choose>
</when>
<when test="node.type == 'LOGIC'">
<if test="node.logic == 'AND'">AND</if>
<if test="node.logic == 'OR'">OR</if>
(
<include refid="nestedConditions" />
)
</when>
</choose>
</foreach>
</where>
</select>
java复制public class ConditionParser {
// 解析JSON到语法树
public ConditionNode parse(String json) {
// 使用Jackson反序列化
// 构建AST树结构
}
}
public interface ConditionNode {
enum NodeType { SIMPLE, LOGIC }
NodeType getType();
}
public class SimpleCondition implements ConditionNode {
private String field;
private Operator operator;
private Object value;
// getters/setters
}
public class LogicCondition implements ConditionNode {
private LogicType logic; // AND/OR
private List<ConditionNode> conditions;
// getters/setters
}
java复制@PostMapping("/reports")
public PageResult<Report> queryReports(
@RequestBody QueryCondition condition,
@RequestParam Pageable pageable) {
ConditionNode root = conditionParser.parse(condition);
return reportService.queryByConditions(root, pageable);
}
推荐使用JSON Schema生成动态表单,例如:
javascript复制// 使用vue-json-schema-form
<template>
<JsonSchemaForm
:schema="conditionSchema"
v-model="formData"
@submit="handleQuery"
/>
</template>
SQL注入防护:
${field}java复制private static final Set<String> ALLOWED_FIELDS = Set.of(
"order_date", "status", "amount" /* 其他合法字段 */);
public boolean isValidField(String field) {
return ALLOWED_FIELDS.contains(field);
}
参数校验:
建议监控:
条件保存与共享:
智能条件推荐:
可视化条件构建器:
javascript复制// 类似Segment.io的条件构建UI
{
"rules": [
{
"field": "device",
"operator": "is",
"value": "mobile"
},
{
"operator": "and",
"rules": [
{"field": "os", "operator": "equals", "value": "iOS"},
{"field": "version", "operator": "greater", "value": 12}
]
}
]
}
对于超大规模数据场景:
坑1:空条件处理不当
初期版本没有处理conditions为空的case,导致生成WHERE后面直接接AND的非法SQL。修正方案:
xml复制<where>
<if test="!conditionNodes.isEmpty()">
<!-- 原有逻辑 -->
</if>
</where>
坑2:日期格式兼容问题
不同浏览器传参格式不一致,最终统一采用:
java复制@JsonFormat(pattern = "yyyy-MM-dd")
private Date startDate;
坑3:大IN列表性能
当IN列表超过1000项时性能急剧下降,解决方案:
改造前后关键指标对比:
| 指标 | 传统方案 | 本方案 |
|---|---|---|
| 新增条件开发耗时 | 2人日/条件 | 0.5人日/条件 |
| 条件组合可能性 | 固定几种 | 任意组合 |
| 平均查询响应时间 | 120ms | 150ms(+25%) |
| 业务满意度评分 | 3.2/5 | 4.7/5 |
虽然查询性能有轻微下降,但业务灵活度提升带来的收益远超这点代价。特别是在618大促期间,运营团队自主创建了37种临时查询组合,没有占用任何研发资源。