This repository has been archived on 2024-11-28. You can view files and clone it, but cannot push or open issues or pull requests.
Files
schisandra-cloud-album/api/oauth_api/wechat_api.go
2024-08-20 21:45:14 +08:00

320 lines
8.7 KiB
Go

package oauth_api
import (
"encoding/json"
"errors"
"github.com/ArtisanCloud/PowerLibs/v3/fmt"
"github.com/ArtisanCloud/PowerLibs/v3/http/helper"
"github.com/ArtisanCloud/PowerWeChat/v3/src/basicService/qrCode/response"
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/contract"
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/messages"
models2 "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/models"
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount/server/handlers/models"
ginI18n "github.com/gin-contrib/i18n"
"github.com/gin-gonic/gin"
uuid "github.com/satori/go.uuid"
"github.com/yitter/idgenerator-go/idgen"
"gorm.io/gorm"
"schisandra-cloud-album/api/user_api/dto"
"schisandra-cloud-album/api/websocket_api"
"schisandra-cloud-album/common/constant"
"schisandra-cloud-album/common/enum"
"schisandra-cloud-album/common/redis"
"schisandra-cloud-album/common/result"
"schisandra-cloud-album/global"
"schisandra-cloud-album/model"
"schisandra-cloud-album/utils"
"strconv"
"strings"
"time"
)
// GenerateClientId 生成客户端ID
// @Summary 生成客户端ID
// @Description 生成客户端ID
// @Produce json
// @Router /api/oauth/generate_client_id [get]
func (OAuthAPI) GenerateClientId(c *gin.Context) {
// 尝试从 X-Real-IP 头部获取真实 IP
ip := c.GetHeader("X-Real-IP")
// 如果 X-Real-IP 头部不存在,则尝试从 X-Forwarded-For 头部获取
if ip == "" {
ip = c.GetHeader("X-Forwarded-For")
}
// 如果两者都不存在,则使用默认的 ClientIP 方法获取 IP
if ip == "" {
ip = c.ClientIP()
}
clientId := redis.Get(constant.UserLoginClientRedisKey + ip).Val()
if clientId != "" {
result.OkWithData(clientId, c)
return
}
v1 := uuid.NewV1()
redis.Set(constant.UserLoginClientRedisKey+ip, v1.String(), 0)
result.OkWithData(v1.String(), c)
return
}
// CallbackNotify 微信回调验证
// @Summary 微信回调验证
// @Description 微信回调验证
// @Produce json
// @Router /api/oauth/callback_notify [POST]
func (OAuthAPI) CallbackNotify(c *gin.Context) {
rs, err := global.Wechat.Server.Notify(c.Request, func(event contract.EventInterface) interface{} {
fmt.Dump("event", event)
switch event.GetMsgType() {
case models2.CALLBACK_MSG_TYPE_EVENT:
switch event.GetEvent() {
case models.CALLBACK_EVENT_SUBSCRIBE:
msg := models.EventSubscribe{}
err := event.ReadMessage(&msg)
if err != nil {
println(err.Error())
return "error"
}
key := strings.TrimPrefix(msg.EventKey, "qrscene_")
res := wechatLoginHandler(msg.FromUserName, key)
if !res {
return messages.NewText(ginI18n.MustGetMessage(c, "LoginFailed"))
}
return messages.NewText(ginI18n.MustGetMessage(c, "LoginSuccess"))
case models.CALLBACK_EVENT_UNSUBSCRIBE:
msg := models.EventUnSubscribe{}
err := event.ReadMessage(&msg)
if err != nil {
println(err.Error())
return "error"
}
fmt.Dump(msg)
return messages.NewText("再见,我的宝!")
case models.CALLBACK_EVENT_SCAN:
msg := models.EventScan{}
err := event.ReadMessage(&msg)
if err != nil {
println(err.Error())
return "error"
}
res := wechatLoginHandler(msg.FromUserName, msg.EventKey)
if !res {
return messages.NewText(ginI18n.MustGetMessage(c, "LoginFailed"))
}
return messages.NewText(ginI18n.MustGetMessage(c, "LoginSuccess"))
}
case models2.CALLBACK_MSG_TYPE_TEXT:
msg := models.MessageText{}
err := event.ReadMessage(&msg)
if err != nil {
println(err.Error())
return "error"
}
fmt.Dump(msg)
}
return messages.NewText("ok")
})
if err != nil {
panic(err)
}
err = helper.HttpResponseSend(rs, c.Writer)
if err != nil {
panic(err)
}
}
// CallbackVerify 微信回调验证
// @Summary 微信回调验证
// @Description 微信回调验证
// @Produce json
// @Router /api/oauth/callback_verify [get]
func (OAuthAPI) CallbackVerify(c *gin.Context) {
rs, err := global.Wechat.Server.VerifyURL(c.Request)
if err != nil {
panic(err)
}
err = helper.HttpResponseSend(rs, c.Writer)
}
// GetTempQrCode 获取临时二维码
// @Summary 获取临时二维码
// @Description 获取临时二维码
// @Produce json
// @Param client_id query string true "客户端ID"
// @Router /api/oauth/get_temp_qrcode [get]
func (OAuthAPI) GetTempQrCode(c *gin.Context) {
clientId := c.Query("client_id")
// 尝试从 X-Real-IP 头部获取真实 IP
ip := c.GetHeader("X-Real-IP")
// 如果 X-Real-IP 头部不存在,则尝试从 X-Forwarded-For 头部获取
if ip == "" {
ip = c.GetHeader("X-Forwarded-For")
}
// 如果两者都不存在,则使用默认的 ClientIP 方法获取 IP
if ip == "" {
ip = c.ClientIP()
}
if clientId == "" {
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
return
}
qrcode := redis.Get(constant.UserLoginQrcodeRedisKey + ip + ":" + clientId).Val()
if qrcode != "" {
data := response.ResponseQRCodeCreate{}
err := json.Unmarshal([]byte(qrcode), &data)
if err != nil {
return
}
result.OK(ginI18n.MustGetMessage(c, "QRCodeGetSuccess"), data.Url, c)
return
}
data, err := global.Wechat.QRCode.Temporary(c.Request.Context(), clientId, 30*24*3600)
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
return
}
serializedData, err := json.Marshal(data)
if err != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
return
}
wrong := redis.Set(constant.UserLoginQrcodeRedisKey+ip+":"+clientId, serializedData, time.Hour*24*30).Err()
if wrong != nil {
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
return
}
result.OK(ginI18n.MustGetMessage(c, "QRCodeGetSuccess"), data.Url, c)
}
// wechatLoginHandler 微信登录处理
func wechatLoginHandler(openId string, clientId string) bool {
if openId == "" {
return false
}
authUserSocial, err := userSocialService.QueryUserSocialByOpenID(openId, enum.OAuthSourceWechat)
if errors.Is(err, gorm.ErrRecordNotFound) {
uid := idgen.NextId()
uidStr := strconv.FormatInt(uid, 10)
createUser := model.ScaAuthUser{
UID: &uidStr,
Username: &openId,
}
addUser, err := userService.AddUser(createUser)
if err != nil {
return false
}
wechat := enum.OAuthSourceWechat
userSocial := model.ScaAuthUserSocial{
UserID: &addUser.ID,
OpenID: &openId,
Source: &wechat,
}
wrong := userSocialService.AddUserSocial(userSocial)
if wrong != nil {
return false
}
userRole := model.ScaAuthUserRole{
UserID: addUser.ID,
RoleID: enum.User,
}
e := userRoleService.AddUserRole(userRole)
if e != nil {
return false
}
res := handelUserLogin(addUser, clientId)
if !res {
return false
}
return true
} else {
user, err := userService.QueryUserById(authUserSocial.UserID)
if err != nil {
return false
}
res := handelUserLogin(user, clientId)
if !res {
return false
}
return true
}
}
// handelUserLogin 处理用户登录
func handelUserLogin(user model.ScaAuthUser, clientId string) bool {
ids, err := userRoleService.GetUserRoleIdsByUserId(user.ID)
if err != nil {
return false
}
permissionIds := rolePermissionService.QueryPermissionIdsByRoleId(ids)
permissions, err := permissionServiceService.GetPermissionsByIds(permissionIds)
if err != nil {
return false
}
serializedPermissions, err := json.Marshal(permissions)
if err != nil {
return false
}
wrong := redis.Set(constant.UserAuthPermissionRedisKey+*user.UID, serializedPermissions, 0).Err()
if wrong != nil {
return false
}
roleList, err := roleService.GetRoleListByIds(ids)
if err != nil {
return false
}
serializedRoleList, err := json.Marshal(roleList)
if err != nil {
return false
}
er := redis.Set(constant.UserAuthRoleRedisKey+*user.UID, serializedRoleList, 0).Err()
if er != nil {
return false
}
accessToken, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: user.UID, RoleID: ids})
if err != nil {
return false
}
refreshToken, expiresAt := utils.GenerateRefreshToken(utils.RefreshJWTPayload{UserID: user.UID, RoleID: ids}, time.Hour*24*7)
data := dto.ResponseData{
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresAt: expiresAt,
UID: user.UID,
}
fail := redis.Set(constant.UserLoginTokenRedisKey+*user.UID, data, time.Hour*24*7).Err()
if fail != nil {
return false
}
responseData := map[string]interface{}{
"code": 0,
"message": "success",
"data": data,
"success": true,
}
tokenData, err := json.Marshal(responseData)
if err != nil {
return false
}
// gws方式发送消息
err = websocket_api.Handler.SendMessageToClient(clientId, tokenData)
if err != nil {
return false
}
// gorilla websocket方式发送消息
//res := websocket_api.SendMessageData(clientId, responseData)
//if !res {
// return false
//}
return true
}