2024-11-03 15:28:10 +08:00
|
|
|
package user
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2025-01-18 22:34:27 +08:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"time"
|
2025-04-27 12:17:18 +08:00
|
|
|
"tyc-server/app/main/api/internal/svc"
|
|
|
|
"tyc-server/app/main/api/internal/types"
|
|
|
|
"tyc-server/app/main/model"
|
2025-04-09 15:58:06 +08:00
|
|
|
jwtx "tyc-server/common/jwt"
|
|
|
|
"tyc-server/common/xerr"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
2024-11-03 15:28:10 +08:00
|
|
|
|
|
|
|
"github.com/zeromicro/go-zero/core/logx"
|
|
|
|
)
|
|
|
|
|
|
|
|
type WxMiniAuthLogic struct {
|
|
|
|
logx.Logger
|
|
|
|
ctx context.Context
|
|
|
|
svcCtx *svc.ServiceContext
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewWxMiniAuthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WxMiniAuthLogic {
|
|
|
|
return &WxMiniAuthLogic{
|
|
|
|
Logger: logx.WithContext(ctx),
|
|
|
|
ctx: ctx,
|
|
|
|
svcCtx: svcCtx,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *WxMiniAuthLogic) WxMiniAuth(req *types.WXMiniAuthReq) (resp *types.WXMiniAuthResp, err error) {
|
2025-01-18 22:34:27 +08:00
|
|
|
// 1. 使用微信提供的 code 换取 session_key 和 openid
|
|
|
|
weChatResponse, err := l.exchangeCodeForSession(req.Code)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(xerr.NewErrMsg("微信登录失败"), "微信登录, code 换取 session 失败: %s, err: %+v", req.Code, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. 根据 openid 查找用户
|
|
|
|
userAuth, findErr := l.svcCtx.UserAuthModel.FindOneByAuthTypeAuthKey(l.ctx, model.UserAuthTypeWxMini, weChatResponse.OpenId)
|
|
|
|
if findErr != nil && findErr != model.ErrNotFound {
|
|
|
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "微信登录, 读取用户认证信息失败, openid: %s, err: %+v", weChatResponse.OpenId, findErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
var user *model.User
|
|
|
|
if findErr == model.ErrNotFound {
|
|
|
|
// 用户不存在,创建新用户
|
|
|
|
user = &model.User{}
|
2025-01-18 23:21:48 +08:00
|
|
|
user.Mobile = weChatResponse.OpenId
|
2025-01-18 22:34:27 +08:00
|
|
|
if transErr := l.svcCtx.UserModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
|
|
|
// 插入新用户
|
|
|
|
insertResult, userInsertErr := l.svcCtx.UserModel.Insert(ctx, session, user)
|
|
|
|
if userInsertErr != nil {
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "微信登录, 数据库插入新用户失败, openid: %s, err: %+v", weChatResponse.OpenId, userInsertErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 获取新用户的 ID
|
|
|
|
lastId, lastInsertIdErr := insertResult.LastInsertId()
|
|
|
|
if lastInsertIdErr != nil {
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "微信登录, 获取新用户ID失败, err:%+v, user:%+v", lastInsertIdErr, user)
|
|
|
|
}
|
|
|
|
user.Id = lastId
|
2024-11-03 15:28:10 +08:00
|
|
|
|
2025-01-18 22:34:27 +08:00
|
|
|
// 创建用户认证信息
|
|
|
|
newUserAuth := &model.UserAuth{
|
|
|
|
UserId: lastId,
|
|
|
|
AuthKey: weChatResponse.OpenId,
|
|
|
|
AuthType: model.UserAuthTypeWxMini,
|
|
|
|
}
|
|
|
|
if _, userAuthInsertErr := l.svcCtx.UserAuthModel.Insert(ctx, session, newUserAuth); userAuthInsertErr != nil {
|
|
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "微信登录, 数据库插入用户认证信息失败, err:%+v", userAuthInsertErr)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}); transErr != nil {
|
|
|
|
return nil, transErr
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// 获取用户信息
|
|
|
|
user, err = l.svcCtx.UserModel.FindOne(l.ctx, userAuth.UserId)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "微信登录, 读取用户信息失败, userId: %d, err: %+v", userAuth.UserId, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. 生成 JWT 令牌
|
|
|
|
token, generateErr := jwtx.GenerateJwtToken(user.Id, l.svcCtx.Config.JwtAuth.AccessSecret, l.svcCtx.Config.JwtAuth.AccessExpire)
|
|
|
|
if generateErr != nil {
|
|
|
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "微信登录, 生成token失败 : %d", user.Id)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 4. 获取当前时间戳
|
|
|
|
now := time.Now().Unix()
|
|
|
|
|
|
|
|
return &types.WXMiniAuthResp{
|
|
|
|
AccessToken: token,
|
|
|
|
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
|
|
|
|
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type WxLoginResp struct {
|
|
|
|
OpenId string `json:"openid"`
|
|
|
|
SessionKey string `json:"session_key"`
|
|
|
|
Unionid string `json:"unionid"`
|
|
|
|
ErrCode int `json:"errcode"`
|
|
|
|
ErrMsg string `json:"errmsg"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *WxMiniAuthLogic) exchangeCodeForSession(code string) (response WxLoginResp, err error) {
|
|
|
|
|
|
|
|
// 向微信发出登录请求
|
|
|
|
baseURL := "https://api.weixin.qq.com/sns/jscode2session"
|
|
|
|
// 创建查询参数
|
|
|
|
params := url.Values{}
|
|
|
|
params.Add("appid", l.svcCtx.Config.Wxpay.AppID)
|
|
|
|
params.Add("secret", l.svcCtx.Config.Wxpay.AppSecret)
|
|
|
|
params.Add("js_code", code)
|
|
|
|
params.Add("grant_type", "authorization_code")
|
|
|
|
|
|
|
|
// 构建完整的请求 URL
|
|
|
|
requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode())
|
|
|
|
|
|
|
|
// 发送 GET 请求
|
|
|
|
resp, err := http.Get(requestURL)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
// 读取响应体
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// 将响应体解析为结构体
|
|
|
|
err = json.Unmarshal(body, &response)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if response.ErrCode != 0 {
|
|
|
|
err = errors.New(response.ErrMsg)
|
|
|
|
return
|
|
|
|
}
|
2024-11-03 15:28:10 +08:00
|
|
|
return
|
|
|
|
}
|