package auth import ( "crypto/rand" "encoding/base64" "errors" "eta/eta_mini_ht_api/common/component/cache" logger "eta/eta_mini_ht_api/common/component/log" "eta/eta_mini_ht_api/common/component/wechat" "eta/eta_mini_ht_api/common/exception" authUtils "eta/eta_mini_ht_api/common/utils/auth" "eta/eta_mini_ht_api/common/utils/jwt" "eta/eta_mini_ht_api/common/utils/redis" "eta/eta_mini_ht_api/common/utils/sms" stringUtils "eta/eta_mini_ht_api/common/utils/string" smsService "eta/eta_mini_ht_api/domian/sms" userService "eta/eta_mini_ht_api/domian/user" "fmt" "gorm.io/gorm" "strings" ) var ( smsSender sms.SMSClient wechatClient *wechat.Client redisCache *cache.RedisCache ) func message() sms.SMSClient { if smsSender == nil { smsSender = sms.GetInstance() } return smsSender } func wx() *wechat.Client { if wechatClient == nil { wechatClient = wechat.GetInstance() } return wechatClient } func SendSMSCode(mobile string) (err error) { code := rd().GetString(redis.GenerateSmsKey(mobile)) if stringUtils.IsEmptyOrNil(code) { code, err = authUtils.GenerateCode(6) if err != nil { logger.Warn("生成验证码失败:%v", err) return exception.New(exception.SMSCodeGenerateFailed) } codeDTO := smsService.CodeDTO{ Mobile: mobile, Code: code, ExpireMinute: message().GetExpireMinute(), } //消息domain层 var smid int smid, err = smsService.SendSMSCode(codeDTO) if err != nil { logger.Error("发送短信失败:%v", err) err = exception.New(exception.SendingSMSFailed) } logger.Debug("验证码:%v", code) _, err = message().SendSms(mobile, code, smid) if err != nil { logger.Error("发送短信失败:%v", err) err = exception.New(exception.SendingSMSFailed) } return } return exception.New(exception.SMSCodeAlreadySent) } func CheckUser(mobile string, code string) (err error) { smsCode := rd().GetString(redis.GenerateSmsKey(mobile)) if stringUtils.IsEmptyOrNil(smsCode) { logger.Warn("验证码已过期:%v", code) codeDTO := smsService.CodeDTO{ Mobile: mobile, Code: code, } _ = smsService.TryExpireCode(codeDTO) err = exception.New(exception.SMSCodeExpired) return } if smsCode != code { err = exception.New(exception.SMSCodeError) return } return nil } type LoginDTO struct { VerifyCode string Code string Mobile string } func Login(login LoginDTO) (token string, err error) { var user userService.UserDTO user, err = userService.GetUserByMobile(login.Mobile) if err != nil { if !errors.Is(err, gorm.ErrRecordNotFound) { err = exception.New(exception.UnknownError) return } //注册用户 var wechatInfo wechat.WxUser //微信请求异常 wechatInfo, err = wx().Login(login.Code) //微信客户端的异常不做处理,已经是EtaError if err != nil { return } user = initUser(wechatInfo, login) err = userService.RegisterTemplateUser(&user) if err != nil { err = exception.New(exception.TemplateUserCreateFailed) return } } token, err = jwt.CreateToken(user.OpenId, user.Mobile) if err != nil { err = exception.New(exception.GenerateTokenFailed) return } err = rd().SetString(redis.GenerateTokenKey(user.Mobile), token, 90*24*60*60) if err != nil { err = exception.New(exception.GenerateTokenFailed) return } codeDTO := smsService.CodeDTO{ Mobile: login.Mobile, Code: login.VerifyCode, } //登录成功删除短信验证码,数据库留痕 err = rd().Delete(redis.GenerateSmsKey(login.Mobile)) if err != nil { logger.Error("清除redis 短信验证码失败,%v", err) _ = rd().SetString(redis.GenerateSmsKey(login.Mobile), "", 1) return "", err } _ = smsService.TryVerifiedCode(codeDTO) return } func BindMobile(userId int, mobile, verifyCode string) (err error) { err = userService.BindUserMobile(userId, mobile) if err != nil { err = exception.New(exception.BindMobileFailed) return } codeDTO := smsService.CodeDTO{ Mobile: mobile, Code: verifyCode, } //登录成功删除短信验证码,数据库留痕 err = rd().Delete(redis.GenerateSmsKey(mobile)) if err != nil { logger.Error("清除redis 短信验证码失败,%v", err) _ = rd().SetString(redis.GenerateSmsKey(mobile), "", 1) return err } _ = smsService.TryVerifiedCode(codeDTO) return } func RefreshToken(code string) (token string, isBindMobile int, err error) { //注册用户 var wechatInfo wechat.WxUser //微信请求异常 wechatInfo, err = wx().Login(code) //微信客户端的异常不做处理,已经是EtaError if err != nil { fmt.Println("wx.Login,err:" + err.Error()) return } user, err := userService.GetTemplateUserByOpenId(wechatInfo.OpenId) var isAdd bool if err != nil { if err == gorm.ErrRecordNotFound { isAdd = true } else { err = exception.New(exception.TemplateUserNotFound) return } } token, err = jwt.CreateToken(wechatInfo.OpenId, wechatInfo.OpenId) if err != nil { err = exception.New(exception.GenerateTokenFailed) return } err = rd().SetString(redis.GenerateTokenKey(wechatInfo.OpenId), token, 90*24*60*60) if err != nil { err = exception.New(exception.GenerateTokenFailed) return } if user.Mobile == "" { isBindMobile = 0 } else { isBindMobile = 1 } if isAdd { user.OpenId = wechatInfo.OpenId user.UnionId = wechatInfo.UnionId err = userService.RegisterTemplateUser(&user) } return } func GetAreaCodes() (list []userService.AreaCodeDTO, err error) { list, err = userService.GetAreaCodes() if err != nil { err = exception.New(exception.GetAreaCodesFailed) } return } func GetValidAreaCodes() (list []string) { str := rd().GetString(redis.ValidAreaCode) if str == "" { codes, err := userService.GetValidAreaCodes() if err != nil { return []string{} } var validCodes []string for _, code := range codes { validCodes = append(validCodes, code.CodeValue) } err = rd().SetString(redis.ValidAreaCode, strings.Join(validCodes, ","), 60*60) if err != nil { logger.Error("设置Redis区号失败:%v", err) list = validCodes } list = strings.Split(rd().GetString(redis.ValidAreaCode), ",") } else { list = strings.Split(str, ",") } return } func initUser(user wechat.WxUser, dto LoginDTO) userService.UserDTO { username := generateRandomUsername(user.OpenId) if stringUtils.IsBlank(username) { username = dto.Mobile } return userService.UserDTO{ OpenId: user.OpenId, UnionId: user.UnionId, Username: username, Mobile: dto.Mobile, } } // GenerateRandomUsername 生成基于UnionId的随机用户名 func generateRandomUsername(openId string) string { // 生成一个随机的4字节二进制数据 randomBytes := make([]byte, 4) _, err := rand.Read(randomBytes) if err != nil { return "" } // 将随机字节转换为base64编码的URL安全字符串 // 并去掉可能出现的等于号 randomStr := strings.TrimRight(base64.URLEncoding.EncodeToString(randomBytes), "=") // 拼接UnionId和随机字符串 username := fmt.Sprintf("%s_%s", openId, randomStr) return username } func rd() *cache.RedisCache { if redisCache == nil { redisCache = cache.GetInstance() } return redisCache }