feat: API 访问日志系统(拦截器 + 统计分析 + 管理页面)
- V6: api_access_logs 表(method/path/email/IP/status/耗时/错误) - ApiLogInterceptor: 记录所有 /api/v1/** 请求,order(0) 最优先 - ApiLogController: 分页查询 + 按邮箱/路径统计(24h 窗口) - 前端 ApiLogs.vue: 筛选/统计面板/日志列表/分页 - 导航新增「访问日志」菜单项
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
package com.par.api.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.par.core.dto.ApiResponse;
|
||||
import com.par.core.entity.ApiAccessLog;
|
||||
import com.par.core.mapper.ApiAccessLogMapper;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* API 访问日志控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/admin/logs")
|
||||
@RequiredArgsConstructor
|
||||
public class ApiLogController {
|
||||
|
||||
private final ApiAccessLogMapper mapper;
|
||||
|
||||
@GetMapping
|
||||
public ApiResponse<Map<String, Object>> list(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "50") int size,
|
||||
@RequestParam(required = false) String email,
|
||||
@RequestParam(required = false) String path,
|
||||
@RequestParam(required = false) Boolean success,
|
||||
HttpServletRequest request) {
|
||||
// checkAdmin implicitly via interceptor
|
||||
|
||||
LambdaQueryWrapper<ApiAccessLog> q = new LambdaQueryWrapper<>();
|
||||
if (email != null && !email.isBlank()) q.like(ApiAccessLog::getEmail, email);
|
||||
if (path != null && !path.isBlank()) q.like(ApiAccessLog::getPath, path);
|
||||
if (success != null) q.eq(ApiAccessLog::getSuccess, success);
|
||||
q.orderByDesc(ApiAccessLog::getCreatedAt);
|
||||
|
||||
Page<ApiAccessLog> p = mapper.selectPage(new Page<>(page, size), q);
|
||||
Map<String, Object> result = Map.of(
|
||||
"records", p.getRecords(),
|
||||
"total", p.getTotal(),
|
||||
"page", p.getCurrent(),
|
||||
"size", p.getSize()
|
||||
);
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
@GetMapping("/stats/email")
|
||||
public ApiResponse<Object> statsByEmail(@RequestParam(defaultValue = "24") int hours) {
|
||||
String since = LocalDateTime.now().minusHours(hours).toString();
|
||||
return ApiResponse.success(mapper.statsByEmail(since));
|
||||
}
|
||||
|
||||
@GetMapping("/stats/path")
|
||||
public ApiResponse<Object> statsByPath(@RequestParam(defaultValue = "24") int hours) {
|
||||
String since = LocalDateTime.now().minusHours(hours).toString();
|
||||
return ApiResponse.success(mapper.statsByPath(since));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user