1
0
mirror of https://github.com/silenceper/wechat.git synced 2026-02-04 12:52:27 +08:00

开放平台基本框架

This commit is contained in:
silenceper
2020-01-21 16:36:34 +08:00
parent 15ebd71a04
commit 7e8c94a6c4
11 changed files with 401 additions and 7 deletions

View File

@@ -2,4 +2,7 @@
[官方文档](https://developers.weixin.qq.com/minigame/dev/api-backend/)
## 快速入门
## 快速入门
```go
```

View File

@@ -2,4 +2,19 @@
[官方文档](https://developers.weixin.qq.com/miniprogram/dev/framework/)
## 包说明
- analysis 数据分析相关API
## 快速入门
```go
wc := wechat.NewWechat()
memory := cache.NewMemory()
cfg := &miniConfig.Config{
AppID: "xxx",
AppSecret: "xxx",
Cache: memory,
}
miniprogram := wc.GetMiniProgram(cfg)
miniprogram.GetAnalysis().GetAnalysisDailyRetain()
```

View File

@@ -72,6 +72,7 @@ const (
)
const (
//微信开放平台需要用到
// InfoTypeVerifyTicket 返回ticket
InfoTypeVerifyTicket InfoType = "component_verify_ticket"
// InfoTypeAuthorized 授权

View File

@@ -24,9 +24,9 @@ type OfficialAccount struct {
//NewOfficialAccount 实例化公众号API
func NewOfficialAccount(cfg *config.Config) *OfficialAccount {
if cfg.Cache == nil {
panic("cache未设置")
}
//if cfg.Cache == nil {
// panic("cache未设置")
//}
ctx := &context.Context{
Config: cfg,
}

View File

@@ -2,4 +2,52 @@
[官方文档](https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Third_party_platform_appid.html)
## 快速入门
## 快速入门
```go
wc := wechat.NewWechat()
memory := cache.NewMemory()
cfg := &openplatform.Config{
AppID: "xxx",
AppSecret: "xxx",
Token: "xxx",
EncodingAESKey: "xxx",
Cache: memory,
}
//授权的第三方公众号的appID
appID := "xxx"
openPlatform := wc.GetOpenPlatform(cfg)
officialAccount := openPlatform.GetOfficialAccount(appID)
// 传入request和responseWriter
server := officialAccount.GetServer(req, rw)
//设置接收消息的处理方法
server.SetMessageHandler(func(msg message.MixMessage) *message.Reply {
if msg.InfoType == message.InfoTypeVerifyTicket {
componentVerifyTicket, err := openPlatform.SetComponentAccessToken(msg.ComponentVerifyTicket)
if err != nil {
log.Println(err)
return nil
}
//debug
fmt.Println(componentVerifyTicket)
rw.Write([]byte("success"))
return nil
}
//handle other message
//
return nil
})
//处理消息接收以及回复
err := server.Serve()
if err != nil {
fmt.Println(err)
return
}
//发送回复的消息
server.Send()
```

View File

@@ -0,0 +1,14 @@
package config
import (
"github.com/silenceper/wechat/cache"
)
//Config config for 微信开放平台
type Config struct {
AppID string `json:"app_id"` //appid
AppSecret string `json:"app_secret"` //appsecret
Token string `json:"token"` //token
EncodingAESKey string `json:"encoding_aes_key"` //EncodingAESKey
Cache cache.Cache
}

View File

@@ -0,0 +1,224 @@
package context
import (
"encoding/json"
"fmt"
"time"
"github.com/silenceper/wechat/util"
)
const (
componentAccessTokenURL = "https://api.weixin.qq.com/cgi-bin/component/api_component_token"
getPreCodeURL = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=%s"
queryAuthURL = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=%s"
refreshTokenURL = "https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=%s"
getComponentInfoURL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=%s"
getComponentConfigURL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option?component_access_token=%s"
)
// ComponentAccessToken 第三方平台
type ComponentAccessToken struct {
AccessToken string `json:"component_access_token"`
ExpiresIn int64 `json:"expires_in"`
}
// GetComponentAccessToken 获取 ComponentAccessToken
func (ctx *Context) GetComponentAccessToken() (string, error) {
accessTokenCacheKey := fmt.Sprintf("component_access_token_%s", ctx.AppID)
val := ctx.Cache.Get(accessTokenCacheKey)
if val == nil {
return "", fmt.Errorf("cann't get component access token")
}
return val.(string), nil
}
// SetComponentAccessToken 通过component_verify_ticket 获取 ComponentAccessToken
func (ctx *Context) SetComponentAccessToken(verifyTicket string) (*ComponentAccessToken, error) {
body := map[string]string{
"component_appid": ctx.AppID,
"component_appsecret": ctx.AppSecret,
"component_verify_ticket": verifyTicket,
}
respBody, err := util.PostJSON(componentAccessTokenURL, body)
if err != nil {
return nil, err
}
at := &ComponentAccessToken{}
if err := json.Unmarshal(respBody, at); err != nil {
return nil, err
}
accessTokenCacheKey := fmt.Sprintf("component_access_token_%s", ctx.AppID)
expires := at.ExpiresIn - 1500
if err := ctx.Cache.Set(accessTokenCacheKey, at.AccessToken, time.Duration(expires)*time.Second); err != nil {
return nil, nil
}
return at, nil
}
// GetPreCode 获取预授权码
func (ctx *Context) GetPreCode() (string, error) {
cat, err := ctx.GetComponentAccessToken()
if err != nil {
return "", err
}
req := map[string]string{
"component_appid": ctx.AppID,
}
uri := fmt.Sprintf(getPreCodeURL, cat)
body, err := util.PostJSON(uri, req)
if err != nil {
return "", err
}
var ret struct {
PreCode string `json:"pre_auth_code"`
}
if err := json.Unmarshal(body, &ret); err != nil {
return "", err
}
return ret.PreCode, nil
}
// ID 微信返回接口中各种类型字段
type ID struct {
ID int `json:"id"`
}
// AuthBaseInfo 授权的基本信息
type AuthBaseInfo struct {
AuthrAccessToken
FuncInfo []AuthFuncInfo `json:"func_info"`
}
// AuthFuncInfo 授权的接口内容
type AuthFuncInfo struct {
FuncscopeCategory ID `json:"funcscope_category"`
}
// AuthrAccessToken 授权方AccessToken
type AuthrAccessToken struct {
Appid string `json:"authorizer_appid"`
AccessToken string `json:"authorizer_access_token"`
ExpiresIn int64 `json:"expires_in"`
RefreshToken string `json:"authorizer_refresh_token"`
}
// QueryAuthCode 使用授权码换取公众号或小程序的接口调用凭据和授权信息
func (ctx *Context) QueryAuthCode(authCode string) (*AuthBaseInfo, error) {
cat, err := ctx.GetComponentAccessToken()
if err != nil {
return nil, err
}
req := map[string]string{
"component_appid": ctx.AppID,
"authorization_code": authCode,
}
uri := fmt.Sprintf(queryAuthURL, cat)
body, err := util.PostJSON(uri, req)
if err != nil {
return nil, err
}
var ret struct {
Info *AuthBaseInfo `json:"authorization_info"`
}
if err := json.Unmarshal(body, &ret); err != nil {
return nil, err
}
return ret.Info, nil
}
// RefreshAuthrToken 获取(刷新)授权公众号或小程序的接口调用凭据(令牌)
func (ctx *Context) RefreshAuthrToken(appid, refreshToken string) (*AuthrAccessToken, error) {
cat, err := ctx.GetComponentAccessToken()
if err != nil {
return nil, err
}
req := map[string]string{
"component_appid": ctx.AppID,
"authorizer_appid": appid,
"authorizer_refresh_token": refreshToken,
}
uri := fmt.Sprintf(refreshTokenURL, cat)
body, err := util.PostJSON(uri, req)
if err != nil {
return nil, err
}
ret := &AuthrAccessToken{}
if err := json.Unmarshal(body, ret); err != nil {
return nil, err
}
authrTokenKey := "authorizer_access_token_" + appid
if err := ctx.Cache.Set(authrTokenKey, ret.AccessToken, time.Minute*80); err != nil {
return nil, err
}
return ret, nil
}
// GetAuthrAccessToken 获取授权方AccessToken
func (ctx *Context) GetAuthrAccessToken(appid string) (string, error) {
authrTokenKey := "authorizer_access_token_" + appid
val := ctx.Cache.Get(authrTokenKey)
if val == nil {
return "", fmt.Errorf("cannot get authorizer %s access token", appid)
}
return val.(string), nil
}
// AuthorizerInfo 授权方详细信息
type AuthorizerInfo struct {
NickName string `json:"nick_name"`
HeadImg string `json:"head_img"`
ServiceTypeInfo ID `json:"service_type_info"`
VerifyTypeInfo ID `json:"verify_type_info"`
UserName string `json:"user_name"`
PrincipalName string `json:"principal_name"`
BusinessInfo struct {
OpenStore string `json:"open_store"`
OpenScan string `json:"open_scan"`
OpenPay string `json:"open_pay"`
OpenCard string `json:"open_card"`
OpenShake string `json:"open_shake"`
}
Alias string `json:"alias"`
QrcodeURL string `json:"qrcode_url"`
}
// GetAuthrInfo 获取授权方的帐号基本信息
func (ctx *Context) GetAuthrInfo(appid string) (*AuthorizerInfo, *AuthBaseInfo, error) {
cat, err := ctx.GetComponentAccessToken()
if err != nil {
return nil, nil, err
}
req := map[string]string{
"component_appid": ctx.AppID,
"authorizer_appid": appid,
}
uri := fmt.Sprintf(getComponentInfoURL, cat)
body, err := util.PostJSON(uri, req)
if err != nil {
return nil, nil, err
}
var ret struct {
AuthorizerInfo *AuthorizerInfo `json:"authorizer_info"`
AuthorizationInfo *AuthBaseInfo `json:"authorization_info"`
}
if err := json.Unmarshal(body, &ret); err != nil {
return nil, nil, err
}
return ret.AuthorizerInfo, ret.AuthorizationInfo, nil
}

View File

@@ -0,0 +1,10 @@
package context
import (
"github.com/silenceper/wechat/openplatform/config"
)
// Context struct
type Context struct {
*config.Config
}

View File

@@ -0,0 +1,32 @@
package officialaccount
import (
"github.com/silenceper/wechat/officialaccount"
offConfig "github.com/silenceper/wechat/officialaccount/config"
offContext "github.com/silenceper/wechat/officialaccount/context"
opContext "github.com/silenceper/wechat/openplatform/context"
)
type OfficialAccount struct {
//授权的公众号的appID
appID string
*officialaccount.OfficialAccount
opContext *opContext.Context
}
//NewOfficialAccount 实例化
//appID :为授权方公众号 APPID非开放平台第三方平台 APPID
func NewOfficialAccount(opCtx *opContext.Context, appID string) *OfficialAccount {
officialAccount := officialaccount.NewOfficialAccount(&offConfig.Config{
AppID: opCtx.AppID,
EncodingAESKey: opCtx.EncodingAESKey,
Token: opCtx.Token,
Cache: opCtx.Cache,
})
//设置获取access_token的函数
officialAccount.GetContext().SetGetAccessTokenFunc(func(offCtx *offContext.Context) (accessToken string, err error) {
// 获取授权方的access_token
return opCtx.GetAuthrAccessToken(appID)
})
return &OfficialAccount{appID: appID, OfficialAccount: officialAccount}
}

View File

@@ -0,0 +1,28 @@
package openplatform
import (
"github.com/silenceper/wechat/openplatform/config"
"github.com/silenceper/wechat/openplatform/context"
"github.com/silenceper/wechat/openplatform/officialaccount"
)
//OpenPlatform 微信开放平台相关api
type OpenPlatform struct {
*context.Context
}
//NewOpenPlatform new openplatform
func NewOpenPlatform(cfg *config.Config) *OpenPlatform {
if cfg.Cache == nil {
panic("cache 未设置")
}
ctx := &context.Context{
Config: cfg,
}
return &OpenPlatform{ctx}
}
//GetOfficialAccount 公众号代处理
func (openPlatform *OpenPlatform) GetOfficialAccount(appID string) *officialaccount.OfficialAccount {
return officialaccount.NewOfficialAccount(openPlatform.Context, appID)
}

View File

@@ -1,17 +1,20 @@
package wechat
import (
"github.com/silenceper/wechat/cache"
"github.com/silenceper/wechat/miniprogram"
miniConfig "github.com/silenceper/wechat/miniprogram/config"
payConfig "github.com/silenceper/wechat/pay/config"
"github.com/silenceper/wechat/officialaccount"
offConfig "github.com/silenceper/wechat/officialaccount/config"
"github.com/silenceper/wechat/openplatform"
opConfig "github.com/silenceper/wechat/openplatform/config"
"github.com/silenceper/wechat/pay"
payConfig "github.com/silenceper/wechat/pay/config"
)
// Wechat struct
type Wechat struct {
cache cache.Cache
}
// Config for user
@@ -26,13 +29,24 @@ func NewWechat() *Wechat {
return &Wechat{}
}
//SetCache 设置cache
func (wc *Wechat) SetCache(cahce cache.Cache) {
wc.cache = cahce
}
//GetOfficialAccount 获取微信公众号实例
func (wc *Wechat) GetOfficialAccount(cfg *offConfig.Config) *officialaccount.OfficialAccount {
if cfg.Cache == nil {
cfg.Cache = wc.cache
}
return officialaccount.NewOfficialAccount(cfg)
}
// GetMiniProgram 获取小程序的实例
func (wc *Wechat) GetMiniProgram(cfg *miniConfig.Config) *miniprogram.MiniProgram {
if cfg.Cache == nil {
cfg.Cache = wc.cache
}
return miniprogram.NewMiniProgram(cfg)
}
@@ -40,3 +54,8 @@ func (wc *Wechat) GetMiniProgram(cfg *miniConfig.Config) *miniprogram.MiniProgra
func (wc *Wechat) GetPay(cfg *payConfig.Config) *pay.Pay {
return pay.NewPay(cfg)
}
// GetOpenPlatform 获取微信开放平台的实例
func (wc *Wechat) GetOpenPlatform(cfg *opConfig.Config) *openplatform.OpenPlatform {
return openplatform.NewOpenPlatform(cfg)
}