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

206 lines
5.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 service
import (
"context"
"time"
"carrot_bbs/internal/repository"
"go.uber.org/zap"
)
// LogCleanupConfig 日志清理配置
type LogCleanupConfig struct {
Enabled bool
OperationLog int // 保留天数
LoginLog int
DataChangeLog int
CleanupTime string // 清理时间,如 "02:00"
}
// LogCleanupService 日志清理服务接口
type LogCleanupService interface {
Start()
Stop()
CleanupOldLogs(ctx context.Context) error
CleanupOperationLogs(ctx context.Context, beforeDate string) error
CleanupLoginLogs(ctx context.Context, beforeDate string) error
CleanupDataChangeLogs(ctx context.Context, beforeDate string) error
}
// logCleanupServiceImpl 日志清理服务实现
type logCleanupServiceImpl struct {
operationRepo *repository.OperationLogRepository
loginRepo *repository.LoginLogRepository
dataChangeRepo *repository.DataChangeLogRepository
cfg *LogCleanupConfig
logger *zap.Logger
ctx context.Context
cancel context.CancelFunc
}
// NewLogCleanupService 创建日志清理服务
func NewLogCleanupService(
operationRepo *repository.OperationLogRepository,
loginRepo *repository.LoginLogRepository,
dataChangeRepo *repository.DataChangeLogRepository,
cfg *LogCleanupConfig,
logger *zap.Logger,
) LogCleanupService {
return &logCleanupServiceImpl{
operationRepo: operationRepo,
loginRepo: loginRepo,
dataChangeRepo: dataChangeRepo,
cfg: cfg,
logger: logger,
}
}
// Start 启动日志清理服务
func (s *logCleanupServiceImpl) Start() {
if !s.cfg.Enabled {
s.logger.Info("log cleanup service disabled")
return
}
s.ctx, s.cancel = context.WithCancel(context.Background())
go s.scheduleCleanup()
s.logger.Info("log cleanup service started",
zap.Int("operation_log_days", s.cfg.OperationLog),
zap.Int("login_log_days", s.cfg.LoginLog),
zap.Int("data_change_log_days", s.cfg.DataChangeLog),
)
}
// Stop 停止日志清理服务
func (s *logCleanupServiceImpl) Stop() {
if s.cancel != nil {
s.cancel()
s.logger.Info("log cleanup service stopped")
}
}
// scheduleCleanup 定时调度清理任务
func (s *logCleanupServiceImpl) scheduleCleanup() {
for {
select {
case <-s.ctx.Done():
return
default:
now := time.Now()
nextCleanup := s.getNextCleanupTime(now)
duration := nextCleanup.Sub(now)
s.logger.Info("next log cleanup scheduled",
zap.Time("time", nextCleanup),
zap.Duration("in", duration),
)
timer := time.NewTimer(duration)
select {
case <-s.ctx.Done():
timer.Stop()
return
case <-timer.C:
if err := s.CleanupOldLogs(s.ctx); err != nil {
s.logger.Error("failed to cleanup old logs", zap.Error(err))
}
}
}
}
}
// getNextCleanupTime 计算下次清理时间
func (s *logCleanupServiceImpl) getNextCleanupTime(now time.Time) time.Time {
cleanupTime, err := time.Parse("15:04", s.cfg.CleanupTime)
if err != nil {
cleanupTime = time.Date(now.Year(), now.Month(), now.Day(), 2, 0, 0, 0, now.Location())
}
nextCleanup := time.Date(now.Year(), now.Month(), now.Day(), cleanupTime.Hour(), cleanupTime.Minute(), 0, 0, now.Location())
if nextCleanup.Before(now) {
nextCleanup = nextCleanup.Add(24 * time.Hour)
}
return nextCleanup
}
// CleanupOldLogs 清理旧日志
func (s *logCleanupServiceImpl) CleanupOldLogs(ctx context.Context) error {
s.logger.Info("starting log cleanup")
// 清理操作日志
if err := s.CleanupOperationLogs(ctx, s.getBeforeDate(s.cfg.OperationLog)); err != nil {
s.logger.Error("failed to cleanup operation logs", zap.Error(err))
}
// 清理登录日志
if err := s.CleanupLoginLogs(ctx, s.getBeforeDate(s.cfg.LoginLog)); err != nil {
s.logger.Error("failed to cleanup login logs", zap.Error(err))
}
// 清理数据变更日志
if err := s.CleanupDataChangeLogs(ctx, s.getBeforeDate(s.cfg.DataChangeLog)); err != nil {
s.logger.Error("failed to cleanup data change logs", zap.Error(err))
}
s.logger.Info("log cleanup completed")
return nil
}
// CleanupOperationLogs 清理操作日志
func (s *logCleanupServiceImpl) CleanupOperationLogs(ctx context.Context, beforeDate string) error {
result := s.operationRepo.DeleteOldLogs(ctx, beforeDate)
s.logger.Info("cleanup operation logs", zap.String("before_date", beforeDate))
return result
}
// CleanupLoginLogs 清理登录日志
func (s *logCleanupServiceImpl) CleanupLoginLogs(ctx context.Context, beforeDate string) error {
result := s.loginRepo.DeleteOldLogs(ctx, beforeDate)
s.logger.Info("cleanup login logs", zap.String("before_date", beforeDate))
return result
}
// CleanupDataChangeLogs 清理数据变更日志
func (s *logCleanupServiceImpl) CleanupDataChangeLogs(ctx context.Context, beforeDate string) error {
result := s.dataChangeRepo.DeleteOldLogs(ctx, beforeDate)
s.logger.Info("cleanup data change logs", zap.String("before_date", beforeDate))
return result
}
// getBeforeDate 计算截止日期
func (s *logCleanupServiceImpl) getBeforeDate(days int) string {
return time.Now().AddDate(0, 0, -days).Format("2006-01-02")
}
// LogService 日志服务组合
type LogService struct {
OperationLog OperationLogService
LoginLog LoginLogService
DataChangeLog DataChangeLogService
}
// NewLogService 创建日志服务组合
func NewLogService(
operationLogService OperationLogService,
loginLogService LoginLogService,
dataChangeLogService DataChangeLogService,
) *LogService {
return &LogService{
OperationLog: operationLogService,
LoginLog: loginLogService,
DataChangeLog: dataChangeLogService,
}
}
// GetLogCleanupServiceFromApp 从应用中获取日志清理服务
func GetLogCleanupServiceFromApp(app interface{}) LogCleanupService {
// 这里需要根据实际的app结构来获取
// 暂时返回nil实际使用时需要实现
return nil
}