package user import ( "context" "encoding/json" "fmt" "io" "net/http" "net/url" "time" "tyc-server/app/main/api/internal/svc" "tyc-server/app/main/api/internal/types" "tyc-server/app/main/model" jwtx "tyc-server/common/jwt" "tyc-server/common/xerr" "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/stores/sqlx" "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) { // 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{} user.Mobile = weChatResponse.OpenId 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 // 创建用户认证信息 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 } return }