feat: 添加日志管理和敏感词过滤功能

- 新增日志管理模块:操作日志、登录日志、数据变更日志
- 新增敏感词过滤器 (sanitizer)
- 新增日志异步写入管理器
- 新增日志定时清理服务
- 优化帖子相关服务和上传服务
This commit is contained in:
2026-03-15 02:25:10 +08:00
parent d3065b30cc
commit ebd9cb8b04
31 changed files with 2753 additions and 60 deletions

View File

@@ -44,8 +44,8 @@ type UserService interface {
// 关注相关
GetFollowers(ctx context.Context, userID string, page, pageSize int) ([]*model.User, int64, error)
GetFollowing(ctx context.Context, userID string, page, pageSize int) ([]*model.User, int64, error)
GetFollowingList(ctx context.Context, userID, page, pageSize string) ([]*model.User, error)
GetFollowersList(ctx context.Context, userID, page, pageSize string) ([]*model.User, error)
GetFollowingList(ctx context.Context, userID string, page, pageSize string) ([]*model.User, error)
GetFollowersList(ctx context.Context, userID string, page, pageSize string) ([]*model.User, error)
FollowUser(ctx context.Context, followerID, followeeID string) error
UnfollowUser(ctx context.Context, followerID, followeeID string) error
GetMutualFollowStatus(ctx context.Context, currentUserID string, targetUserIDs []string) (map[string][2]bool, error)
@@ -55,6 +55,9 @@ type UserService interface {
UnblockUser(ctx context.Context, blockerID, blockedID string) error
GetBlockedUsers(ctx context.Context, blockerID string, page, pageSize int) ([]*model.User, int64, error)
IsBlocked(ctx context.Context, blockerID, blockedID string) (bool, error)
// 日志服务设置
SetLogService(logService *LogService)
}
// userServiceImpl 用户服务实现
@@ -62,6 +65,7 @@ type userServiceImpl struct {
userRepo *repository.UserRepository
systemMessageService SystemMessageService
emailCodeService EmailCodeService
logService *LogService
}
// NewUserService 创建用户服务
@@ -75,9 +79,15 @@ func NewUserService(
userRepo: userRepo,
systemMessageService: systemMessageService,
emailCodeService: NewEmailCodeService(emailService, cacheBackend),
logService: nil,
}
}
// SetLogService 设置日志服务
func (s *userServiceImpl) SetLogService(logService *LogService) {
s.logService = logService
}
// SendRegisterCode 发送注册验证码
func (s *userServiceImpl) SendRegisterCode(ctx context.Context, email string) error {
user, err := s.userRepo.GetByEmail(email)
@@ -241,12 +251,28 @@ func (s *userServiceImpl) Register(ctx context.Context, username, email, passwor
return nil, err
}
// 记录注册日志
if s.logService != nil {
s.logService.OperationLog.RecordOperation(&model.OperationLog{
UserID: user.ID,
UserName: user.Username,
NickName: user.Nickname,
Operation: string(model.OpUserRegister),
Module: "user",
TargetType: "user",
TargetID: user.ID,
IP: "",
Status: "success",
})
}
return user, nil
}
// Login 用户登录
func (s *userServiceImpl) Login(ctx context.Context, account, password string) (*model.User, error) {
account = strings.TrimSpace(account)
var (
user *model.User
err error
@@ -258,18 +284,86 @@ func (s *userServiceImpl) Login(ctx context.Context, account, password string) (
} else {
user, err = s.userRepo.GetByUsername(account)
}
// 登录失败处理
loginResult := string(model.LoginResultSuccess)
var failReason string
if err != nil || user == nil {
loginResult = string(model.LoginResultFail)
failReason = string(model.FailReasonUserNotFound)
// 记录失败登录日志
if s.logService != nil {
s.logService.LoginLog.RecordLogin(&model.LoginLog{
UserName: account,
Event: string(model.LoginEventFail),
IP: "",
UserAgent: "",
Result: loginResult,
FailReason: failReason,
})
}
return nil, ErrInvalidCredentials
}
if !utils.CheckPasswordHash(password, user.PasswordHash) {
loginResult = string(model.LoginResultFail)
failReason = string(model.FailReasonWrongPassword)
// 记录失败登录日志
if s.logService != nil {
s.logService.LoginLog.RecordLogin(&model.LoginLog{
UserID: user.ID,
UserName: user.Username,
NickName: user.Nickname,
Event: string(model.LoginEventFail),
IP: "",
UserAgent: "",
Result: loginResult,
FailReason: failReason,
})
}
return nil, ErrInvalidCredentials
}
if user.Status != model.UserStatusActive {
loginResult = string(model.LoginResultFail)
failReason = string(model.FailReasonAccountBanned)
// 记录失败登录日志
if s.logService != nil {
s.logService.LoginLog.RecordLogin(&model.LoginLog{
UserID: user.ID,
UserName: user.Username,
NickName: user.Nickname,
Event: string(model.LoginEventFail),
IP: "",
UserAgent: "",
Result: loginResult,
FailReason: failReason,
})
}
return nil, ErrUserBanned
}
// 记录成功登录日志
if s.logService != nil {
s.logService.LoginLog.RecordLogin(&model.LoginLog{
UserID: user.ID,
UserName: user.Username,
NickName: user.Nickname,
LoginType: string(model.LoginTypePassword),
Event: string(model.LoginEventLogin),
IP: "",
UserAgent: "",
Result: string(model.LoginResultSuccess),
})
}
return user, nil
}
@@ -337,7 +431,27 @@ func (s *userServiceImpl) GetUserByIDWithMutualFollowStatus(ctx context.Context,
// UpdateUser 更新用户
func (s *userServiceImpl) UpdateUser(ctx context.Context, user *model.User) error {
return s.userRepo.Update(user)
err := s.userRepo.Update(user)
if err != nil {
return err
}
// 记录数据变更日志
if s.logService != nil {
s.logService.DataChangeLog.RecordDataChange(&model.DataChangeLog{
UserID: user.ID,
ChangeType: string(model.ChangeProfile),
TargetType: "user",
TargetID: user.ID,
FieldName: "profile",
OldValue: "",
NewValue: fmt.Sprintf("昵称:%s", user.Nickname),
OperatorType: string(model.OperatorTypeSelf),
Reason: "用户修改个人资料",
})
}
return nil
}
// GetFollowers 获取粉丝
@@ -542,7 +656,26 @@ func (s *userServiceImpl) ChangePassword(ctx context.Context, userID, oldPasswor
// 更新密码
user.PasswordHash = hashedPassword
return s.userRepo.Update(user)
if err := s.userRepo.Update(user); err != nil {
return err
}
// 记录数据变更日志
if s.logService != nil {
s.logService.DataChangeLog.RecordDataChange(&model.DataChangeLog{
UserID: userID,
ChangeType: string(model.ChangePassword),
TargetType: "user",
TargetID: userID,
FieldName: "password",
OldValue: "***",
NewValue: "***",
OperatorType: string(model.OperatorTypeSelf),
Reason: "用户主动修改密码",
})
}
return nil
}
// ResetPasswordByEmail 通过邮箱重置密码