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 }