# Dashboard数据显示问题诊断报告 ## 问题描述 用户反馈Dashboard页面显示异常: - 登录次数一直显示为0 - 操作日志一直显示为0 ## 问题根因分析 ### 1. 登录次数显示为0 **前端代码分析**([Dashboard.vue](file:///Users/zhangxiang/Codes/Novalon/novalon-manage-system/novalon-manage-web/src/views/system/Dashboard.vue#L127)): ```javascript const todayLoginRes: any = await request.get('/logs/login/today/count') stats.todayLogin = todayLoginRes || 0 ``` **后端路由配置**([SystemRouter.java](file:///Users/zhangxiang/Codes/Novalon/novalon-manage-system/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/SystemRouter.java#L139)): ```java @Bean public RouterFunction logRoutes(SysLogHandler logHandler) { return route() .GET("/api/logs/login", logHandler::getAllLoginLogs) .GET("/api/logs/login/page", logHandler::getLoginLogsByPage) .GET("/api/logs/login/count", logHandler::getLoginLogCount) // 注意:缺少 /api/logs/login/today/count 端点 .build(); } ``` **服务接口分析**([ISysLoginLogService.java](file:///Users/zhangxiang/Codes/Novalon/novalon-manage-system/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/ISysLoginLogService.java)): ```java public interface ISysLoginLogService { Mono findById(Long id); Flux findAll(); Flux findByUsername(String username); Flux findByLoginTimeBetween(LocalDateTime startTime, LocalDateTime endTime); Mono save(SysLoginLog loginLog); Mono> findLoginLogsByPage(PageRequest pageRequest); Mono count(); // 注意:缺少 countToday() 方法 } ``` **问题根因**: 1. 前端请求 `/logs/login/today/count` 端点 2. 后端没有配置这个路由端点 3. `ISysLoginLogService` 接口缺少 `countToday()` 方法 4. 请求失败导致返回undefined,前端显示为0 ### 2. 操作日志显示为0 **前端代码分析**([Dashboard.vue](file:///Users/zhangxiang/Codes/Novalon/novalon-manage-system/novalon-manage-web/src/views/system/Dashboard.vue#L131)): ```javascript const operationLogRes: any = await request.get('/logs/operation/count') stats.operationLog = operationLogRes || 0 ``` **后端路由配置**([SystemRouter.java](file:///Users/zhangxiang/Codes/Novalon/novalon-manage-system/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/SystemRouter.java#L152)): ```java @Bean public RouterFunction operationLogRoutes(OperationLogHandler operationLogHandler) { return route() .GET("/api/logs/operation", operationLogHandler::getAllOperationLogs) .GET("/api/logs/operation/page", operationLogHandler::getOperationLogsByPage) .GET("/api/logs/operation/count", operationLogHandler::getOperationLogCount) // 端点存在 .build(); } ``` **可能原因**: 1. 数据库中确实没有操作日志记录 2. 操作日志拦截器可能没有正确记录日志 3. 统计查询可能有问题 ## 修复方案 ### 方案1:添加今日登录统计功能(推荐) #### 1.1 更新服务接口 **文件**:`ISysLoginLogService.java` ```java public interface ISysLoginLogService { Mono findById(Long id); Flux findAll(); Flux findByUsername(String username); Flux findByLoginTimeBetween(LocalDateTime startTime, LocalDateTime endTime); Mono save(SysLoginLog loginLog); Mono> findLoginLogsByPage(PageRequest pageRequest); Mono count(); Mono countToday(); // 新增方法 } ``` #### 1.2 实现今日登录统计 **文件**:`SysLoginLogService.java` ```java @Override public Mono countToday() { LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0); LocalDateTime todayEnd = todayStart.plusDays(1); return repository.findByLoginTimeBetween(todayStart, todayEnd) .count(); } ``` #### 1.3 添加Repository方法 **文件**:`ISysLoginLogRepository.java` ```java Flux findByLoginTimeBetween(LocalDateTime startTime, LocalDateTime endTime); ``` #### 1.4 更新Handler **文件**:`SysLogHandler.java` ```java @Operation(summary = "获取今日登录次数", description = "获取今日登录次数统计") public Mono getTodayLoginCount(ServerRequest request) { return loginLogService.countToday() .flatMap(count -> ServerResponse.ok().bodyValue(count)); } ``` #### 1.5 更新路由配置 **文件**:`SystemRouter.java` ```java @Bean public RouterFunction logRoutes(SysLogHandler logHandler) { return route() .GET("/api/logs/login", logHandler::getAllLoginLogs) .GET("/api/logs/login/page", logHandler::getLoginLogsByPage) .GET("/api/logs/login/count", logHandler::getLoginLogCount) .GET("/api/logs/login/today/count", logHandler::getTodayLoginCount) // 新增路由 .build(); } ``` ### 方案2:使用统一统计API(备选) #### 2.1 更新前端Dashboard **文件**:`Dashboard.vue` ```javascript const fetchStats = async () => { loading.value = true try { // 使用统一的统计API const statsRes: any = await request.get('/stats/overview') stats.userCount = statsRes.userCount || 0 stats.roleCount = statsRes.roleCount || 0 stats.todayLogin = statsRes.todayOperationCount || 0 // 注意字段映射 stats.operationLog = statsRes.operationLogCount || 0 } catch (error) { console.error('Failed to fetch stats:', error) } finally { loading.value = false } } ``` #### 2.2 更新StatsHandler **文件**:`StatsHandler.java` ```java @Operation(summary = "获取系统概览", description = "获取系统统计概览信息") public Mono getOverview(ServerRequest request) { return Mono.zip( userService.count(), roleService.count(), operationLogService.count(), loginLogService.countToday(), // 添加今日登录统计 operationLogService.countToday() ).flatMap(tuple -> { OverviewStats stats = new OverviewStats(); stats.setUserCount(tuple.getT1()); stats.setRoleCount(tuple.getT2()); stats.setOperationLogCount(tuple.getT3()); stats.setTodayLoginCount(tuple.getT4()); // 新增字段 stats.setTodayOperationCount(tuple.getT5()); return ServerResponse.ok().bodyValue(stats); }); } public static class OverviewStats { private Long userCount; private Long roleCount; private Long operationLogCount; private Long todayLoginCount; // 新增字段 private Long todayOperationCount; // getters and setters... } ``` ### 方案3:检查操作日志记录 #### 3.1 检查操作日志拦截器 **文件**:`OperationLogFilter.java` 确认拦截器是否正确配置和记录操作日志: ```java @Component @Order(Ordered.HIGHEST_PRECEDENCE) public class OperationLogFilter implements WebFilter { @Override public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String path = request.getPath().value(); // 排除不需要记录的路径 if (shouldSkipLogging(path)) { return chain.filter(exchange); } // 记录操作日志 return chain.filter(exchange).doFinally(signalType -> { OperationLog log = new OperationLog(); log.setUsername(getUsername(exchange)); log.setOperation(path); log.setMethod(request.getMethod().name()); log.setIp(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()); log.setStatus(exchange.getResponse().getStatusCode().value()); operationLogService.save(log).subscribe(); }); } } ``` #### 3.2 验证数据库 ```sql -- 检查操作日志表是否有数据 SELECT COUNT(*) FROM operation_log; -- 检查今日操作日志 SELECT COUNT(*) FROM operation_log WHERE created_at >= CURRENT_DATE; -- 检查最近10条操作日志 SELECT * FROM operation_log ORDER BY created_at DESC LIMIT 10; ``` ## 推荐实施步骤 1. **立即修复**:实施方案1,添加今日登录统计功能 2. **验证操作日志**:检查操作日志拦截器和数据库记录 3. **长期优化**:考虑实施方案2,使用统一统计API简化前端逻辑 ## 测试验证 修复后需要验证: 1. Dashboard页面正确显示今日登录次数 2. Dashboard页面正确显示操作日志数量 3. 执行一些操作后,操作日志数量增加 4. 登录后,今日登录次数增加 ## 相关文件清单 需要修改的文件: - `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/ISysLoginLogService.java` - `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/SysLoginLogService.java` - `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/repository/ISysLoginLogRepository.java` - `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/log/SysLogHandler.java` - `novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/SystemRouter.java` - `novalon-manage-web/src/views/system/Dashboard.vue`(可选,如果使用方案2) 需要检查的文件: - `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/interceptor/OperationLogFilter.java` - 数据库表 `operation_log` 的数据记录情况