微信相关能力
This commit is contained in:
185
pkg/wechat/official_account.go
Normal file
185
pkg/wechat/official_account.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package wechat
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// OfficialAccountService 微信公众号服务实现
|
||||
type OfficialAccountService struct {
|
||||
app *officialAccount.OfficialAccount
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewOfficialAccountService 创建微信公众号服务
|
||||
func NewOfficialAccountService(app *officialAccount.OfficialAccount, logger *zap.Logger) *OfficialAccountService {
|
||||
return &OfficialAccountService{
|
||||
app: app,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserInfo 通过授权码获取用户基本信息(静默授权)
|
||||
// 返回 OpenID 和 UnionID(如果有)
|
||||
func (s *OfficialAccountService) GetUserInfo(ctx context.Context, code string) (openID, unionID string, err error) {
|
||||
if code == "" {
|
||||
return "", "", errors.New(errors.CodeInvalidParam, "授权码不能为空")
|
||||
}
|
||||
|
||||
// 设置为静默授权模式(snsapi_base),只能获取 OpenID
|
||||
s.app.OAuth.SetScopes([]string{"snsapi_base"})
|
||||
|
||||
user, err := s.app.OAuth.UserFromCode(code)
|
||||
if err != nil {
|
||||
s.logger.Error("微信 OAuth 授权失败",
|
||||
zap.String("code", code),
|
||||
zap.Error(err),
|
||||
)
|
||||
return "", "", errors.Wrap(errors.CodeWechatOAuthFailed, err)
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
s.logger.Error("微信 OAuth 返回空用户信息", zap.String("code", code))
|
||||
return "", "", errors.New(errors.CodeWechatOAuthFailed, "获取用户信息失败")
|
||||
}
|
||||
|
||||
openID = user.GetOpenID()
|
||||
|
||||
// 从原始数据中获取 UnionID
|
||||
raw, _ := user.GetRaw()
|
||||
if raw != nil {
|
||||
if uid, ok := (*raw)["unionid"].(string); ok {
|
||||
unionID = uid
|
||||
}
|
||||
}
|
||||
|
||||
s.logger.Debug("微信 OAuth 授权成功",
|
||||
zap.String("open_id", openID),
|
||||
zap.String("union_id", unionID),
|
||||
)
|
||||
|
||||
return openID, unionID, nil
|
||||
}
|
||||
|
||||
// GetUserInfoDetailed 通过授权码获取用户详细信息(用户授权)
|
||||
// 需要用户点击授权,可以获取昵称、头像等信息
|
||||
func (s *OfficialAccountService) GetUserInfoDetailed(ctx context.Context, code string) (*UserInfo, error) {
|
||||
if code == "" {
|
||||
return nil, errors.New(errors.CodeInvalidParam, "授权码不能为空")
|
||||
}
|
||||
|
||||
// 设置为用户信息授权模式(snsapi_userinfo)
|
||||
s.app.OAuth.SetScopes([]string{"snsapi_userinfo"})
|
||||
|
||||
user, err := s.app.OAuth.UserFromCode(code)
|
||||
if err != nil {
|
||||
s.logger.Error("微信 OAuth 授权失败",
|
||||
zap.String("code", code),
|
||||
zap.Error(err),
|
||||
)
|
||||
return nil, errors.Wrap(errors.CodeWechatOAuthFailed, err)
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
s.logger.Error("微信 OAuth 返回空用户信息", zap.String("code", code))
|
||||
return nil, errors.New(errors.CodeWechatOAuthFailed, "获取用户信息失败")
|
||||
}
|
||||
|
||||
raw, _ := user.GetRaw()
|
||||
|
||||
userInfo := &UserInfo{
|
||||
OpenID: user.GetOpenID(),
|
||||
}
|
||||
|
||||
if raw != nil {
|
||||
if uid, ok := (*raw)["unionid"].(string); ok {
|
||||
userInfo.UnionID = uid
|
||||
}
|
||||
if nickname, ok := (*raw)["nickname"].(string); ok {
|
||||
userInfo.Nickname = nickname
|
||||
}
|
||||
if headimgurl, ok := (*raw)["headimgurl"].(string); ok {
|
||||
userInfo.Avatar = headimgurl
|
||||
}
|
||||
if sex, ok := (*raw)["sex"].(float64); ok {
|
||||
userInfo.Sex = int(sex)
|
||||
}
|
||||
if province, ok := (*raw)["province"].(string); ok {
|
||||
userInfo.Province = province
|
||||
}
|
||||
if city, ok := (*raw)["city"].(string); ok {
|
||||
userInfo.City = city
|
||||
}
|
||||
if country, ok := (*raw)["country"].(string); ok {
|
||||
userInfo.Country = country
|
||||
}
|
||||
}
|
||||
|
||||
s.logger.Debug("微信 OAuth 获取用户详细信息成功",
|
||||
zap.String("open_id", userInfo.OpenID),
|
||||
zap.String("nickname", userInfo.Nickname),
|
||||
)
|
||||
|
||||
return userInfo, nil
|
||||
}
|
||||
|
||||
// GetUserInfoByToken 通过 AccessToken 和 OpenID 获取用户详细信息
|
||||
func (s *OfficialAccountService) GetUserInfoByToken(ctx context.Context, accessToken, openID string) (*UserInfo, error) {
|
||||
if accessToken == "" || openID == "" {
|
||||
return nil, errors.New(errors.CodeInvalidParam, "AccessToken 和 OpenID 不能为空")
|
||||
}
|
||||
|
||||
user, err := s.app.OAuth.UserFromToken(accessToken, openID)
|
||||
if err != nil {
|
||||
s.logger.Error("通过 Token 获取微信用户信息失败",
|
||||
zap.String("open_id", openID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return nil, errors.Wrap(errors.CodeWechatUserInfoFailed, err)
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
s.logger.Error("微信返回空用户信息", zap.String("open_id", openID))
|
||||
return nil, errors.New(errors.CodeWechatUserInfoFailed, "获取用户信息失败")
|
||||
}
|
||||
|
||||
raw, _ := user.GetRaw()
|
||||
|
||||
userInfo := &UserInfo{
|
||||
OpenID: user.GetOpenID(),
|
||||
}
|
||||
|
||||
if raw != nil {
|
||||
if uid, ok := (*raw)["unionid"].(string); ok {
|
||||
userInfo.UnionID = uid
|
||||
}
|
||||
if nickname, ok := (*raw)["nickname"].(string); ok {
|
||||
userInfo.Nickname = nickname
|
||||
}
|
||||
if headimgurl, ok := (*raw)["headimgurl"].(string); ok {
|
||||
userInfo.Avatar = headimgurl
|
||||
}
|
||||
if sex, ok := (*raw)["sex"].(float64); ok {
|
||||
userInfo.Sex = int(sex)
|
||||
}
|
||||
if province, ok := (*raw)["province"].(string); ok {
|
||||
userInfo.Province = province
|
||||
}
|
||||
if city, ok := (*raw)["city"].(string); ok {
|
||||
userInfo.City = city
|
||||
}
|
||||
if country, ok := (*raw)["country"].(string); ok {
|
||||
userInfo.Country = country
|
||||
}
|
||||
}
|
||||
|
||||
s.logger.Debug("通过 Token 获取微信用户详细信息成功",
|
||||
zap.String("open_id", userInfo.OpenID),
|
||||
zap.String("nickname", userInfo.Nickname),
|
||||
)
|
||||
|
||||
return userInfo, nil
|
||||
}
|
||||
Reference in New Issue
Block a user