// // validate.go // Copyright (C) 2025 veypi // // Distributed under terms of the MIT license. // package sms import ( "fmt" "time" "github.com/veypi/OneAuth/cfg" "github.com/veypi/OneAuth/libs/utils" "github.com/veypi/OneAuth/models" "gorm.io/gorm" ) // VerifyCode 验证验证码 func VerifyCode(Phone, Code, Region, Purpose string) error { normalizedPhone := utils.NormalizePhoneNumber(Phone) // 1. 查找最新的待验证码 var smsCode models.SMSCode err := cfg.DB().Where("phone = ? AND region = ? AND purpose = ? AND status = ?", normalizedPhone, Region, Purpose, models.CodeStatusPending). Order("created_at DESC"). First(&smsCode).Error if err != nil { if err == gorm.ErrRecordNotFound { return fmt.Errorf("verification code not found or already used") } return fmt.Errorf("failed to query sms code: %w", err) } // 2. 检查验证码是否过期 if smsCode.IsExpired() { cfg.DB().Model(&smsCode).Updates(map[string]any{ "status": models.CodeStatusExpired, }) return fmt.Errorf("verification code has expired") } // 3. 检查是否已达到最大尝试次数 if !smsCode.CanRetry(cfg.Config.SMS.Global.MaxAttempts) { cfg.DB().Model(&smsCode).Updates(map[string]any{ "status": models.CodeStatusFailed, }) return fmt.Errorf("verification failed too many times") } // 4. 验证码不匹配 if smsCode.Code != Code { cfg.DB().Model(&smsCode).Updates(map[string]any{ "attempts": smsCode.Attempts + 1, }) remaining := cfg.Config.SMS.Global.MaxAttempts - smsCode.Attempts - 1 return fmt.Errorf("verification code incorrect, %d attempts remaining", remaining) } // 5. 验证成功 now := time.Now() cfg.DB().Model(&smsCode).Updates(map[string]any{ "status": models.CodeStatusUsed, "used_at": &now, }) return nil }