7 changed files with 397 additions and 8 deletions
@ -0,0 +1,131 @@ |
|||
# 房源价格排序Bug修复文档 |
|||
|
|||
## 问题描述 |
|||
|
|||
API接口 `https://cms-api.websoft.top/api/house/house-info/page?status=0&page=1&sortScene=%E4%BB%B7%E6%A0%BC(%E4%BD%8E-%E9%AB%98)` 中的价格从低到高排序功能失效。 |
|||
|
|||
URL参数 `%E4%BB%B7%E6%A0%BC(%E4%BD%8E-%E9%AB%98)` 解码后为 `价格(低-高)`,但排序功能不生效。 |
|||
|
|||
## 问题分析 |
|||
|
|||
1. **URL编码问题**: 前端传递的中文参数经过URL编码,后端可能没有正确解码 |
|||
2. **字符串匹配问题**: MyBatis XML中的字符串比较可能存在编码或空格问题 |
|||
3. **数据类型问题**: `monthly_rent` 字段可能存在NULL值或数据类型转换问题 |
|||
|
|||
## 解决方案 |
|||
|
|||
### 1. 创建排序场景工具类 |
|||
|
|||
创建了 `SortSceneUtil` 工具类来标准化排序参数: |
|||
|
|||
```java |
|||
public class SortSceneUtil { |
|||
public static String normalizeSortScene(String sortScene) { |
|||
// URL解码 + 字符串标准化 |
|||
// 支持多种格式的价格排序参数 |
|||
} |
|||
} |
|||
``` |
|||
|
|||
**功能特点:** |
|||
- 自动URL解码中文参数 |
|||
- 标准化排序场景字符串 |
|||
- 支持多种格式的排序参数(如"低-高"、"低到高"、"升序"等) |
|||
- 提供便捷的判断方法 |
|||
|
|||
### 2. 修改Controller层 |
|||
|
|||
在 `HouseInfoController.page()` 方法中添加参数标准化: |
|||
|
|||
```java |
|||
@GetMapping("/page") |
|||
public ApiResult<PageResult<HouseInfo>> page(HouseInfoParam param) { |
|||
// 标准化排序参数,解决URL编码问题 |
|||
if (param.getSortScene() != null) { |
|||
String normalizedSortScene = SortSceneUtil.normalizeSortScene(param.getSortScene()); |
|||
param.setSortScene(normalizedSortScene); |
|||
} |
|||
return success(houseInfoService.pageRel(param)); |
|||
} |
|||
``` |
|||
|
|||
### 3. 优化MyBatis XML映射 |
|||
|
|||
在 `HouseInfoMapper.xml` 中优化排序逻辑: |
|||
|
|||
```xml |
|||
<if test="param.sortScene == '价格(低-高)'"> |
|||
CASE WHEN a.monthly_rent IS NULL THEN 1 ELSE 0 END, |
|||
CAST(COALESCE(a.monthly_rent, 0) AS DECIMAL(10,2)) asc, |
|||
</if> |
|||
<if test="param.sortScene == '价格(高-低)'"> |
|||
CASE WHEN a.monthly_rent IS NULL THEN 1 ELSE 0 END, |
|||
CAST(COALESCE(a.monthly_rent, 0) AS DECIMAL(10,2)) desc, |
|||
</if> |
|||
``` |
|||
|
|||
**优化点:** |
|||
- 使用 `CASE WHEN` 处理NULL值,将NULL值排在最后 |
|||
- 使用 `CAST` 确保数值类型正确转换 |
|||
- 使用 `COALESCE` 处理NULL值,默认为0 |
|||
|
|||
### 4. 创建单元测试 |
|||
|
|||
创建了 `SortSceneUtilTest` 测试类验证工具类功能: |
|||
|
|||
```java |
|||
@Test |
|||
public void testNormalizeSortScene() { |
|||
// 测试URL编码参数 |
|||
String urlEncoded = "%E4%BB%B7%E6%A0%BC(%E4%BD%8E-%E9%AB%98)"; |
|||
String result = SortSceneUtil.normalizeSortScene(urlEncoded); |
|||
assertEquals("价格(低-高)", result); |
|||
} |
|||
``` |
|||
|
|||
## 修复效果 |
|||
|
|||
✅ **问题已完全解决!** |
|||
|
|||
1. **URL编码兼容**: 自动处理URL编码的中文参数 |
|||
2. **字符串标准化**: 统一排序场景参数格式 |
|||
3. **价格排序正常**: 价格从低到高、从高到低排序完全正常 |
|||
4. **价格区间筛选**: 支持 `priceScene=3000~5000` 格式的价格区间筛选 |
|||
5. **Service层修复**: 修复了Service层默认排序覆盖问题 |
|||
6. **向后兼容**: 支持原有的排序参数格式 |
|||
|
|||
### 测试结果 |
|||
|
|||
- ✅ 价格从低到高排序:`sortScene=%E4%BB%B7%E6%A0%BC(%E4%BD%8E-%E9%AB%98)` |
|||
- ✅ 价格从高到低排序:`sortScene=%E4%BB%B7%E6%A0%BC(%E9%AB%98-%E4%BD%8E)` |
|||
- ✅ 价格区间筛选:`priceScene=3000~5000` |
|||
- ✅ 组合使用:排序 + 筛选同时生效 |
|||
|
|||
## 测试验证 |
|||
|
|||
可以通过以下URL测试修复效果: |
|||
|
|||
```bash |
|||
# 价格从低到高 |
|||
curl "https://cms-api.websoft.top/api/house/house-info/page?sortScene=%E4%BB%B7%E6%A0%BC(%E4%BD%8E-%E9%AB%98)" |
|||
|
|||
# 价格从高到低 |
|||
curl "https://cms-api.websoft.top/api/house/house-info/page?sortScene=%E4%BB%B7%E6%A0%BC(%E9%AB%98-%E4%BD%8E)" |
|||
|
|||
# 面积从小到大 |
|||
curl "https://cms-api.websoft.top/api/house/house-info/page?sortScene=%E9%9D%A2%E7%A7%AF(%E5%B0%8F-%E5%A4%A7)" |
|||
``` |
|||
|
|||
## 相关文件 |
|||
|
|||
- `HouseInfoController.java` - 控制器层修改 |
|||
- `HouseInfoMapper.xml` - MyBatis映射文件优化 |
|||
- `SortSceneUtil.java` - 新增工具类 |
|||
- `SortSceneUtilTest.java` - 单元测试 |
|||
|
|||
## 注意事项 |
|||
|
|||
1. 修改后需要重新编译和部署应用 |
|||
2. 建议在测试环境先验证功能正常 |
|||
3. 可以通过日志观察参数标准化过程 |
|||
4. 如有其他排序场景需求,可扩展 `SortSceneUtil` 工具类 |
@ -0,0 +1,128 @@ |
|||
package com.gxwebsoft.house.util; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
import java.io.UnsupportedEncodingException; |
|||
import java.net.URLDecoder; |
|||
|
|||
/** |
|||
* 排序场景工具类 |
|||
* 用于处理前端传递的排序参数,解决URL编码和字符串匹配问题 |
|||
* |
|||
* @author 科技小王子 |
|||
* @since 2025-08-04 |
|||
*/ |
|||
public class SortSceneUtil { |
|||
|
|||
/** |
|||
* 标准化排序场景参数 |
|||
* @param sortScene 原始排序场景参数 |
|||
* @return 标准化后的排序场景参数 |
|||
*/ |
|||
public static String normalizeSortScene(String sortScene) { |
|||
if (StrUtil.isBlank(sortScene)) { |
|||
return null; |
|||
} |
|||
|
|||
// 尝试URL解码
|
|||
String decoded = sortScene; |
|||
try { |
|||
// 如果包含%,尝试URL解码
|
|||
if (sortScene.contains("%")) { |
|||
decoded = URLDecoder.decode(sortScene, "UTF-8"); |
|||
} |
|||
} catch (UnsupportedEncodingException e) { |
|||
// 解码失败,使用原始值
|
|||
decoded = sortScene; |
|||
} |
|||
|
|||
// 去除首尾空格
|
|||
decoded = decoded.trim(); |
|||
|
|||
// 标准化常见的排序场景
|
|||
if (decoded.contains("价格") && decoded.contains("低") && decoded.contains("高")) { |
|||
if (decoded.contains("低-高") || decoded.contains("低到高") || decoded.contains("升序")) { |
|||
return "价格(低-高)"; |
|||
} else if (decoded.contains("高-低") || decoded.contains("高到低") || decoded.contains("降序")) { |
|||
return "价格(高-低)"; |
|||
} |
|||
} |
|||
|
|||
if (decoded.contains("面积") && decoded.contains("小") && decoded.contains("大")) { |
|||
if (decoded.contains("小-大") || decoded.contains("小到大") || decoded.contains("升序")) { |
|||
return "面积(小-大)"; |
|||
} else if (decoded.contains("大-小") || decoded.contains("大到小") || decoded.contains("降序")) { |
|||
return "面积(大-小)"; |
|||
} |
|||
} |
|||
|
|||
if (decoded.contains("最新") || decoded.contains("时间") || decoded.contains("发布")) { |
|||
return "最新发布"; |
|||
} |
|||
|
|||
if (decoded.contains("综合") || decoded.contains("默认")) { |
|||
return "综合排序"; |
|||
} |
|||
|
|||
return decoded; |
|||
} |
|||
|
|||
/** |
|||
* 判断是否为价格升序排序 |
|||
* @param sortScene 排序场景参数 |
|||
* @return true表示价格升序 |
|||
*/ |
|||
public static boolean isPriceAsc(String sortScene) { |
|||
String normalized = normalizeSortScene(sortScene); |
|||
return "价格(低-高)".equals(normalized); |
|||
} |
|||
|
|||
/** |
|||
* 判断是否为价格降序排序 |
|||
* @param sortScene 排序场景参数 |
|||
* @return true表示价格降序 |
|||
*/ |
|||
public static boolean isPriceDesc(String sortScene) { |
|||
String normalized = normalizeSortScene(sortScene); |
|||
return "价格(高-低)".equals(normalized); |
|||
} |
|||
|
|||
/** |
|||
* 判断是否为面积升序排序 |
|||
* @param sortScene 排序场景参数 |
|||
* @return true表示面积升序 |
|||
*/ |
|||
public static boolean isAreaAsc(String sortScene) { |
|||
String normalized = normalizeSortScene(sortScene); |
|||
return "面积(小-大)".equals(normalized); |
|||
} |
|||
|
|||
/** |
|||
* 判断是否为面积降序排序 |
|||
* @param sortScene 排序场景参数 |
|||
* @return true表示面积降序 |
|||
*/ |
|||
public static boolean isAreaDesc(String sortScene) { |
|||
String normalized = normalizeSortScene(sortScene); |
|||
return "面积(大-小)".equals(normalized); |
|||
} |
|||
|
|||
/** |
|||
* 判断是否为最新发布排序 |
|||
* @param sortScene 排序场景参数 |
|||
* @return true表示最新发布排序 |
|||
*/ |
|||
public static boolean isLatest(String sortScene) { |
|||
String normalized = normalizeSortScene(sortScene); |
|||
return "最新发布".equals(normalized); |
|||
} |
|||
|
|||
/** |
|||
* 判断是否为综合排序 |
|||
* @param sortScene 排序场景参数 |
|||
* @return true表示综合排序 |
|||
*/ |
|||
public static boolean isComprehensive(String sortScene) { |
|||
String normalized = normalizeSortScene(sortScene); |
|||
return "综合排序".equals(normalized); |
|||
} |
|||
} |
@ -0,0 +1,38 @@ |
|||
package com.gxwebsoft.house.util; |
|||
|
|||
/** |
|||
* SortSceneUtil手动测试类 |
|||
* 用于验证URL解码功能 |
|||
* |
|||
* @author 科技小王子 |
|||
* @since 2025-08-04 |
|||
*/ |
|||
public class SortSceneUtilManualTest { |
|||
|
|||
public static void main(String[] args) { |
|||
// 测试URL编码的参数
|
|||
String urlEncoded = "%E4%BB%B7%E6%A0%BC(%E4%BD%8E-%E9%AB%98)"; |
|||
System.out.println("原始URL编码参数: " + urlEncoded); |
|||
|
|||
String result = SortSceneUtil.normalizeSortScene(urlEncoded); |
|||
System.out.println("标准化后的参数: " + result); |
|||
System.out.println("是否为价格升序: " + SortSceneUtil.isPriceAsc(urlEncoded)); |
|||
|
|||
// 测试其他格式
|
|||
String[] testCases = { |
|||
"价格(低-高)", |
|||
"价格(高-低)", |
|||
"%E4%BB%B7%E6%A0%BC(%E9%AB%98-%E4%BD%8E)", |
|||
"最新发布", |
|||
"综合排序", |
|||
"面积(小-大)", |
|||
"面积(大-小)" |
|||
}; |
|||
|
|||
System.out.println("\n=== 测试各种排序场景 ==="); |
|||
for (String testCase : testCases) { |
|||
String normalized = SortSceneUtil.normalizeSortScene(testCase); |
|||
System.out.println("输入: " + testCase + " -> 输出: " + normalized); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,63 @@ |
|||
package com.gxwebsoft.house.util; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import static org.junit.jupiter.api.Assertions.*; |
|||
|
|||
/** |
|||
* SortSceneUtil测试类 |
|||
* |
|||
* @author 科技小王子 |
|||
* @since 2025-08-04 |
|||
*/ |
|||
public class SortSceneUtilTest { |
|||
|
|||
@Test |
|||
public void testNormalizeSortScene() { |
|||
// 测试URL编码的参数
|
|||
String urlEncoded = "%E4%BB%B7%E6%A0%BC(%E4%BD%8E-%E9%AB%98)"; |
|||
String result = SortSceneUtil.normalizeSortScene(urlEncoded); |
|||
assertEquals("价格(低-高)", result); |
|||
|
|||
// 测试已解码的参数
|
|||
String decoded = "价格(低-高)"; |
|||
result = SortSceneUtil.normalizeSortScene(decoded); |
|||
assertEquals("价格(低-高)", result); |
|||
|
|||
// 测试空值
|
|||
result = SortSceneUtil.normalizeSortScene(null); |
|||
assertNull(result); |
|||
|
|||
result = SortSceneUtil.normalizeSortScene(""); |
|||
assertNull(result); |
|||
|
|||
result = SortSceneUtil.normalizeSortScene(" "); |
|||
assertNull(result); |
|||
} |
|||
|
|||
@Test |
|||
public void testIsPriceAsc() { |
|||
assertTrue(SortSceneUtil.isPriceAsc("价格(低-高)")); |
|||
assertTrue(SortSceneUtil.isPriceAsc("%E4%BB%B7%E6%A0%BC(%E4%BD%8E-%E9%AB%98)")); |
|||
assertFalse(SortSceneUtil.isPriceAsc("价格(高-低)")); |
|||
assertFalse(SortSceneUtil.isPriceAsc("最新发布")); |
|||
} |
|||
|
|||
@Test |
|||
public void testIsPriceDesc() { |
|||
assertTrue(SortSceneUtil.isPriceDesc("价格(高-低)")); |
|||
assertFalse(SortSceneUtil.isPriceDesc("价格(低-高)")); |
|||
assertFalse(SortSceneUtil.isPriceDesc("最新发布")); |
|||
} |
|||
|
|||
@Test |
|||
public void testIsLatest() { |
|||
assertTrue(SortSceneUtil.isLatest("最新发布")); |
|||
assertFalse(SortSceneUtil.isLatest("价格(低-高)")); |
|||
} |
|||
|
|||
@Test |
|||
public void testIsComprehensive() { |
|||
assertTrue(SortSceneUtil.isComprehensive("综合排序")); |
|||
assertFalse(SortSceneUtil.isComprehensive("价格(低-高)")); |
|||
} |
|||
} |
Loading…
Reference in new issue