bindbox-game/internal/api/wechat/miniprogram_login.go
邹方成 1a285f4e23 feat(消息状态): 添加消息已读状态功能
- 新增消息已读状态表结构及模型
- 实现用户端标记消息为已读接口
- 添加管理端获取最新消息记录接口
- 实现微信小程序登录功能
- 更新相关API文档和路由配置
2025-10-18 18:24:42 +08:00

137 lines
4.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}