Files
backend/internal/repository/login_log_repo.go
lan ebd9cb8b04 feat: 添加日志管理和敏感词过滤功能
- 新增日志管理模块:操作日志、登录日志、数据变更日志
- 新增敏感词过滤器 (sanitizer)
- 新增日志异步写入管理器
- 新增日志定时清理服务
- 优化帖子相关服务和上传服务
2026-03-15 02:25:10 +08:00

160 lines
4.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package repository
import (
"context"
"time"
"carrot_bbs/internal/model"
"gorm.io/gorm"
)
// LoginLogRepository 登录日志仓储
type LoginLogRepository struct {
db *gorm.DB
}
// NewLoginLogRepository 创建登录日志仓储
func NewLoginLogRepository(db *gorm.DB) *LoginLogRepository {
return &LoginLogRepository{db: db}
}
// CreateLoginLog 创建单条登录日志
func (r *LoginLogRepository) CreateLoginLog(ctx context.Context, log *model.LoginLog) error {
return r.db.WithContext(ctx).Create(log).Error
}
// BatchCreateLoginLogs 批量创建登录日志
func (r *LoginLogRepository) BatchCreateLoginLogs(ctx context.Context, logs []*model.LoginLog) error {
if len(logs) == 0 {
return nil
}
return r.db.WithContext(ctx).Create(logs).Error
}
// GetLoginLogs 获取登录日志列表(分页)
func (r *LoginLogRepository) GetLoginLogs(ctx context.Context, filters LoginFilter, page, pageSize int) ([]*model.LoginLog, int64, error) {
query := r.db.WithContext(ctx).Model(&model.LoginLog{})
if filters.UserID != "" {
query = query.Where("user_id = ?", filters.UserID)
}
if filters.Event != "" {
query = query.Where("event = ?", filters.Event)
}
if filters.Result != "" {
query = query.Where("result = ?", filters.Result)
}
if filters.FailReason != "" {
query = query.Where("fail_reason = ?", filters.FailReason)
}
if filters.LoginType != "" {
query = query.Where("login_type = ?", filters.LoginType)
}
if !filters.StartTime.IsZero() {
query = query.Where("created_at >= ?", filters.StartTime)
}
if !filters.EndTime.IsZero() {
query = query.Where("created_at <= ?", filters.EndTime)
}
if filters.IP != "" {
query = query.Where("ip = ?", filters.IP)
}
var total int64
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
var logs []*model.LoginLog
offset := (page - 1) * pageSize
if err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&logs).Error; err != nil {
return nil, 0, err
}
return logs, total, nil
}
// GetLoginLogsByUser 获取指定用户的登录日志
func (r *LoginLogRepository) GetLoginLogsByUser(ctx context.Context, userID string, page, pageSize int) ([]*model.LoginLog, int64, error) {
return r.GetLoginLogs(ctx, LoginFilter{UserID: userID}, page, pageSize)
}
// GetFailedLoginsByIP 获取指定IP的失败登录记录用于风控
func (r *LoginLogRepository) GetFailedLoginsByIP(ctx context.Context, ip string, timeWindow time.Duration) (int64, error) {
startTime := time.Now().Add(-timeWindow)
var count int64
err := r.db.WithContext(ctx).
Model(&model.LoginLog{}).
Where("ip = ? AND result = ? AND created_at >= ?", ip, string(model.LoginResultFail), startTime).
Count(&count).Error
return count, err
}
// GetRecentSuccessLogins 获取最近成功的登录记录
func (r *LoginLogRepository) GetRecentSuccessLogins(ctx context.Context, userID string, limit int) ([]*model.LoginLog, error) {
var logs []*model.LoginLog
err := r.db.WithContext(ctx).
Where("user_id = ? AND result = ? AND event = ?", userID, string(model.LoginResultSuccess), string(model.LoginEventLogin)).
Order("created_at DESC").
Limit(limit).
Find(&logs).Error
return logs, err
}
// DeleteOldLogs 删除旧的登录日志(用于定时清理)
func (r *LoginLogRepository) DeleteOldLogs(ctx context.Context, beforeDate string) error {
return r.db.WithContext(ctx).Where("created_at < ?", beforeDate).Delete(&model.LoginLog{}).Error
}
// GetStatistics 获取登录日志统计
func (r *LoginLogRepository) GetStatistics(ctx context.Context, startTime, endTime string) (*LoginLogStatistics, error) {
stats := &LoginLogStatistics{}
query := r.db.WithContext(ctx).Model(&model.LoginLog{})
if startTime != "" {
query = query.Where("created_at >= ?", startTime)
}
if endTime != "" {
query = query.Where("created_at <= ?", endTime)
}
if err := query.Count(&stats.TotalCount).Error; err != nil {
return nil, err
}
if err := query.Where("result = ?", string(model.LoginResultSuccess)).Count(&stats.SuccessCount).Error; err != nil {
return nil, err
}
if err := query.Where("result = ?", string(model.LoginResultFail)).Count(&stats.FailCount).Error; err != nil {
return nil, err
}
if err := query.Where("event = ?", string(model.LoginEventLogin)).Count(&stats.LoginCount).Error; err != nil {
return nil, err
}
return stats, nil
}
// LoginFilter 登录日志过滤条件
type LoginFilter struct {
UserID string
Event string
Result string
FailReason string
LoginType string
IP string
StartTime time.Time
EndTime time.Time
}
// LoginLogStatistics 登录日志统计
type LoginLogStatistics struct {
TotalCount int64
SuccessCount int64
FailCount int64
LoginCount int64
}