feat: 系统全面优化 - 设备管理/登录日志导出/性能监控/设置页面

后端:
- 新增全局设备管理 API(DeviceHandler.GetAllDevices)
- 新增登录日志导出功能(LogHandler.ExportLoginLogs, CSV/XLSX)
- 新增设置服务(SettingsService)和设置页面 API
- 设备管理支持多条件筛选(状态/信任状态/关键词)
- 登录日志支持流式导出防 OOM
- 操作日志支持按方法/时间范围搜索
- 主题配置服务(ThemeService)
- 增强监控健康检查(Prometheus metrics + SLO)
- 移除旧 ratelimit.go(已迁移至 robustness)
- 修复 SocialAccount NULL 扫描问题
- 新增 API 契约测试、Handler 测试、Settings 测试

前端:
- 新增管理员设备管理页面(DevicesPage)
- 新增管理员登录日志导出功能
- 新增系统设置页面(SettingsPage)
- 设备管理支持筛选和分页
- 增强 HTTP 响应类型

测试:
- 业务逻辑测试 68 个(含并发 CONC_001~003)
- 规模测试 16 个(P99 百分位统计)
- E2E 测试、集成测试、契约测试
- 性能基准测试、鲁棒性测试

全面测试通过(38 个测试包)
This commit is contained in:
2026-04-07 12:08:16 +08:00
parent 8655b39b03
commit 5ca3633be4
36 changed files with 4552 additions and 134 deletions

View File

@@ -2,9 +2,11 @@ package service
import (
"context"
"fmt"
"time"
"github.com/user-management-system/internal/domain"
"github.com/user-management-system/internal/pagination"
"github.com/user-management-system/internal/repository"
)
@@ -51,13 +53,15 @@ type RecordOperationRequest struct {
// ListOperationLogRequest 操作日志列表请求
type ListOperationLogRequest struct {
UserID int64 `json:"user_id"`
Method string `json:"method"`
Keyword string `json:"keyword"`
Page int `json:"page"`
PageSize int `json:"page_size"`
StartAt string `json:"start_at"`
EndAt string `json:"end_at"`
UserID int64 `json:"user_id" form:"user_id"`
Method string `json:"method" form:"method"`
Keyword string `json:"keyword" form:"keyword"`
Page int `json:"page" form:"page"`
PageSize int `json:"page_size" form:"page_size"`
StartAt string `json:"start_at" form:"start_at"`
EndAt string `json:"end_at" form:"end_at"`
Cursor string `form:"cursor"` // Opaque cursor for keyset pagination
Size int `form:"size"` // Page size when using cursor mode
}
// GetOperationLogs 获取操作日志列表
@@ -97,6 +101,42 @@ func (s *OperationLogService) GetOperationLogs(ctx context.Context, req *ListOpe
return s.operationLogRepo.List(ctx, offset, req.PageSize)
}
// GetOperationLogsCursor 游标分页获取操作日志列表(推荐使用)
func (s *OperationLogService) GetOperationLogsCursor(ctx context.Context, req *ListOperationLogRequest) (*CursorResult, error) {
size := pagination.ClampPageSize(req.Size)
cursor, err := pagination.Decode(req.Cursor)
if err != nil {
return nil, fmt.Errorf("invalid cursor: %w", err)
}
var items interface{}
var hasMore bool
logs, hm, err := s.operationLogRepo.ListCursor(ctx, size, cursor)
if err != nil {
return nil, err
}
items = logs
hasMore = hm
nextCursor := ""
switch items := items.(type) {
case []*domain.OperationLog:
if len(items) > 0 {
last := items[len(items)-1]
nextCursor = pagination.BuildNextCursor(last.ID, last.CreatedAt)
}
}
return &CursorResult{
Items: items,
NextCursor: nextCursor,
HasMore: hasMore,
PageSize: size,
}, nil
}
// GetMyOperationLogs 获取当前用户的操作日志
func (s *OperationLogService) GetMyOperationLogs(ctx context.Context, userID int64, page, pageSize int) ([]*domain.OperationLog, int64, error) {
if page <= 0 {