后端接口升级实战:如何优雅地为API添加一个新查询参数?

🚀 后端接口升级实战:如何优雅地为API添加一个新查询参数?

嘿,各位开发者伙伴们!👋

你是否也遇到过这样的场景:产品经理或前端小伙伴跑过来说:“这个列表页功能很棒,但是…我们能增加一个按‘品名’搜索的功能吗?” 🙋‍♂️

这听起来是个小需求,但如何安全、优雅、且不影响现有功能地完成它,就是考验我们后端工程师内功的时候了。

今天,我们就以一个真实的案例——为一个订阅查询接口增加“品名”模糊搜索功能——来走一遍完整的后端改造流程。

我们的技术栈: Spring Boot + Spring Data JPA (Java Persistence API, Java持久化API) (原生SQL) + Vue.js

我们的目标: 让前端的“品名”输入框从“摆设”变成真正的“搜索神器”!✨

需求分析 & 代码勘探 🧭

在动手之前,我们先来看看“案发现场”。

前端 UI (User Interface, 用户界面):
前端页面上已经有一个“品名”输入框,它绑定了 listQuery.name

<el-input v-model="listQuery.name" size="mini" placeholder="品名" />

当用户点击搜索时,listQuery.name 会被作为请求参数发送到后端。但问题是,我们之前的后端接口并“不认识”这个 name 参数,所以它被无情地忽略了。😅

后端接口 (修改前):
我们的 SubscribeController 里的 findSuper 方法长这样:

// 修改前
public BaseResult findSuper(
    @RequestParam(value = "brand", required = false) String brand,
    @RequestParam(value = "jancode", required = false) String jancode,
    // ... 其他参数
    Integer adminId) {
    // 注意看,参数列表里没有 name!
    return BaseResult.success(subscribeService.findSubscribeList(brand, jancode, ..., adminId));
}

好了,侦查完毕!现在,让我们开始对后端进行“微创手术”。

后端改造三步走 🛠️

我们的改造计划遵循一个黄金法则:从外到内,层层递进。也就是从 Controller -> Service -> SQL (Structured Query Language, 结构化查询语言) 构建。

第一站:Controller 层 - 接待新参数 🚪

Controller 是我们 API (Application Programming Interface, 应用程序编程接口) 的入口,就像大厦的门卫。我们首先要告诉“门卫”,有一个叫 name 的新访客是合法的,请放行。

操作:findfindSuper 方法的参数列表中,添加对 name 的接收。

// SubscribeController.java

@GetMapping("super")
@ApiOperation("超管查询某位管理员所有订阅")
@PreAuthorize("hasRole('ROLE_SUPER')")
public BaseResult findSuper(
    @RequestParam(value = "brandId", required = false) @ApiParam("品牌id") Integer brandId,
    @RequestParam(value = "brand", required = false) @ApiParam("品牌名") String brand,
    // 👇 LOOK HERE! 新增的 name 参数
    @RequestParam(value = "name", required = false) @ApiParam("品名") String name,
    @RequestParam(value = "jancode", required = false) @ApiParam("条码") String jancode,
    // ... 其他参数
    Integer adminId) {
    
    // 将 name 参数一路传递下去
    return BaseResult.success(subscribeService.findSubscribeList(brandId, brand, name, jancode, code, status, page, adminId));
}

要点:

  • @RequestParam(value = "name", required = false): 我们将 name 设置为非必需 (required = false)。这是向后兼容的关键!旧的前端调用或不使用此筛选的场景完全不受影响。
  • @ApiParam("品名"): 别忘了给 Swagger 文档也加上清晰的说明。
第二站:Service 层 - 传递指挥棒 🏃‍♂️

Controller 接到了 name 参数,现在需要把它交给真正干活的 Service 层。这个过程就像接力赛,指挥棒必须稳稳地传递下去。

操作: 修改 SubscribeService 中所有相关方法的签名,加入 name 参数。

// SubscribeService.java

// 1. 主方法签名修改
public Page<SubscribeDTO> findSubscribeList(Integer brandId, String brand, String name, String jancode, /*...*/) {
    // ...
    // 2. 将 name 传递给 SQL 构建方法
    subscribeListSql(brand, name, jancode, /*...*/);
    // ...
    // 3. 将 name 传递给 count 方法
    return new PageImpl<>(..., countSubscribeList(brandId, brand, name, jancode, /*...*/));
}

// 4. count 方法签名修改
private long countSubscribeList(Integer brandId, String brand, String name, String jancode, /*...*/) {
    // ...
}

// 5. SQL 构建方法签名修改
private void subscribeListSql(String brand, String name, String jancode, /*...*/) {
    // ...
}

这一步虽然看起来只是“透传”,但它保证了数据流的完整性,让我们的核心逻辑能拿到所需的全部信息。

第三站:SQL 构建 - 真正的魔法发生地! ✨

万事俱备,只欠东风!现在我们来到了最核心的一步:动态构建我们的原生 SQL 查询。

我们的 subscribeListSql 方法负责拼接 SQL 语句。我们要做的就是,当 name 参数存在时,给 SQL 加上 AND p.name LIKE ... 的条件。

操作:subscribeListSql 方法中添加条件判断。

// SubscribeService.java

private void subscribeListSql(String brand, String name, String jancode, String code, Integer status, StringBuilder sql, Map<String, Object> params) {
    // ... (前面的 SQL 拼接不变)
    sql.append("    where 1=1 ");
    
    if (!StringUtils.isEmpty(brand)) {
        sql.append("    and b.name like :brand ");
        params.put("brand", "%" + brand + "%");
    }

    // ==================== ✨ 魔法在这里 ✨ ====================
    if (!StringUtils.isEmpty(name)) {
        // p 是 product 表的别名, name 是 product 表的字段
        sql.append("    and p.name like :name ");
        params.put("name", "%" + name + "%");
    }
    // =======================================================

    if (!StringUtils.isEmpty(jancode)) {
        sql.append("    and p.jancode like :jancode ");
        params.put("jancode", "%" + jancode + "%");
    }
    
    // ... (后面的 SQL 拼接不变)
}

要点分析:

  • 动态拼接if 语句保证了只有在 name 不为空时,才会添加这个查询条件。
  • 安全第一 🛡️:我们使用了命名参数 :nameparams.put(),而不是直接把 name 字符串拼到 SQL 里。这是防止 SQL 注入的教科书式操作!
  • 模糊查询"%" + name + "%" 意味着只要商品名称中包含用户输入的关键字,就能被匹配到。

成果验收 🎉

好了,大功告成!重新部署后端服务后,前端的“品名”搜索框现在已经火力全开!🚀

(示意图)

总结与图表分析 📊

📝 修改步骤总结表
层次文件核心操作目的关键点
🌐 表现层SubscribeController.java增加 @RequestParam接收前端传入的 name 参数required = false 保证向后兼容
💼 业务层SubscribeService.java修改方法签名,透传 name 参数将参数从 Controller 传递到数据访问逻辑保证数据流的完整性
💾 数据层SubscribeService.javasubscribeListSql 中动态拼接 SQLname 参数应用到数据库查询使用命名参数防止 SQL 注入
🗺️ 流程图 (Flowchart)

这是整个请求处理流程的直观展示:

前端发起GET请求
(携带name参数)
Controller层
findSuper方法接收请求
参数 name 是否为空?
Service层
findSubscribeList方法
Service层
subscribeListSql方法
动态构建SQL
name 参数存在?
SQL中拼接
AND p.name LIKE :name
跳过name条件
执行SQL查询
返回查询结果
🔄 时序图 (Sequence Diagram)

这个图展示了各个组件之间交互的时间顺序:

前端用户SubscribeControllerSubscribeService数据库GET /subscribe/super?name=商品AfindSubscribeList(..., name="商品A", ...)subscribeListSql(..., name="商品A", ...)动态构建SQL语句包含 p.name LIKE '%商品A%'执行原生SQL查询返回订阅列表数据返回 Page<SubscribeDTO>返回JSON格式的查询结果前端用户SubscribeControllerSubscribeService数据库
🚦 状态图 (State Diagram)

展示一个订阅对象可能存在的状态:

执行绑定商品操作
续费
订阅到期
重新续费
未绑定
已绑定
已过期
🏛️ 类图 (Class Diagram)

展示了本次修改涉及的核心类及其关系:

"依赖"
"创建"
"关联"
1
0..1
SubscribeController
+findSuper(name: String, ...) : BaseResult
SubscribeService
+findSubscribeList(name: String, ...) : Page
-countSubscribeList(name: String, ...) : long
-subscribeListSql(name: String, ...) : void
SubscribeDTO
-String name
-String brand
Product
-String name
Subscribe
-Integer productId
🔗 实体关系图 (Entity Relationship Diagram)

展示了数据库中相关实体之间的关系:

SUBSCRIBEintidPK订阅IDintadmin_id管理员IDintproduct_idFK商品IDSUBSCRIBE_DATEintidPK订阅日期IDintsubscribe_idFK订阅IDdatestart_date开始日期dateend_date结束日期PRODUCTintidPK商品IDintbrand_idFK品牌IDvarcharname商品名称 (品名)varcharjancode条码BRANDintidPK品牌IDvarcharname品牌名称包含可能绑定属于
🧠 思维导图 (Markdown Format)

最后,用一个思维导图来梳理整个过程:
在这里插入图片描述

希望这篇包含详细图表的分享对你有所帮助。Happy Coding! 💻💖

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值