package wechat import ( "encoding/json" "fmt" "net/http" "mini-chat/internal/code" "mini-chat/internal/pkg/core" "mini-chat/internal/pkg/httpclient" "mini-chat/internal/pkg/validation" ) type miniprogramLoginRequest struct { AppID string `json:"app_id" binding:"required"` // 小程序AppID JSCode string `json:"js_code" binding:"required"` // 登录时获取的code } type miniprogramLoginResponse struct { Success bool `json:"success"` Message string `json:"message"` OpenID string `json:"openid,omitempty"` // 用户唯一标识 SessionKey string `json:"session_key,omitempty"` // 会话密钥 UnionID string `json:"unionid,omitempty"` // 用户在开放平台的唯一标识符 } // Code2SessionResponse 微信code2Session接口响应 type Code2SessionResponse struct { OpenID string `json:"openid"` // 用户唯一标识 SessionKey string `json:"session_key"` // 会话密钥 UnionID string `json:"unionid"` // 用户在开放平台的唯一标识符 ErrCode int `json:"errcode"` // 错误码 ErrMsg string `json:"errmsg"` // 错误信息 } // MiniprogramLogin 小程序登录 // @Summary 小程序登录 // @Description 通过AppID和code获取用户的openid和session_key // @Tags 微信 // @Accept json // @Produce json // @Param request body miniprogramLoginRequest true "请求参数" // @Success 200 {object} miniprogramLoginResponse // @Failure 400 {object} code.Failure // @Failure 500 {object} code.Failure // @Router /api/wechat/miniprogram/login [post] func (h *handler) MiniprogramLogin() core.HandlerFunc { return func(ctx core.Context) { req := new(miniprogramLoginRequest) res := new(miniprogramLoginResponse) if err := ctx.ShouldBindJSON(req); err != nil { ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, validation.Error(err), )) return } // 根据AppID查询小程序信息获取AppSecret miniProgram, err := h.readDB.MiniProgram.WithContext(ctx.RequestContext()). Where(h.readDB.MiniProgram.AppID.Eq(req.AppID)). First() if err != nil { h.logger.Error(fmt.Sprintf("查询小程序信息失败: %s", err.Error())) ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ServerError, "小程序不存在或查询失败", )) return } // 调用微信code2Session接口 openID, sessionKey, unionID, err := h.callCode2Session(ctx, req.AppID, miniProgram.AppSecret, req.JSCode) if err != nil { h.logger.Error(fmt.Sprintf("调用微信code2Session接口失败: %s", err.Error())) ctx.AbortWithError(core.Error( http.StatusInternalServerError, code.ServerError, err.Error(), )) return } res.Success = true res.Message = "登录成功" res.OpenID = openID res.SessionKey = sessionKey res.UnionID = unionID ctx.Payload(res) } } // callCode2Session 调用微信code2Session接口 func (h *handler) callCode2Session(ctx core.Context, appID, appSecret, jsCode string) (string, string, string, error) { // 构建请求URL url := "https://api.weixin.qq.com/sns/jscode2session" // 发送HTTP请求 client := httpclient.GetHttpClientWithContext(ctx.RequestContext()) resp, err := client.R(). SetQueryParams(map[string]string{ "appid": appID, "secret": appSecret, "js_code": jsCode, "grant_type": "authorization_code", }). Get(url) if err != nil { return "", "", "", fmt.Errorf("HTTP请求失败: %v", err) } if resp.StatusCode() != http.StatusOK { return "", "", "", fmt.Errorf("HTTP请求失败,状态码: %d", resp.StatusCode()) } var result Code2SessionResponse if err := json.Unmarshal(resp.Body(), &result); err != nil { return "", "", "", fmt.Errorf("解析响应失败: %v", err) } // 检查微信API返回的错误码 if result.ErrCode != 0 { return "", "", "", fmt.Errorf("微信API错误: errcode=%d, errmsg=%s", result.ErrCode, result.ErrMsg) } if result.OpenID == "" { return "", "", "", fmt.Errorf("获取到的openid为空") } return result.OpenID, result.SessionKey, result.UnionID, nil }