123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- 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"
- userDao "eta/eta_mini_ht_api/models/user"
- "fmt"
- "gorm.io/gorm"
- "strings"
- )
- var (
- smsSender sms.SMSClient
- wechatClient *wechat.Client
- redisCache *cache.RedisCache
- )
- const (
- emptyPhoneNum = "-"
- )
- 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)
- code := rd().GetString(redis.GenerateSmsKey(mobile))
- if stringUtils.IsEmptyOrNil(code) {
- logger.Info("更新验证码 手机号[%v],旧验证码[%v]", mobile, code)
- }
- code, err = authUtils.GenerateCode(6)
- logger.Info("更新验证码 手机号[%v],新验证码[%v]", mobile, code)
- 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
- }
- 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 Logout(userId int, openId string) (err error) {
- //设置用户为登录状态
- err = userService.UserLogout(userId)
- if err != nil {
- logger.Error("用户退出登录失败:%v", err)
- err = exception.New(exception.LogoutFailed)
- return
- }
- _ = userService.UserLogoutFlow(userId)
- err = rd().Delete(redis.GenerateTokenKey(openId))
- if err != nil {
- err = rd().SetString(redis.GenerateTokenKey(openId), "", 1)
- }
- if err != nil {
- logger.Error("用户退出登录失败:%v", err)
- err = exception.New(exception.LogoutFailed)
- return
- }
- return
- }
- func Login(userId int, openId, mobile, verifyCode string) (token string, err error) {
- //设置用户为登录状态
- err = userService.UserLogin(userId)
- if err != nil {
- logger.Error("用户登录失败:%v", err)
- err = exception.New(exception.GenerateTokenFailed)
- return
- }
- _ = userService.UserLoginFlow(userId)
- token, err = jwt.CreateToken(openId, mobile, jwt.AccessToken)
- if err != nil {
- err = exception.New(exception.GenerateTokenFailed)
- return
- }
- //保险先删一下
- _ = rd().Delete(redis.GenerateTokenKey(openId))
- err = rd().SetString(redis.GenerateTokenKey(openId), token, 90*24*60*60)
- if err != nil {
- err = exception.New(exception.GenerateTokenFailed)
- return
- }
- //清除手机验证码
- codeDTO := smsService.CodeDTO{
- Mobile: mobile,
- Code: verifyCode,
- }
- //登录成功删除短信验证码,数据库留痕
- err = rd().Delete(redis.GenerateSmsKey(mobile))
- if err != nil {
- logger.Error("清除redis 短信验证码失败,%v", err)
- err = rd().SetString(redis.GenerateSmsKey(mobile), "", 1)
- return
- }
- _ = smsService.TryVerifiedCode(codeDTO)
- return
- }
- func BindMobile(userId int, mobile string, areaCode string) (err error) {
- err = userService.BindUserMobile(userId, mobile, areaCode)
- if err != nil {
- err = exception.New(exception.BindMobileFailed)
- return
- }
- return
- }
- func RefreshToken(code string) (token string, isBindMobile int, status string, tokenType string, err error) {
- //注册用户
- var wechatInfo wechat.WxUser
- //微信请求异常
- wechatInfo, err = wx().Login(code)
- //微信客户端的异常不做处理,已经是EtaError
- if err != nil {
- logger.Error("获取微信用户信息失败,错误信息:%v", err)
- return
- }
- user, err := userService.GetTemplateUserByOpenId(wechatInfo.OpenId)
- if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
- err = exception.New(exception.TemplateUserFoundFailed)
- return
- }
- //用户记录不存在则返回unbind
- //未注册/未绑定
- var mobile string
- if errors.Is(err, gorm.ErrRecordNotFound) || user.Mobile == "" {
- status = string(userService.UnBind)
- tokenType = jwt.GuestToken
- isBindMobile = 0
- mobile = emptyPhoneNum
- } else {
- isBindMobile = 1
- //已绑定(登出)
- if user.LoginStatus == userDao.Logout {
- tokenType = jwt.GuestToken
- mobile = emptyPhoneNum
- status = string(userService.Logout)
- } else {
- //已绑定(登录)
- tokenType = jwt.AccessToken
- mobile = user.Mobile
- status = string(userService.Login)
- }
- }
- //通过unionId 查询用户是否已注册,存在是因为先发生了公众号的关注,所以只需要绑定小程序openId就行
- wxUser, err := userService.GetTemplateUserByUnionId(wechatInfo.UnionId)
- if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
- err = exception.New(exception.TemplateUserFoundFailed)
- return
- }
- if errors.Is(err, gorm.ErrRecordNotFound) {
- user.OpenId = wechatInfo.OpenId
- user.UnionId = wechatInfo.UnionId
- err = userService.RegisterTemplateUser(&user)
- if err != nil {
- err = exception.New(exception.TemplateUserCreateFailed)
- return
- }
- } else { //修改微信小程序openid
- err = userService.BindUserXcxOpenId(wxUser.Id, user.OpenId)
- if err != nil {
- err = exception.New(exception.TemplateUserBindFailed)
- return
- }
- }
- token, err = jwt.CreateToken(wechatInfo.OpenId, mobile, tokenType)
- if err != nil {
- logger.Error("生成token失败:%v", err)
- 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
- }
- 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
- }
- func BindXcxOpenId(userId int, openId string) (err error) {
- err = userService.BindUserXcxOpenId(userId, openId)
- return
- }
- // 绑定微信公众号
- func BindWxGzh(code string) (isBind bool, err error) {
- //注册用户
- wxUser, err := GetWxUserInfo(code)
- if err != nil {
- logger.Error("获取微信公众号用户信息失败" + err.Error())
- err = exception.New(exception.TemplateUserNotFound)
- return
- }
- logger.Info("GetWxUserInfo:")
- if wxUser.ErrCode != 0 {
- logger.Error("获取微信公众号用户信息失败" + wxUser.ErrMsg)
- err = exception.New(exception.TemplateUserNotFound)
- return
- }
- return BindWxGzhByOpenId(wxUser.OpenId)
- }
- // BindWxGzhByOpenId
- // @Description: 通过openid绑定微信公众号
- // @author: Roc
- // @datetime 2024-08-13 13:19:49
- // @param openId string
- // @return isBind bool
- // @return err error
- func BindWxGzhByOpenId(openId string) (isBind bool, err error) {
- wxUserDetail, err := GetWxUserDetail(openId)
- if err != nil {
- logger.Error("获取微信公众号用户详细信息失败" + err.Error())
- err = exception.New(exception.WeChatResponseError)
- return
- }
- unionId := wxUserDetail.UnionID
- followingGzh := int(wxUserDetail.Subscribe)
- user, err := userService.GetTemplateUserByGzhOpenId(openId)
- if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
- err = exception.New(exception.TemplateUserFoundFailed)
- return
- }
- if errors.Is(err, gorm.ErrRecordNotFound) {
- user.GzhOpenId = openId
- user.UnionId = unionId
- user.FollowingGzh = followingGzh
- //判断unionid是否存在
- wxUser, unionErr := userService.GetTemplateUserByUnionId(unionId)
- if unionErr != nil && !errors.Is(unionErr, gorm.ErrRecordNotFound) {
- err = exception.New(exception.TemplateUserFoundFailed)
- return
- }
- if errors.Is(unionErr, gorm.ErrRecordNotFound) {
- err = userService.RegisterTemplateUser(&user)
- if err != nil {
- logger.Info("RegisterTemplateUser,Err" + err.Error())
- }
- } else { //修改微信小程序openid
- logger.Info("wxUser.Id:%d", wxUser.Id)
- err = userService.BindUserGzhOpenId(wxUser.Id, openId, followingGzh)
- if err != nil {
- logger.Info("BindUserGzhOpenId,Err" + err.Error())
- }
- }
- } else {
- isBind = true
- err = userService.BindUserGzhOpenId(user.Id, openId, followingGzh)
- if err != nil {
- logger.Info("BindUserGzhOpenId,Err" + err.Error())
- }
- }
- return
- }
- // UnSubscribeWxGzhByOpenId
- // @Description: 通过openid取消关注微信公众号
- // @author: Roc
- // @datetime 2024-08-13 13:19:49
- // @param openId string
- // @return isBind bool
- // @return err error
- func UnSubscribeWxGzhByOpenId(openId string) {
- logger.Info("openId:" + openId)
- var err error
- defer func() {
- if err != nil {
- logger.Info("通过openid取消关注微信公众号失败,openId:", openId, ";错误信息:", err.Error())
- }
- }()
- // 通过公众号openid获取用户信息
- user, err := userService.GetTemplateUserByGzhOpenId(openId)
- if err != nil {
- if err == gorm.ErrRecordNotFound {
- err = nil
- // 找不到就直接返回了
- return
- } else {
- err = exception.New(exception.TemplateUserNotFound)
- return
- }
- }
- // 解绑用户
- err = userService.BindUserGzhOpenId(user.Id, openId, 0)
- if err != nil {
- logger.Info("UnBindWxGzhByOpenId,Err" + err.Error())
- }
- return
- }
|