// Code scaffolded by goctl. Safe to edit. // goctl 1.10.1 package auth import ( "context" "encoding/json" "fmt" "io" "net/http" "net/url" "time" "sundynix-micro-go/app/user/api/internal/svc" "sundynix-micro-go/app/user/api/internal/types" "sundynix-micro-go/app/user/rpc/user" jwtUtil "sundynix-micro-go/common/utils/jwt" jwtv5 "github.com/golang-jwt/jwt/v5" "github.com/zeromicro/go-zero/core/logx" ) type MiniLoginLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } // 微信小程序登录 func NewMiniLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MiniLoginLogic { return &MiniLoginLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } // WxCode2SessionResp 微信code2session响应 type WxCode2SessionResp struct { Openid string `json:"openid"` SessionKey string `json:"session_key"` Unionid string `json:"unionid"` Errcode int `json:"errcode"` Errmsg string `json:"errmsg"` } func (l *MiniLoginLogic) MiniLogin(req *types.MiniLoginReq) (resp *types.LoginResp, err error) { // 1. 调用微信接口获取openid和session_key // TODO: 从配置中获取AppId和AppSecret,当前先用固定值 appID := "wxb463820bf36dd5d6" appSecret := "731784a74c76c6d31fa00bb847af2c7d" params := url.Values{} params.Set("appid", appID) params.Set("secret", appSecret) params.Set("js_code", req.Code) params.Set("grant_type", "authorization_code") fullURL := "https://api.weixin.qq.com/sns/jscode2session?" + params.Encode() httpResp, err := http.Get(fullURL) if err != nil { l.Errorf("微信登录接口请求失败: %v", err) return nil, fmt.Errorf("微信登录接口请求失败") } defer httpResp.Body.Close() body, err := io.ReadAll(httpResp.Body) if err != nil { l.Errorf("读取微信接口响应失败: %v", err) return nil, fmt.Errorf("读取微信登录接口响应失败") } var wxResp WxCode2SessionResp if err = json.Unmarshal(body, &wxResp); err != nil { l.Errorf("解析微信接口响应失败: %v", err) return nil, fmt.Errorf("解析微信登录接口响应失败") } if wxResp.Errcode != 0 { l.Errorf("微信接口返回错误: errcode=%d, errmsg=%s", wxResp.Errcode, wxResp.Errmsg) return nil, fmt.Errorf("微信登录失败: %s", wxResp.Errmsg) } if wxResp.Openid == "" { return nil, fmt.Errorf("openid为空") } // 2. 通过 user-rpc 创建或获取用户 createResp, err := l.svcCtx.UserRpc.CreateUser(l.ctx, &user.CreateUserReq{ Name: "", OpenId: wxResp.Openid, SessionKey: wxResp.SessionKey, ClientId: req.ClientId, }) if err != nil { l.Errorf("创建用户失败: %v", err) return nil, fmt.Errorf("登录失败") } // 3. 生成JWT Token j := jwtUtil.NewJWT(l.svcCtx.Config.Auth.AccessSecret) claims := jwtUtil.CustomClaims{ BaseClaims: jwtUtil.BaseClaims{ ID: createResp.User.Id, Account: createResp.User.Account, }, BufferTime: 3600, RegisteredClaims: jwtv5.RegisteredClaims{ Audience: jwtv5.ClaimStrings{"sundynix"}, NotBefore: jwtv5.NewNumericDate(time.Now().Add(-1000)), ExpiresAt: jwtv5.NewNumericDate(time.Now().Add(time.Duration(l.svcCtx.Config.Auth.AccessExpire) * time.Second)), Issuer: "sundynix", }, } token, err := j.CreateToken(claims) if err != nil { l.Errorf("生成Token失败: %v", err) return nil, fmt.Errorf("登录失败") } return &types.LoginResp{ Token: token, UserInfo: createResp.User, }, nil }