package internal import ( "context" "encoding/json" "fmt" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/os/glog" rand "math/rand" "strings" "sync" "time" "yx-dataset-server/app/errors" "yx-dataset-server/app/model" "yx-dataset-server/app/schema" "yx-dataset-server/library/logger" "yx-dataset-server/library/redis" "yx-dataset-server/library/sms" "yx-dataset-server/library/utils" ) // NewVerifyCode 创建VerifyCode func NewVerifyCode( mUser model.IUser, ) *VerifyCode { return &VerifyCode{ userModel: mUser, } } // VerifyCode 创建VerifyCode对象 type VerifyCode struct { sync.RWMutex userModel model.IUser } func (a *VerifyCode) getKey(businessType int, tel, storeType string) string { return utils.Base64Encode(fmt.Sprintf("%d%s%s", businessType, tel, storeType)) } // Check 检查手机号及验证码的有效性 func (a *VerifyCode) Check(ctx context.Context, req schema.VerifyCodeCheck) (bool, error) { if req.Tel == "" || len(req.Tel) < 11 { return false, errors.New("请输入合法的手机号") } var businessType string switch req.BusinessType { case 1: businessType = "login" case 2: businessType = "changePass" default: return false, errors.New("业务类型参数不正确") } if req.Token == "" { req.Token = fmt.Sprintf("%s:%s:%s", businessType, "code", req.Tel) } val, err := redis.GetClient().Get(ctx, req.Token) if err != nil { return false, errors.ErrBadRequest } else if val == "" { return false, errors.New("验证码已失效,请重新获取验证码") } var item schema.VerifyCodeItem if err := json.Unmarshal([]byte(val), &item); err != nil { return false, err } return item.Code == req.Code && item.BusinessType == req.BusinessType, nil } // SendCode 发送短信验证码 func (a *VerifyCode) ALiYunSendSms(ctx context.Context, req schema.VerifyCodeRequest) (*schema.VerifyCodeResponse, error) { a.Lock() defer a.Unlock() switch req.Bzty { case 1: req.TemplateId = utils.GetConfig("aliyun_sms.loginTemplateCode").String() req.BusinessTypeStr = "login" case 2: req.TemplateId = utils.GetConfig("aliyun_sms.passwordTemplateCode").String() req.BusinessTypeStr = "changePass" } telStoreKey := fmt.Sprintf("%s:%s", "telCount", req.Tel) telStoreData, err := redis.GetClient().Get(ctx, telStoreKey) if err != nil { return nil, errors.ErrBadRequest } if telStoreData != "" { var telStoreItem schema.StoreCountItem _ = json.NewDecoder(strings.NewReader(telStoreData)).Decode(&telStoreItem) if telStoreItem.Count >= 50 { return nil, errors.New("该手机号已被限制获取验证码,请于次日重试") } req.TelCount = telStoreItem.Count } codeKey := fmt.Sprintf("%s:%s:%s", req.BusinessTypeStr, "code", req.Tel) err = a.aliyunSend(ctx, req) if err != nil { return nil, err } logger.Printf(ctx, "短信发送成功:[手机号:%s]", req.Tel[7:11]) return &schema.VerifyCodeResponse{ Token: codeKey, }, nil } func (a *VerifyCode) aliyunSend(ctx context.Context, req schema.VerifyCodeRequest) error { if req.Batch { return sms.SendBatchSms(sms.ALiYunSendBatchSmsArgs{ TemplateCode: req.TemplateId, TemplateParam: req.ParamsJson, PhoneNumbers: req.Tel, }) } // 随机生成6位验证码 code := fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000)) paramJson := gjson.New("") _ = paramJson.Set("code", code) glog.Infof(ctx, "验证码发送成功[手机号%s,验证码:%s]", req.Tel, code) //发送短信验证码 err := sms.SendSms(sms.ALiYunSendSmsArgs{ TemplateCode: req.TemplateId, TemplateParam: paramJson.MustToJsonString(), PhoneNumbers: req.Tel, }) if err != nil { return err } expired := time.Minute * 5 resendLimit := time.Second * 60 // 存储重发限制 resendLimitKey := fmt.Sprintf("%s:%s:%s", req.BusinessTypeStr, "checkResend", req.Tel) err = redis.GetClient().Set(ctx, resendLimitKey, 1, resendLimit) if err != nil { return errors.ErrBadRequest } // 存储验证码 codeKey := fmt.Sprintf("%s:%s:%s", req.BusinessTypeStr, "code", req.Tel) codeData := schema.VerifyCodeItem{ Code: code, BusinessType: req.Bzty, } err = redis.GetClient().Set(ctx, codeKey, codeData, expired) if err != nil { return errors.ErrBadRequest } return nil }