1c48fbdeaf
- Added WeChat Official Account configuration options to .env.example and config.go for OAuth2 integration. - Updated main.go to initialize WeChat OAuth handler and register routes for handling OAuth requests. - Enhanced documentation to include references for WeChat Official Account functionality. - Updated route registration to accommodate the new OAuth handler for improved API structure.
81 lines
2.4 KiB
Go
81 lines
2.4 KiB
Go
package handler
|
||
|
||
import (
|
||
"errors"
|
||
"net/http"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
|
||
"wx_service/internal/common/wechat_official/service"
|
||
"wx_service/internal/model"
|
||
)
|
||
|
||
type OAuthHandler struct {
|
||
oaService *service.WeChatOAService
|
||
}
|
||
|
||
func NewOAuthHandler(oaService *service.WeChatOAService) *OAuthHandler {
|
||
return &OAuthHandler{oaService: oaService}
|
||
}
|
||
|
||
type codeToUserRequest struct {
|
||
Code string `json:"code" binding:"required"`
|
||
WithUserInfo *bool `json:"with_userinfo"`
|
||
}
|
||
|
||
// CodeToUser 使用微信公众号网页授权 code 换取 openid,并可选拉取用户信息(需要 snsapi_userinfo 授权)。
|
||
func (h *OAuthHandler) CodeToUser(c *gin.Context) {
|
||
var req codeToUserRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "请求参数错误"))
|
||
return
|
||
}
|
||
|
||
withUserInfo := true
|
||
if req.WithUserInfo != nil {
|
||
withUserInfo = *req.WithUserInfo
|
||
}
|
||
|
||
token, err := h.oaService.ExchangeCode(c.Request.Context(), req.Code)
|
||
if err != nil {
|
||
switch {
|
||
case errors.Is(err, service.ErrWeChatOANotConfigured):
|
||
c.JSON(http.StatusServiceUnavailable, model.Error(http.StatusServiceUnavailable, "未配置公众号服务,请联系管理员"))
|
||
case errors.Is(err, service.ErrWeChatOACodeRequired):
|
||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "code 不能为空"))
|
||
default:
|
||
var apiErr *service.WeChatOAError
|
||
if errors.As(err, &apiErr) {
|
||
c.JSON(http.StatusBadGateway, model.Error(http.StatusBadGateway, "微信接口异常,请稍后重试"))
|
||
return
|
||
}
|
||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "获取 openid 失败,请稍后重试"))
|
||
}
|
||
return
|
||
}
|
||
|
||
payload := gin.H{
|
||
"openid": token.OpenID,
|
||
"unionid": token.UnionID,
|
||
"scope": token.Scope,
|
||
"expires_in": token.ExpiresIn,
|
||
}
|
||
|
||
if withUserInfo {
|
||
info, err := h.oaService.FetchUserInfo(c.Request.Context(), token.AccessToken, token.OpenID)
|
||
if err != nil {
|
||
// 可能是 scope 不够(snsapi_base),此时仍返回 openid,并给出提示。
|
||
payload["userinfo_error"] = "未获取到用户信息(可能未授权 snsapi_userinfo)"
|
||
} else {
|
||
// 统一从 userinfo 里回填 unionid(如果有)
|
||
if info.UnionID != "" {
|
||
payload["unionid"] = info.UnionID
|
||
}
|
||
payload["userinfo"] = info
|
||
}
|
||
}
|
||
|
||
// 为安全起见,这里不向前端返回 access_token/refresh_token。
|
||
c.JSON(http.StatusOK, model.Success(payload))
|
||
}
|