temp
This commit is contained in:
@@ -43,83 +43,132 @@ func NewSMSCodeService(
|
||||
|
||||
// SendCode 发送验证码
|
||||
func (s *SMSCodeService) SendCode(ctx context.Context, phone string, scene entities.SMSScene, clientIP, userAgent string) error {
|
||||
// 检查频率限制
|
||||
// 1. 检查频率限制
|
||||
if err := s.checkRateLimit(ctx, phone); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 生成验证码
|
||||
// 2. 生成验证码
|
||||
code := s.smsClient.GenerateCode(s.config.CodeLength)
|
||||
|
||||
// 创建SMS验证码记录
|
||||
smsCode := &entities.SMSCode{
|
||||
ID: uuid.New().String(),
|
||||
Phone: phone,
|
||||
Code: code,
|
||||
Scene: scene,
|
||||
IP: clientIP,
|
||||
UserAgent: userAgent,
|
||||
Used: false,
|
||||
ExpiresAt: time.Now().Add(s.config.ExpireTime),
|
||||
// 3. 使用工厂方法创建SMS验证码记录
|
||||
smsCode, err := entities.NewSMSCode(phone, code, scene, s.config.ExpireTime, clientIP, userAgent)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建验证码记录失败: %w", err)
|
||||
}
|
||||
|
||||
// 保存验证码
|
||||
// 4. 设置ID
|
||||
smsCode.ID = uuid.New().String()
|
||||
|
||||
// 5. 保存验证码
|
||||
if err := s.repo.Create(ctx, smsCode); err != nil {
|
||||
s.logger.Error("保存短信验证码失败",
|
||||
zap.String("phone", phone),
|
||||
zap.String("scene", string(scene)),
|
||||
zap.String("phone", smsCode.GetMaskedPhone()),
|
||||
zap.String("scene", smsCode.GetSceneName()),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("保存验证码失败: %w", err)
|
||||
}
|
||||
|
||||
// 发送短信
|
||||
// 6. 发送短信
|
||||
if err := s.smsClient.SendVerificationCode(ctx, phone, code); err != nil {
|
||||
// 记录发送失败但不删除验证码记录,让其自然过期
|
||||
s.logger.Error("发送短信验证码失败",
|
||||
zap.String("phone", phone),
|
||||
zap.String("code", code),
|
||||
zap.String("phone", smsCode.GetMaskedPhone()),
|
||||
zap.String("code", smsCode.GetMaskedCode()),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("短信发送失败: %w", err)
|
||||
}
|
||||
|
||||
// 更新发送记录缓存
|
||||
// 7. 更新发送记录缓存
|
||||
s.updateSendRecord(ctx, phone)
|
||||
|
||||
s.logger.Info("短信验证码发送成功",
|
||||
zap.String("phone", phone),
|
||||
zap.String("scene", string(scene)))
|
||||
zap.String("phone", smsCode.GetMaskedPhone()),
|
||||
zap.String("scene", smsCode.GetSceneName()),
|
||||
zap.String("remaining_time", smsCode.GetRemainingTime().String()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyCode 验证验证码
|
||||
func (s *SMSCodeService) VerifyCode(ctx context.Context, phone, code string, scene entities.SMSScene) error {
|
||||
// 根据手机号和场景获取有效的验证码记录
|
||||
// 1. 根据手机号和场景获取有效的验证码记录
|
||||
smsCode, err := s.repo.GetValidCode(ctx, phone, scene)
|
||||
if err != nil {
|
||||
return fmt.Errorf("验证码无效或已过期")
|
||||
}
|
||||
|
||||
// 验证验证码是否匹配
|
||||
if smsCode.Code != code {
|
||||
return fmt.Errorf("验证码无效或已过期")
|
||||
// 2. 使用实体的验证方法
|
||||
if err := smsCode.VerifyCode(code); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 标记验证码为已使用
|
||||
if err := s.repo.MarkAsUsed(ctx, smsCode.ID); err != nil {
|
||||
s.logger.Error("标记验证码为已使用失败",
|
||||
// 3. 保存更新后的验证码状态
|
||||
if err := s.repo.Update(ctx, smsCode); err != nil {
|
||||
s.logger.Error("更新验证码状态失败",
|
||||
zap.String("code_id", smsCode.ID),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("验证码状态更新失败")
|
||||
}
|
||||
|
||||
s.logger.Info("短信验证码验证成功",
|
||||
zap.String("phone", phone),
|
||||
zap.String("scene", string(scene)))
|
||||
zap.String("phone", smsCode.GetMaskedPhone()),
|
||||
zap.String("scene", smsCode.GetSceneName()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanResendCode 检查是否可以重新发送验证码
|
||||
func (s *SMSCodeService) CanResendCode(ctx context.Context, phone string, scene entities.SMSScene) (bool, error) {
|
||||
// 1. 获取最近的验证码记录
|
||||
recentCode, err := s.repo.GetRecentCode(ctx, phone, scene)
|
||||
if err != nil {
|
||||
// 如果没有记录,可以发送
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 2. 使用实体的方法检查是否可以重新发送
|
||||
canResend := recentCode.CanResend(s.config.RateLimit.MinInterval)
|
||||
|
||||
// 3. 记录检查结果
|
||||
if !canResend {
|
||||
remainingTime := s.config.RateLimit.MinInterval - time.Since(recentCode.CreatedAt)
|
||||
s.logger.Info("验证码发送频率限制",
|
||||
zap.String("phone", recentCode.GetMaskedPhone()),
|
||||
zap.String("scene", recentCode.GetSceneName()),
|
||||
zap.Duration("remaining_wait_time", remainingTime))
|
||||
}
|
||||
|
||||
return canResend, nil
|
||||
}
|
||||
|
||||
// GetCodeStatus 获取验证码状态信息
|
||||
func (s *SMSCodeService) GetCodeStatus(ctx context.Context, phone string, scene entities.SMSScene) (map[string]interface{}, error) {
|
||||
// 1. 获取最近的验证码记录
|
||||
recentCode, err := s.repo.GetRecentCode(ctx, phone, scene)
|
||||
if err != nil {
|
||||
return map[string]interface{}{
|
||||
"has_code": false,
|
||||
"message": "没有找到验证码记录",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 2. 构建状态信息
|
||||
status := map[string]interface{}{
|
||||
"has_code": true,
|
||||
"is_valid": recentCode.IsValid(),
|
||||
"is_expired": recentCode.IsExpired(),
|
||||
"is_used": recentCode.Used,
|
||||
"remaining_time": recentCode.GetRemainingTime().String(),
|
||||
"scene": recentCode.GetSceneName(),
|
||||
"can_resend": recentCode.CanResend(s.config.RateLimit.MinInterval),
|
||||
"created_at": recentCode.CreatedAt,
|
||||
"security_info": recentCode.GetSecurityInfo(),
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// checkRateLimit 检查发送频率限制
|
||||
func (s *SMSCodeService) checkRateLimit(ctx context.Context, phone string) error {
|
||||
now := time.Now()
|
||||
|
||||
Reference in New Issue
Block a user