Files
user-system/internal/service/operation_log.go

156 lines
4.6 KiB
Go
Raw Normal View History

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"
)
// OperationLogService 操作日志服务
type OperationLogService struct {
operationLogRepo *repository.OperationLogRepository
}
// NewOperationLogService 创建操作日志服务
func NewOperationLogService(operationLogRepo *repository.OperationLogRepository) *OperationLogService {
return &OperationLogService{operationLogRepo: operationLogRepo}
}
// RecordOperation 记录操作日志
func (s *OperationLogService) RecordOperation(ctx context.Context, req *RecordOperationRequest) error {
log := &domain.OperationLog{
OperationType: req.OperationType,
OperationName: req.OperationName,
RequestMethod: req.RequestMethod,
RequestPath: req.RequestPath,
RequestParams: req.RequestParams,
ResponseStatus: req.ResponseStatus,
IP: req.IP,
UserAgent: req.UserAgent,
}
if req.UserID != 0 {
log.UserID = &req.UserID
}
return s.operationLogRepo.Create(ctx, log)
}
// RecordOperationRequest 记录操作请求
type RecordOperationRequest struct {
UserID int64 `json:"user_id"`
OperationType string `json:"operation_type"`
OperationName string `json:"operation_name"`
RequestMethod string `json:"request_method"`
RequestPath string `json:"request_path"`
RequestParams string `json:"request_params"`
ResponseStatus int `json:"response_status"`
IP string `json:"ip"`
UserAgent string `json:"user_agent"`
}
// ListOperationLogRequest 操作日志列表请求
type ListOperationLogRequest struct {
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 获取操作日志列表
func (s *OperationLogService) GetOperationLogs(ctx context.Context, req *ListOperationLogRequest) ([]*domain.OperationLog, int64, error) {
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 20
}
offset := (req.Page - 1) * req.PageSize
// 按关键词搜索
if req.Keyword != "" {
return s.operationLogRepo.Search(ctx, req.Keyword, offset, req.PageSize)
}
// 按用户 ID 查询
if req.UserID > 0 {
return s.operationLogRepo.ListByUserID(ctx, req.UserID, offset, req.PageSize)
}
// 按 HTTP 方法查询
if req.Method != "" {
return s.operationLogRepo.ListByMethod(ctx, req.Method, offset, req.PageSize)
}
// 按时间范围查询
if req.StartAt != "" && req.EndAt != "" {
start, err1 := time.Parse(time.RFC3339, req.StartAt)
end, err2 := time.Parse(time.RFC3339, req.EndAt)
if err1 == nil && err2 == nil {
return s.operationLogRepo.ListByTimeRange(ctx, start, end, offset, req.PageSize)
}
}
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 {
page = 1
}
if pageSize <= 0 {
pageSize = 20
}
offset := (page - 1) * pageSize
return s.operationLogRepo.ListByUserID(ctx, userID, offset, pageSize)
}
// CleanupOldLogs 清理旧日志(保留最近 N 天)
func (s *OperationLogService) CleanupOldLogs(ctx context.Context, retentionDays int) error {
return s.operationLogRepo.DeleteOlderThan(ctx, retentionDays)
}