|
@@ -0,0 +1,182 @@
|
|
|
+package auth
|
|
|
+
|
|
|
+import (
|
|
|
+ "crypto/rand"
|
|
|
+ "encoding/base64"
|
|
|
+ "errors"
|
|
|
+ "eta_mini_ht_api/common/component/cache"
|
|
|
+ logger "eta_mini_ht_api/common/component/log"
|
|
|
+ "eta_mini_ht_api/common/component/wechat"
|
|
|
+ "eta_mini_ht_api/common/exception"
|
|
|
+ authUtils "eta_mini_ht_api/common/utils/auth"
|
|
|
+ "eta_mini_ht_api/common/utils/jwt"
|
|
|
+ "eta_mini_ht_api/common/utils/redis"
|
|
|
+ "eta_mini_ht_api/common/utils/sms"
|
|
|
+ stringUtils "eta_mini_ht_api/common/utils/string"
|
|
|
+ smsService "eta_mini_ht_api/domian/sms"
|
|
|
+ userService "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 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
|
|
|
+}
|