feat: 实现登录日志和操作日志的分页查询功能
refactor: 重构日志服务层代码,将分页逻辑移至Repository层 test: 添加日志分页查询的单元测试和组件测试 docs: 更新README文档,记录API响应格式修复过程 chore: 清理无用文件,更新.gitignore配置 build: 添加Jacoco代码覆盖率插件配置 ci: 添加测试环境配置文件application-h2-test.yml style: 统一日志服务代码格式,添加必要的日志输出
This commit is contained in:
@@ -1245,3 +1245,121 @@ MIT
|
||||
|
||||
**最后更新**: 2026-04-02
|
||||
**维护人员**: 张翔
|
||||
|
||||
## API响应格式修复记录 (2026-04-02)
|
||||
|
||||
### 问题描述
|
||||
|
||||
测试套件运行失败,多个API测试返回响应格式不符合预期:
|
||||
|
||||
```
|
||||
AssertionError: assert "content" in data
|
||||
Expected: {"content": [...], "totalElements": 5, "totalPages": 1, ...}
|
||||
Actual: [...]
|
||||
```
|
||||
|
||||
### 根因分析
|
||||
|
||||
**问题根源**: API路径与后端路由不匹配
|
||||
|
||||
| 测试调用 | 后端路由 | Handler方法 | 返回格式 |
|
||||
|---------|---------|------------|---------|
|
||||
| `/api/logs/login?page=0&size=10` | `/api/logs/login` | `getAllLoginLogs()` | 列表 `[]` |
|
||||
| 应该调用 `/api/logs/login/page?page=0&size=10` | `/api/logs/login/page` | `getLoginLogsByPage()` | PageResponse `{}` |
|
||||
|
||||
**影响范围**:
|
||||
- 用户API: `/api/users?page=0&size=10`
|
||||
- 角色API: `/api/roles?page=0&size=10`
|
||||
- 登录日志API: `/api/logs/login?page=0&size=10`
|
||||
- 异常日志API: `/api/logs/exception?page=0&size=10`
|
||||
|
||||
### 修复方案
|
||||
|
||||
**方案选择**: 修改后端Handler,让 `getAllXxx()` 方法支持分页参数
|
||||
|
||||
**理由**:
|
||||
1. 符合RESTful API最佳实践: `GET /resources` 应支持查询参数
|
||||
2. 向后兼容: 无分页参数时返回列表,有分页参数时返回分页对象
|
||||
3. 减少测试代码修改
|
||||
|
||||
### 修复内容
|
||||
|
||||
#### 1. SysLogHandler.java
|
||||
|
||||
修改 `getAllLoginLogs()` 和 `getAllExceptionLogs()` 方法:
|
||||
|
||||
```java
|
||||
@Operation(summary = "获取所有登录日志", description = "获取系统中所有登录日志列表,支持分页参数")
|
||||
public Mono<ServerResponse> getAllLoginLogs(ServerRequest request) {
|
||||
boolean hasPageParams = request.queryParam("page").isPresent() || request.queryParam("size").isPresent();
|
||||
|
||||
if (hasPageParams) {
|
||||
// 返回分页对象
|
||||
int page = Integer.parseInt(request.queryParam("page").orElse("0"));
|
||||
int size = Integer.parseInt(request.queryParam("size").orElse("10"));
|
||||
// ... 构建PageRequest并调用分页服务
|
||||
return loginLogService.findLoginLogsByPage(pageRequest)
|
||||
.flatMap(response -> ServerResponse.ok().bodyValue(response));
|
||||
} else {
|
||||
// 返回列表
|
||||
return ServerResponse.ok()
|
||||
.body(loginLogService.findAll(), SysLoginLog.class);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. SysUserHandler.java
|
||||
|
||||
修改 `getAllUsers()` 方法,支持分页参数。
|
||||
|
||||
#### 3. SysRoleHandler.java
|
||||
|
||||
修改 `getAllRoles()` 方法,支持分页参数。
|
||||
|
||||
### 修复效果
|
||||
|
||||
**修复前**:
|
||||
- `/api/logs/login` → 返回列表 `[]`
|
||||
- `/api/logs/login?page=0&size=10` → 返回列表 `[]` ❌
|
||||
|
||||
**修复后**:
|
||||
- `/api/logs/login` → 返回列表 `[]` ✅
|
||||
- `/api/logs/login?page=0&size=10` → 返回分页对象 `{}` ✅
|
||||
|
||||
### API设计原则
|
||||
|
||||
遵循RESTful API最佳实践:
|
||||
|
||||
1. **资源路径**: `/api/resources`
|
||||
2. **查询参数**: 用于过滤、排序、分页
|
||||
- `?page=0&size=10` - 分页参数
|
||||
- `?keyword=admin` - 关键词搜索
|
||||
- `?sort=id&order=desc` - 排序参数
|
||||
3. **响应格式**:
|
||||
- 无分页参数: 返回资源列表
|
||||
- 有分页参数: 返回分页对象
|
||||
|
||||
```json
|
||||
{
|
||||
"content": [...],
|
||||
"totalElements": 100,
|
||||
"totalPages": 10,
|
||||
"currentPage": 0,
|
||||
"pageSize": 10,
|
||||
"first": true,
|
||||
"last": false
|
||||
}
|
||||
```
|
||||
|
||||
### 验证状态
|
||||
|
||||
- ✅ 代码编译通过
|
||||
- ⏳ 集成测试验证 (需要数据库环境)
|
||||
- ⏳ E2E测试验证 (需要完整环境)
|
||||
|
||||
### 相关文件
|
||||
|
||||
- [SysLogHandler.java](novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/log/SysLogHandler.java)
|
||||
- [SysUserHandler.java](novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/user/SysUserHandler.java)
|
||||
- [SysRoleHandler.java](novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/role/SysRoleHandler.java)
|
||||
- [PageResponse.java](novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dto/PageResponse.java)
|
||||
|
||||
Reference in New Issue
Block a user