auth_service.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. package auth
  2. import (
  3. "crypto/rand"
  4. "encoding/base64"
  5. "errors"
  6. "eta/eta_mini_ht_api/common/component/cache"
  7. logger "eta/eta_mini_ht_api/common/component/log"
  8. "eta/eta_mini_ht_api/common/component/wechat"
  9. "eta/eta_mini_ht_api/common/exception"
  10. authUtils "eta/eta_mini_ht_api/common/utils/auth"
  11. "eta/eta_mini_ht_api/common/utils/jwt"
  12. "eta/eta_mini_ht_api/common/utils/redis"
  13. "eta/eta_mini_ht_api/common/utils/sms"
  14. stringUtils "eta/eta_mini_ht_api/common/utils/string"
  15. smsService "eta/eta_mini_ht_api/domian/sms"
  16. userService "eta/eta_mini_ht_api/domian/user"
  17. "fmt"
  18. "gorm.io/gorm"
  19. "strings"
  20. )
  21. var (
  22. smsSender sms.SMSClient
  23. wechatClient *wechat.Client
  24. redisCache *cache.RedisCache
  25. )
  26. func message() sms.SMSClient {
  27. if smsSender == nil {
  28. smsSender = sms.GetInstance()
  29. }
  30. return smsSender
  31. }
  32. func wx() *wechat.Client {
  33. if wechatClient == nil {
  34. wechatClient = wechat.GetInstance()
  35. }
  36. return wechatClient
  37. }
  38. func SendSMSCode(mobile string) (err error) {
  39. //code := rd().GetString(redis.GenerateSmsKey(mobile))
  40. //if stringUtils.IsEmptyOrNil(code) {
  41. // code, err = authUtils.GenerateCode(6)
  42. // if err != nil {
  43. // logger.Warn("生成验证码失败:%v", err)
  44. // return exception.New(exception.SMSCodeGenerateFailed)
  45. // }
  46. // codeDTO := smsService.CodeDTO{
  47. // Mobile: mobile,
  48. // Code: code,
  49. // ExpireMinute: message().GetExpireMinute(),
  50. // }
  51. // //消息domain层
  52. // var smid int
  53. // smid, err = smsService.SendSMSCode(codeDTO)
  54. // if err != nil {
  55. // logger.Error("发送短信失败:%v", err)
  56. // err = exception.New(exception.SendingSMSFailed)
  57. // }
  58. // logger.Debug("验证码:%v", code)
  59. // _, err = message().SendSms(mobile, code, smid)
  60. // if err != nil {
  61. // logger.Error("发送短信失败:%v", err)
  62. // err = exception.New(exception.SendingSMSFailed)
  63. // }
  64. // return
  65. //}
  66. //return exception.New(exception.SMSCodeAlreadySent)
  67. code := rd().GetString(redis.GenerateSmsKey(mobile))
  68. if stringUtils.IsEmptyOrNil(code) {
  69. logger.Info("更新验证码 手机号[%v],旧验证码[%v]", mobile, code)
  70. }
  71. code, err = authUtils.GenerateCode(6)
  72. logger.Info("更新验证码 手机号[%v],新验证码[%v]", mobile, code)
  73. if err != nil {
  74. logger.Warn("生成验证码失败:%v", err)
  75. return exception.New(exception.SMSCodeGenerateFailed)
  76. }
  77. codeDTO := smsService.CodeDTO{
  78. Mobile: mobile,
  79. Code: code,
  80. ExpireMinute: message().GetExpireMinute(),
  81. }
  82. //消息domain层
  83. var smid int
  84. smid, err = smsService.SendSMSCode(codeDTO)
  85. if err != nil {
  86. logger.Error("发送短信失败:%v", err)
  87. err = exception.New(exception.SendingSMSFailed)
  88. }
  89. logger.Debug("验证码:%v", code)
  90. _, err = message().SendSms(mobile, code, smid)
  91. if err != nil {
  92. logger.Error("发送短信失败:%v", err)
  93. err = exception.New(exception.SendingSMSFailed)
  94. }
  95. return
  96. }
  97. func CheckUser(mobile string, code string) (err error) {
  98. smsCode := rd().GetString(redis.GenerateSmsKey(mobile))
  99. if stringUtils.IsEmptyOrNil(smsCode) {
  100. logger.Warn("验证码已过期:%v", code)
  101. codeDTO := smsService.CodeDTO{
  102. Mobile: mobile,
  103. Code: code,
  104. }
  105. _ = smsService.TryExpireCode(codeDTO)
  106. err = exception.New(exception.SMSCodeExpired)
  107. return
  108. }
  109. if smsCode != code {
  110. err = exception.New(exception.SMSCodeError)
  111. return
  112. }
  113. return nil
  114. }
  115. type LoginDTO struct {
  116. VerifyCode string
  117. Code string
  118. Mobile string
  119. }
  120. func Login(login LoginDTO) (token string, err error) {
  121. var user userService.UserDTO
  122. user, err = userService.GetUserByMobile(login.Mobile)
  123. if err != nil {
  124. if !errors.Is(err, gorm.ErrRecordNotFound) {
  125. err = exception.New(exception.UnknownError)
  126. return
  127. }
  128. //注册用户
  129. var wechatInfo wechat.WxUser
  130. //微信请求异常
  131. wechatInfo, err = wx().Login(login.Code)
  132. //微信客户端的异常不做处理,已经是EtaError
  133. if err != nil {
  134. return
  135. }
  136. user = initUser(wechatInfo, login)
  137. err = userService.RegisterTemplateUser(&user)
  138. if err != nil {
  139. err = exception.New(exception.TemplateUserCreateFailed)
  140. return
  141. }
  142. }
  143. token, err = jwt.CreateToken(user.OpenId, user.Mobile)
  144. if err != nil {
  145. err = exception.New(exception.GenerateTokenFailed)
  146. return
  147. }
  148. err = rd().SetString(redis.GenerateTokenKey(user.Mobile), token, 90*24*60*60)
  149. if err != nil {
  150. err = exception.New(exception.GenerateTokenFailed)
  151. return
  152. }
  153. codeDTO := smsService.CodeDTO{
  154. Mobile: login.Mobile,
  155. Code: login.VerifyCode,
  156. }
  157. //登录成功删除短信验证码,数据库留痕
  158. err = rd().Delete(redis.GenerateSmsKey(login.Mobile))
  159. if err != nil {
  160. logger.Error("清除redis 短信验证码失败,%v", err)
  161. _ = rd().SetString(redis.GenerateSmsKey(login.Mobile), "", 1)
  162. return "", err
  163. }
  164. _ = smsService.TryVerifiedCode(codeDTO)
  165. return
  166. }
  167. func BindMobile(userId int, mobile, verifyCode string) (err error) {
  168. err = userService.BindUserMobile(userId, mobile)
  169. if err != nil {
  170. err = exception.New(exception.BindMobileFailed)
  171. return
  172. }
  173. codeDTO := smsService.CodeDTO{
  174. Mobile: mobile,
  175. Code: verifyCode,
  176. }
  177. //登录成功删除短信验证码,数据库留痕
  178. err = rd().Delete(redis.GenerateSmsKey(mobile))
  179. if err != nil {
  180. logger.Error("清除redis 短信验证码失败,%v", err)
  181. _ = rd().SetString(redis.GenerateSmsKey(mobile), "", 1)
  182. return err
  183. }
  184. _ = smsService.TryVerifiedCode(codeDTO)
  185. return
  186. }
  187. func RefreshToken(code string) (token string, isBindMobile int, err error) {
  188. //注册用户
  189. var wechatInfo wechat.WxUser
  190. //微信请求异常
  191. wechatInfo, err = wx().Login(code)
  192. //微信客户端的异常不做处理,已经是EtaError
  193. if err != nil {
  194. fmt.Println("wx.Login,err:" + err.Error())
  195. return
  196. }
  197. user, err := userService.GetTemplateUserByOpenId(wechatInfo.OpenId)
  198. var isAdd bool
  199. if err != nil {
  200. if err == gorm.ErrRecordNotFound {
  201. isAdd = true
  202. } else {
  203. err = exception.New(exception.TemplateUserNotFound)
  204. return
  205. }
  206. }
  207. token, err = jwt.CreateToken(wechatInfo.OpenId, wechatInfo.OpenId)
  208. if err != nil {
  209. err = exception.New(exception.GenerateTokenFailed)
  210. return
  211. }
  212. err = rd().SetString(redis.GenerateTokenKey(wechatInfo.OpenId), token, 90*24*60*60)
  213. if err != nil {
  214. err = exception.New(exception.GenerateTokenFailed)
  215. return
  216. }
  217. if user.Mobile == "" {
  218. isBindMobile = 0
  219. } else {
  220. isBindMobile = 1
  221. }
  222. if isAdd {
  223. user.OpenId = wechatInfo.OpenId
  224. user.UnionId = wechatInfo.UnionId
  225. var isRegister bool
  226. //判断unionid是否存在
  227. wxUser, unionErr := userService.GetTemplateUserByUnionId(wechatInfo.UnionId)
  228. if unionErr != nil {
  229. if unionErr == gorm.ErrRecordNotFound { //注册用户
  230. isRegister = true
  231. } else {
  232. err = exception.New(exception.TemplateUserNotFound)
  233. return
  234. }
  235. }
  236. if isRegister {
  237. err = userService.RegisterTemplateUser(&user)
  238. } else { //修改微信小程序openid
  239. err = userService.BindUserXcxOpenId(wxUser.Id, user.OpenId)
  240. }
  241. }
  242. return
  243. }
  244. func GetAreaCodes() (list []userService.AreaCodeDTO, err error) {
  245. list, err = userService.GetAreaCodes()
  246. if err != nil {
  247. err = exception.New(exception.GetAreaCodesFailed)
  248. }
  249. return
  250. }
  251. func GetValidAreaCodes() (list []string) {
  252. str := rd().GetString(redis.ValidAreaCode)
  253. if str == "" {
  254. codes, err := userService.GetValidAreaCodes()
  255. if err != nil {
  256. return []string{}
  257. }
  258. var validCodes []string
  259. for _, code := range codes {
  260. validCodes = append(validCodes, code.CodeValue)
  261. }
  262. err = rd().SetString(redis.ValidAreaCode, strings.Join(validCodes, ","), 60*60)
  263. if err != nil {
  264. logger.Error("设置Redis区号失败:%v", err)
  265. list = validCodes
  266. }
  267. list = strings.Split(rd().GetString(redis.ValidAreaCode), ",")
  268. } else {
  269. list = strings.Split(str, ",")
  270. }
  271. return
  272. }
  273. func initUser(user wechat.WxUser, dto LoginDTO) userService.UserDTO {
  274. username := generateRandomUsername(user.OpenId)
  275. if stringUtils.IsBlank(username) {
  276. username = dto.Mobile
  277. }
  278. return userService.UserDTO{
  279. OpenId: user.OpenId,
  280. UnionId: user.UnionId,
  281. Username: username,
  282. Mobile: dto.Mobile,
  283. }
  284. }
  285. // GenerateRandomUsername 生成基于UnionId的随机用户名
  286. func generateRandomUsername(openId string) string {
  287. // 生成一个随机的4字节二进制数据
  288. randomBytes := make([]byte, 4)
  289. _, err := rand.Read(randomBytes)
  290. if err != nil {
  291. return ""
  292. }
  293. // 将随机字节转换为base64编码的URL安全字符串
  294. // 并去掉可能出现的等于号
  295. randomStr := strings.TrimRight(base64.URLEncoding.EncodeToString(randomBytes), "=")
  296. // 拼接UnionId和随机字符串
  297. username := fmt.Sprintf("%s_%s", openId, randomStr)
  298. return username
  299. }
  300. func rd() *cache.RedisCache {
  301. if redisCache == nil {
  302. redisCache = cache.GetInstance()
  303. }
  304. return redisCache
  305. }
  306. func BindXcxOpenId(userId int, openId string) (err error) {
  307. err = userService.BindUserXcxOpenId(userId, openId)
  308. return
  309. }
  310. // 绑定微信公众号
  311. func BindWxGzh(code string) (isBind bool, err error) {
  312. //注册用户
  313. wxUser, err := GetWxUserInfo(code)
  314. if err != nil {
  315. logger.Error("获取微信公众号用户信息失败" + err.Error())
  316. err = exception.New(exception.TemplateUserNotFound)
  317. return
  318. }
  319. logger.Info("GetWxUserInfo:")
  320. if wxUser.ErrCode != 0 {
  321. logger.Error("获取微信公众号用户信息失败" + wxUser.ErrMsg)
  322. err = exception.New(exception.TemplateUserNotFound)
  323. return
  324. }
  325. return BindWxGzhByOpenId(wxUser.OpenId)
  326. }
  327. // BindWxGzhByOpenId
  328. // @Description: 通过openid绑定微信公众号
  329. // @author: Roc
  330. // @datetime 2024-08-13 13:19:49
  331. // @param openId string
  332. // @return isBind bool
  333. // @return err error
  334. func BindWxGzhByOpenId(openId string) (isBind bool, err error) {
  335. logger.Info("openId:" + openId)
  336. wxUserDetail, err := GetWxUserDetail(openId)
  337. if err != nil {
  338. logger.Error("获取微信公众号用户详细信息失败" + err.Error())
  339. err = exception.New(exception.TemplateUserNotFound)
  340. return
  341. }
  342. unionId := wxUserDetail.UnionID
  343. followingGzh := int(wxUserDetail.Subscribe)
  344. logger.Info("unionId:" + unionId)
  345. var isAdd bool
  346. user, err := userService.GetTemplateUserByGzhOpenId(openId)
  347. if err != nil {
  348. if err == gorm.ErrRecordNotFound {
  349. isAdd = true
  350. } else {
  351. err = exception.New(exception.TemplateUserNotFound)
  352. return
  353. }
  354. }
  355. logger.Info("IsAdd", isAdd)
  356. if isAdd {
  357. user.GzhOpenId = openId
  358. user.UnionId = unionId
  359. user.FollowingGzh = followingGzh
  360. var isRegister bool
  361. //判断unionid是否存在
  362. wxUser, unionErr := userService.GetTemplateUserByUnionId(unionId)
  363. if unionErr != nil {
  364. if unionErr == gorm.ErrRecordNotFound { //注册用户
  365. isRegister = true
  366. } else {
  367. err = exception.New(exception.TemplateUserNotFound)
  368. return
  369. }
  370. }
  371. logger.Info("isRegister", isRegister)
  372. if isRegister {
  373. err = userService.RegisterTemplateUser(&user)
  374. if err != nil {
  375. logger.Info("RegisterTemplateUser,Err" + err.Error())
  376. }
  377. } else { //修改微信小程序openid
  378. logger.Info("wxUser.Id:%d", wxUser.Id)
  379. err = userService.BindUserGzhOpenId(wxUser.Id, openId, followingGzh)
  380. if err != nil {
  381. logger.Info("BindUserGzhOpenId,Err" + err.Error())
  382. }
  383. }
  384. } else {
  385. isBind = true
  386. err = userService.BindUserGzhOpenId(user.Id, openId, followingGzh)
  387. if err != nil {
  388. logger.Info("BindUserGzhOpenId,Err" + err.Error())
  389. }
  390. }
  391. return
  392. }
  393. // UnSubscribeWxGzhByOpenId
  394. // @Description: 通过openid取消关注微信公众号
  395. // @author: Roc
  396. // @datetime 2024-08-13 13:19:49
  397. // @param openId string
  398. // @return isBind bool
  399. // @return err error
  400. func UnSubscribeWxGzhByOpenId(openId string) {
  401. logger.Info("openId:" + openId)
  402. var err error
  403. defer func() {
  404. if err != nil {
  405. logger.Info("通过openid取消关注微信公众号失败,openId:", openId, ";错误信息:", err.Error())
  406. }
  407. }()
  408. // 通过公众号openid获取用户信息
  409. user, err := userService.GetTemplateUserByGzhOpenId(openId)
  410. if err != nil {
  411. if err == gorm.ErrRecordNotFound {
  412. err = nil
  413. // 找不到就直接返回了
  414. return
  415. } else {
  416. err = exception.New(exception.TemplateUserNotFound)
  417. return
  418. }
  419. }
  420. // 解绑用户
  421. err = userService.BindUserGzhOpenId(user.Id, openId, 0)
  422. if err != nil {
  423. logger.Info("UnBindWxGzhByOpenId,Err" + err.Error())
  424. }
  425. return
  426. }