refactor: 移除全局单例、修复分层违规、统一错误与响应
Some checks failed
Build / build (push) Successful in 7m52s
Build / build-docker (push) Has been cancelled

This commit is contained in:
lafay
2026-06-15 16:40:36 +08:00
parent d9de39a0a3
commit 7d1c78f965
55 changed files with 593 additions and 1744 deletions

View File

@@ -26,6 +26,7 @@ import (
// userService UserService的实现
type userService struct {
cfg *config.Config
userRepo repository.UserRepository
jwtService *auth.JWTService
redis *redis.Client
@@ -38,6 +39,7 @@ type userService struct {
// NewUserService 创建UserService实例
func NewUserService(
cfg *config.Config,
userRepo repository.UserRepository,
jwtService *auth.JWTService,
redisClient *redis.Client,
@@ -48,6 +50,7 @@ func NewUserService(
// CacheKeyBuilder 使用空前缀,因为 CacheManager 已经处理了前缀
// 这样缓存键的格式为: CacheManager前缀 + CacheKeyBuilder生成的键
return &userService{
cfg: cfg,
userRepo: userRepo,
jwtService: jwtService,
redis: redisClient,
@@ -81,7 +84,7 @@ func (s *userService) Register(ctx context.Context, username, password, email, a
// 加密密码
hashedPassword, err := auth.HashPassword(password)
if err != nil {
return nil, "", errors.New("密码加密失败")
return nil, "", fmt.Errorf("密码加密失败: %w", err)
}
// 确定头像URL
@@ -112,7 +115,7 @@ func (s *userService) Register(ctx context.Context, username, password, email, a
// 生成JWT Token
token, err := s.jwtService.GenerateToken(user.ID, user.Username, user.Role)
if err != nil {
return nil, "", errors.New("生成Token失败")
return nil, "", fmt.Errorf("生成Token失败: %w", err)
}
return user, token, nil
@@ -167,15 +170,20 @@ func (s *userService) Login(ctx context.Context, usernameOrEmail, password, ipAd
// 生成JWT Token
token, err := s.jwtService.GenerateToken(user.ID, user.Username, user.Role)
if err != nil {
return nil, "", errors.New("生成Token失败")
return nil, "", fmt.Errorf("生成Token失败: %w", err)
}
// 更新最后登录时间
// 更新最后登录时间(非关键路径,错误降级为 Warn
now := time.Now()
user.LastLoginAt = &now
_ = s.userRepo.UpdateFields(ctx, user.ID, map[string]interface{}{
if err := s.userRepo.UpdateFields(ctx, user.ID, map[string]interface{}{
"last_login_at": now,
})
}); err != nil {
s.logger.Warn("更新最后登录时间失败",
zap.Int64("user_id", user.ID),
zap.Error(err),
)
}
// 记录成功登录日志
s.logSuccessLogin(ctx, user.ID, ipAddress, userAgent)
@@ -239,7 +247,10 @@ func (s *userService) UpdateAvatar(ctx context.Context, userID int64, avatarURL
func (s *userService) ChangePassword(ctx context.Context, userID int64, oldPassword, newPassword string) error {
user, err := s.userRepo.FindByID(ctx, userID)
if err != nil || user == nil {
if err != nil {
return fmt.Errorf("查询用户失败: %w", err)
}
if user == nil {
return errors.New("用户不存在")
}
@@ -249,7 +260,7 @@ func (s *userService) ChangePassword(ctx context.Context, userID int64, oldPassw
hashedPassword, err := auth.HashPassword(newPassword)
if err != nil {
return errors.New("密码加密失败")
return fmt.Errorf("密码加密失败: %w", err)
}
err = s.userRepo.UpdateFields(ctx, userID, map[string]interface{}{
@@ -267,13 +278,16 @@ func (s *userService) ChangePassword(ctx context.Context, userID int64, oldPassw
func (s *userService) ResetPassword(ctx context.Context, email, newPassword string) error {
user, err := s.userRepo.FindByEmail(ctx, email)
if err != nil || user == nil {
if err != nil {
return fmt.Errorf("查询用户失败: %w", err)
}
if user == nil {
return errors.New("用户不存在")
}
hashedPassword, err := auth.HashPassword(newPassword)
if err != nil {
return errors.New("密码加密失败")
return fmt.Errorf("密码加密失败: %w", err)
}
err = s.userRepo.UpdateFields(ctx, user.ID, map[string]interface{}{
@@ -350,14 +364,13 @@ func (s *userService) ValidateAvatarURL(ctx context.Context, avatarURL string) e
return errors.New("URL缺少主机名")
}
// 从配置获取允许的域名列表
cfg, err := config.GetConfig()
if err != nil {
allowedDomains := []string{"localhost", "127.0.0.1"}
return s.checkDomainAllowed(host, allowedDomains)
// 从注入的配置获取允许的域名列表
allowedDomains := s.cfg.Security.AllowedDomains
if len(allowedDomains) == 0 {
allowedDomains = []string{"localhost", "127.0.0.1"}
}
return s.checkDomainAllowed(host, cfg.Security.AllowedDomains)
return s.checkDomainAllowed(host, allowedDomains)
}
func (s *userService) UploadAvatar(ctx context.Context, userID int64, fileData []byte, fileName string) (string, error) {
@@ -422,29 +435,23 @@ func (s *userService) UploadAvatar(ctx context.Context, userID int64, fileData [
}
func (s *userService) GetMaxProfilesPerUser() int {
cfg, err := config.GetConfig()
if err != nil || cfg.Site.MaxProfilesPerUser <= 0 {
if s.cfg.Site.MaxProfilesPerUser <= 0 {
return 5
}
return cfg.Site.MaxProfilesPerUser
return s.cfg.Site.MaxProfilesPerUser
}
func (s *userService) GetMaxTexturesPerUser() int {
cfg, err := config.GetConfig()
if err != nil || cfg.Site.MaxTexturesPerUser <= 0 {
if s.cfg.Site.MaxTexturesPerUser <= 0 {
return 50
}
return cfg.Site.MaxTexturesPerUser
return s.cfg.Site.MaxTexturesPerUser
}
// 私有辅助方法
func (s *userService) getDefaultAvatar() string {
cfg, err := config.GetConfig()
if err != nil {
return ""
}
return cfg.Site.DefaultAvatar
return s.cfg.Site.DefaultAvatar
}
func (s *userService) checkDomainAllowed(host string, allowedDomains []string) error {
@@ -505,3 +512,21 @@ func (s *userService) logFailedLogin(ctx context.Context, userID int64, ipAddres
}
_ = s.userRepo.CreateLoginLog(ctx, log)
}
// ==================== 管理员操作 ====================
// ListUsers 分页查询用户列表(管理员)
func (s *userService) ListUsers(ctx context.Context, page, pageSize int) ([]*model.User, int64, error) {
page, pageSize = NormalizePagination(page, pageSize)
return s.userRepo.List(ctx, page, pageSize)
}
// SetRole 设置用户角色(管理员)
func (s *userService) SetRole(ctx context.Context, userID int64, role string) error {
return s.userRepo.UpdateFields(ctx, userID, map[string]interface{}{"role": role})
}
// SetStatus 设置用户状态(管理员)
func (s *userService) SetStatus(ctx context.Context, userID int64, status int16) error {
return s.userRepo.UpdateFields(ctx, userID, map[string]interface{}{"status": status})
}