refactor: 移除全局单例、修复分层违规、统一错误与响应
This commit is contained in:
@@ -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})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user