后端 API 优化实战:字段补充与结果排序

在实际开发中,接口返回数据不一致、缺少关键字段、返回结果无序等问题经常困扰前端同学。本文记录了两个典型的后端 API 优化场景:为接口补充友好字段、为统计数据添加排序,分享问题定位思路和最小侵入式修改方案。

前言

在一次需求开发中,前端反馈了两个问题:一是水情汇总接口返回的测站类型是代码(如 ZQ),需要额外查询转换;二是管理单位统计接口返回的数据顺序是随机的,展示效果不佳。这两个问题都属于”不影响功能但影响体验”的类型,本文记录解决过程。

场景一:为接口新增友好字段

问题背景

/api/water/summary/list 接口返回的 STTP(测站类型代码)是英文缩写,比如 ZQ 代表水文站、ZZ 代表水位站。前端展示时需要额外维护一套映射关系,将代码转换为中文名称。

而同一个项目中,/basic/b/list 接口已经返回了 stationTypeName 字段,直接提供中文名称。两处实现不一致,增加了前端的维护成本。

解决方案

参照 /basic/b/list 的实现,为 summary/list 接口新增 stationTypeLabel 字段,复用现有的 MappingService.getStationTypeMap() 获取测站类型映射。

实现步骤

1. DTO 新增字段

在响应 DTO 中添加新字段:

1
2
3
4
// WaterSummaryListResponseDTO.java

@Schema(description = "测站类型名称(中文),根据STTP代码转换的中文名称", example = "水文站")
private String stationTypeLabel;

2. Controller 注入 MappingService

复用现有的映射服务,避免重复造轮子:

1
2
3
4
// WaterSummaryController.java

@Autowired
private MappingService mappingService;

3. 数据转换时填充字段

在 DTO 转换逻辑中填充新字段,注意批量获取映射避免 N+1 问题:

1
2
3
4
5
6
7
8
9
10
// 批量转换时获取测站类型映射
Map<String, String> stationTypeMap = mappingService.getStationTypeMap();

// 单个转换时填充 stationTypeLabel
String sttp = summary.getSTTP();
if (StringUtils.isNotEmpty(sttp) && stationTypeMap != null) {
dto.setStationTypeLabel(stationTypeMap.getOrDefault(sttp, ""));
} else {
dto.setStationTypeLabel("");
}

返回示例

优化后的接口返回:

1
2
3
4
5
6
7
8
{
"STCD": "62906600",
"STNM": "加田水文站",
"STTP": "ZQ",
"stationTypeLabel": "水文站",
"LGTD": 113.123456,
"LTTD": 23.456789
}

场景二:统计接口结果排序

问题背景

/basic/b/statistics?staticType=unit 接口返回各机构的测站统计数据,但 data 数组中的顺序是随机的。前端展示时希望按测站总数(total)从大到小排序,让数据量大的机构排在前面。

解决方案

在业务层返回数据前添加排序逻辑,保持对原有代码的最小侵入。

实现步骤

StStbprpUnitStatisticsBiz 的两个统计方法中,于 return dataList 前添加一行排序代码:

1
2
3
4
5
6
// StStbprpUnitStatisticsBiz.java

// 按 total 倒序排序
dataList.sort((a, b) -> Integer.compare((Integer) b.get("total"), (Integer) a.get("total")));

return dataList;

需要修改的方法:

  • statisticsForSecondLevel()
  • statisticsForThirdLevel()

影响分析与注意事项

字段新增场景

检查项 状态 说明
DTO 使用范围 ✅ 安全 DTO 仅在 Controller 内部使用
方法签名变更 ✅ 安全 转换方法是 private,无外部调用
测试文件 ✅ 无需修改 现有测试是集成测试
API 兼容性 ✅ 向后兼容 新增字段不影响现有前端

注意事项:如果使用了缓存(如 cache=1),旧缓存数据不包含新字段,反序列化后为 null。可以等待缓存过期(30 天),或手动清除 Redis 缓存:KEYS water:summary:*

结果排序场景

检查项 状态 说明
业务逻辑 ✅ 无侵入 只在返回前添加排序
分页功能 ✅ 无影响 该接口无分页
API 兼容性 ✅ 向后兼容 数据结构不变,只改变顺序

经验总结

本次优化涉及两个小改动,但体现了几个重要的开发原则:

  • 复用现有服务MappingService 已经提供了类型映射,直接复用避免重复维护
  • 保持一致性:不同接口的返回结构应保持一致,减少前端适配成本
  • 最小侵入修改:排序逻辑只在返回前添加,不侵入核心业务流程
  • 批量获取映射:避免在循环中单独查询,防止 N+1 问题
  • 向后兼容:新增字段不破坏现有功能,排序不改数据结构

这些”小优化”看似不起眼,但积累起来能显著提升 API 的易用性和前端开发体验。

结语

后端开发不仅是实现功能,还要关注 API 的友好性和一致性。希望这两个小案例能给你带来一些启发,在实际项目中遇到类似问题时,可以快速定位并优雅解决。