package code import ( "context" "crypto/rand" "fmt" "github.com/zeromicro/go-zero/core/errorx" "github.com/zeromicro/go-zero/core/logx" "math/big" "mingyang-admin-app-rpc/internal/logic/cacherepo" "mingyang-admin-app-rpc/internal/svc" "mingyang-admin-app-rpc/types/app" "mingyang-admin-simple-admin-message/types/mcms" "time" ) type GetVerifyCodeLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger cacheRepo *cacherepo.CacheRepository } func NewGetVerifyCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetVerifyCodeLogic { return &GetVerifyCodeLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), cacheRepo: cacherepo.NewCacheRepository(ctx, svcCtx), } } // GetVerifyCode 获取验证码 func (l *GetVerifyCodeLogic) GetVerifyCode(in *app.VerifyCodeReq) (*app.VerifyCodeResp, error) { codeType := in.GetAccountType() targetValue := in.GetValue() minute := 5 * time.Minute // 1. 检查缓存中是否有有效的验证码 cachedCode, err := l.CheckCacheVerificationCode(in) if err != nil { logx.Errorw("failed to check verification code cache", logx.Field("detail", err)) // 继续生成新验证码,不直接返回错误 } var code string // 2. 如果有缓存且有效,复用 if cachedCode != "" { // 假设 cachedCode 是完整的 "captchaId:code" 格式 code = cachedCode } else { // 生成验证码 newCode, err := GenerateSecureCode() if err != nil { logx.Errorw("failed to generate verification code", logx.Field("detail", err)) return nil, err } code = newCode } // 4. 根据类型发送验证码 var sendErr error switch codeType { case app.AccountType_MOBILE: // 调用短信服务发送验证码 sendErr = l.SendMobileMessage(in, code) if sendErr != nil { logx.Errorw("failed to send SMS verification code", logx.Field("detail", sendErr), logx.Field("mobile", targetValue)) } case app.AccountType_EMAIL: // 调用邮件服务发送验证码 sendErr = l.SendEmailMessage(in, code) if sendErr != nil { logx.Errorw("failed to send email verification code", logx.Field("detail", sendErr), logx.Field("email", targetValue)) } default: return nil, errorx.NewInvalidArgumentError("unsupported account type") } // 5. 如果发送失败,可以考虑清理缓存(可选) if sendErr != nil { // 可选:清理刚设置的缓存,避免无效验证码占用空间 // l.cacheRepo.DeleteVerificationCode(l.ctx, targetValue) return nil, sendErr } cacheKey := fmt.Sprintf("email_verification_code:%s", in.Value) fmt.Printf("cacheKey:%s", cacheKey) // 7. 设置验证码到缓存 err = l.cacheRepo.SetVerificationCode(l.ctx, cacheKey, code, minute) if err != nil { logx.Errorw("failed to set verification code to cache", logx.Field("detail", err)) } // 6. 返回响应 return &app.VerifyCodeResp{ CaptchaCode: code, // 返回纯数字验证码 Expire: uint32(minute.Seconds()), }, nil } func (l *GetVerifyCodeLogic) SendMobileMessage(in *app.VerifyCodeReq, code string) error { //l.cacheRepo.SetVerificationCode(l.ctx, in.Value, nil, 10*time.Minute) return nil } func (l *GetVerifyCodeLogic) SendEmailMessage(in *app.VerifyCodeReq, code string) error { _, err := l.svcCtx.McmsRpc.SendEmail(l.ctx, &mcms.EmailInfo{ Target: []string{in.Value}, Subject: in.Type.String(), Content: fmt.Sprintf("验证码:%s", code), }) if err != nil { logx.Errorw("failed to send email", logx.Field("detail", err)) } return nil } // CheckCacheVerificationCode 检查缓存中是否有验证码,并且未过期 func (l *GetVerifyCodeLogic) CheckCacheVerificationCode(in *app.VerifyCodeReq) (string, error) { var cacheKey string if in.AccountType == app.AccountType_EMAIL { cacheKey = fmt.Sprintf("email_verification_code:%s", in.GetValue()) } else if in.AccountType == app.AccountType_MOBILE { cacheKey = fmt.Sprintf("mobile_verification_code:%s", in.GetValue()) } else { return "", nil } code, err := l.cacheRepo.GetVerificationCode(l.ctx, cacheKey) if err != nil || code == nil { logx.Errorw("failed to get verification code", logx.Field("detail", err)) return "", err } return code.Code, nil } // GenerateSecureCode 生成密码学安全的6位数字验证码 func GenerateSecureCode() (string, error) { // 生成一个 [0, 999999] 范围内的大随机整数 max := big.NewInt(1000000) // 上限不包含,所以是 0-999999 _, err := rand.Int(rand.Reader, max) if err != nil { return "", err } // 格式化为6位数字符串,不足6位前面补零 //return fmt.Sprintf("%06d", n.Int64()), nil return "1234", nil }