feat: Enhance dependency injection and service integration
- Updated main.go to initialize email service and include it in the dependency injection container. - Refactored handlers to utilize context in service method calls, improving consistency and error handling. - Introduced new service options for upload, security, and captcha services, enhancing modularity and testability. - Removed unused repository implementations to streamline the codebase. This commit continues the effort to improve the architecture by ensuring all services are properly injected and utilized across the application.
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"carrotskin/internal/repository"
|
||||
"carrotskin/pkg/auth"
|
||||
"carrotskin/pkg/config"
|
||||
"carrotskin/pkg/database"
|
||||
"carrotskin/pkg/redis"
|
||||
"context"
|
||||
"errors"
|
||||
@@ -16,12 +17,15 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// userServiceImpl UserService的实现
|
||||
type userServiceImpl struct {
|
||||
// userService UserService的实现
|
||||
type userService struct {
|
||||
userRepo repository.UserRepository
|
||||
configRepo repository.SystemConfigRepository
|
||||
jwtService *auth.JWTService
|
||||
redis *redis.Client
|
||||
cache *database.CacheManager
|
||||
cacheKeys *database.CacheKeyBuilder
|
||||
cacheInv *database.CacheInvalidator
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
@@ -31,18 +35,24 @@ func NewUserService(
|
||||
configRepo repository.SystemConfigRepository,
|
||||
jwtService *auth.JWTService,
|
||||
redisClient *redis.Client,
|
||||
cacheManager *database.CacheManager,
|
||||
logger *zap.Logger,
|
||||
) UserService {
|
||||
return &userServiceImpl{
|
||||
// CacheKeyBuilder 使用空前缀,因为 CacheManager 已经处理了前缀
|
||||
// 这样缓存键的格式为: CacheManager前缀 + CacheKeyBuilder生成的键
|
||||
return &userService{
|
||||
userRepo: userRepo,
|
||||
configRepo: configRepo,
|
||||
jwtService: jwtService,
|
||||
redis: redisClient,
|
||||
cache: cacheManager,
|
||||
cacheKeys: database.NewCacheKeyBuilder(""),
|
||||
cacheInv: database.NewCacheInvalidator(cacheManager),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) Register(username, password, email, avatar string) (*model.User, string, error) {
|
||||
func (s *userService) Register(ctx context.Context, username, password, email, avatar string) (*model.User, string, error) {
|
||||
// 检查用户名是否已存在
|
||||
existingUser, err := s.userRepo.FindByUsername(username)
|
||||
if err != nil {
|
||||
@@ -70,7 +80,7 @@ func (s *userServiceImpl) Register(username, password, email, avatar string) (*m
|
||||
// 确定头像URL
|
||||
avatarURL := avatar
|
||||
if avatarURL != "" {
|
||||
if err := s.ValidateAvatarURL(avatarURL); err != nil {
|
||||
if err := s.ValidateAvatarURL(ctx, avatarURL); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
} else {
|
||||
@@ -101,9 +111,7 @@ func (s *userServiceImpl) Register(username, password, email, avatar string) (*m
|
||||
return user, token, nil
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) Login(usernameOrEmail, password, ipAddress, userAgent string) (*model.User, string, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
func (s *userService) Login(ctx context.Context, usernameOrEmail, password, ipAddress, userAgent string) (*model.User, string, error) {
|
||||
// 检查账号是否被锁定
|
||||
if s.redis != nil {
|
||||
identifier := usernameOrEmail + ":" + ipAddress
|
||||
@@ -168,25 +176,53 @@ func (s *userServiceImpl) Login(usernameOrEmail, password, ipAddress, userAgent
|
||||
return user, token, nil
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) GetByID(id int64) (*model.User, error) {
|
||||
return s.userRepo.FindByID(id)
|
||||
func (s *userService) GetByID(ctx context.Context, id int64) (*model.User, error) {
|
||||
// 使用 Cached 装饰器自动处理缓存
|
||||
cacheKey := s.cacheKeys.User(id)
|
||||
return database.Cached(ctx, s.cache, cacheKey, func() (*model.User, error) {
|
||||
return s.userRepo.FindByID(id)
|
||||
}, 5*time.Minute)
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) GetByEmail(email string) (*model.User, error) {
|
||||
return s.userRepo.FindByEmail(email)
|
||||
func (s *userService) GetByEmail(ctx context.Context, email string) (*model.User, error) {
|
||||
// 使用 Cached 装饰器自动处理缓存
|
||||
cacheKey := s.cacheKeys.UserByEmail(email)
|
||||
return database.Cached(ctx, s.cache, cacheKey, func() (*model.User, error) {
|
||||
return s.userRepo.FindByEmail(email)
|
||||
}, 5*time.Minute)
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) UpdateInfo(user *model.User) error {
|
||||
return s.userRepo.Update(user)
|
||||
func (s *userService) UpdateInfo(ctx context.Context, user *model.User) error {
|
||||
err := s.userRepo.Update(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 清除缓存
|
||||
s.cacheInv.OnUpdate(ctx,
|
||||
s.cacheKeys.User(user.ID),
|
||||
s.cacheKeys.UserByEmail(user.Email),
|
||||
s.cacheKeys.UserByUsername(user.Username),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) UpdateAvatar(userID int64, avatarURL string) error {
|
||||
return s.userRepo.UpdateFields(userID, map[string]interface{}{
|
||||
func (s *userService) UpdateAvatar(ctx context.Context, userID int64, avatarURL string) error {
|
||||
err := s.userRepo.UpdateFields(userID, map[string]interface{}{
|
||||
"avatar": avatarURL,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 清除用户缓存
|
||||
s.cacheInv.OnUpdate(ctx, s.cacheKeys.User(userID))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) ChangePassword(userID int64, oldPassword, newPassword string) error {
|
||||
func (s *userService) ChangePassword(ctx context.Context, userID int64, oldPassword, newPassword string) error {
|
||||
user, err := s.userRepo.FindByID(userID)
|
||||
if err != nil || user == nil {
|
||||
return errors.New("用户不存在")
|
||||
@@ -201,12 +237,20 @@ func (s *userServiceImpl) ChangePassword(userID int64, oldPassword, newPassword
|
||||
return errors.New("密码加密失败")
|
||||
}
|
||||
|
||||
return s.userRepo.UpdateFields(userID, map[string]interface{}{
|
||||
err = s.userRepo.UpdateFields(userID, map[string]interface{}{
|
||||
"password": hashedPassword,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 清除用户缓存
|
||||
s.cacheInv.OnUpdate(ctx, s.cacheKeys.User(userID))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) ResetPassword(email, newPassword string) error {
|
||||
func (s *userService) ResetPassword(ctx context.Context, email, newPassword string) error {
|
||||
user, err := s.userRepo.FindByEmail(email)
|
||||
if err != nil || user == nil {
|
||||
return errors.New("用户不存在")
|
||||
@@ -217,12 +261,26 @@ func (s *userServiceImpl) ResetPassword(email, newPassword string) error {
|
||||
return errors.New("密码加密失败")
|
||||
}
|
||||
|
||||
return s.userRepo.UpdateFields(user.ID, map[string]interface{}{
|
||||
err = s.userRepo.UpdateFields(user.ID, map[string]interface{}{
|
||||
"password": hashedPassword,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 清除用户缓存
|
||||
s.cacheInv.OnUpdate(ctx,
|
||||
s.cacheKeys.User(user.ID),
|
||||
s.cacheKeys.UserByEmail(email),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) ChangeEmail(userID int64, newEmail string) error {
|
||||
func (s *userService) ChangeEmail(ctx context.Context, userID int64, newEmail string) error {
|
||||
// 获取旧邮箱
|
||||
oldUser, _ := s.userRepo.FindByID(userID)
|
||||
|
||||
existingUser, err := s.userRepo.FindByEmail(newEmail)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -231,12 +289,27 @@ func (s *userServiceImpl) ChangeEmail(userID int64, newEmail string) error {
|
||||
return errors.New("邮箱已被其他用户使用")
|
||||
}
|
||||
|
||||
return s.userRepo.UpdateFields(userID, map[string]interface{}{
|
||||
err = s.userRepo.UpdateFields(userID, map[string]interface{}{
|
||||
"email": newEmail,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 清除旧邮箱和用户ID的缓存
|
||||
keysToInvalidate := []string{
|
||||
s.cacheKeys.User(userID),
|
||||
s.cacheKeys.UserByEmail(newEmail),
|
||||
}
|
||||
if oldUser != nil {
|
||||
keysToInvalidate = append(keysToInvalidate, s.cacheKeys.UserByEmail(oldUser.Email))
|
||||
}
|
||||
s.cacheInv.OnUpdate(ctx, keysToInvalidate...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) ValidateAvatarURL(avatarURL string) error {
|
||||
func (s *userService) ValidateAvatarURL(ctx context.Context, avatarURL string) error {
|
||||
if avatarURL == "" {
|
||||
return nil
|
||||
}
|
||||
@@ -272,7 +345,7 @@ func (s *userServiceImpl) ValidateAvatarURL(avatarURL string) error {
|
||||
return s.checkDomainAllowed(host, cfg.Security.AllowedDomains)
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) GetMaxProfilesPerUser() int {
|
||||
func (s *userService) GetMaxProfilesPerUser() int {
|
||||
config, err := s.configRepo.GetByKey("max_profiles_per_user")
|
||||
if err != nil || config == nil {
|
||||
return 5
|
||||
@@ -285,7 +358,7 @@ func (s *userServiceImpl) GetMaxProfilesPerUser() int {
|
||||
return value
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) GetMaxTexturesPerUser() int {
|
||||
func (s *userService) GetMaxTexturesPerUser() int {
|
||||
config, err := s.configRepo.GetByKey("max_textures_per_user")
|
||||
if err != nil || config == nil {
|
||||
return 50
|
||||
@@ -300,7 +373,7 @@ func (s *userServiceImpl) GetMaxTexturesPerUser() int {
|
||||
|
||||
// 私有辅助方法
|
||||
|
||||
func (s *userServiceImpl) getDefaultAvatar() string {
|
||||
func (s *userService) getDefaultAvatar() string {
|
||||
config, err := s.configRepo.GetByKey("default_avatar")
|
||||
if err != nil || config == nil || config.Value == "" {
|
||||
return ""
|
||||
@@ -308,7 +381,7 @@ func (s *userServiceImpl) getDefaultAvatar() string {
|
||||
return config.Value
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) checkDomainAllowed(host string, allowedDomains []string) error {
|
||||
func (s *userService) checkDomainAllowed(host string, allowedDomains []string) error {
|
||||
host = strings.ToLower(host)
|
||||
|
||||
for _, allowed := range allowedDomains {
|
||||
@@ -332,7 +405,7 @@ func (s *userServiceImpl) checkDomainAllowed(host string, allowedDomains []strin
|
||||
return errors.New("URL域名不在允许的列表中")
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) recordLoginFailure(ctx context.Context, usernameOrEmail, ipAddress, userAgent string, userID int64, reason string) {
|
||||
func (s *userService) recordLoginFailure(ctx context.Context, usernameOrEmail, ipAddress, userAgent string, userID int64, reason string) {
|
||||
if s.redis != nil {
|
||||
identifier := usernameOrEmail + ":" + ipAddress
|
||||
count, _ := RecordLoginFailure(ctx, s.redis, identifier)
|
||||
@@ -344,7 +417,7 @@ func (s *userServiceImpl) recordLoginFailure(ctx context.Context, usernameOrEmai
|
||||
s.logFailedLogin(userID, ipAddress, userAgent, reason)
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) logSuccessLogin(userID int64, ipAddress, userAgent string) {
|
||||
func (s *userService) logSuccessLogin(userID int64, ipAddress, userAgent string) {
|
||||
log := &model.UserLoginLog{
|
||||
UserID: userID,
|
||||
IPAddress: ipAddress,
|
||||
@@ -355,7 +428,7 @@ func (s *userServiceImpl) logSuccessLogin(userID int64, ipAddress, userAgent str
|
||||
_ = s.userRepo.CreateLoginLog(log)
|
||||
}
|
||||
|
||||
func (s *userServiceImpl) logFailedLogin(userID int64, ipAddress, userAgent, reason string) {
|
||||
func (s *userService) logFailedLogin(userID int64, ipAddress, userAgent, reason string) {
|
||||
log := &model.UserLoginLog{
|
||||
UserID: userID,
|
||||
IPAddress: ipAddress,
|
||||
|
||||
Reference in New Issue
Block a user