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:
@@ -24,22 +24,25 @@ const (
|
||||
CodeRateLimit = 1 * time.Minute // 发送频率限制
|
||||
)
|
||||
|
||||
// GenerateVerificationCode 生成6位数字验证码
|
||||
func GenerateVerificationCode() (string, error) {
|
||||
const digits = "0123456789"
|
||||
code := make([]byte, CodeLength)
|
||||
for i := range code {
|
||||
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(digits))))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
code[i] = digits[num.Int64()]
|
||||
}
|
||||
return string(code), nil
|
||||
// verificationService VerificationService的实现
|
||||
type verificationService struct {
|
||||
redis *redis.Client
|
||||
emailService *email.Service
|
||||
}
|
||||
|
||||
// SendVerificationCode 发送验证码
|
||||
func SendVerificationCode(ctx context.Context, redisClient *redis.Client, emailService *email.Service, email, codeType string) error {
|
||||
// NewVerificationService 创建VerificationService实例
|
||||
func NewVerificationService(
|
||||
redisClient *redis.Client,
|
||||
emailService *email.Service,
|
||||
) VerificationService {
|
||||
return &verificationService{
|
||||
redis: redisClient,
|
||||
emailService: emailService,
|
||||
}
|
||||
}
|
||||
|
||||
// SendCode 发送验证码
|
||||
func (s *verificationService) SendCode(ctx context.Context, email, codeType string) error {
|
||||
// 测试环境下直接跳过,不存储也不发送
|
||||
cfg, err := config.GetConfig()
|
||||
if err == nil && cfg.IsTestEnvironment() {
|
||||
@@ -48,7 +51,7 @@ func SendVerificationCode(ctx context.Context, redisClient *redis.Client, emailS
|
||||
|
||||
// 检查发送频率限制
|
||||
rateLimitKey := fmt.Sprintf("verification:rate_limit:%s:%s", codeType, email)
|
||||
exists, err := redisClient.Exists(ctx, rateLimitKey)
|
||||
exists, err := s.redis.Exists(ctx, rateLimitKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("检查发送频率失败: %w", err)
|
||||
}
|
||||
@@ -57,26 +60,26 @@ func SendVerificationCode(ctx context.Context, redisClient *redis.Client, emailS
|
||||
}
|
||||
|
||||
// 生成验证码
|
||||
code, err := GenerateVerificationCode()
|
||||
code, err := s.generateCode()
|
||||
if err != nil {
|
||||
return fmt.Errorf("生成验证码失败: %w", err)
|
||||
}
|
||||
|
||||
// 存储验证码到Redis
|
||||
codeKey := fmt.Sprintf("verification:code:%s:%s", codeType, email)
|
||||
if err := redisClient.Set(ctx, codeKey, code, CodeExpiration); err != nil {
|
||||
if err := s.redis.Set(ctx, codeKey, code, CodeExpiration); err != nil {
|
||||
return fmt.Errorf("存储验证码失败: %w", err)
|
||||
}
|
||||
|
||||
// 设置发送频率限制
|
||||
if err := redisClient.Set(ctx, rateLimitKey, "1", CodeRateLimit); err != nil {
|
||||
if err := s.redis.Set(ctx, rateLimitKey, "1", CodeRateLimit); err != nil {
|
||||
return fmt.Errorf("设置发送频率限制失败: %w", err)
|
||||
}
|
||||
|
||||
// 发送邮件
|
||||
if err := sendVerificationEmail(emailService, email, code, codeType); err != nil {
|
||||
if err := s.sendEmail(email, code, codeType); err != nil {
|
||||
// 发送失败,删除验证码
|
||||
_ = redisClient.Del(ctx, codeKey)
|
||||
_ = s.redis.Del(ctx, codeKey)
|
||||
return fmt.Errorf("发送邮件失败: %w", err)
|
||||
}
|
||||
|
||||
@@ -84,7 +87,7 @@ func SendVerificationCode(ctx context.Context, redisClient *redis.Client, emailS
|
||||
}
|
||||
|
||||
// VerifyCode 验证验证码
|
||||
func VerifyCode(ctx context.Context, redisClient *redis.Client, email, code, codeType string) error {
|
||||
func (s *verificationService) VerifyCode(ctx context.Context, email, code, codeType string) error {
|
||||
// 测试环境下直接通过验证
|
||||
cfg, err := config.GetConfig()
|
||||
if err == nil && cfg.IsTestEnvironment() {
|
||||
@@ -92,7 +95,7 @@ func VerifyCode(ctx context.Context, redisClient *redis.Client, email, code, cod
|
||||
}
|
||||
|
||||
// 检查是否被锁定
|
||||
locked, ttl, err := CheckVerifyLocked(ctx, redisClient, email, codeType)
|
||||
locked, ttl, err := CheckVerifyLocked(ctx, s.redis, email, codeType)
|
||||
if err == nil && locked {
|
||||
return fmt.Errorf("验证码错误次数过多,请在 %d 分钟后重试", int(ttl.Minutes())+1)
|
||||
}
|
||||
@@ -100,10 +103,10 @@ func VerifyCode(ctx context.Context, redisClient *redis.Client, email, code, cod
|
||||
codeKey := fmt.Sprintf("verification:code:%s:%s", codeType, email)
|
||||
|
||||
// 从Redis获取验证码
|
||||
storedCode, err := redisClient.Get(ctx, codeKey)
|
||||
storedCode, err := s.redis.Get(ctx, codeKey)
|
||||
if err != nil {
|
||||
// 记录失败尝试并检查是否触发锁定
|
||||
count, _ := RecordVerifyFailure(ctx, redisClient, email, codeType)
|
||||
count, _ := RecordVerifyFailure(ctx, s.redis, email, codeType)
|
||||
if count >= MaxVerifyAttempts {
|
||||
return fmt.Errorf("验证码错误次数过多,账号已被锁定 %d 分钟", int(VerifyLockDuration.Minutes()))
|
||||
}
|
||||
@@ -117,7 +120,7 @@ func VerifyCode(ctx context.Context, redisClient *redis.Client, email, code, cod
|
||||
// 验证验证码
|
||||
if storedCode != code {
|
||||
// 记录失败尝试并检查是否触发锁定
|
||||
count, _ := RecordVerifyFailure(ctx, redisClient, email, codeType)
|
||||
count, _ := RecordVerifyFailure(ctx, s.redis, email, codeType)
|
||||
if count >= MaxVerifyAttempts {
|
||||
return fmt.Errorf("验证码错误次数过多,账号已被锁定 %d 分钟", int(VerifyLockDuration.Minutes()))
|
||||
}
|
||||
@@ -129,28 +132,42 @@ func VerifyCode(ctx context.Context, redisClient *redis.Client, email, code, cod
|
||||
}
|
||||
|
||||
// 验证成功,删除验证码和失败计数
|
||||
_ = redisClient.Del(ctx, codeKey)
|
||||
_ = ClearVerifyAttempts(ctx, redisClient, email, codeType)
|
||||
_ = s.redis.Del(ctx, codeKey)
|
||||
_ = ClearVerifyAttempts(ctx, s.redis, email, codeType)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteVerificationCode 删除验证码
|
||||
// generateCode 生成6位数字验证码
|
||||
func (s *verificationService) generateCode() (string, error) {
|
||||
const digits = "0123456789"
|
||||
code := make([]byte, CodeLength)
|
||||
for i := range code {
|
||||
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(digits))))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
code[i] = digits[num.Int64()]
|
||||
}
|
||||
return string(code), nil
|
||||
}
|
||||
|
||||
// sendEmail 根据类型发送邮件
|
||||
func (s *verificationService) sendEmail(to, code, codeType string) error {
|
||||
switch codeType {
|
||||
case VerificationTypeRegister:
|
||||
return s.emailService.SendEmailVerification(to, code)
|
||||
case VerificationTypeResetPassword:
|
||||
return s.emailService.SendResetPassword(to, code)
|
||||
case VerificationTypeChangeEmail:
|
||||
return s.emailService.SendChangeEmail(to, code)
|
||||
default:
|
||||
return s.emailService.SendVerificationCode(to, code, codeType)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteVerificationCode 删除验证码(工具函数,保持向后兼容)
|
||||
func DeleteVerificationCode(ctx context.Context, redisClient *redis.Client, email, codeType string) error {
|
||||
codeKey := fmt.Sprintf("verification:code:%s:%s", codeType, email)
|
||||
return redisClient.Del(ctx, codeKey)
|
||||
}
|
||||
|
||||
// sendVerificationEmail 根据类型发送邮件
|
||||
func sendVerificationEmail(emailService *email.Service, to, code, codeType string) error {
|
||||
switch codeType {
|
||||
case VerificationTypeRegister:
|
||||
return emailService.SendEmailVerification(to, code)
|
||||
case VerificationTypeResetPassword:
|
||||
return emailService.SendResetPassword(to, code)
|
||||
case VerificationTypeChangeEmail:
|
||||
return emailService.SendChangeEmail(to, code)
|
||||
default:
|
||||
return emailService.SendVerificationCode(to, code, codeType)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user