Files
novalon-manage-system/DASHBOARD_ISSUE_DIAGNOSIS.md
T
张翔 31d66103e4 feat(登录日志): 添加今日登录次数统计功能
新增今日登录次数统计接口,修复Dashboard显示问题
- 在ISysLoginLogService接口添加countToday方法
- 实现SysLoginLogService中的countToday逻辑
- 更新ISysLoginLogRepository接口
- 添加SysLogHandler中的getTodayLoginCount方法
- 在SystemRouter中配置新路由端点

fix(测试): 更新系统配置URL匹配规则
- 将uat-phase1.spec.ts中的sysconfig改为sys/config

docs: 添加E2E测试报告和Dashboard问题诊断文档
2026-03-24 17:12:10 +08:00

9.6 KiB
Raw Blame History

Dashboard数据显示问题诊断报告

问题描述

用户反馈Dashboard页面显示异常:

  • 登录次数一直显示为0
  • 操作日志一直显示为0

问题根因分析

1. 登录次数显示为0

前端代码分析Dashboard.vue):

const todayLoginRes: any = await request.get('/logs/login/today/count')
stats.todayLogin = todayLoginRes || 0

后端路由配置SystemRouter.java):

@Bean
public RouterFunction<ServerResponse> 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):

public interface ISysLoginLogService {
    Mono<SysLoginLog> findById(Long id);
    Flux<SysLoginLog> findAll();
    Flux<SysLoginLog> findByUsername(String username);
    Flux<SysLoginLog> findByLoginTimeBetween(LocalDateTime startTime, LocalDateTime endTime);
    Mono<SysLoginLog> save(SysLoginLog loginLog);
    Mono<PageResponse<SysLoginLog>> findLoginLogsByPage(PageRequest pageRequest);
    Mono<Long> count();
    // 注意:缺少 countToday() 方法
}

问题根因

  1. 前端请求 /logs/login/today/count 端点
  2. 后端没有配置这个路由端点
  3. ISysLoginLogService 接口缺少 countToday() 方法
  4. 请求失败导致返回undefined,前端显示为0

2. 操作日志显示为0

前端代码分析Dashboard.vue):

const operationLogRes: any = await request.get('/logs/operation/count')
stats.operationLog = operationLogRes || 0

后端路由配置SystemRouter.java):

@Bean
public RouterFunction<ServerResponse> 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

public interface ISysLoginLogService {
    Mono<SysLoginLog> findById(Long id);
    Flux<SysLoginLog> findAll();
    Flux<SysLoginLog> findByUsername(String username);
    Flux<SysLoginLog> findByLoginTimeBetween(LocalDateTime startTime, LocalDateTime endTime);
    Mono<SysLoginLog> save(SysLoginLog loginLog);
    Mono<PageResponse<SysLoginLog>> findLoginLogsByPage(PageRequest pageRequest);
    Mono<Long> count();
    Mono<Long> countToday(); // 新增方法
}

1.2 实现今日登录统计

文件SysLoginLogService.java

@Override
public Mono<Long> 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

Flux<SysLoginLog> findByLoginTimeBetween(LocalDateTime startTime, LocalDateTime endTime);

1.4 更新Handler

文件SysLogHandler.java

@Operation(summary = "获取今日登录次数", description = "获取今日登录次数统计")
public Mono<ServerResponse> getTodayLoginCount(ServerRequest request) {
    return loginLogService.countToday()
            .flatMap(count -> ServerResponse.ok().bodyValue(count));
}

1.5 更新路由配置

文件SystemRouter.java

@Bean
public RouterFunction<ServerResponse> 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

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

@Operation(summary = "获取系统概览", description = "获取系统统计概览信息")
public Mono<ServerResponse> 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

确认拦截器是否正确配置和记录操作日志:

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class OperationLogFilter implements WebFilter {

    @Override
    public Mono<Void> 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 验证数据库

-- 检查操作日志表是否有数据
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 的数据记录情况