1
0
mirror of https://github.com/silenceper/wechat.git synced 2026-02-06 05:32:26 +08:00

Compare commits

..

4 Commits

Author SHA1 Message Date
silenceper
39dbfd1c13 fix linux build failed when cgo disable (#453) 2021-08-30 18:00:08 +08:00
silenceper
5ad3475cdb 修正字段问题 (#451)
fix #443
2021-08-30 15:13:35 +08:00
silenceper
bcc41989ed Update go.yml (#452) 2021-08-30 15:13:09 +08:00
silenceper
e189b87e71 merge branch release-2.0 to v2 (#450)
* feat: add/delete subscribe template (#449)

* feat: 添加 SDKApiNotOpen 错误信息 (#448)

* 添加微信客服SDK

* polish:优化签名函数

* polish:优化注释内容

* polish:复用已有的Token以及CommonError,移除无用的输出

* polish:复用已有的消息加解密

* fix:修复错误信息被覆盖的问题

* polish:go fmt 文件

* polish:客服链接支持自定义参数并更新注释文档内容

* feat:支持微信客服回调请求的校验和消息的解析,复用原有的Signature和DecryptMsg方法

* feat:对外暴露SDKApiForbidden等错误

可以通过调用升级服务相关接口然后根据该错误判断微信客服配置来源

* feat:添加无效的open_kfid错误信息

* fix: 添加SDKApiNotOpen 错误信息

目前主要用于判断客户是否关闭了API授权,如果客户关闭了API功能导致服务异常,则可以引导用户执行相应的操作重新开启改功能

Co-authored-by: Afeyer <afeyer@h5base.cn>

Co-authored-by: ZmJ <wzmmmmj@gmail.com>
Co-authored-by: Afeyer <1500527791@qq.com>
Co-authored-by: Afeyer <afeyer@h5base.cn>
2021-08-30 11:14:25 +08:00
162 changed files with 1208 additions and 6162 deletions

View File

@@ -1,3 +1,2 @@
## 问题及现象 ## 问题及现象
<!-- 描述你的问题现象,报错**贴截图**粘贴或者贴具体信息,提供**必要的代码段** <!-- 描述你的问题现象,报错**贴截图**粘贴或者贴具体信息,提供**必要的代码段**

View File

@@ -10,17 +10,16 @@ jobs:
golangci: golangci:
strategy: strategy:
matrix: matrix:
go-version: [1.16.x,1.17.x,1.18.x] go-version: [1.15.x]
name: golangci-lint name: golangci-lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/setup-go@v3 - uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v3.2.0 uses: golangci/golangci-lint-action@v2
with: with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: latest version: v1.31
build: build:
name: Test name: Test
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -34,18 +33,12 @@ jobs:
image: memcached image: memcached
ports: ports:
- 11211:11211 - 11211:11211
# strategy set
strategy:
matrix:
go: ["1.16", "1.17", "1.18"]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Go 1.x - name: Set up Go 1.x
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: ${{ matrix.go }} go-version: ^1.13
id: go id: go
- name: Test - name: Test
run: go test -v -race ./... run: go test -v -race ./...

View File

@@ -1,29 +0,0 @@
name: goreleaser
on:
push:
tags:
- '*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
-
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.15
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -36,13 +36,8 @@ linters:
- whitespace - whitespace
issues: issues:
include:
- EXC0002 # disable excluding of issues about comments from golint
exclude-rules:
- linters:
- stylecheck
text: "ST1000:"
# Excluding configuration per-path, per-linter, per-text and per-source # Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
- path: _test\.go - path: _test\.go
linters: linters:
- gomnd - gomnd
@@ -57,10 +52,10 @@ linters-settings:
lines: 66 lines: 66
statements: 40 statements: 40
#issues: issues:
# include: include:
# - EXC0002 # disable excluding of issues about comments from golint - EXC0002 # disable excluding of issues about comments from golint
# exclude-rules: exclude-rules:
# - linters: - linters:
# - stylecheck - stylecheck
# text: "ST1000:" text: "ST1000:"

View File

@@ -1,29 +0,0 @@
# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
before:
hooks:
# You may remove this if you don't use go modules.
- go mod download
# you may remove this if you don't need go generate
- go generate ./...
builds:
- skip: true
archives:
- replacements:
darwin: Darwin
linux: Linux
windows: Windows
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'

View File

@@ -1,23 +1,18 @@
# WeChat SDK for Go # WeChat SDK for Go
![Go](https://github.com/silenceper/wechat/workflows/Go/badge.svg?branch=release-2.0) ![Go](https://github.com/silenceper/wechat/workflows/Go/badge.svg?branch=release-2.0)
[![Go Report Card](https://goreportcard.com/badge/github.com/silenceper/wechat)](https://goreportcard.com/report/github.com/silenceper/wechat) [![Go Report Card](https://goreportcard.com/badge/github.com/silenceper/wechat)](https://goreportcard.com/report/github.com/silenceper/wechat)
[![pkg](https://img.shields.io/badge/dev-reference-007d9c?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/silenceper/wechat/v2?tab=doc) [![pkg](https://img.shields.io/badge/dev-reference-007d9c?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/silenceper/wechat/v2?tab=doc)
![version](https://img.shields.io/badge/version-v2-green)
使用Golang开发的微信SDK简单、易用。 使用Golang开发的微信SDK简单、易用。
> 注意:当前版本为v2版本v1版本已废弃 >当前版本为v2版本
## 文档 && 例子 ## 文档 && 例子
[API列表](https://github.com/silenceper/wechat/tree/v2/doc/api)
[Wechat SDK 2.0 文档](https://silenceper.com/wechat) [Wechat SDK 2.0 文档](https://silenceper.com/wechat)
[Wechat SDK 2.0 例子](https://github.com/gowechat/example) [Wechat SDK 2.0 例子](https://github.com/gowechat/example)
## 快速开始 ## 快速开始
``` ```
import "github.com/silenceper/wechat/v2" import "github.com/silenceper/wechat/v2"
``` ```
@@ -25,41 +20,40 @@ import "github.com/silenceper/wechat/v2"
以下是一个微信公众号处理消息接收以及回复的例子: 以下是一个微信公众号处理消息接收以及回复的例子:
```go ```go
// 使用memcache保存access_token也可选择redis或自定义cache //使用memcache保存access_token也可选择redis或自定义cache
wc := wechat.NewWechat() wc := wechat.NewWechat()
memory := cache.NewMemory() memory := cache.NewMemory()
cfg := &offConfig.Config{ cfg := &offConfig.Config{
AppID: "xxx", AppID: "xxx",
AppSecret: "xxx", AppSecret: "xxx",
Token: "xxx", Token: "xxx",
// EncodingAESKey: "xxxx", //EncodingAESKey: "xxxx",
Cache: memory, Cache: memory,
} }
officialAccount := wc.GetOfficialAccount(cfg) officialAccount := wc.GetOfficialAccount(cfg)
// 传入request和responseWriter // 传入request和responseWriter
server := officialAccount.GetServer(req, rw) server := officialAccount.GetServer(req, rw)
// 设置接收消息的处理方法 //设置接收消息的处理方法
server.SetMessageHandler(func(msg *message.MixMessage) *message.Reply { server.SetMessageHandler(func(msg *message.MixMessage) *message.Reply {
// 回复消息:演示回复用户发送的消息 //回复消息:演示回复用户发送的消息
text := message.NewText(msg.Content) text := message.NewText(msg.Content)
return &message.Reply{MsgType: message.MsgTypeText, MsgData: text} return &message.Reply{MsgType: message.MsgTypeText, MsgData: text}
}) })
// 处理消息接收以及回复 //处理消息接收以及回复
err := server.Serve() err := server.Serve()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
// 发送回复的消息 //发送回复的消息
server.Send() server.Send()
``` ```
## 目录说明 ## 目录说明
- officialaccount: 微信公众号API - officialaccount: 微信公众号API
- miniprogram: 小程序API - miniprogram: 小程序API
- minigame:小游戏API - minigame:小游戏API
@@ -67,16 +61,12 @@ server.Send()
- openplatform:开放平台API - openplatform:开放平台API
- work:企业微信 - work:企业微信
- aispeech:智能对话 - aispeech:智能对话
- doc: api文档
## 贡献 ## 贡献
- 在[API列表](https://github.com/silenceper/wechat/tree/v2/doc/api)中查看哪些API未实现
- 提交issue描述需要贡献的内容 - 提交issue描述需要贡献的内容
- 完成更改后提交PR - 完成更改后提交PR
## 公众号 ## 公众号
![img](https://silenceper.oss-cn-beijing.aliyuncs.com/qrcode/search_study_program.png) ![img](https://silenceper.oss-cn-beijing.aliyuncs.com/qrcode/search_study_program.png)
## License ## License

10
cache/memory.go vendored
View File

@@ -5,7 +5,7 @@ import (
"time" "time"
) )
// Memory struct contains *memcache.Client //Memory struct contains *memcache.Client
type Memory struct { type Memory struct {
sync.Mutex sync.Mutex
@@ -17,14 +17,14 @@ type data struct {
Expired time.Time Expired time.Time
} }
// NewMemory create new memcache //NewMemory create new memcache
func NewMemory() *Memory { func NewMemory() *Memory {
return &Memory{ return &Memory{
data: map[string]*data{}, data: map[string]*data{},
} }
} }
// Get return cached value //Get return cached value
func (mem *Memory) Get(key string) interface{} { func (mem *Memory) Get(key string) interface{} {
if ret, ok := mem.data[key]; ok { if ret, ok := mem.data[key]; ok {
if ret.Expired.Before(time.Now()) { if ret.Expired.Before(time.Now()) {
@@ -48,7 +48,7 @@ func (mem *Memory) IsExist(key string) bool {
return false return false
} }
// Set cached value with key and expire time. //Set cached value with key and expire time.
func (mem *Memory) Set(key string, val interface{}, timeout time.Duration) (err error) { func (mem *Memory) Set(key string, val interface{}, timeout time.Duration) (err error) {
mem.Lock() mem.Lock()
defer mem.Unlock() defer mem.Unlock()
@@ -60,7 +60,7 @@ func (mem *Memory) Set(key string, val interface{}, timeout time.Duration) (err
return nil return nil
} }
// Delete delete value in memcache. //Delete delete value in memcache.
func (mem *Memory) Delete(key string) error { func (mem *Memory) Delete(key string) error {
mem.deleteKey(key) mem.deleteKey(key)
return nil return nil

111
cache/redis.go vendored
View File

@@ -1,72 +1,111 @@
package cache package cache
import ( import (
"context" "encoding/json"
"time" "time"
"github.com/go-redis/redis/v8" "github.com/gomodule/redigo/redis"
) )
// Redis .redis cache //Redis redis cache
type Redis struct { type Redis struct {
ctx context.Context conn *redis.Pool
conn redis.UniversalClient
} }
// RedisOpts redis 连接属性 //RedisOpts redis 连接属性
type RedisOpts struct { type RedisOpts struct {
Host string `yml:"host" json:"host"` Host string `yml:"host" json:"host"`
Password string `yml:"password" json:"password"` Password string `yml:"password" json:"password"`
Database int `yml:"database" json:"database"` Database int `yml:"database" json:"database"`
MaxIdle int `yml:"max_idle" json:"max_idle"` MaxIdle int `yml:"max_idle" json:"max_idle"`
MaxActive int `yml:"max_active" json:"max_active"` MaxActive int `yml:"max_active" json:"max_active"`
IdleTimeout int `yml:"idle_timeout" json:"idle_timeout"` // second IdleTimeout int `yml:"idle_timeout" json:"idle_timeout"` //second
} }
// NewRedis 实例化 //NewRedis 实例化
func NewRedis(ctx context.Context, opts *RedisOpts) *Redis { func NewRedis(opts *RedisOpts) *Redis {
conn := redis.NewUniversalClient(&redis.UniversalOptions{ pool := &redis.Pool{
Addrs: []string{opts.Host}, MaxActive: opts.MaxActive,
DB: opts.Database, MaxIdle: opts.MaxIdle,
Password: opts.Password, IdleTimeout: time.Second * time.Duration(opts.IdleTimeout),
IdleTimeout: time.Second * time.Duration(opts.IdleTimeout), Dial: func() (redis.Conn, error) {
MinIdleConns: opts.MaxIdle, return redis.Dial("tcp", opts.Host,
}) redis.DialDatabase(opts.Database),
return &Redis{ctx: ctx, conn: conn} redis.DialPassword(opts.Password),
)
},
TestOnBorrow: func(conn redis.Conn, t time.Time) error {
if time.Since(t) < time.Minute {
return nil
}
_, err := conn.Do("PING")
return err
},
}
return &Redis{pool}
} }
// SetConn 设置conn //SetRedisPool 设置redis连接池
func (r *Redis) SetConn(conn redis.UniversalClient) { func (r *Redis) SetRedisPool(pool *redis.Pool) {
r.conn = pool
}
//SetConn 设置conn
func (r *Redis) SetConn(conn *redis.Pool) {
r.conn = conn r.conn = conn
} }
// SetRedisCtx 设置redis ctx 参数 //Get 获取一个值
func (r *Redis) SetRedisCtx(ctx context.Context) {
r.ctx = ctx
}
// Get 获取一个值
func (r *Redis) Get(key string) interface{} { func (r *Redis) Get(key string) interface{} {
result, err := r.conn.Do(r.ctx, "GET", key).Result() conn := r.conn.Get()
if err != nil { defer conn.Close()
var data []byte
var err error
if data, err = redis.Bytes(conn.Do("GET", key)); err != nil {
return nil return nil
} }
return result var reply interface{}
if err = json.Unmarshal(data, &reply); err != nil {
return nil
}
return reply
} }
// Set 设置一个值 //Set 设置一个值
func (r *Redis) Set(key string, val interface{}, timeout time.Duration) error { func (r *Redis) Set(key string, val interface{}, timeout time.Duration) (err error) {
return r.conn.SetEX(r.ctx, key, val, timeout).Err() conn := r.conn.Get()
defer conn.Close()
var data []byte
if data, err = json.Marshal(val); err != nil {
return
}
_, err = conn.Do("SETEX", key, int64(timeout/time.Second), data)
return
} }
// IsExist 判断key是否存在 //IsExist 判断key是否存在
func (r *Redis) IsExist(key string) bool { func (r *Redis) IsExist(key string) bool {
result, _ := r.conn.Exists(r.ctx, key).Result() conn := r.conn.Get()
defer conn.Close()
return result > 0 a, _ := conn.Do("EXISTS", key)
i := a.(int64)
return i > 0
} }
// Delete 删除 //Delete 删除
func (r *Redis) Delete(key string) error { func (r *Redis) Delete(key string) error {
return r.conn.Del(r.ctx, key).Err() conn := r.conn.Get()
defer conn.Close()
if _, err := conn.Do("DEL", key); err != nil {
return err
}
return nil
} }

29
cache/redis_test.go vendored
View File

@@ -1,40 +1,33 @@
package cache package cache
import ( import (
"context"
"testing" "testing"
"time" "time"
) )
func TestRedis(t *testing.T) { func TestRedis(t *testing.T) {
var ( opts := &RedisOpts{
timeoutDuration = time.Second Host: "127.0.0.1:6379",
ctx = context.Background() }
opts = &RedisOpts{ redis := NewRedis(opts)
Host: "127.0.0.1:6379",
}
redis = NewRedis(ctx, opts)
err error
val = "silenceper"
key = "username"
)
redis.SetConn(redis.conn) redis.SetConn(redis.conn)
redis.SetRedisCtx(ctx) var err error
timeoutDuration := 1 * time.Second
if err = redis.Set(key, val, timeoutDuration); err != nil { if err = redis.Set("username", "silenceper", timeoutDuration); err != nil {
t.Error("set Error", err) t.Error("set Error", err)
} }
if !redis.IsExist(key) { if !redis.IsExist("username") {
t.Error("IsExist Error") t.Error("IsExist Error")
} }
name := redis.Get(key).(string) name := redis.Get("username").(string)
if name != val { if name != "silenceper" {
t.Error("get Error") t.Error("get Error")
} }
if err = redis.Delete(key); err != nil { if err = redis.Delete("username"); err != nil {
t.Errorf("delete Error , err=%v", err) t.Errorf("delete Error , err=%v", err)
} }
} }

View File

@@ -1,14 +1,6 @@
package credential package credential
import "context" //AccessTokenHandle AccessToken 接口
// AccessTokenHandle AccessToken 接口
type AccessTokenHandle interface { type AccessTokenHandle interface {
GetAccessToken() (accessToken string, err error) GetAccessToken() (accessToken string, err error)
} }
// AccessTokenContextHandle AccessToken 接口
type AccessTokenContextHandle interface {
AccessTokenHandle
GetAccessTokenContext(ctx context.Context) (accessToken string, err error)
}

View File

@@ -1,7 +1,6 @@
package credential package credential
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"sync" "sync"
@@ -12,19 +11,19 @@ import (
) )
const ( const (
// AccessTokenURL 获取access_token的接口 //AccessTokenURL 获取access_token的接口
accessTokenURL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s" accessTokenURL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"
// AccessTokenURL 企业微信获取access_token的接口 //AccessTokenURL 企业微信获取access_token的接口
workAccessTokenURL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s" workAccessTokenURL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s"
// CacheKeyOfficialAccountPrefix 微信公众号cache key前缀 //CacheKeyOfficialAccountPrefix 微信公众号cache key前缀
CacheKeyOfficialAccountPrefix = "gowechat_officialaccount_" CacheKeyOfficialAccountPrefix = "gowechat_officialaccount_"
// CacheKeyMiniProgramPrefix 小程序cache key前缀 //CacheKeyMiniProgramPrefix 小程序cache key前缀
CacheKeyMiniProgramPrefix = "gowechat_miniprogram_" CacheKeyMiniProgramPrefix = "gowechat_miniprogram_"
// CacheKeyWorkPrefix 企业微信cache key前缀 //CacheKeyWorkPrefix 企业微信cache key前缀
CacheKeyWorkPrefix = "gowechat_work_" CacheKeyWorkPrefix = "gowechat_work_"
) )
// DefaultAccessToken 默认AccessToken 获取 //DefaultAccessToken 默认AccessToken 获取
type DefaultAccessToken struct { type DefaultAccessToken struct {
appID string appID string
appSecret string appSecret string
@@ -33,8 +32,8 @@ type DefaultAccessToken struct {
accessTokenLock *sync.Mutex accessTokenLock *sync.Mutex
} }
// NewDefaultAccessToken new DefaultAccessToken //NewDefaultAccessToken new DefaultAccessToken
func NewDefaultAccessToken(appID, appSecret, cacheKeyPrefix string, cache cache.Cache) AccessTokenContextHandle { func NewDefaultAccessToken(appID, appSecret, cacheKeyPrefix string, cache cache.Cache) AccessTokenHandle {
if cache == nil { if cache == nil {
panic("cache is ineed") panic("cache is ineed")
} }
@@ -47,7 +46,7 @@ func NewDefaultAccessToken(appID, appSecret, cacheKeyPrefix string, cache cache.
} }
} }
// ResAccessToken struct //ResAccessToken struct
type ResAccessToken struct { type ResAccessToken struct {
util.CommonError util.CommonError
@@ -55,20 +54,15 @@ type ResAccessToken struct {
ExpiresIn int64 `json:"expires_in"` ExpiresIn int64 `json:"expires_in"`
} }
// GetAccessToken 获取access_token,先从cache中获取没有则从服务端获取 //GetAccessToken 获取access_token,先从cache中获取没有则从服务端获取
func (ak *DefaultAccessToken) GetAccessToken() (accessToken string, err error) { func (ak *DefaultAccessToken) GetAccessToken() (accessToken string, err error) {
return ak.GetAccessTokenContext(context.Background())
}
// GetAccessTokenContext 获取access_token,先从cache中获取没有则从服务端获取
func (ak *DefaultAccessToken) GetAccessTokenContext(ctx context.Context) (accessToken string, err error) {
// 先从cache中取 // 先从cache中取
accessTokenCacheKey := fmt.Sprintf("%s_access_token_%s", ak.cacheKeyPrefix, ak.appID) accessTokenCacheKey := fmt.Sprintf("%s_access_token_%s", ak.cacheKeyPrefix, ak.appID)
if val := ak.cache.Get(accessTokenCacheKey); val != nil { if val := ak.cache.Get(accessTokenCacheKey); val != nil {
return val.(string), nil return val.(string), nil
} }
// 加上lock是为了防止在并发获取token时cache刚好失效导致从微信服务器上获取到不同token //加上lock是为了防止在并发获取token时cache刚好失效导致从微信服务器上获取到不同token
ak.accessTokenLock.Lock() ak.accessTokenLock.Lock()
defer ak.accessTokenLock.Unlock() defer ak.accessTokenLock.Unlock()
@@ -77,9 +71,9 @@ func (ak *DefaultAccessToken) GetAccessTokenContext(ctx context.Context) (access
return val.(string), nil return val.(string), nil
} }
// cache失效从微信服务器获取 //cache失效从微信服务器获取
var resAccessToken ResAccessToken var resAccessToken ResAccessToken
resAccessToken, err = GetTokenFromServerContext(ctx, fmt.Sprintf(accessTokenURL, ak.appID, ak.appSecret)) resAccessToken, err = GetTokenFromServer(fmt.Sprintf(accessTokenURL, ak.appID, ak.appSecret))
if err != nil { if err != nil {
return return
} }
@@ -93,7 +87,7 @@ func (ak *DefaultAccessToken) GetAccessTokenContext(ctx context.Context) (access
return return
} }
// WorkAccessToken 企业微信AccessToken 获取 //WorkAccessToken 企业微信AccessToken 获取
type WorkAccessToken struct { type WorkAccessToken struct {
CorpID string CorpID string
CorpSecret string CorpSecret string
@@ -102,8 +96,8 @@ type WorkAccessToken struct {
accessTokenLock *sync.Mutex accessTokenLock *sync.Mutex
} }
// NewWorkAccessToken new WorkAccessToken //NewWorkAccessToken new WorkAccessToken
func NewWorkAccessToken(corpID, corpSecret, cacheKeyPrefix string, cache cache.Cache) AccessTokenContextHandle { func NewWorkAccessToken(corpID, corpSecret, cacheKeyPrefix string, cache cache.Cache) AccessTokenHandle {
if cache == nil { if cache == nil {
panic("cache the not exist") panic("cache the not exist")
} }
@@ -116,14 +110,9 @@ func NewWorkAccessToken(corpID, corpSecret, cacheKeyPrefix string, cache cache.C
} }
} }
// GetAccessToken 企业微信获取access_token,先从cache中获取没有则从服务端获取 //GetAccessToken 企业微信获取access_token,先从cache中获取没有则从服务端获取
func (ak *WorkAccessToken) GetAccessToken() (accessToken string, err error) { func (ak *WorkAccessToken) GetAccessToken() (accessToken string, err error) {
return ak.GetAccessTokenContext(context.Background()) //加上lock是为了防止在并发获取token时cache刚好失效导致从微信服务器上获取到不同token
}
// GetAccessTokenContext 企业微信获取access_token,先从cache中获取没有则从服务端获取
func (ak *WorkAccessToken) GetAccessTokenContext(ctx context.Context) (accessToken string, err error) {
// 加上lock是为了防止在并发获取token时cache刚好失效导致从微信服务器上获取到不同token
ak.accessTokenLock.Lock() ak.accessTokenLock.Lock()
defer ak.accessTokenLock.Unlock() defer ak.accessTokenLock.Unlock()
accessTokenCacheKey := fmt.Sprintf("%s_access_token_%s", ak.cacheKeyPrefix, ak.CorpID) accessTokenCacheKey := fmt.Sprintf("%s_access_token_%s", ak.cacheKeyPrefix, ak.CorpID)
@@ -133,9 +122,9 @@ func (ak *WorkAccessToken) GetAccessTokenContext(ctx context.Context) (accessTok
return return
} }
// cache失效从微信服务器获取 //cache失效从微信服务器获取
var resAccessToken ResAccessToken var resAccessToken ResAccessToken
resAccessToken, err = GetTokenFromServerContext(ctx, fmt.Sprintf(workAccessTokenURL, ak.CorpID, ak.CorpSecret)) resAccessToken, err = GetTokenFromServer(fmt.Sprintf(workAccessTokenURL, ak.CorpID, ak.CorpSecret))
if err != nil { if err != nil {
return return
} }
@@ -149,15 +138,10 @@ func (ak *WorkAccessToken) GetAccessTokenContext(ctx context.Context) (accessTok
return return
} }
// GetTokenFromServer 强制从微信服务器获取token //GetTokenFromServer 强制从微信服务器获取token
func GetTokenFromServer(url string) (resAccessToken ResAccessToken, err error) { func GetTokenFromServer(url string) (resAccessToken ResAccessToken, err error) {
return GetTokenFromServerContext(context.Background(), url)
}
// GetTokenFromServerContext 强制从微信服务器获取token
func GetTokenFromServerContext(ctx context.Context, url string) (resAccessToken ResAccessToken, err error) {
var body []byte var body []byte
body, err = util.HTTPGetContext(ctx, url) body, err = util.HTTPGet(url)
if err != nil { if err != nil {
return return
} }

View File

@@ -7,7 +7,6 @@ import (
"gopkg.in/h2non/gock.v1" "gopkg.in/h2non/gock.v1"
) )
// TestGetTicketFromServer .
func TestGetTicketFromServer(t *testing.T) { func TestGetTicketFromServer(t *testing.T) {
defer gock.Off() defer gock.Off()
gock.New(getTicketURL).Reply(200).JSON(&ResTicket{Ticket: "mock-ticket", ExpiresIn: 10}) gock.New(getTicketURL).Reply(200).JSON(&ResTicket{Ticket: "mock-ticket", ExpiresIn: 10})

View File

@@ -10,19 +10,19 @@ import (
"github.com/silenceper/wechat/v2/util" "github.com/silenceper/wechat/v2/util"
) )
// getTicketURL 获取ticket的url //获取ticket的url
const getTicketURL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi" const getTicketURL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi"
// DefaultJsTicket 默认获取js ticket方法 //DefaultJsTicket 默认获取js ticket方法
type DefaultJsTicket struct { type DefaultJsTicket struct {
appID string appID string
cacheKeyPrefix string cacheKeyPrefix string
cache cache.Cache cache cache.Cache
// jsAPITicket 读写锁 同一个AppID一个 //jsAPITicket 读写锁 同一个AppID一个
jsAPITicketLock *sync.Mutex jsAPITicketLock *sync.Mutex
} }
// NewDefaultJsTicket new //NewDefaultJsTicket new
func NewDefaultJsTicket(appID string, cacheKeyPrefix string, cache cache.Cache) JsTicketHandle { func NewDefaultJsTicket(appID string, cacheKeyPrefix string, cache cache.Cache) JsTicketHandle {
return &DefaultJsTicket{ return &DefaultJsTicket{
appID: appID, appID: appID,
@@ -40,9 +40,9 @@ type ResTicket struct {
ExpiresIn int64 `json:"expires_in"` ExpiresIn int64 `json:"expires_in"`
} }
// GetTicket 获取jsapi_ticket //GetTicket 获取jsapi_ticket
func (js *DefaultJsTicket) GetTicket(accessToken string) (ticketStr string, err error) { func (js *DefaultJsTicket) GetTicket(accessToken string) (ticketStr string, err error) {
// 先从cache中取 //先从cache中取
jsAPITicketCacheKey := fmt.Sprintf("%s_jsapi_ticket_%s", js.cacheKeyPrefix, js.appID) jsAPITicketCacheKey := fmt.Sprintf("%s_jsapi_ticket_%s", js.cacheKeyPrefix, js.appID)
if val := js.cache.Get(jsAPITicketCacheKey); val != nil { if val := js.cache.Get(jsAPITicketCacheKey); val != nil {
return val.(string), nil return val.(string), nil
@@ -67,7 +67,7 @@ func (js *DefaultJsTicket) GetTicket(accessToken string) (ticketStr string, err
return return
} }
// GetTicketFromServer 从服务器中获取ticket //GetTicketFromServer 从服务器中获取ticket
func GetTicketFromServer(accessToken string) (ticket ResTicket, err error) { func GetTicketFromServer(accessToken string) (ticket ResTicket, err error) {
var response []byte var response []byte
url := fmt.Sprintf(getTicketURL, accessToken) url := fmt.Sprintf(getTicketURL, accessToken)

View File

@@ -1,7 +1,7 @@
package credential package credential
// JsTicketHandle js ticket获取 //JsTicketHandle js ticket获取
type JsTicketHandle interface { type JsTicketHandle interface {
// GetTicket 获取ticket //GetTicket 获取ticket
GetTicket(accessToken string) (ticket string, err error) GetTicket(accessToken string) (ticket string, err error)
} }

2
doc.go
View File

@@ -7,6 +7,4 @@ Package wechat provide wechat sdk for go
更多信息https://github.com/silenceper/wechat 更多信息https://github.com/silenceper/wechat
*/ */
// Package wechat provide wechat sdk for go
package wechat package wechat

View File

@@ -1,17 +0,0 @@
# API 文档
已完成以及未完成API列表汇总
如果有兴趣参与贡献可以在具体的API表格后面标识自己为贡献者以及完成时间例如
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者|完成时间|
| :---------------------: | -------- | :------------------------- | ---------- | -------- |-------- |-------- |
| 获取公众号类目 | GET | /wxaapi/newtmpl/getcategory | NO | |silenceper| 2021-12-20|
- [微信公众号](./officialaccount.md)
- [小程序](./miniprogram.md)
- [小游戏](./minigame.md)
- [开放平台](./oplatform.md)
- [微信支付](./wxpay.md)
- [企业微信](./work.md)
- [智能对话](./aispeech.md)

View File

@@ -1,3 +0,0 @@
# 智能对话
TODO

View File

@@ -1,3 +0,0 @@
# 小游戏
TODO

View File

@@ -1,50 +0,0 @@
# 小程序
## 基础接口
TODO
## 内容安全
[官方文档](https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/sec-check/security.mediaCheckAsync.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| :-------------------------------: | -------- | :------------------------------- | ---------- | -------------------------------------- |
| 异步校验图片/音频 <sub>v1.0</sub> | POST | /wxa/media_check_async | YES | (security *Security) MediaCheckAsyncV1 |
| 同步校验一张图片 <sub>v1.0</sub> | POST | /wxa/img_sec_check | YES | (security *Security) ImageCheckV1 |
| 异步校验图片/音频 | POST | /wxa/media_check_async?version=2 | YES | (security *Security) MediaCheckAsync |
| 同步检查一段文本 <sub>v1.0</sub> | POST | /wxa/msg_sec_check | YES | (security *Security) MsgCheckV1 |
| 同步检查一段文本 | POST | /wxa/msg_sec_check?version=2 | YES | (security *Security) MsgCheck |
## OCR
[官方文档](https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/ocr/ocr.bankcard.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| :------------: | -------- | :--------------------- | ---------- | -------- |
| 银行卡识别 | POST | /cv/ocr/bankcard | | |
| 营业执照识别 | POST | /cv/ocr/bizlicense | | |
| 驾驶证识别 | POST | /cv/ocr/drivinglicense | | |
| 身份证识别 | POST | /cv/ocr/idcard | | |
| 通用印刷体识别 | POST | /cv/ocr/comm | | |
| 行驶证识别 | POST | /cv/ocr/driving | | |
## 手机号
[官方文档](https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/phonenumber/phonenumber.getPhoneNumber.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| :----------------: | -------- | :------------------------------- | ---------- | ----------------------------------- |
| code换取用户手机号 | POST | /wxa/business/getuserphonenumber | YES | (business *Business) GetPhoneNumber |
## 安全风控
[官方文档](https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/safety-control-capability/riskControl.getUserRiskRank.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| :----------------: | -------- | :------------------- | ---------- | ------------------------------------------ |
| 获取用户的安全等级 | POST | /wxa/getuserriskrank | YES | (riskControl *RiskControl) GetUserRiskRank |

View File

@@ -1,238 +0,0 @@
# 微信公众号 API 列表
## 基础接口
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| :-----------------------: | -------- | :------------------------- | ---------- | -------- |
| 获取 Access token | GET | /cgi-bin/token | YES | |
| 获取微信服务器 IP 地址 | GET | /cgi-bin/get_api_domain_ip | YES | |
| 获取微信 callback IP 地址 | GET | /cgi-bin/getcallbackip | YES | |
| 清理接口调用次数 | POST | /cgi-bin/clear_quota | YES | |
## 订阅通知
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Subscription_Messages/api.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| -------------------- | -------- | -------------------------------------- | ---------- | -------------------------------------------- |
| 选用模板 | POST | /wxaapi/newtmpl/addtemplate | YES | (tpl \*Subscribe) Add |
| 删除模板 | POST | /wxaapi/newtmpl/deltemplate | YES | (tpl \*Subscribe) Delete |
| 获取公众号类目 | GET | /wxaapi/newtmpl/getcategory | YES | (tpl \*Subscribe) GetCategory |
| 获取模板中的关键词 | GET | /wxaapi/newtmpl/getpubtemplatekeywords | YES | (tpl \*Subscribe) GetPubTplKeyWordsByID |
| 获取类目下的公共模板 | GET | /wxaapi/newtmpl/getpubtemplatetitles | YES | (tpl \*Subscribe) GetPublicTemplateTitleList |
| 获取私有模板列表 | GET | /wxaapi/newtmpl/gettemplate | YES | (tpl \*Subscribe) List() |
| 发送订阅通知 | POST | /cgi-bin/message/subscribe/bizsend | YES | (tpl \*Subscribe) Send |
## 客服消息
### PC 客服能力
#### 客服管理
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Customer_Service/Customer_Service_Management.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ---------------- | --------- | -------------------------------------- | ---------- | -------------------------------- |
| 获取客服基本信息 | GET | /cgi-bin/customservice/getkflist | YES | (csm \*Manager) List |
| 添加客服帐号 | POST | /customservice/kfaccount/add | YES | (csm \*Manager) Add |
| 邀请绑定客服帐号 | POST | /customservice/kfaccount/inviteworker | YES | (csm \*Manager) InviteBind |
| 设置客服信息 | POST | /customservice/kfaccount/update | YES | (csm \*Manager) Update |
| 上传客服头像 | POST/FORM | /customservice/kfaccount/uploadheadimg | YES | (csm \*Manager) UploadHeadImg |
| 删除客服帐号 | POST | /customservice/kfaccount/del | YES | (csm \*Manager) Delete |
| 获取在线客服 | POST | /cgi-bin/customservice/getonlinekflist | YES | (csm \*Manager) OnlineList |
| 下发客服输入状态 | POST | /cgi-bin/message/custom/typing | YES | (csm \*Manager) SendTypingStatus |
#### 会话控制
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Customer_Service/Session_control.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------------ | -------- | --------------------------------------- | ---------- | -------- |
| 创建会话 | POST | /customservice/kfsession/create | NO | |
| 获取客户会话状态 | GET | /customservice/kfsession/getsession | NO | |
| 获取客服会话列表 | GET | /customservice/kfsession/getsessionlist | NO | |
| 获取未接入会话列表 | POST | /customservice/kfsession/getwaitcase | NO | |
#### 获取聊天记录
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Customer_Service/Obtain_chat_transcript.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------ | -------- | ----------------------------------- | ---------- | -------- |
| 获取聊天记录 | POST | /customservice/msgrecord/getmsglist | NO | |
### 对话能力
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Shopping_Guide/guide.html)
#### 顾问管理
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------------------------ | -------- | -------------------------------------- | ---------- | -------- |
| 添加顾问 | POST | /cgi-bin/guide/addguideacct | NO | |
| 获取顾问信息 | POST | /cgi-bin/guide/getguideacct | NO | |
| 修改顾问信息 | POST | /cgi-bin/guide/updateguideacct | NO | |
| 删除顾问 | POST | /cgi-bin/guide/delguideacct | NO | |
| 获取服务号顾问列表 | POST | /cgi-bin/guide/getguideacctlist | NO | |
| 生成顾问二维码 | POST | /cgi-bin/guide/guidecreateqrcode | NO | |
| 获取顾问聊天记录 | POST | /cgi-bin/guide/getguidebuyerchatrecord | NO | |
| 设置快捷回复与关注自动回复 | POST | /cgi-bin/guide/setguideconfig | NO | |
| 获取快捷回复与关注自动回复 | POST | /cgi-bin/guide/getguideconfig | NO | |
| 设置敏感词与离线自动回复 | POST | /cgi-bin/guide/setguideacctconfig | NO | |
| 获取离线自动回复与敏感词 | POST | /cgi-bin/guide/getguideacctconfig | NO | |
| 允许微信用户复制小程序页面路径 | POST | /cgi-bin/guide/pushshowwxapathmenu | NO | |
| 新建顾问分组 | POST | /cgi-bin/guide/newguidegroup | NO | |
| 获取顾问分组列表 | POST | /cgi-bin/guide/getguidegrouplist | NO | |
| 获取顾问分组信息 | POST | /cgi-bin/guide/getgroupinfo | NO | |
| 分组内添加顾问 | POST | /cgi-bin/guide/addguide2guidegroup | NO | |
| 分组内删除顾问 | POST | /cgi-bin/guide/delguide2guidegroup | NO | |
| 获取顾问所在分组 | POST | /cgi-bin/guide/getgroupbyguide | NO | |
| 删除指定顾问分组 | POST | /cgi-bin/guide/delguidegroup | NO | |
#### 客户管理
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------------------ | -------- | ------------------------------------------- | ---------- | -------- |
| 为顾问分配客户 | POST | /cgi-bin/guide/addguidebuyerrelation | NO | |
| 为顾问移除客户 | POST | /cgi-bin/guide/delguidebuyerrelation | NO | |
| 获取顾问的客户列表 | POST | /cgi-bin/guide/getguidebuyerrelationlist | NO | |
| 为客户更换顾问 | POST | /cgi-bin/guide/rebindguideacctforbuyer | NO | |
| 修改客户昵称 | POST | /cgi-bin/guide/updateguidebuyerrelation | NO | |
| 查询客户所属顾问 | POST | /cgi-bin/guide/getguidebuyerrelationbybuyer | NO | |
| 查询指定顾问和客户的关系 | POST | /cgi-bin/guide/getguidebuyerrelation | NO | |
#### 标签管理
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------------ | -------- | -------------------------------------- | ---------- | -------- |
| 新建标签类型 | POST | /cgi-bin/guide/newguidetagoption | NO | |
| 删除标签类型 | POST | /cgi-bin/guide/delguidetagoption | NO | |
| 为标签添加可选值 | POST | /cgi-bin/guide/addguidetagoption | NO | |
| 获取标签和可选值 | POST | /cgi-bin/guide/getguidetagoption | NO | |
| 为客户设置标签 | POST | /cgi-bin/guide/addguidebuyertag | NO | |
| 查询客户标签 | POST | /cgi-bin/guide/getguidebuyertag | NO | |
| 根据标签值筛选客户 | POST | /cgi-bin/guide/queryguidebuyerbytag | NO | |
| 删除客户标签 | POST | /cgi-bin/guide/delguidebuyertag | NO | |
| 设置自定义客户信息 | POST | /cgi-bin/guide/addguidebuyerdisplaytag | NO | |
| 获取自定义客户信息 | POST | /cgi-bin/guide/getguidebuyerdisplaytag | NO | |
#### 素材管理
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------------ | -------- | ------------------------------------ | ---------- | -------- |
| 添加小程序卡片素材 | POST | /cgi-bin/guide/setguidecardmaterial | NO | |
| 查询小程序卡片素材 | POST | /cgi-bin/guide/getguidecardmaterial | NO | |
| 删除小程序卡片素材 | POST | /cgi-bin/guide/delguidecardmaterial | NO | |
| 添加图片素材 | POST | /cgi-bin/guide/setguideimagematerial | NO | |
| 查询图片素材 | POST | /cgi-bin/guide/getguideimagematerial | NO | |
| 删除图片素材 | POST | /cgi-bin/guide/delguideimagematerial | NO | |
| 添加文字素材 | POST | /cgi-bin/guide/setguidewordmaterial | NO | |
| 查询文字素材 | POST | /cgi-bin/guide/getguidewordmaterial | NO | |
| 删除文字素材 | POST | /cgi-bin/guide/delguidewordmaterial | NO | |
#### 群发任务管理
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Shopping_Guide/task-account/shopping-guide.addGuideMassendJob.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| -------------------- | -------- | ------------------------------------- | ---------- | -------- |
| 添加群发任务 | POST | /cgi-bin/guide/addguidemassendjob | NO | |
| 获取群发任务列表 | POST | /cgi-bin/guide/getguidemassendjoblist | NO | |
| 获取指定群发任务信息 | POST | /cgi-bin/guide/getguidemassendjob | NO | |
| 修改群发任务 | POST | /cgi-bin/guide/updateguidemassendjob | NO | |
| 取消群发任务 | POST | /cgi-bin/guide/cancelguidemassendjob | NO | |
## 微信网页开发
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ----------------------------------------------------------------------- | -------- | --------------------------------------------------- | ---------- | ------------------------------------ |
| 获取跳转的 url 地址 | GET | https://open.weixin.qq.com/connect/oauth2/authorize | YES | (oauth \*Oauth) GetRedirectURL |
| 获取网页应用跳转的 url 地址 | GET | https://open.weixin.qq.com/connect/qrconnect | YES | (oauth \*Oauth) GetWebAppRedirectURL |
| 通过网页授权的 code 换取 access_token(区别于 context 中的 access_token) | GET | /sns/oauth2/access_token | YES | (oauth \*Oauth) GetUserAccessToken |
| 刷新 access_token | GET | /sns/oauth2/refresh_token? | YES | (oauth \*Oauth) RefreshAccessToken |
| 检验 access_token 是否有效 | GET | /sns/auth | YES | (oauth \*Oauth) CheckAccessToken( |
| 拉取用户信息(需 scope 为 snsapi_userinfo) | GET | /sns/userinfo | YES | (oauth \*Oauth) GetUserInfo |
| 获取 jssdk 需要的配置参数 | GET | /cgi-bin/ticket/getticket | YES | (js \*Js) GetConfig |
## 素材管理
## 草稿箱
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Draft_Box/Add_draft.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| --------------------------- | -------- | ------------------------------------------------------------ | ---------- | ----------------------------- |
| 新建草稿 | POST | /cgi-bin/draft/add | YES | (draft \*Draft) AddDraft |
| 获取草稿 | POST | /cgi-bin/draft/get | YES | (draft \*Draft) GetDraft |
| 删除草稿 | POST | /cgi-bin/draft/delete | YES | (draft \*Draft) DeleteDraft |
| 修改草稿 | POST | /cgi-bin/draft/update | YES | (draft \*Draft) UpdateDraft |
| 获取草稿总数 | GET | /cgi-bin/draft/count | YES | (draft \*Draft) CountDraft |
| 获取草稿列表 | POST | /cgi-bin/draft/batchget | YES | (draft \*Draft) PaginateDraft |
| MP 端开关(仅内测期间使用) | POST | /cgi-bin/draft/switch<br />/cgi-bin/draft/switch?checkonly=1 | NO | |
## 发布能力
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Publish/Publish.html)
说明:「发表记录」包括群发和发布。
注意:该接口,只能处理 "发布" 相关的信息,无法操作和获取 "群发" 相关内容!![官方回复](https://developers.weixin.qq.com/community/develop/doc/0002a4fb2109d8f7a91d421c556c00)
- 群发:主动推送给粉丝,历史消息可看,被搜一搜收录,可以限定部分的粉丝接收到。
- 发布:不会主动推给粉丝,历史消息列表看不到,但是是公开给所有人的文章。也不会占用群发的次数。每天可以发布多篇内容。可以用于自动回复、自定义菜单、页面模板和话题中,发布成功时会生成一个永久链接。
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------------------------ | -------- | ------------------------------- | ---------- | ---------------------------------------- |
| 发布接口 | POST | /cgi-bin/freepublish/submit | YES | (freePublish \*FreePublish) Publish |
| 发布状态轮询接口 | POST | /cgi-bin/freepublish/get | YES | (freePublish \*FreePublish) SelectStatus |
| 事件推送发布结果 | | | YES | EventPublishJobFinish |
| 删除发布 | POST | /cgi-bin/freepublish/delete | YES | (freePublish \*FreePublish) Delete |
| 通过 article_id 获取已发布文章 | POST | /cgi-bin/freepublish/getarticle | YES | (freePublish \*FreePublish) First |
| 获取成功发布列表 | POST | /cgi-bin/freepublish/batchget | YES | (freePublish \*FreePublish) Paginate |
## 图文消息留言管理
## 用户管理
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------------------------------------ | -------- | -------------------------------------- | ---------- | ---------------------------------- |
| 获取指定 OpenID 变化列表(公众号账号迁移) | POST | /cgi-bin/changeopenid | YES | (user \*User) ListChangeOpenIDs |
| 获取所有用户 OpenID 列表(公众号账号迁移) | | | YES | (user \*User) ListAllChangeOpenIDs |
| 获取用户基本信息 | GET | /cgi-bin/user/info | YES | (user \*User) GetUserInfo |
| 设置用户备注名 | POST | /cgi-bin/user/info/updateremark | YES | (user \*User) UpdateRemark |
| 获取用户列表 | GET | /cgi-bin/user/get | YES | (user \*User) ListUserOpenIDs |
| 获取所有用户 OpenID 列表 | | | YES | (user \*User) ListAllUserOpenIDs |
| 获取公众号的黑名单列表 | POST | /cgi-bin/tags/members/getblacklist | YES | (user \*User) GetBlackList |
| 获取公众号的所有黑名单列表 | | | YES | (user \*User) GetAllBlackList |
| 拉黑用户 | POST | /cgi-bin/tags/members/batchblacklist | YES | (user \*User) BatchBlackList |
| 取消拉黑用户 | POST | /cgi-bin/tags/members/batchunblacklist | YES | (user \*User) BatchUnBlackList |
| 创建标签 | POST | /cgi-bin/tags/create | YES | (user \*User) CreateTag |
| 删除标签 | POST | /cgi-bin/tags/delete | YES | (user \*User) DeleteTag |
| 编辑标签 | POST | /cgi-bin/tags/update | YES | (user \*User) UpdateTag |
| 获取公众号已创建的标签 | GET | /cgi-bin/tags/get | YES | (user \*User) GetTag |
| 获取标签下粉丝列表 | POST | /cgi-bin/user/tag/get | YES | (user \*User) OpenIDListByTag |
| 批量为用户打标签 | POST | /cgi-bin/tags/members/batchtagging | YES | (user \*User) BatchTag |
| 批量为用户取消标签 | POST | /cgi-bin/tags/members/batchuntagging | YES | (user \*User) BatchUntag |
| 获取用户身上的标签列表 | POST | /cgi-bin/tags/getidlist | YES | (user \*User) UserTidList |
## 账号管理
## 数据统计
## 微信卡券
## 微信门店
## 智能接口
## 微信设备功能
## 微信“一物一码”
## 微信发票
## 微信非税缴费

View File

@@ -1 +0,0 @@
# 开放平台

View File

@@ -1,115 +0,0 @@
# 企业微信
host: https://qyapi.weixin.qq.com/
## 微信客服
[官方文档](https://work.weixin.qq.com/api/doc/90000/90135/94638)
### 客服账号管理
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/94684)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者 |
| :--------------: | -------- | :-------------------------- | ---------- | -------------------------------|------------|
| 添加客服帐号 | POST | /cgi-bin/kf/account/add | YES | (r *Client) AccountAdd | NICEXAI |
| 删除客服帐号 | POST | /cgi-bin/kf/account/del | YES | (r *Client) AccountDel | NICEXAI |
| 修改客服帐号 | POST | /cgi-bin/kf/account/update | YES | (r *Client) AccountUpdate | NICEXAI |
| 获取客服帐号列表 | GET | /cgi-bin/kf/account/list | YES | (r *Client) AccountList | NICEXAI |
| 获取客服帐号链接 | GET | /cgi-bin/kf/add_contact_way | YES | (r *Client) AddContactWay | NICEXAI |
### 接待人员列表
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/94693)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者 |
| :--------------: | -------- | :-------------------------- | ---------- | -------------------------------|------------|
| 添加接待人员 | POST | /cgi-bin/kf/servicer/add | YES | (r *Client) ReceptionistAdd | NICEXAI |
| 删除接待人员 | POST | /cgi-bin/kf/servicer/del | YES | (r *Client) ReceptionistDel | NICEXAI |
| 获取接待人员列表 | GET | /cgi-bin/kf/servicer/list | YES | (r *Client) ReceptionistList | NICEXAI |
### 会话分配与消息收发
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/94694)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者 |
| :--------------: | -------- | :-------------------------------| ---------- | ------------------------------- |------------|
| 获取会话状态 | POST | /cgi-bin/kf/service_state/get | YES | (r *Client) ServiceStateGet | NICEXAI |
| 变更会话状态 | POST | /cgi-bin/kf/service_state/trans | YES | (r *Client) ServiceStateTrans | NICEXAI |
| 读取消息 | POST | /cgi-bin/kf/sync_msg | YES | (r *Client) SyncMsg | NICEXAI |
| 发送消息 | POST | /cgi-bin/kf/send_msg | YES | (r *Client) SendMsg | NICEXAI |
| 发送事件响应消息 | POST | /cgi-bin/kf/send_msg_on_event | YES | (r *Client) SendMsgOnEvent | NICEXAI |
### 升级服务配置
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/94702)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者 |
| :--------------: | -------- | :-------------------------------------------------| ---------- | ------------------------------- |------------|
| 获取配置的专员与客户群 | POST | /cgi-bin/kf/customer/get_upgrade_service_config | YES | (r *Client) UpgradeServiceConfig | NICEXAI |
| 为客户升级为专员或客户群服务 | POST | /cgi-bin/kf/customer/upgrade_service | YES | (r *Client) UpgradeService | NICEXAI |
| 为客户取消推荐 | POST | /cgi-bin/kf/customer/cancel_upgrade_service | YES | (r *Client) UpgradeServiceCancel | NICEXAI |
### 其他基础信息获取
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/95148)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 | 贡献者 |
| :--------------: | -------- | :---------------------------------------| ---------- | ------------------------------- |------------|
| 获取客户基础信息 | POST | /cgi-bin/kf/customer/batchget | YES | (r *Client) CustomerBatchGet | NICEXAI |
| 获取视频号绑定状态 | GET | /cgi-bin/kf/get_corp_qualification | YES | (r *Client) GetCorpQualification | NICEXAI |
### 客户联系
[官方文档](https://developer.work.weixin.qq.com/document/path/92132/92133/92228)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 | 贡献者 |
|:------------------------:| -------- | :---------------------------------------| ---------- | ------------------------------- |----------|
| 获取「联系客户统计」数据 | POST | /cgi-bin/externalcontact/get_user_behavior_data | YES | (r *Client) GetUserBehaviorData | MARKWANG |
| 获取「群聊数据统计」数据 (按群主聚合的方式) | POST | /cgi-bin/externalcontact/groupchat/statistic | YES | (r *Client) GetGroupChatStat | MARKWANG |
| 获取「群聊数据统计」数据 (按自然日聚合的方式) | POST | /cgi-bin/externalcontact/groupchat/statistic_group_by_day | YES | (r *Client) GetGroupChatStatByDay | MARKWANG |
| 配置客户联系「联系我」方式 | POST | /cgi-bin/externalcontact/add_contact_way | YES | (r *Client) AddContactWay | MARKWANG |
| 获取企业已配置的「联系我」方式 | POST | /cgi-bin/externalcontact/get_contact_way | YES | (r *Client) GetContactWay | MARKWANG |
| 更新企业已配置的「联系我」方式 | POST | /cgi-bin/externalcontact/update_contact_way | YES | (r *Client) UpdateContactWay | MARKWANG |
| 获取企业已配置的「联系我」列表 | POST | /cgi-bin/externalcontact/list_contact_way | YES | (r *Client) ListContactWay | MARKWANG |
| 删除企业已配置的「联系我」方式 | POST | /cgi-bin/externalcontact/del_contact_way | YES | (r *Client) DelContactWay | MARKWANG |
| 创建企业群发 | POST | /cgi-bin/externalcontact/add_msg_template | YES | (r *Client) AddMsgTemplate | MARKWANG |
| 获取群发记录列表 | POST | /cgi-bin/externalcontact/get_groupmsg_list_v2 | YES | (r *Client) GetGroupMsgListV2 | MARKWANG |
| 获取群发成员发送任务列表 | POST | /cgi-bin/externalcontact/get_groupmsg_task | YES | (r *Client) GetGroupMsgTask | MARKWANG |
| 获取企业群发成员执行结果 | POST | /cgi-bin/externalcontact/get_groupmsg_send_result | YES | (r *Client) GetGroupMsgSendResult | MARKWANG |
| 发送新客户欢迎语 | POST | /cgi-bin/externalcontact/send_welcome_msg | YES | (r *Client) SendWelcomeMsg | MARKWANG |
## 通讯录管理
[官方文档](https://developer.work.weixin.qq.com/document/path/90193)
### 部门管理
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 | 贡献者 |
|:---------:|------|:----------------------------------------| ---------- | ------------------------------- |----------|
| 获取子部门ID列表 | GET | /cgi-bin/department/simplelist | YES | (r *Client) DepartmentSimpleList| MARKWANG |
| 获取部门成员 | GET | /cgi-bin/user/simplelist | YES | (r *Client) UserSimpleList | MARKWANG |
## 素材管理
[官方文档](https://developer.work.weixin.qq.com/document/path/91054)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 | 贡献者 |
|:---------:|------|:----------------------------------------| ---------- | ------------------------------- |----------|
| 上传图片 | POST | /cgi-bin/media/uploadimg | YES | (r *Client) UploadImg| MARKWANG |
### 成员管理
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 | 贡献者 |
| -------- | -------- | ----------------- | ---------- | ------------------- | -------- |
| 读取成员 | GET | /cgi-bin/user/get | YES | (r *Client) UserGet | chcthink |
## 群机器人
[官方文档](https://developer.work.weixin.qq.com/document/path/91770)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 | 贡献者 |
| ---------------- | -------- | --------------------- | ---------- | -------------------------- | -------- |
| 群机器人发送消息 | POST | /cgi-bin/webhook/send | YES | (r *Client) RobotBroadcast | chcthink |
## 应用管理
TODO

View File

@@ -1,3 +0,0 @@
# 微信支付
TODO

21
go.mod
View File

@@ -1,15 +1,18 @@
module github.com/silenceper/wechat/v2 module github.com/silenceper/wechat/v2
go 1.16 go 1.14
require ( require (
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
github.com/fatih/structs v1.1.0 github.com/fatih/structs v1.1.0
github.com/go-redis/redis/v8 v8.11.5 github.com/gomodule/redigo v1.8.4
github.com/sirupsen/logrus v1.9.0 github.com/kr/text v0.2.0 // indirect
github.com/spf13/cast v1.4.1 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/stretchr/testify v1.7.1 github.com/sirupsen/logrus v1.8.1
github.com/tidwall/gjson v1.14.1 github.com/spf13/cast v1.3.1
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d github.com/stretchr/testify v1.7.0
gopkg.in/h2non/gock.v1 v1.1.2 golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/h2non/gock.v1 v1.0.15
) )

143
go.sum
View File

@@ -1,140 +1,49 @@
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw= github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gomodule/redigo v1.8.4 h1:Z5JUg94HMTR1XpwBaSH4vq3+PNSIykBLxMdglbw10gg=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo=
github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

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

View File

@@ -2,12 +2,11 @@
[官方文档](https://developers.weixin.qq.com/miniprogram/dev/framework/) [官方文档](https://developers.weixin.qq.com/miniprogram/dev/framework/)
## 包说明
## 包说明
- analysis 数据分析相关API - analysis 数据分析相关API
## 快速入门 ## 快速入门
```go ```go
wc := wechat.NewWechat() wc := wechat.NewWechat()
memory := cache.NewMemory() memory := cache.NewMemory()

View File

@@ -32,12 +32,12 @@ const (
getAnalysisVisitPageURL = "https://api.weixin.qq.com/datacube/getweanalysisappidvisitpage?access_token=%s" getAnalysisVisitPageURL = "https://api.weixin.qq.com/datacube/getweanalysisappidvisitpage?access_token=%s"
) )
// Analysis analyis 数据分析 //Analysis analyis 数据分析
type Analysis struct { type Analysis struct {
*context.Context *context.Context
} }
// NewAnalysis new //NewAnalysis new
func NewAnalysis(ctx *context.Context) *Analysis { func NewAnalysis(ctx *context.Context) *Analysis {
return &Analysis{ctx} return &Analysis{ctx}
} }
@@ -190,9 +190,9 @@ func (analysis *Analysis) GetAnalysisWeeklyVisitTrend(beginDate, endDate string)
// UserPortraitItem 用户画像项目 // UserPortraitItem 用户画像项目
type UserPortraitItem struct { type UserPortraitItem struct {
ID int `json:"id"` // 属性值id ID int `json:"id"` // 属性值id
Name string `json:"name"` // 属性值名称 Name string `json:"name"` // 属性值名称
Value int `json:"value"` // 该场景访问uv AccessSourceVisitUV int `json:"access_source_visit_uv"` // 该场景访问uv
} }
// UserPortrait 用户画像 // UserPortrait 用户画像

View File

@@ -1,7 +1,6 @@
package auth package auth
import ( import (
context2 "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
@@ -11,18 +10,14 @@ import (
const ( const (
code2SessionURL = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code" code2SessionURL = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"
checkEncryptedDataURL = "https://api.weixin.qq.com/wxa/business/checkencryptedmsg?access_token=%s"
getPhoneNumber = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s"
) )
// Auth 登录/用户信息 //Auth 登录/用户信息
type Auth struct { type Auth struct {
*context.Context *context.Context
} }
// NewAuth new auth //NewAuth new auth
func NewAuth(ctx *context.Context) *Auth { func NewAuth(ctx *context.Context) *Auth {
return &Auth{ctx} return &Auth{ctx}
} }
@@ -36,26 +31,16 @@ type ResCode2Session struct {
UnionID string `json:"unionid"` // 用户在开放平台的唯一标识符在满足UnionID下发条件的情况下会返回 UnionID string `json:"unionid"` // 用户在开放平台的唯一标识符在满足UnionID下发条件的情况下会返回
} }
// RspCheckEncryptedData . //Code2Session 登录凭证校验。
type RspCheckEncryptedData struct {
util.CommonError
Vaild bool `json:"vaild"` // 是否是合法的数据
CreateTime uint `json:"create_time"` // 加密数据生成的时间戳
}
// Code2Session 登录凭证校验。
func (auth *Auth) Code2Session(jsCode string) (result ResCode2Session, err error) { func (auth *Auth) Code2Session(jsCode string) (result ResCode2Session, err error) {
return auth.Code2SessionContext(context2.Background(), jsCode) urlStr := fmt.Sprintf(code2SessionURL, auth.AppID, auth.AppSecret, jsCode)
}
// Code2SessionContext 登录凭证校验。
func (auth *Auth) Code2SessionContext(ctx context2.Context, jsCode string) (result ResCode2Session, err error) {
var response []byte var response []byte
if response, err = util.HTTPGetContext(ctx, fmt.Sprintf(code2SessionURL, auth.AppID, auth.AppSecret, jsCode)); err != nil { response, err = util.HTTPGet(urlStr)
if err != nil {
return return
} }
if err = json.Unmarshal(response, &result); err != nil { err = json.Unmarshal(response, &result)
if err != nil {
return return
} }
if result.ErrCode != 0 { if result.ErrCode != 0 {
@@ -65,86 +50,7 @@ func (auth *Auth) Code2SessionContext(ctx context2.Context, jsCode string) (resu
return return
} }
// GetPaidUnionID 用户支付完成后,获取该用户的 UnionId无需用户授权 //GetPaidUnionID 用户支付完成后,获取该用户的 UnionId无需用户授权
func (auth *Auth) GetPaidUnionID() { func (auth *Auth) GetPaidUnionID() {
// TODO //TODO
}
// CheckEncryptedData .检查加密信息是否由微信生成当前只支持手机号加密数据只能检测最近3天生成的加密数据
func (auth *Auth) CheckEncryptedData(encryptedMsgHash string) (result RspCheckEncryptedData, err error) {
return auth.CheckEncryptedDataContext(context2.Background(), encryptedMsgHash)
}
// CheckEncryptedDataContext .检查加密信息是否由微信生成当前只支持手机号加密数据只能检测最近3天生成的加密数据
func (auth *Auth) CheckEncryptedDataContext(ctx context2.Context, encryptedMsgHash string) (result RspCheckEncryptedData, err error) {
var response []byte
var (
at string
)
if at, err = auth.GetAccessToken(); err != nil {
return
}
// 由于GetPhoneNumberContext需要传入JSON所以HTTPPostContext入参改为[]byte
if response, err = util.HTTPPostContext(ctx, fmt.Sprintf(checkEncryptedDataURL, at), []byte("encrypted_msg_hash="+encryptedMsgHash), nil); err != nil {
return
}
if err = util.DecodeWithError(response, &result, "CheckEncryptedDataAuth"); err != nil {
return
}
return
}
// GetPhoneNumberResponse 新版获取用户手机号响应结构体
type GetPhoneNumberResponse struct {
util.CommonError
PhoneInfo PhoneInfo `json:"phone_info"`
}
// PhoneInfo 获取用户手机号内容
type PhoneInfo struct {
PhoneNumber string `json:"phoneNumber"` // 用户绑定的手机号
PurePhoneNumber string `json:"purePhoneNumber"` // 没有区号的手机号
CountryCode string `json:"countryCode"` // 区号
WaterMark struct {
Timestamp int64 `json:"timestamp"`
AppID string `json:"appid"`
} `json:"watermark"` // 数据水印
}
// GetPhoneNumberContext 小程序通过code获取用户手机号
func (auth *Auth) GetPhoneNumberContext(ctx context2.Context, code string) (*GetPhoneNumberResponse, error) {
var response []byte
var (
at string
err error
)
if at, err = auth.GetAccessToken(); err != nil {
return nil, err
}
body := map[string]interface{}{
"code": code,
}
bodyBytes, err := json.Marshal(body)
if err != nil {
return nil, err
}
header := map[string]string{"Content-Type": "application/json;charset=utf-8"}
if response, err = util.HTTPPostContext(ctx, fmt.Sprintf(getPhoneNumber, at), bodyBytes, header); err != nil {
return nil, err
}
var result GetPhoneNumberResponse
if err = util.DecodeWithError(response, &result, "phonenumber.getPhoneNumber"); err != nil {
return nil, err
}
return &result, nil
}
// GetPhoneNumber 小程序通过code获取用户手机号
func (auth *Auth) GetPhoneNumber(code string) (*GetPhoneNumberResponse, error) {
return auth.GetPhoneNumberContext(context2.Background(), code)
} }

View File

@@ -1,13 +0,0 @@
package business
import "github.com/silenceper/wechat/v2/miniprogram/context"
// Business 业务
type Business struct {
*context.Context
}
// NewBusiness init
func NewBusiness(ctx *context.Context) *Business {
return &Business{ctx}
}

View File

@@ -1,54 +0,0 @@
package business
import (
"fmt"
"github.com/silenceper/wechat/v2/util"
)
const (
getPhoneNumberURL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s"
)
// GetPhoneNumberRequest 获取手机号请求
type GetPhoneNumberRequest struct {
Code string `json:"code"` // 手机号获取凭证
}
// PhoneInfo 手机号信息
type PhoneInfo struct {
PhoneNumber string `json:"phoneNumber"` // 用户绑定的手机号(国外手机号会有区号)
PurePhoneNumber string `json:"purePhoneNumber"` // 没有区号的手机号
CountryCode string `json:"countryCode"` // 区号
Watermark struct {
AppID string `json:"appid"` // 小程序appid
Timestamp int64 `json:"timestamp"` // 用户获取手机号操作的时间戳
} `json:"watermark"`
}
// GetPhoneNumber code换取用户手机号。 每个code只能使用一次code的有效期为5min
func (business *Business) GetPhoneNumber(in *GetPhoneNumberRequest) (info PhoneInfo, err error) {
accessToken, err := business.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf(getPhoneNumberURL, accessToken)
response, err := util.PostJSON(uri, in)
if err != nil {
return
}
// 使用通用方法返回错误
var resp struct {
util.CommonError
PhoneInfo PhoneInfo `json:"phone_info"`
}
err = util.DecodeWithError(response, &resp, "business.GetPhoneNumber")
if nil != err {
return
}
info = resp.PhoneInfo
return
}

View File

@@ -5,9 +5,9 @@ import (
"github.com/silenceper/wechat/v2/cache" "github.com/silenceper/wechat/v2/cache"
) )
// Config .config for 小程序 // Config config for 小程序
type Config struct { type Config struct {
AppID string `json:"app_id"` // appid AppID string `json:"app_id"` // appid
AppSecret string `json:"app_secret"` // appSecret AppSecret string `json:"app_secret"` // appsecret
Cache cache.Cache Cache cache.Cache
} }

View File

@@ -12,20 +12,18 @@ const (
checkImageURL = "https://api.weixin.qq.com/wxa/img_sec_check?access_token=%s" checkImageURL = "https://api.weixin.qq.com/wxa/img_sec_check?access_token=%s"
) )
// Content 内容安全 //Content 内容安全
type Content struct { type Content struct {
*context.Context *context.Context
} }
// NewContent 内容安全接口 //NewContent 内容安全接口
func NewContent(ctx *context.Context) *Content { func NewContent(ctx *context.Context) *Content {
return &Content{ctx} return &Content{ctx}
} }
// CheckText 检测文字 //CheckText 检测文字
// @text 需要检测的文字 //@text 需要检测的文字
// Deprecated
// 采用 security.MsgCheckV1 替代,返回值更加丰富
func (content *Content) CheckText(text string) error { func (content *Content) CheckText(text string) error {
accessToken, err := content.GetAccessToken() accessToken, err := content.GetAccessToken()
if err != nil { if err != nil {
@@ -43,11 +41,9 @@ func (content *Content) CheckText(text string) error {
return util.DecodeWithCommonError(response, "ContentCheckText") return util.DecodeWithCommonError(response, "ContentCheckText")
} }
// CheckImage 检测图片 //CheckImage 检测图片
// 所传参数为要检测的图片文件的绝对路径图片格式支持PNG、JPEG、JPG、GIF, 像素不超过 750 x 1334同时文件大小以不超过 300K 为宜,否则可能报错 //所传参数为要检测的图片文件的绝对路径图片格式支持PNG、JPEG、JPG、GIF, 像素不超过 750 x 1334同时文件大小以不超过 300K 为宜,否则可能报错
// @media 图片文件的绝对路径 //@media 图片文件的绝对路径
// Deprecated
// 采用 security.ImageCheckV1 替代,返回值更加丰富
func (content *Content) CheckImage(media string) error { func (content *Content) CheckImage(media string) error {
accessToken, err := content.GetAccessToken() accessToken, err := content.GetAccessToken()
if err != nil { if err != nil {

View File

@@ -6,17 +6,16 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"github.com/silenceper/wechat/v2/miniprogram/context" "github.com/silenceper/wechat/v2/miniprogram/context"
) )
// Encryptor struct //Encryptor struct
type Encryptor struct { type Encryptor struct {
*context.Context *context.Context
} }
// NewEncryptor 实例 //NewEncryptor 实例
func NewEncryptor(context *context.Context) *Encryptor { func NewEncryptor(context *context.Context) *Encryptor {
basic := new(Encryptor) basic := new(Encryptor)
basic.Context = context basic.Context = context
@@ -91,9 +90,6 @@ func GetCipherText(sessionKey, encryptedData, iv string) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(ivBytes) != aes.BlockSize {
return nil, fmt.Errorf("bad iv length %d", len(ivBytes))
}
block, err := aes.NewCipher(aesKey) block, err := aes.NewCipher(aesKey)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -1,15 +0,0 @@
package encryptor
import (
"encoding/base64"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetCipherText_BadIV(t *testing.T) {
keyData := base64.StdEncoding.EncodeToString([]byte("1234567890123456"))
badData := base64.StdEncoding.EncodeToString([]byte("1"))
_, err := GetCipherText(keyData, badData, badData)
assert.Error(t, err)
}

View File

@@ -12,13 +12,13 @@ type EventType string
type InfoType string type InfoType string
const ( const (
// MsgTypeText 文本消息 //MsgTypeText 文本消息
MsgTypeText MsgType = "text" MsgTypeText MsgType = "text"
// MsgTypeImage 图片消息 //MsgTypeImage 图片消息
MsgTypeImage = "image" MsgTypeImage = "image"
// MsgTypeLink 图文链接 //MsgTypeLink 图文链接
MsgTypeLink = "link" MsgTypeLink = "link"
// MsgTypeMiniProgramPage 小程序卡片 //MsgTypeMiniProgramPage 小程序卡片
MsgTypeMiniProgramPage = "miniprogrampage" MsgTypeMiniProgramPage = "miniprogrampage"
) )

View File

@@ -11,29 +11,29 @@ const (
customerSendMessage = "https://api.weixin.qq.com/cgi-bin/message/custom/send" customerSendMessage = "https://api.weixin.qq.com/cgi-bin/message/custom/send"
) )
// Manager 消息管理者,可以发送消息 //Manager 消息管理者,可以发送消息
type Manager struct { type Manager struct {
*context.Context *context.Context
} }
// NewCustomerMessageManager 实例化消息管理者 //NewCustomerMessageManager 实例化消息管理者
func NewCustomerMessageManager(context *context.Context) *Manager { func NewCustomerMessageManager(context *context.Context) *Manager {
return &Manager{ return &Manager{
context, context,
} }
} }
// MediaText 文本消息的文字 //MediaText 文本消息的文字
type MediaText struct { type MediaText struct {
Content string `json:"content"` Content string `json:"content"`
} }
// MediaResource 消息使用的临时素材id //MediaResource 消息使用的临时素材id
type MediaResource struct { type MediaResource struct {
MediaID string `json:"media_id"` MediaID string `json:"media_id"`
} }
// MediaMiniprogrampage 小程序卡片 //MediaMiniprogrampage 小程序卡片
type MediaMiniprogrampage struct { type MediaMiniprogrampage struct {
Title string `json:"title"` Title string `json:"title"`
Appid string `json:"appid"` Appid string `json:"appid"`
@@ -49,17 +49,17 @@ type MediaLink struct {
ThumbURL string `json:"thumb_url"` ThumbURL string `json:"thumb_url"`
} }
// CustomerMessage 客服消息 //CustomerMessage 客服消息
type CustomerMessage struct { type CustomerMessage struct {
ToUser string `json:"touser"` // 接受者OpenID ToUser string `json:"touser"` //接受者OpenID
Msgtype MsgType `json:"msgtype"` // 客服消息类型 Msgtype MsgType `json:"msgtype"` //客服消息类型
Text *MediaText `json:"text,omitempty"` // 可选 Text *MediaText `json:"text,omitempty"` //可选
Image *MediaResource `json:"image,omitempty"` // 可选 Image *MediaResource `json:"image,omitempty"` //可选
Link *MediaLink `json:"link,omitempty"` // 可选 Link *MediaLink `json:"link,omitempty"` //可选
Miniprogrampage *MediaMiniprogrampage `json:"miniprogrampage,omitempty"` // 可选 Miniprogrampage *MediaMiniprogrampage `json:"miniprogrampage,omitempty"` //可选
} }
// NewCustomerTextMessage 文本消息结构体构造方法 //NewCustomerTextMessage 文本消息结构体构造方法
func NewCustomerTextMessage(toUser, text string) *CustomerMessage { func NewCustomerTextMessage(toUser, text string) *CustomerMessage {
return &CustomerMessage{ return &CustomerMessage{
ToUser: toUser, ToUser: toUser,
@@ -70,7 +70,7 @@ func NewCustomerTextMessage(toUser, text string) *CustomerMessage {
} }
} }
// NewCustomerImgMessage 图片消息的构造方法 //NewCustomerImgMessage 图片消息的构造方法
func NewCustomerImgMessage(toUser, mediaID string) *CustomerMessage { func NewCustomerImgMessage(toUser, mediaID string) *CustomerMessage {
return &CustomerMessage{ return &CustomerMessage{
ToUser: toUser, ToUser: toUser,
@@ -81,7 +81,7 @@ func NewCustomerImgMessage(toUser, mediaID string) *CustomerMessage {
} }
} }
// NewCustomerLinkMessage 图文链接消息的构造方法 //NewCustomerLinkMessage 图文链接消息的构造方法
func NewCustomerLinkMessage(toUser, title, description, url, thumbURL string) *CustomerMessage { func NewCustomerLinkMessage(toUser, title, description, url, thumbURL string) *CustomerMessage {
return &CustomerMessage{ return &CustomerMessage{
ToUser: toUser, ToUser: toUser,
@@ -95,7 +95,7 @@ func NewCustomerLinkMessage(toUser, title, description, url, thumbURL string) *C
} }
} }
// NewCustomerMiniprogrampageMessage 小程序卡片消息的构造方法 //NewCustomerMiniprogrampageMessage 小程序卡片消息的构造方法
func NewCustomerMiniprogrampageMessage(toUser, title, pagepath, thumbMediaID string) *CustomerMessage { func NewCustomerMiniprogrampageMessage(toUser, title, pagepath, thumbMediaID string) *CustomerMessage {
return &CustomerMessage{ return &CustomerMessage{
ToUser: toUser, ToUser: toUser,
@@ -108,7 +108,7 @@ func NewCustomerMiniprogrampageMessage(toUser, title, pagepath, thumbMediaID str
} }
} }
// Send 发送客服消息 //Send 发送客服消息
func (manager *Manager) Send(msg *CustomerMessage) error { func (manager *Manager) Send(msg *CustomerMessage) error {
accessToken, err := manager.Context.GetAccessToken() accessToken, err := manager.Context.GetAccessToken()
if err != nil { if err != nil {

View File

@@ -4,30 +4,23 @@ import (
"github.com/silenceper/wechat/v2/credential" "github.com/silenceper/wechat/v2/credential"
"github.com/silenceper/wechat/v2/miniprogram/analysis" "github.com/silenceper/wechat/v2/miniprogram/analysis"
"github.com/silenceper/wechat/v2/miniprogram/auth" "github.com/silenceper/wechat/v2/miniprogram/auth"
"github.com/silenceper/wechat/v2/miniprogram/business"
"github.com/silenceper/wechat/v2/miniprogram/config" "github.com/silenceper/wechat/v2/miniprogram/config"
"github.com/silenceper/wechat/v2/miniprogram/content" "github.com/silenceper/wechat/v2/miniprogram/content"
"github.com/silenceper/wechat/v2/miniprogram/context" "github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/miniprogram/encryptor" "github.com/silenceper/wechat/v2/miniprogram/encryptor"
"github.com/silenceper/wechat/v2/miniprogram/message" "github.com/silenceper/wechat/v2/miniprogram/message"
"github.com/silenceper/wechat/v2/miniprogram/privacy"
"github.com/silenceper/wechat/v2/miniprogram/qrcode" "github.com/silenceper/wechat/v2/miniprogram/qrcode"
"github.com/silenceper/wechat/v2/miniprogram/riskcontrol"
"github.com/silenceper/wechat/v2/miniprogram/security"
"github.com/silenceper/wechat/v2/miniprogram/shortlink"
"github.com/silenceper/wechat/v2/miniprogram/subscribe" "github.com/silenceper/wechat/v2/miniprogram/subscribe"
"github.com/silenceper/wechat/v2/miniprogram/tcb" "github.com/silenceper/wechat/v2/miniprogram/tcb"
"github.com/silenceper/wechat/v2/miniprogram/urllink"
"github.com/silenceper/wechat/v2/miniprogram/urlscheme"
"github.com/silenceper/wechat/v2/miniprogram/werun" "github.com/silenceper/wechat/v2/miniprogram/werun"
) )
// MiniProgram 微信小程序相关API //MiniProgram 微信小程序相关API
type MiniProgram struct { type MiniProgram struct {
ctx *context.Context ctx *context.Context
} }
// NewMiniProgram 实例化小程序API //NewMiniProgram 实例化小程序API
func NewMiniProgram(cfg *config.Config) *MiniProgram { func NewMiniProgram(cfg *config.Config) *MiniProgram {
defaultAkHandle := credential.NewDefaultAccessToken(cfg.AppID, cfg.AppSecret, credential.CacheKeyMiniProgramPrefix, cfg.Cache) defaultAkHandle := credential.NewDefaultAccessToken(cfg.AppID, cfg.AppSecret, credential.CacheKeyMiniProgramPrefix, cfg.Cache)
ctx := &context.Context{ ctx := &context.Context{
@@ -37,7 +30,7 @@ func NewMiniProgram(cfg *config.Config) *MiniProgram {
return &MiniProgram{ctx} return &MiniProgram{ctx}
} }
// SetAccessTokenHandle 自定义access_token获取方式 //SetAccessTokenHandle 自定义access_token获取方式
func (miniProgram *MiniProgram) SetAccessTokenHandle(accessTokenHandle credential.AccessTokenHandle) { func (miniProgram *MiniProgram) SetAccessTokenHandle(accessTokenHandle credential.AccessTokenHandle) {
miniProgram.ctx.AccessTokenHandle = accessTokenHandle miniProgram.ctx.AccessTokenHandle = accessTokenHandle
} }
@@ -52,37 +45,27 @@ func (miniProgram *MiniProgram) GetEncryptor() *encryptor.Encryptor {
return encryptor.NewEncryptor(miniProgram.ctx) return encryptor.NewEncryptor(miniProgram.ctx)
} }
// GetAuth 登录/用户信息相关接口 //GetAuth 登录/用户信息相关接口
func (miniProgram *MiniProgram) GetAuth() *auth.Auth { func (miniProgram *MiniProgram) GetAuth() *auth.Auth {
return auth.NewAuth(miniProgram.ctx) return auth.NewAuth(miniProgram.ctx)
} }
// GetAnalysis 数据分析 //GetAnalysis 数据分析
func (miniProgram *MiniProgram) GetAnalysis() *analysis.Analysis { func (miniProgram *MiniProgram) GetAnalysis() *analysis.Analysis {
return analysis.NewAnalysis(miniProgram.ctx) return analysis.NewAnalysis(miniProgram.ctx)
} }
// GetBusiness 业务接口 //GetQRCode 小程序码相关API
func (miniProgram *MiniProgram) GetBusiness() *business.Business {
return business.NewBusiness(miniProgram.ctx)
}
// GetPrivacy 小程序隐私协议相关API
func (miniProgram *MiniProgram) GetPrivacy() *privacy.Privacy {
return privacy.NewPrivacy(miniProgram.ctx)
}
// GetQRCode 小程序码相关API
func (miniProgram *MiniProgram) GetQRCode() *qrcode.QRCode { func (miniProgram *MiniProgram) GetQRCode() *qrcode.QRCode {
return qrcode.NewQRCode(miniProgram.ctx) return qrcode.NewQRCode(miniProgram.ctx)
} }
// GetTcb 小程序云开发API //GetTcb 小程序云开发API
func (miniProgram *MiniProgram) GetTcb() *tcb.Tcb { func (miniProgram *MiniProgram) GetTcb() *tcb.Tcb {
return tcb.NewTcb(miniProgram.ctx) return tcb.NewTcb(miniProgram.ctx)
} }
// GetSubscribe 小程序订阅消息 //GetSubscribe 小程序订阅消息
func (miniProgram *MiniProgram) GetSubscribe() *subscribe.Subscribe { func (miniProgram *MiniProgram) GetSubscribe() *subscribe.Subscribe {
return subscribe.NewSubscribe(miniProgram.ctx) return subscribe.NewSubscribe(miniProgram.ctx)
} }
@@ -101,28 +84,3 @@ func (miniProgram *MiniProgram) GetWeRun() *werun.WeRun {
func (miniProgram *MiniProgram) GetContentSecurity() *content.Content { func (miniProgram *MiniProgram) GetContentSecurity() *content.Content {
return content.NewContent(miniProgram.ctx) return content.NewContent(miniProgram.ctx)
} }
// GetURLLink 小程序URL Link接口
func (miniProgram *MiniProgram) GetURLLink() *urllink.URLLink {
return urllink.NewURLLink(miniProgram.ctx)
}
// GetRiskControl 安全风控接口
func (miniProgram *MiniProgram) GetRiskControl() *riskcontrol.RiskControl {
return riskcontrol.NewRiskControl(miniProgram.ctx)
}
// GetSecurity 内容安全接口
func (miniProgram *MiniProgram) GetSecurity() *security.Security {
return security.NewSecurity(miniProgram.ctx)
}
// GetShortLink 小程序短链接口
func (miniProgram *MiniProgram) GetShortLink() *shortlink.ShortLink {
return shortlink.NewShortLink(miniProgram.ctx)
}
// GetSURLScheme 小程序URL Scheme接口
func (miniProgram *MiniProgram) GetSURLScheme() *urlscheme.URLScheme {
return urlscheme.NewURLScheme(miniProgram.ctx)
}

View File

@@ -1,167 +0,0 @@
package privacy
import (
"errors"
"fmt"
"github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/util"
)
// Privacy 小程序授权隐私设置
type Privacy struct {
*context.Context
}
// NewPrivacy 实例化小程序隐私接口
// 文档地址 https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/set_privacy_setting.html
func NewPrivacy(context *context.Context) *Privacy {
if context == nil {
panic("NewPrivacy got a nil context")
}
return &Privacy{
context,
}
}
// OwnerSetting 收集方(开发者)信息配置
type OwnerSetting struct {
ContactEmail string `json:"contact_email"`
ContactPhone string `json:"contact_phone"`
ContactQQ string `json:"contact_qq"`
ContactWeixin string `json:"contact_weixin"`
ExtFileMediaID string `json:"ext_file_media_id"`
NoticeMethod string `json:"notice_method"`
StoreExpireTimestamp string `json:"store_expire_timestamp"`
}
// SettingItem 收集权限的配置
type SettingItem struct {
PrivacyKey string `json:"privacy_key"`
PrivacyText string `json:"privacy_text"`
}
// SetPrivacySettingRequest 设置权限的请求参数
type SetPrivacySettingRequest struct {
PrivacyVer int `json:"privacy_ver"`
OwnerSetting OwnerSetting `json:"owner_setting"`
SettingList []SettingItem `json:"setting_list"`
}
const (
setPrivacySettingURL = "https://api.weixin.qq.com/cgi-bin/component/setprivacysetting"
getPrivacySettingURL = "https://api.weixin.qq.com/cgi-bin/component/getprivacysetting"
uploadPrivacyExtFileURL = "https://api.weixin.qq.com/cgi-bin/component/uploadprivacyextfile"
// PrivacyV1 用户隐私保护指引的版本1表示现网版本。
PrivacyV1 = 1
// PrivacyV2 2表示开发版。默认是2开发版。
PrivacyV2 = 2
)
// GetPrivacySettingResponse 获取权限配置的响应结果
type GetPrivacySettingResponse struct {
util.CommonError
CodeExist int `json:"code_exist"`
PrivacyList []string `json:"privacy_list"`
SettingList []SettingResponseItem `json:"setting_list"`
UpdateTime int64 `json:"update_time"`
OwnerSetting OwnerSetting `json:"owner_setting"`
PrivacyDesc DescList `json:"privacy_desc"`
}
// SettingResponseItem 获取权限设置的响应明细
type SettingResponseItem struct {
PrivacyKey string `json:"privacy_key"`
PrivacyText string `json:"privacy_text"`
PrivacyLabel string `json:"privacy_label"`
}
// DescList 权限列表(保持与官方一致)
type DescList struct {
PrivacyDescList []Desc `json:"privacy_desc_list"`
}
// Desc 权限列表明细(保持与官方一致)
type Desc struct {
PrivacyDesc string `json:"privacy_desc"`
PrivacyKey string `json:"privacy_key"`
}
// GetPrivacySetting 获取小程序权限配置
func (s *Privacy) GetPrivacySetting(privacyVer int) (GetPrivacySettingResponse, error) {
accessToken, err := s.GetAccessToken()
if err != nil {
return GetPrivacySettingResponse{}, err
}
response, err := util.PostJSON(fmt.Sprintf("%s?access_token=%s", getPrivacySettingURL, accessToken), map[string]int{
"privacy_ver": privacyVer,
})
if err != nil {
return GetPrivacySettingResponse{}, err
}
// 返回错误信息
var result GetPrivacySettingResponse
if err = util.DecodeWithError(response, &result, "getprivacysetting"); err != nil {
return GetPrivacySettingResponse{}, err
}
return result, nil
}
// SetPrivacySetting 更新小程序权限配置
func (s *Privacy) SetPrivacySetting(privacyVer int, ownerSetting OwnerSetting, settingList []SettingItem) error {
if privacyVer == PrivacyV1 && len(settingList) > 0 {
return errors.New("当privacy_ver传2或者不传时setting_list是必填当privacy_ver传1时该参数不可传")
}
accessToken, err := s.GetAccessToken()
if err != nil {
return err
}
response, err := util.PostJSON(fmt.Sprintf("%s?access_token=%s", setPrivacySettingURL, accessToken), SetPrivacySettingRequest{
PrivacyVer: privacyVer,
OwnerSetting: ownerSetting,
SettingList: settingList,
})
if err != nil {
return err
}
// 返回错误信息
if err = util.DecodeWithCommonError(response, "setprivacysetting"); err != nil {
return err
}
return err
}
// UploadPrivacyExtFileResponse 上传权限定义模板响应参数
type UploadPrivacyExtFileResponse struct {
util.CommonError
ExtFileMediaID string `json:"ext_file_media_id"`
}
// UploadPrivacyExtFile 上传权限定义模板
func (s *Privacy) UploadPrivacyExtFile(fileData []byte) (UploadPrivacyExtFileResponse, error) {
accessToken, err := s.GetAccessToken()
if err != nil {
return UploadPrivacyExtFileResponse{}, err
}
response, err := util.PostJSON(fmt.Sprintf("%s?access_token=%s", uploadPrivacyExtFileURL, accessToken), map[string][]byte{
"file": fileData,
})
if err != nil {
return UploadPrivacyExtFileResponse{}, err
}
// 返回错误信息
var result UploadPrivacyExtFileResponse
if err = util.DecodeWithError(response, &result, "setprivacysetting"); err != nil {
return UploadPrivacyExtFileResponse{}, err
}
return result, err
}

View File

@@ -15,12 +15,12 @@ const (
getWXACodeUnlimitURL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s" getWXACodeUnlimitURL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s"
) )
// QRCode struct //QRCode struct
type QRCode struct { type QRCode struct {
*context.Context *context.Context
} }
// NewQRCode 实例 //NewQRCode 实例
func NewQRCode(context *context.Context) *QRCode { func NewQRCode(context *context.Context) *QRCode {
qrCode := new(QRCode) qrCode := new(QRCode)
qrCode.Context = context qrCode.Context = context
@@ -40,20 +40,16 @@ type QRCoder struct {
Page string `json:"page,omitempty"` Page string `json:"page,omitempty"`
// path 扫码进入的小程序页面路径 // path 扫码进入的小程序页面路径
Path string `json:"path,omitempty"` Path string `json:"path,omitempty"`
// checkPath 检查page 是否存在,为 true 时 page 必须是已经发布的小程序存在的页面(否则报错);为 false 时允许小程序未发布或者 page 不存在, 但page 有数量上限60000个请勿滥用默认true
CheckPath *bool `json:"check_path,omitempty"`
// width 图片宽度 // width 图片宽度
Width int `json:"width,omitempty"` Width int `json:"width,omitempty"`
// scene 最大32个可见字符只支持数字大小写英文以及部分特殊字符!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) // scene 最大32个可见字符只支持数字大小写英文以及部分特殊字符!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式)
Scene string `json:"scene,omitempty"` Scene string `json:"scene,omitempty"`
// autoColor 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调默认false // autoColor 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调
AutoColor bool `json:"auto_color,omitempty"` AutoColor bool `json:"auto_color,omitempty"`
// lineColor AutoColor 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"},十进制表示 // lineColor AutoColor 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"},十进制表示
LineColor *Color `json:"line_color,omitempty"` LineColor Color `json:"line_color,omitempty"`
// isHyaline 是否需要透明底色默认false // isHyaline 是否需要透明底色
IsHyaline bool `json:"is_hyaline,omitempty"` IsHyaline bool `json:"is_hyaline,omitempty"`
// envVersion 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop"
EnvVersion string `json:"env_version,omitempty"`
} }
// fetchCode 请求并返回二维码二进制数据 // fetchCode 请求并返回二维码二进制数据

View File

@@ -1,60 +0,0 @@
package riskcontrol
import (
"fmt"
"github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/util"
)
const (
getUserRiskRankURL = "https://api.weixin.qq.com/wxa/getuserriskrank?access_token=%s"
)
// RiskControl 安全风控
type RiskControl struct {
*context.Context
}
// NewRiskControl init
func NewRiskControl(ctx *context.Context) *RiskControl {
return &RiskControl{ctx}
}
// UserRiskRankRequest 获取用户安全等级请求
type UserRiskRankRequest struct {
AppID string `json:"appid"` // 小程序 app id
OpenID string `json:"openid"` // 用户的 openid
Scene uint8 `json:"scene"` // 场景值0:注册1:营销作弊
ClientIP string `json:"client_ip"` // 用户访问源ip
Mobile string `json:"mobile_no"` // 用户手机号
Email string `json:"email_address"` // 用户邮箱地址
ExtendedInfo string `json:"extended_info"` // 额外补充信息
IsTest bool `json:"is_test"` // false正式调用true测试调用
}
// UserRiskRank 用户安全等级
type UserRiskRank struct {
util.CommonError
UnionID int64 `json:"union_id"` // 唯一请求标识
RiskRank uint8 `json:"risk_rank"` // 用户风险等级
}
// GetUserRiskRank 根据提交的用户信息数据获取用户的安全等级 risk_rank无需用户授权。
func (riskControl *RiskControl) GetUserRiskRank(in *UserRiskRankRequest) (res UserRiskRank, err error) {
accessToken, err := riskControl.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf(getUserRiskRankURL, accessToken)
response, err := util.PostJSON(uri, in)
if err != nil {
return
}
// 使用通用方法返回错误
err = util.DecodeWithError(response, &res, "GetUserRiskRank")
return
}

View File

@@ -1,256 +0,0 @@
package security
import (
"fmt"
"strconv"
"github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/util"
)
const (
mediaCheckAsyncURL = "https://api.weixin.qq.com/wxa/media_check_async?access_token=%s"
imageCheckURL = "https://api.weixin.qq.com/wxa/img_sec_check?access_token=%s"
msgCheckURL = "https://api.weixin.qq.com/wxa/msg_sec_check?access_token=%s"
)
// Security 内容安全
type Security struct {
*context.Context
}
// NewSecurity init
func NewSecurity(ctx *context.Context) *Security {
return &Security{ctx}
}
// MediaCheckAsyncV1Request 图片/音频异步校验请求参数
type MediaCheckAsyncV1Request struct {
MediaURL string `json:"media_url"` // 要检测的图片或音频的url支持图片格式包括jpg, jepg, png, bmp, gif取首帧支持的音频格式包括mp3, aac, ac3, wma, flac, vorbis, opus, wav
MediaType uint8 `json:"media_type"` // 1:音频;2:图片
}
// MediaCheckAsyncV1 异步校验图片/音频是否含有违法违规内容
// Deprecated
// 在2021年9月1日停止更新请尽快更新至 2.0 接口。建议使用 MediaCheckAsync
func (security *Security) MediaCheckAsyncV1(in *MediaCheckAsyncV1Request) (traceID string, err error) {
accessToken, err := security.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf(mediaCheckAsyncURL, accessToken)
response, err := util.PostJSON(uri, in)
if err != nil {
return
}
// 使用通用方法返回错误
var res struct {
util.CommonError
TraceID string `json:"trace_id"`
}
err = util.DecodeWithError(response, &res, "MediaCheckAsyncV1")
if err != nil {
return
}
traceID = res.TraceID
return
}
// MediaCheckAsyncRequest 图片/音频异步校验请求参数
type MediaCheckAsyncRequest struct {
MediaURL string `json:"media_url"` // 要检测的图片或音频的url支持图片格式包括jpg, jepg, png, bmp, gif取首帧支持的音频格式包括mp3, aac, ac3, wma, flac, vorbis, opus, wav
MediaType uint8 `json:"media_type"` // 1:音频;2:图片
OpenID string `json:"openid"` // 用户的openid用户需在近两小时访问过小程序
Scene uint8 `json:"scene"` // 场景枚举值1 资料2 评论3 论坛4 社交日志)
}
// MediaCheckAsync 异步校验图片/音频是否含有违法违规内容
func (security *Security) MediaCheckAsync(in *MediaCheckAsyncRequest) (traceID string, err error) {
accessToken, err := security.GetAccessToken()
if err != nil {
return
}
var req struct {
MediaCheckAsyncRequest
Version uint `json:"version"` // 接口版本号2.0版本为固定值2
}
req.MediaCheckAsyncRequest = *in
req.Version = 2
uri := fmt.Sprintf(mediaCheckAsyncURL, accessToken)
response, err := util.PostJSON(uri, req)
if err != nil {
return
}
// 使用通用方法返回错误
var res struct {
util.CommonError
TraceID string `json:"trace_id"`
}
err = util.DecodeWithError(response, &res, "MediaCheckAsync")
if err != nil {
return
}
traceID = res.TraceID
return
}
// ImageCheckV1 校验一张图片是否含有违法违规内容(同步)
// https://developers.weixin.qq.com/miniprogram/dev/framework/security.imgSecCheck.html
// Deprecated
// 在2021年9月1日停止更新。建议使用 MediaCheckAsync
func (security *Security) ImageCheckV1(filename string) (err error) {
accessToken, err := security.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf(imageCheckURL, accessToken)
response, err := util.PostFile("media", filename, uri)
if err != nil {
return
}
// 使用通用方法返回错误
return util.DecodeWithCommonError(response, "ImageCheckV1")
}
// CheckSuggest 检查建议
type CheckSuggest string
const (
// CheckSuggestRisky 违规风险建议
CheckSuggestRisky CheckSuggest = "risky"
// CheckSuggestPass 安全
CheckSuggestPass CheckSuggest = "pass"
// CheckSuggestReview 需要审查
CheckSuggestReview CheckSuggest = "review"
)
// MsgScene 文本场景
type MsgScene uint8
const (
// MsgSceneMaterial 资料文件检查场景
MsgSceneMaterial MsgScene = iota + 1
// MsgSceneComment 评论
MsgSceneComment
// MsgSceneForum 论坛
MsgSceneForum
// MsgSceneSocialLog 社交日志
MsgSceneSocialLog
)
// CheckLabel 检查命中标签
type CheckLabel int
func (cl CheckLabel) String() string {
switch cl {
case 100:
return "正常"
case 10001:
return "广告"
case 20001:
return "时政"
case 20002:
return "色情"
case 20003:
return "辱骂"
case 20006:
return "违法犯罪"
case 20008:
return "欺诈"
case 20012:
return "低俗"
case 20013:
return "版权"
case 21000:
return "其他"
default:
return strconv.Itoa(int(cl))
}
}
// MsgCheckRequest 文本检查请求
type MsgCheckRequest struct {
OpenID string `json:"openid"` // 用户的openid用户需在近两小时访问过小程序
Scene MsgScene `json:"scene"` // 场景枚举值1 资料2 评论3 论坛4 社交日志)
Content string `json:"content"` // 需检测的文本内容,文本字数的上限为 2500 字,需使用 UTF-8 编码
Nickname string `json:"nickname"` // 非必填用户昵称需使用UTF-8编码
Title string `json:"title"` // 非必填文本标题需使用UTF-8编码
Signature string `json:"signature"` // (非必填)个性签名,该参数仅在资料类场景有效(scene=1)需使用UTF-8编码
}
// MsgCheckResponse 文本检查响应
type MsgCheckResponse struct {
util.CommonError
TraceID string `json:"trace_id"` // 唯一请求标识
Result struct {
Suggest CheckSuggest `json:"suggest"` // 建议
Label CheckLabel `json:"label"` // 命中标签
} `json:"result"` // 综合结果
Detail []struct {
ErrCode int64 `json:"errcode"` // 错误码仅当该值为0时该项结果有效
Strategy string `json:"strategy"` // 策略类型
Suggest string `json:"suggest"` // 建议
Label CheckLabel `json:"label"` // 命中标签
Prob uint `json:"prob"` // 置信度。0-100越高代表越有可能属于当前返回的标签label
Keyword string `json:"keyword"` // 命中的自定义关键词
} `json:"detail"` // 详细检测结果
}
// MsgCheckV1 检查一段文本是否含有违法违规内容
// Deprecated
// 在2021年9月1日停止更新请尽快更新至 2.0 接口。建议使用 MsgCheck
func (security *Security) MsgCheckV1(content string) (res MsgCheckResponse, err error) {
accessToken, err := security.GetAccessToken()
if err != nil {
return
}
var req struct {
Content string `json:"content"`
}
req.Content = content
uri := fmt.Sprintf(msgCheckURL, accessToken)
response, err := util.PostJSON(uri, req)
if err != nil {
return
}
// 使用通用方法返回错误
err = util.DecodeWithError(response, &res, "security.MsgCheckV1")
return
}
// MsgCheck 检查一段文本是否含有违法违规内容
func (security *Security) MsgCheck(in *MsgCheckRequest) (res MsgCheckResponse, err error) {
accessToken, err := security.GetAccessToken()
if err != nil {
return
}
var req struct {
MsgCheckRequest
Version uint `json:"version"`
}
req.MsgCheckRequest = *in
req.Version = 2
uri := fmt.Sprintf(msgCheckURL, accessToken)
response, err := util.PostJSON(uri, req)
if err != nil {
return
}
// 使用通用方法返回错误
err = util.DecodeWithError(response, &res, "security.MsgCheck")
return
}

View File

@@ -1,86 +0,0 @@
package shortlink
import (
"fmt"
"github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/util"
)
const (
generateShortLinkURL = "https://api.weixin.qq.com/wxa/genwxashortlink?access_token=%s"
)
// ShortLink 短链接
type ShortLink struct {
*context.Context
}
// NewShortLink 实例
func NewShortLink(ctx *context.Context) *ShortLink {
return &ShortLink{ctx}
}
// ShortLinker 请求结构体
type ShortLinker struct {
// pageUrl 通过 Short Link 进入的小程序页面路径,必须是已经发布的小程序存在的页面,可携带 query最大1024个字符
PageURL string `json:"page_url"`
// pageTitle 页面标题不能包含违法信息超过20字符会用... 截断代替
PageTitle string `json:"page_title"`
// isPermanent 生成的 Short Link 类型短期有效false永久有效true
IsPermanent bool `json:"is_permanent,omitempty"`
}
// resShortLinker 返回结构体
type resShortLinker struct {
// 通用错误
*util.CommonError
// 返回的 shortLink
Link string `json:"link"`
}
// Generate 生成 shortLink
func (shortLink *ShortLink) generate(shortLinkParams ShortLinker) (string, error) {
var accessToken string
accessToken, err := shortLink.GetAccessToken()
if err != nil {
return "", err
}
urlStr := fmt.Sprintf(generateShortLinkURL, accessToken)
response, err := util.PostJSON(urlStr, shortLinkParams)
if err != nil {
return "", err
}
// 使用通用方法返回错误
var res resShortLinker
err = util.DecodeWithError(response, &res, "GenerateShortLink")
if err != nil {
return "", err
}
return res.Link, nil
}
// GenerateShortLinkPermanent 生成永久shortLink
func (shortLink *ShortLink) GenerateShortLinkPermanent(PageURL, pageTitle string) (string, error) {
return shortLink.generate(ShortLinker{
PageURL: PageURL,
PageTitle: pageTitle,
IsPermanent: true,
})
}
// GenerateShortLinkTemp 生成临时shortLink
func (shortLink *ShortLink) GenerateShortLinkTemp(PageURL, pageTitle string) (string, error) {
return shortLink.generate(ShortLinker{
PageURL: PageURL,
PageTitle: pageTitle,
IsPermanent: false,
})
}

View File

@@ -8,8 +8,8 @@ import (
) )
const ( const (
// 发送订阅消息 //发送订阅消息
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html //https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html
subscribeSendURL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send" subscribeSendURL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send"
// 获取当前帐号下的个人模板列表 // 获取当前帐号下的个人模板列表
@@ -41,21 +41,21 @@ func NewSubscribe(ctx *context.Context) *Subscribe {
// Message 订阅消息请求参数 // Message 订阅消息请求参数
type Message struct { type Message struct {
ToUser string `json:"touser"` // 必选,接收者(用户)的 openid ToUser string `json:"touser"` //必选,接收者(用户)的 openid
TemplateID string `json:"template_id"` // 必选所需下发的订阅模板id TemplateID string `json:"template_id"` //必选所需下发的订阅模板id
Page string `json:"page"` // 可选,点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,示例index?foo=bar。该字段不填则模板无跳转。 Page string `json:"page"` //可选,点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,示例index?foo=bar。该字段不填则模板无跳转。
Data map[string]*DataItem `json:"data"` // 必选, 模板内容 Data map[string]*DataItem `json:"data"` //必选, 模板内容
MiniprogramState string `json:"miniprogram_state"` // 可选跳转小程序类型developer为开发版trial为体验版formal为正式版默认为正式版 MiniprogramState string `json:"miniprogram_state"` //可选跳转小程序类型developer为开发版trial为体验版formal为正式版默认为正式版
Lang string `json:"lang"` // 入小程序查看”的语言类型支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文)默认为zh_CN Lang string `json:"lang"` //入小程序查看”的语言类型支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文)默认为zh_CN
} }
// DataItem 模版内某个 .DATA 的值 //DataItem 模版内某个 .DATA 的值
type DataItem struct { type DataItem struct {
Value interface{} `json:"value"` Value interface{} `json:"value"`
Color string `json:"color"` Color string `json:"color"`
} }
// TemplateItem template item //TemplateItem template item
type TemplateItem struct { type TemplateItem struct {
PriTmplID string `json:"priTmplId"` PriTmplID string `json:"priTmplId"`
Title string `json:"title"` Title string `json:"title"`
@@ -64,7 +64,7 @@ type TemplateItem struct {
Type int64 `json:"type"` Type int64 `json:"type"`
} }
// TemplateList template list //TemplateList template list
type TemplateList struct { type TemplateList struct {
util.CommonError util.CommonError
Data []TemplateItem `json:"data"` Data []TemplateItem `json:"data"`
@@ -85,7 +85,7 @@ func (s *Subscribe) Send(msg *Message) (err error) {
return util.DecodeWithCommonError(response, "Send") return util.DecodeWithCommonError(response, "Send")
} }
// ListTemplates 获取当前帐号下的个人模板列表 //ListTemplates 获取当前帐号下的个人模板列表
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getTemplateList.html // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getTemplateList.html
func (s *Subscribe) ListTemplates() (*TemplateList, error) { func (s *Subscribe) ListTemplates() (*TemplateList, error) {
accessToken, err := s.GetAccessToken() accessToken, err := s.GetAccessToken()

View File

@@ -21,9 +21,7 @@ wcTcb := wc.GetTcb()
``` ```
### 举例 ### 举例
#### 触发云函数 #### 触发云函数
```golang ```golang
res, err := wcTcb.InvokeCloudFunction("test-xxxx", "add", `{"a":1,"b":2}`) res, err := wcTcb.InvokeCloudFunction("test-xxxx", "add", `{"a":1,"b":2}`)
if err != nil { if err != nil {

View File

@@ -7,18 +7,17 @@ import (
) )
const ( const (
// 触发云函数 //触发云函数
invokeCloudFunctionURL = "https://api.weixin.qq.com/tcb/invokecloudfunction" invokeCloudFunctionURL = "https://api.weixin.qq.com/tcb/invokecloudfunction"
) )
// InvokeCloudFunctionRes 云函数调用返回结果 //InvokeCloudFunctionRes 云函数调用返回结果
type InvokeCloudFunctionRes struct { type InvokeCloudFunctionRes struct {
util.CommonError util.CommonError
RespData string `json:"resp_data"` // 云函数返回的buffer RespData string `json:"resp_data"` //云函数返回的buffer
} }
// InvokeCloudFunction 云函数调用 //InvokeCloudFunction 云函数调用
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/functions/invokeCloudFunction.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/functions/invokeCloudFunction.html
func (tcb *Tcb) InvokeCloudFunction(env, name, args string) (*InvokeCloudFunctionRes, error) { func (tcb *Tcb) InvokeCloudFunction(env, name, args string) (*InvokeCloudFunctionRes, error) {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()

View File

@@ -7,192 +7,191 @@ import (
) )
const ( const (
// 数据库导入 //数据库导入
databaseMigrateImportURL = "https://api.weixin.qq.com/tcb/databasemigrateimport" databaseMigrateImportURL = "https://api.weixin.qq.com/tcb/databasemigrateimport"
// 数据库导出 //数据库导出
databaseMigrateExportURL = "https://api.weixin.qq.com/tcb/databasemigrateexport" databaseMigrateExportURL = "https://api.weixin.qq.com/tcb/databasemigrateexport"
// 数据库迁移状态查询 //数据库迁移状态查询
databaseMigrateQueryInfoURL = "https://api.weixin.qq.com/tcb/databasemigratequeryinfo" databaseMigrateQueryInfoURL = "https://api.weixin.qq.com/tcb/databasemigratequeryinfo"
// 变更数据库索引 //变更数据库索引
updateIndexURL = "https://api.weixin.qq.com/tcb/updateindex" updateIndexURL = "https://api.weixin.qq.com/tcb/updateindex"
// 新增集合 //新增集合
databaseCollectionAddURL = "https://api.weixin.qq.com/tcb/databasecollectionadd" databaseCollectionAddURL = "https://api.weixin.qq.com/tcb/databasecollectionadd"
// 删除集合 //删除集合
databaseCollectionDeleteURL = "https://api.weixin.qq.com/tcb/databasecollectiondelete" databaseCollectionDeleteURL = "https://api.weixin.qq.com/tcb/databasecollectiondelete"
// 获取特定云环境下集合信息 //获取特定云环境下集合信息
databaseCollectionGetURL = "https://api.weixin.qq.com/tcb/databasecollectionget" databaseCollectionGetURL = "https://api.weixin.qq.com/tcb/databasecollectionget"
// 数据库插入记录 //数据库插入记录
databaseAddURL = "https://api.weixin.qq.com/tcb/databaseadd" databaseAddURL = "https://api.weixin.qq.com/tcb/databaseadd"
// 数据库删除记录 //数据库删除记录
databaseDeleteURL = "https://api.weixin.qq.com/tcb/databasedelete" databaseDeleteURL = "https://api.weixin.qq.com/tcb/databasedelete"
// 数据库更新记录 //数据库更新记录
databaseUpdateURL = "https://api.weixin.qq.com/tcb/databaseupdate" databaseUpdateURL = "https://api.weixin.qq.com/tcb/databaseupdate"
// 数据库查询记录 //数据库查询记录
databaseQueryURL = "https://api.weixin.qq.com/tcb/databasequery" databaseQueryURL = "https://api.weixin.qq.com/tcb/databasequery"
// 统计集合记录数或统计查询语句对应的结果记录数 //统计集合记录数或统计查询语句对应的结果记录数
databaseCountURL = "https://api.weixin.qq.com/tcb/databasecount" databaseCountURL = "https://api.weixin.qq.com/tcb/databasecount"
// ConflictModeInster 冲突处理模式 插入 //ConflictModeInster 冲突处理模式 插入
ConflictModeInster ConflictMode = 1 ConflictModeInster ConflictMode = 1
// ConflictModeUpsert 冲突处理模式 更新 //ConflictModeUpsert 冲突处理模式 更新
ConflictModeUpsert ConflictMode = 2 ConflictModeUpsert ConflictMode = 2
// FileTypeJSON 的合法值 json //FileTypeJSON 的合法值 json
FileTypeJSON FileType = 1 FileTypeJSON FileType = 1
// FileTypeCsv 的合法值 csv //FileTypeCsv 的合法值 csv
FileTypeCsv FileType = 2 FileTypeCsv FileType = 2
) )
// ConflictMode 冲突处理模式 //ConflictMode 冲突处理模式
type ConflictMode int type ConflictMode int
// FileType 文件上传和导出的允许文件类型 //FileType 文件上传和导出的允许文件类型
type FileType int type FileType int
// ValidDirections 合法的direction值 //ValidDirections 合法的direction值
var ValidDirections = []string{"1", "-1", "2dsphere"} var ValidDirections = []string{"1", "-1", "2dsphere"}
// DatabaseMigrateExportReq 数据库出 请求参数 //DatabaseMigrateExportReq 数据库出 请求参数
type DatabaseMigrateExportReq struct { type DatabaseMigrateExportReq struct {
Env string `json:"env,omitempty"` // 云环境ID Env string `json:"env,omitempty"` //云环境ID
FilePath string `json:"file_path,omitempty"` // 导出文件路径(导入文件需先上传到同环境的存储中,可使用开发者工具或 HTTP API的上传文件 API上传 FilePath string `json:"file_path,omitempty"` //导出文件路径(导入文件需先上传到同环境的存储中,可使用开发者工具或 HTTP API的上传文件 API上传
FileType FileType `json:"file_type,omitempty"` // 导出文件类型,文件格式参考数据库导入指引中的文件格式部分 1:json 2:csv FileType FileType `json:"file_type,omitempty"` //导出文件类型,文件格式参考数据库导入指引中的文件格式部分 1:json 2:csv
Query string `json:"query,omitempty"` // 导出条件 Query string `json:"query,omitempty"` //导出条件
} }
// DatabaseMigrateExportRes 数据库导出 返回结果 //DatabaseMigrateExportRes 数据库导出 返回结果
type DatabaseMigrateExportRes struct { type DatabaseMigrateExportRes struct {
util.CommonError util.CommonError
JobID int64 `json:"job_id"` // 导出任务ID可使用数据库迁移进度查询 API 查询导入进度及结果 JobID int64 `json:"job_id"` //导出任务ID可使用数据库迁移进度查询 API 查询导入进度及结果
} }
// DatabaseMigrateImportReq 数据库导入 请求参数 //DatabaseMigrateImportReq 数据库导入 请求参数
type DatabaseMigrateImportReq struct { type DatabaseMigrateImportReq struct {
Env string `json:"env,omitempty"` // 云环境ID Env string `json:"env,omitempty"` //云环境ID
CollectionName string `json:"collection_name,omitempty"` // 集合名称 CollectionName string `json:"collection_name,omitempty"` //集合名称
FilePath string `json:"file_path,omitempty"` // 导出文件路径(文件会导出到同环境的云存储中,可使用获取下载链接 API 获取下载链接) FilePath string `json:"file_path,omitempty"` //导出文件路径(文件会导出到同环境的云存储中,可使用获取下载链接 API 获取下载链接)
FileType FileType `json:"file_type,omitempty"` // 导入文件类型,文件格式参考数据库导入指引中的文件格式部分 1:json 2:csv FileType FileType `json:"file_type,omitempty"` //导入文件类型,文件格式参考数据库导入指引中的文件格式部分 1:json 2:csv
StopOnError bool `json:"stop_on_error,omitempty"` // 是否在遇到错误时停止导入 StopOnError bool `json:"stop_on_error,omitempty"` //是否在遇到错误时停止导入
ConflictMode ConflictMode `json:"conflict_mode,omitempty"` // 冲突处理模式 1:inster 2:UPSERT ConflictMode ConflictMode `json:"conflict_mode,omitempty"` //冲突处理模式 1:inster 2:UPSERT
} }
// DatabaseMigrateImportRes 数据库导入 返回结果 //DatabaseMigrateImportRes 数据库导入 返回结果
type DatabaseMigrateImportRes struct { type DatabaseMigrateImportRes struct {
util.CommonError util.CommonError
JobID int64 `json:"job_id"` // 导入任务ID可使用数据库迁移进度查询 API 查询导入进度及结果 JobID int64 `json:"job_id"` //导入任务ID可使用数据库迁移进度查询 API 查询导入进度及结果
} }
// DatabaseMigrateQueryInfoRes 数据库迁移状态查询 //DatabaseMigrateQueryInfoRes 数据库迁移状态查询
type DatabaseMigrateQueryInfoRes struct { type DatabaseMigrateQueryInfoRes struct {
util.CommonError util.CommonError
Status string `json:"status"` // 导出状态 Status string `json:"status"` //导出状态
RecordSuccess int64 `json:"record_success"` // 导出成功记录数 RecordSuccess int64 `json:"record_success"` //导出成功记录数
RecordFail int64 `json:"record_fail"` // 导出失败记录数 RecordFail int64 `json:"record_fail"` //导出失败记录数
ErrMsg string `json:"err_msg"` // 导出错误信息 ErrMsg string `json:"err_msg"` //导出错误信息
FileURL string `json:"file_url"` // 导出文件下载地址 FileURL string `json:"file_url"` //导出文件下载地址
} }
// UpdateIndexReq 变更数据库索引 请求参数 //UpdateIndexReq 变更数据库索引 请求参数
type UpdateIndexReq struct { type UpdateIndexReq struct {
Env string `json:"env,omitempty"` // 云环境ID Env string `json:"env,omitempty"` //云环境ID
CollectionName string `json:"collection_name,omitempty"` // 集合名称 CollectionName string `json:"collection_name,omitempty"` //集合名称
CreateIndexes []CreateIndex `json:"create_indexes,omitempty"` // 新增索引 CreateIndexes []CreateIndex `json:"create_indexes,omitempty"` //新增索引
DropIndexes []DropIndex `json:"drop_indexes,omitempty"` // 删除索引 DropIndexes []DropIndex `json:"drop_indexes,omitempty"` //删除索引
} }
// CreateIndex 新增索引 //CreateIndex 新增索引
type CreateIndex struct { type CreateIndex struct {
Name string `json:"name,omitempty"` // 索引名 Name string `json:"name,omitempty"` //索引名
Unique bool `json:"unique,omitempty"` // 是否唯一 Unique bool `json:"unique,omitempty"` //是否唯一
Keys []CreateIndexKey `json:"keys,omitempty"` // 索引字段 Keys []CreateIndexKey `json:"keys,omitempty"` //索引字段
} }
// CreateIndexKey create index key //CreateIndexKey create index key
type CreateIndexKey struct { type CreateIndexKey struct {
Name string `json:"name,omitempty"` // 字段名 Name string `json:"name,omitempty"` //字段名
Direction string `json:"direction,omitempty"` // 字段排序 Direction string `json:"direction,omitempty"` //字段排序
} }
// DropIndex 删除索引 //DropIndex 删除索引
type DropIndex struct { type DropIndex struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
} }
// DatabaseCollectionReq 新增/删除集合请求参数 //DatabaseCollectionReq 新增/删除集合请求参数
type DatabaseCollectionReq struct { type DatabaseCollectionReq struct {
Env string `json:"env,omitempty"` // 云环境ID Env string `json:"env,omitempty"` //云环境ID
CollectionName string `json:"collection_name,omitempty"` // 集合名称 CollectionName string `json:"collection_name,omitempty"` //集合名称
} }
// DatabaseCollectionGetReq 获取特定云环境下集合信息请求 //DatabaseCollectionGetReq 获取特定云环境下集合信息请求
type DatabaseCollectionGetReq struct { type DatabaseCollectionGetReq struct {
Env string `json:"env,omitempty"` // 云环境ID Env string `json:"env,omitempty"` //云环境ID
Limit int64 `json:"limit,omitempty"` // 获取数量限制 Limit int64 `json:"limit,omitempty"` //获取数量限制
Offset int64 `json:"offset,omitempty"` // 偏移量 Offset int64 `json:"offset,omitempty"` //偏移量
} }
// DatabaseCollectionGetRes 获取特定云环境下集合信息结果 //DatabaseCollectionGetRes 获取特定云环境下集合信息结果
type DatabaseCollectionGetRes struct { type DatabaseCollectionGetRes struct {
util.CommonError util.CommonError
Pager struct { Pager struct {
Limit int64 `json:"limit"` // 单次查询限制 Limit int64 `json:"limit"` //单次查询限制
Offset int64 `json:"offset"` // 偏移量 Offset int64 `json:"offset"` //偏移量
Total int64 `json:"total"` // 符合查询条件的记录总数 Total int64 `json:"total"` //符合查询条件的记录总数
} `json:"pager"` } `json:"pager"`
Collections []struct { Collections []struct {
Name string `json:"name"` // 集合名 Name string `json:"name"` //集合名
Count int64 `json:"count"` // 表中文档数量 Count int64 `json:"count"` //表中文档数量
Size int64 `json:"size"` // 表的大小(即表中文档总大小),单位:字节 Size int64 `json:"size"` //表的大小(即表中文档总大小),单位:字节
IndexCount int64 `json:"index_count"` // 索引数量 IndexCount int64 `json:"index_count"` //索引数量
IndexSize int64 `json:"index_size"` // 索引占用大小,单位:字节 IndexSize int64 `json:"index_size"` //索引占用大小,单位:字节
} `json:"collections"` } `json:"collections"`
} }
// DatabaseReq 数据库插入/删除/更新/查询/统计记录请求参数 //DatabaseReq 数据库插入/删除/更新/查询/统计记录请求参数
type DatabaseReq struct { type DatabaseReq struct {
Env string `json:"env,omitempty"` // 云环境ID Env string `json:"env,omitempty"` //云环境ID
Query string `json:"query,omitempty"` // 数据库操作语句 Query string `json:"query,omitempty"` //数据库操作语句
} }
// DatabaseAddRes 数据库插入记录返回结果 //DatabaseAddRes 数据库插入记录返回结果
type DatabaseAddRes struct { type DatabaseAddRes struct {
util.CommonError util.CommonError
IDList []string `json:"id_list"` // 插入成功的数据集合主键_id。 IDList []string `json:"id_list"` //插入成功的数据集合主键_id。
} }
// DatabaseDeleteRes 数据库删除记录返回结果 //DatabaseDeleteRes 数据库删除记录返回结果
type DatabaseDeleteRes struct { type DatabaseDeleteRes struct {
util.CommonError util.CommonError
Deleted int64 `json:"deleted"` // 删除记录数量 Deleted int64 `json:"deleted"` //删除记录数量
} }
// DatabaseUpdateRes 数据库更新记录返回结果 //DatabaseUpdateRes 数据库更新记录返回结果
type DatabaseUpdateRes struct { type DatabaseUpdateRes struct {
util.CommonError util.CommonError
Matched int64 `json:"matched"` // 更新条件匹配到的结果数 Matched int64 `json:"matched"` //更新条件匹配到的结果数
Modified int64 `json:"modified"` // 修改的记录数注意使用set操作新插入的数据不计入修改数目 Modified int64 `json:"modified"` //修改的记录数注意使用set操作新插入的数据不计入修改数目
ID string `json:"id"` ID string `json:"id"`
} }
// DatabaseQueryRes 数据库查询记录 返回结果 //DatabaseQueryRes 数据库查询记录 返回结果
type DatabaseQueryRes struct { type DatabaseQueryRes struct {
util.CommonError util.CommonError
Pager struct { Pager struct {
Limit int64 `json:"limit"` // 单次查询限制 Limit int64 `json:"limit"` //单次查询限制
Offset int64 `json:"offset"` // 偏移量 Offset int64 `json:"offset"` //偏移量
Total int64 `json:"total"` // 符合查询条件的记录总数 Total int64 `json:"total"` //符合查询条件的记录总数
} `json:"pager"` } `json:"pager"`
Data []string `json:"data"` Data []string `json:"data"`
} }
// DatabaseCountRes 统计集合记录数或统计查询语句对应的结果记录数 返回结果 //DatabaseCountRes 统计集合记录数或统计查询语句对应的结果记录数 返回结果
type DatabaseCountRes struct { type DatabaseCountRes struct {
util.CommonError util.CommonError
Count int64 `json:"count"` // 记录数量 Count int64 `json:"count"` //记录数量
} }
// DatabaseMigrateImport 数据库导入 //DatabaseMigrateImport 数据库导入
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateImport.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateImport.html
func (tcb *Tcb) DatabaseMigrateImport(req *DatabaseMigrateImportReq) (*DatabaseMigrateImportRes, error) { func (tcb *Tcb) DatabaseMigrateImport(req *DatabaseMigrateImportReq) (*DatabaseMigrateImportRes, error) {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()
@@ -209,8 +208,7 @@ func (tcb *Tcb) DatabaseMigrateImport(req *DatabaseMigrateImportReq) (*DatabaseM
return databaseMigrateImportRes, err return databaseMigrateImportRes, err
} }
// DatabaseMigrateExport 数据库导出 //DatabaseMigrateExport 数据库导出
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateExport.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateExport.html
func (tcb *Tcb) DatabaseMigrateExport(req *DatabaseMigrateExportReq) (*DatabaseMigrateExportRes, error) { func (tcb *Tcb) DatabaseMigrateExport(req *DatabaseMigrateExportReq) (*DatabaseMigrateExportRes, error) {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()
@@ -227,8 +225,7 @@ func (tcb *Tcb) DatabaseMigrateExport(req *DatabaseMigrateExportReq) (*DatabaseM
return databaseMigrateExportRes, err return databaseMigrateExportRes, err
} }
// DatabaseMigrateQueryInfo 数据库迁移状态查询 //DatabaseMigrateQueryInfo 数据库迁移状态查询
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateQueryInfo.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateQueryInfo.html
func (tcb *Tcb) DatabaseMigrateQueryInfo(env string, jobID int64) (*DatabaseMigrateQueryInfoRes, error) { func (tcb *Tcb) DatabaseMigrateQueryInfo(env string, jobID int64) (*DatabaseMigrateQueryInfoRes, error) {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()
@@ -248,8 +245,8 @@ func (tcb *Tcb) DatabaseMigrateQueryInfo(env string, jobID int64) (*DatabaseMigr
return databaseMigrateQueryInfoRes, err return databaseMigrateQueryInfoRes, err
} }
// UpdateIndex 变更数据库索引 //UpdateIndex 变更数据库索引
// https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/updateIndex.html //https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/updateIndex.html
func (tcb *Tcb) UpdateIndex(req *UpdateIndexReq) error { func (tcb *Tcb) UpdateIndex(req *UpdateIndexReq) error {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()
if err != nil { if err != nil {
@@ -263,8 +260,7 @@ func (tcb *Tcb) UpdateIndex(req *UpdateIndexReq) error {
return util.DecodeWithCommonError(response, "UpdateIndex") return util.DecodeWithCommonError(response, "UpdateIndex")
} }
// DatabaseCollectionAdd 新增集合 //DatabaseCollectionAdd 新增集合
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionAdd.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionAdd.html
func (tcb *Tcb) DatabaseCollectionAdd(env, collectionName string) error { func (tcb *Tcb) DatabaseCollectionAdd(env, collectionName string) error {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()
@@ -282,8 +278,7 @@ func (tcb *Tcb) DatabaseCollectionAdd(env, collectionName string) error {
return util.DecodeWithCommonError(response, "DatabaseCollectionAdd") return util.DecodeWithCommonError(response, "DatabaseCollectionAdd")
} }
// DatabaseCollectionDelete 删除集合 //DatabaseCollectionDelete 删除集合
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionDelete.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionDelete.html
func (tcb *Tcb) DatabaseCollectionDelete(env, collectionName string) error { func (tcb *Tcb) DatabaseCollectionDelete(env, collectionName string) error {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()
@@ -301,8 +296,7 @@ func (tcb *Tcb) DatabaseCollectionDelete(env, collectionName string) error {
return util.DecodeWithCommonError(response, "DatabaseCollectionDelete") return util.DecodeWithCommonError(response, "DatabaseCollectionDelete")
} }
// DatabaseCollectionGet 获取特定云环境下集合信息 //DatabaseCollectionGet 获取特定云环境下集合信息
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionGet.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionGet.html
func (tcb *Tcb) DatabaseCollectionGet(env string, limit, offset int64) (*DatabaseCollectionGetRes, error) { func (tcb *Tcb) DatabaseCollectionGet(env string, limit, offset int64) (*DatabaseCollectionGetRes, error) {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()
@@ -323,8 +317,7 @@ func (tcb *Tcb) DatabaseCollectionGet(env string, limit, offset int64) (*Databas
return databaseCollectionGetRes, err return databaseCollectionGetRes, err
} }
// DatabaseAdd 数据库插入记录 //DatabaseAdd 数据库插入记录
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseAdd.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseAdd.html
func (tcb *Tcb) DatabaseAdd(env, query string) (*DatabaseAddRes, error) { func (tcb *Tcb) DatabaseAdd(env, query string) (*DatabaseAddRes, error) {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()
@@ -344,8 +337,7 @@ func (tcb *Tcb) DatabaseAdd(env, query string) (*DatabaseAddRes, error) {
return databaseAddRes, err return databaseAddRes, err
} }
// DatabaseDelete 数据库插入记录 //DatabaseDelete 数据库插入记录
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseDelete.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseDelete.html
func (tcb *Tcb) DatabaseDelete(env, query string) (*DatabaseDeleteRes, error) { func (tcb *Tcb) DatabaseDelete(env, query string) (*DatabaseDeleteRes, error) {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()
@@ -365,8 +357,7 @@ func (tcb *Tcb) DatabaseDelete(env, query string) (*DatabaseDeleteRes, error) {
return databaseDeleteRes, err return databaseDeleteRes, err
} }
// DatabaseUpdate 数据库插入记录 //DatabaseUpdate 数据库插入记录
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseUpdate.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseUpdate.html
func (tcb *Tcb) DatabaseUpdate(env, query string) (*DatabaseUpdateRes, error) { func (tcb *Tcb) DatabaseUpdate(env, query string) (*DatabaseUpdateRes, error) {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()
@@ -386,8 +377,7 @@ func (tcb *Tcb) DatabaseUpdate(env, query string) (*DatabaseUpdateRes, error) {
return databaseUpdateRes, err return databaseUpdateRes, err
} }
// DatabaseQuery 数据库查询记录 //DatabaseQuery 数据库查询记录
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseQuery.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseQuery.html
func (tcb *Tcb) DatabaseQuery(env, query string) (*DatabaseQueryRes, error) { func (tcb *Tcb) DatabaseQuery(env, query string) (*DatabaseQueryRes, error) {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()
@@ -407,8 +397,7 @@ func (tcb *Tcb) DatabaseQuery(env, query string) (*DatabaseQueryRes, error) {
return databaseQueryRes, err return databaseQueryRes, err
} }
// DatabaseCount 统计集合记录数或统计查询语句对应的结果记录数 //DatabaseCount 统计集合记录数或统计查询语句对应的结果记录数
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCount.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCount.html
func (tcb *Tcb) DatabaseCount(env, query string) (*DatabaseCountRes, error) { func (tcb *Tcb) DatabaseCount(env, query string) (*DatabaseCountRes, error) {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()

View File

@@ -7,60 +7,60 @@ import (
) )
const ( const (
// 获取文件上传链接 //获取文件上传链接
uploadFilePathURL = "https://api.weixin.qq.com/tcb/uploadfile" uploadFilePathURL = "https://api.weixin.qq.com/tcb/uploadfile"
// 获取文件下载链接 //获取文件下载链接
batchDownloadFileURL = "https://api.weixin.qq.com/tcb/batchdownloadfile" batchDownloadFileURL = "https://api.weixin.qq.com/tcb/batchdownloadfile"
// 删除文件链接 //删除文件链接
batchDeleteFileURL = "https://api.weixin.qq.com/tcb/batchdeletefile" batchDeleteFileURL = "https://api.weixin.qq.com/tcb/batchdeletefile"
) )
// UploadFileReq 上传文件请求值 //UploadFileReq 上传文件请求值
type UploadFileReq struct { type UploadFileReq struct {
Env string `json:"env,omitempty"` Env string `json:"env,omitempty"`
Path string `json:"path,omitempty"` Path string `json:"path,omitempty"`
} }
// UploadFileRes 上传文件返回结果 //UploadFileRes 上传文件返回结果
type UploadFileRes struct { type UploadFileRes struct {
util.CommonError util.CommonError
URL string `json:"url"` // 上传url URL string `json:"url"` //上传url
Token string `json:"token"` // token Token string `json:"token"` //token
Authorization string `json:"authorization"` // authorization Authorization string `json:"authorization"` //authorization
FileID string `json:"file_id"` // 文件ID FileID string `json:"file_id"` //文件ID
CosFileID string `json:"cos_file_id"` // cos文件ID CosFileID string `json:"cos_file_id"` //cos文件ID
} }
// BatchDownloadFileReq 上传文件请求值 //BatchDownloadFileReq 上传文件请求值
type BatchDownloadFileReq struct { type BatchDownloadFileReq struct {
Env string `json:"env,omitempty"` Env string `json:"env,omitempty"`
FileList []*DownloadFile `json:"file_list,omitempty"` FileList []*DownloadFile `json:"file_list,omitempty"`
} }
// DownloadFile 文件信息 //DownloadFile 文件信息
type DownloadFile struct { type DownloadFile struct {
FileID string `json:"fileid"` // 文件ID FileID string `json:"fileid"` //文件ID
MaxAge int64 `json:"max_age"` // 下载链接有效期 MaxAge int64 `json:"max_age"` //下载链接有效期
} }
// BatchDownloadFileRes 上传文件返回结果 //BatchDownloadFileRes 上传文件返回结果
type BatchDownloadFileRes struct { type BatchDownloadFileRes struct {
util.CommonError util.CommonError
FileList []struct { FileList []struct {
FileID string `json:"file_id"` // 文件ID FileID string `json:"file_id"` //文件ID
DownloadURL string `json:"download_url"` // 下载链接 DownloadURL string `json:"download_url"` //下载链接
Status int64 `json:"status"` // 状态码 Status int64 `json:"status"` //状态码
ErrMsg string `json:"errmsg"` // 该文件错误信息 ErrMsg string `json:"errmsg"` //该文件错误信息
} `json:"file_list"` } `json:"file_list"`
} }
// BatchDeleteFileReq 批量删除文件请求参数 //BatchDeleteFileReq 批量删除文件请求参数
type BatchDeleteFileReq struct { type BatchDeleteFileReq struct {
Env string `json:"env,omitempty"` Env string `json:"env,omitempty"`
FileIDList []string `json:"fileid_list,omitempty"` FileIDList []string `json:"fileid_list,omitempty"`
} }
// BatchDeleteFileRes 批量删除文件返回结果 //BatchDeleteFileRes 批量删除文件返回结果
type BatchDeleteFileRes struct { type BatchDeleteFileRes struct {
util.CommonError util.CommonError
DeleteList []struct { DeleteList []struct {
@@ -70,8 +70,7 @@ type BatchDeleteFileRes struct {
} `json:"delete_list"` } `json:"delete_list"`
} }
// UploadFile 上传文件 //UploadFile 上传文件
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/uploadFile.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/uploadFile.html
func (tcb *Tcb) UploadFile(env, path string) (*UploadFileRes, error) { func (tcb *Tcb) UploadFile(env, path string) (*UploadFileRes, error) {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()
@@ -92,8 +91,7 @@ func (tcb *Tcb) UploadFile(env, path string) (*UploadFileRes, error) {
return uploadFileRes, err return uploadFileRes, err
} }
// BatchDownloadFile 获取文件下载链接 //BatchDownloadFile 获取文件下载链接
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/batchDownloadFile.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/batchDownloadFile.html
func (tcb *Tcb) BatchDownloadFile(env string, fileList []*DownloadFile) (*BatchDownloadFileRes, error) { func (tcb *Tcb) BatchDownloadFile(env string, fileList []*DownloadFile) (*BatchDownloadFileRes, error) {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()
@@ -114,8 +112,7 @@ func (tcb *Tcb) BatchDownloadFile(env string, fileList []*DownloadFile) (*BatchD
return batchDownloadFileRes, err return batchDownloadFileRes, err
} }
// BatchDeleteFile 批量删除文件 //BatchDeleteFile 批量删除文件
//
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/batchDeleteFile.html //reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/batchDeleteFile.html
func (tcb *Tcb) BatchDeleteFile(env string, fileIDList []string) (*BatchDeleteFileRes, error) { func (tcb *Tcb) BatchDeleteFile(env string, fileIDList []string) (*BatchDeleteFileRes, error) {
accessToken, err := tcb.GetAccessToken() accessToken, err := tcb.GetAccessToken()

View File

@@ -2,12 +2,12 @@ package tcb
import "github.com/silenceper/wechat/v2/miniprogram/context" import "github.com/silenceper/wechat/v2/miniprogram/context"
// Tcb Tencent Cloud Base //Tcb Tencent Cloud Base
type Tcb struct { type Tcb struct {
*context.Context *context.Context
} }
// NewTcb new Tencent Cloud Base //NewTcb new Tencent Cloud Base
func NewTcb(context *context.Context) *Tcb { func NewTcb(context *context.Context) *Tcb {
return &Tcb{ return &Tcb{
context, context,

View File

@@ -1,52 +0,0 @@
package urllink
import (
"fmt"
"github.com/silenceper/wechat/v2/util"
)
const queryURL = "https://api.weixin.qq.com/wxa/query_urllink"
// ULQueryResult 返回的结果
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-link/urllink.query.html 返回值
type ULQueryResult struct {
util.CommonError
URLLinkInfo struct {
Appid string `json:"appid"`
Path string `json:"path"`
Query string `json:"query"`
CreateTime int64 `json:"create_time"`
ExpireTime int64 `json:"expire_time"`
EnvVersion string `json:"env_version"`
CloudBase struct {
Env string `json:"env"`
Domain string `json:"domain"`
Path string `json:"path"`
Query string `json:"query"`
ResourceAppid string `json:"resource_appid"`
} `json:"cloud_base"`
} `json:"url_link_info"`
VisitOpenid string `json:"visit_openid"`
}
// Query 查询小程序 url_link 配置。
func (u *URLLink) Query(urlLink string) (*ULQueryResult, error) {
accessToken, err := u.GetAccessToken()
if err != nil {
return nil, err
}
uri := fmt.Sprintf("%s?access_token=%s", queryURL, accessToken)
response, err := util.PostJSON(uri, map[string]string{"url_link": urlLink})
if err != nil {
return nil, err
}
var resp ULQueryResult
err = util.DecodeWithError(response, &resp, "URLLink.Query")
if err != nil {
return nil, err
}
return &resp, nil
}

View File

@@ -1,72 +0,0 @@
package urllink
import (
"fmt"
"github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/util"
)
// URLLink 小程序 URL Link
type URLLink struct {
*context.Context
}
// NewURLLink 实例化
func NewURLLink(ctx *context.Context) *URLLink {
return &URLLink{Context: ctx}
}
const generateURL = "https://api.weixin.qq.com/wxa/generate_urllink"
// TExpireType 失效类型 (指定时间戳/指定间隔)
type TExpireType int
const (
// ExpireTypeTime 指定时间戳后失效
ExpireTypeTime TExpireType = 0
// ExpireTypeInterval 间隔指定天数后失效
ExpireTypeInterval TExpireType = 1
)
// ULParams 请求参数
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-link/urllink.generate.html#请求参数
type ULParams struct {
Path string `json:"path"`
Query string `json:"query"`
// envVersion 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop"
EnvVersion string `json:"env_version,omitempty"`
IsExpire bool `json:"is_expire"`
ExpireType TExpireType `json:"expire_type"`
ExpireTime int64 `json:"expire_time"`
ExpireInterval int `json:"expire_interval"`
}
// ULResult 返回的结果
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-link/urllink.generate.html#返回值
type ULResult struct {
util.CommonError
URLLink string `json:"url_link"`
}
// Generate 生成url link
func (u *URLLink) Generate(params *ULParams) (string, error) {
accessToken, err := u.GetAccessToken()
if err != nil {
return "", err
}
uri := fmt.Sprintf("%s?access_token=%s", generateURL, accessToken)
response, err := util.PostJSON(uri, params)
if err != nil {
return "", err
}
var resp ULResult
err = util.DecodeWithError(response, &resp, "URLLink.Generate")
if err != nil {
return "", err
}
return resp.URLLink, nil
}

View File

@@ -1,70 +0,0 @@
package urlscheme
import (
"fmt"
"github.com/silenceper/wechat/v2/util"
)
const (
querySchemeURL = "https://api.weixin.qq.com/wxa/queryscheme?access_token=%s"
)
// QueryScheme 获取小程序访问scheme
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-scheme/urlscheme.query.html#参数
type QueryScheme struct {
// 小程序 scheme 码
Scheme string `json:"scheme"`
}
// SchemeInfo scheme 配置
type SchemeInfo struct {
// 小程序 appid。
AppID string `json:"appid"`
// 小程序页面路径。
Path string `json:"path"`
// 小程序页面query。
Query string `json:"query"`
// 创建时间,为 Unix 时间戳。
CreateTime int64 `json:"create_time"`
// 到期失效时间,为 Unix 时间戳0 表示永久生效
ExpireTime int64 `json:"expire_time"`
// 要打开的小程序版本。正式版为"release",体验版为"trial",开发版为"develop"。
EnvVersion EnvVersion `json:"env_version"`
}
// resQueryScheme 返回结构体
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-scheme/urlscheme.query.html#参数
type resQueryScheme struct {
// 通用错误
*util.CommonError
// scheme 配置
SchemeInfo SchemeInfo `json:"scheme_info"`
// 访问该链接的openid没有用户访问过则为空字符串
VisitOpenid string `json:"visit_openid"`
}
// QueryScheme 查询小程序 scheme 码
func (u *URLScheme) QueryScheme(querySchemeParams QueryScheme) (schemeInfo SchemeInfo, visitOpenid string, err error) {
var accessToken string
accessToken, err = u.GetAccessToken()
if err != nil {
return
}
urlStr := fmt.Sprintf(querySchemeURL, accessToken)
var response []byte
response, err = util.PostJSON(urlStr, querySchemeParams)
if err != nil {
return
}
// 使用通用方法返回错误
var res resQueryScheme
err = util.DecodeWithError(response, &res, "QueryScheme")
if err != nil {
return
}
return res.SchemeInfo, res.VisitOpenid, nil
}

View File

@@ -1,85 +0,0 @@
package urlscheme
import (
"fmt"
"github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/util"
)
// URLScheme 小程序 URL Scheme
type URLScheme struct {
*context.Context
}
// NewURLScheme 实例化
func NewURLScheme(ctx *context.Context) *URLScheme {
return &URLScheme{Context: ctx}
}
const generateURL = "https://api.weixin.qq.com/wxa/generatescheme"
// TExpireType 失效类型 (指定时间戳/指定间隔)
type TExpireType int
// EnvVersion 要打开的小程序版本
type EnvVersion string
const (
// ExpireTypeTime 指定时间戳后失效
ExpireTypeTime TExpireType = 0
// ExpireTypeInterval 间隔指定天数后失效
ExpireTypeInterval TExpireType = 1
// EnvVersionRelease 正式版为"release"
EnvVersionRelease EnvVersion = "release"
// EnvVersionTrial 体验版为"trial"
EnvVersionTrial EnvVersion = "trial"
// EnvVersionDevelop 开发版为"develop"
EnvVersionDevelop EnvVersion = "develop"
)
// JumpWxa 跳转到的目标小程序信息
type JumpWxa struct {
Path string `json:"path"`
Query string `json:"query"`
// envVersion 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop"
EnvVersion EnvVersion `json:"env_version,omitempty"`
}
// USParams 请求参数
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-scheme/urlscheme.generate.html#请求参数
type USParams struct {
JumpWxa *JumpWxa `json:"jump_wxa"`
ExpireType TExpireType `json:"expire_type"`
ExpireTime int64 `json:"expire_time"`
ExpireInterval int `json:"expire_interval"`
}
// USResult 返回的结果
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-scheme/urlscheme.generate.html#返回值
type USResult struct {
util.CommonError
OpenLink string `json:"openlink"`
}
// Generate 生成url link
func (u *URLScheme) Generate(params *USParams) (string, error) {
accessToken, err := u.GetAccessToken()
if err != nil {
return "", err
}
uri := fmt.Sprintf("%s?access_token=%s", generateURL, accessToken)
response, err := util.PostJSON(uri, params)
if err != nil {
return "", err
}
var resp USResult
err = util.DecodeWithError(response, &resp, "URLScheme.Generate")
if err != nil {
return "", err
}
return resp.OpenLink, nil
}

View File

@@ -8,34 +8,34 @@ import (
) )
var ( var (
// 获取微信服务器IP地址 //获取微信服务器IP地址
// 文档https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_the_WeChat_server_IP_address.html //文档https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_the_WeChat_server_IP_address.html
getCallbackIPURL = "https://api.weixin.qq.com/cgi-bin/getcallbackip" getCallbackIPURL = "https://api.weixin.qq.com/cgi-bin/getcallbackip"
getAPIDomainIPURL = "https://api.weixin.qq.com/cgi-bin/get_api_domain_ip" getAPIDomainIPURL = "https://api.weixin.qq.com/cgi-bin/get_api_domain_ip"
// 清理接口调用次数 //清理接口调用次数
clearQuotaURL = "https://api.weixin.qq.com/cgi-bin/clear_quota" clearQuotaURL = "https://api.weixin.qq.com/cgi-bin/clear_quota"
) )
// Basic struct //Basic struct
type Basic struct { type Basic struct {
*context.Context *context.Context
} }
// NewBasic 实例 //NewBasic 实例
func NewBasic(context *context.Context) *Basic { func NewBasic(context *context.Context) *Basic {
basic := new(Basic) basic := new(Basic)
basic.Context = context basic.Context = context
return basic return basic
} }
// IPListRes 获取微信服务器IP地址 返回结果 //IPListRes 获取微信服务器IP地址 返回结果
type IPListRes struct { type IPListRes struct {
util.CommonError util.CommonError
IPList []string `json:"ip_list"` IPList []string `json:"ip_list"`
} }
// GetCallbackIP 获取微信callback IP地址 //GetCallbackIP 获取微信callback IP地址
func (basic *Basic) GetCallbackIP() ([]string, error) { func (basic *Basic) GetCallbackIP() ([]string, error) {
ak, err := basic.GetAccessToken() ak, err := basic.GetAccessToken()
if err != nil { if err != nil {
@@ -51,7 +51,7 @@ func (basic *Basic) GetCallbackIP() ([]string, error) {
return ipListRes.IPList, err return ipListRes.IPList, err
} }
// GetAPIDomainIP 获取微信API接口 IP地址 //GetAPIDomainIP 获取微信API接口 IP地址
func (basic *Basic) GetAPIDomainIP() ([]string, error) { func (basic *Basic) GetAPIDomainIP() ([]string, error) {
ak, err := basic.GetAccessToken() ak, err := basic.GetAccessToken()
if err != nil { if err != nil {
@@ -67,7 +67,7 @@ func (basic *Basic) GetAPIDomainIP() ([]string, error) {
return ipListRes.IPList, err return ipListRes.IPList, err
} }
// ClearQuota 清理接口调用次数 //ClearQuota 清理接口调用次数
func (basic *Basic) ClearQuota() error { func (basic *Basic) ClearQuota() error {
ak, err := basic.GetAccessToken() ak, err := basic.GetAccessToken()
if err != nil { if err != nil {

View File

@@ -17,42 +17,42 @@ const (
setSpeedSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/set" setSpeedSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/set"
) )
// MsgType 发送消息类型 //MsgType 发送消息类型
type MsgType string type MsgType string
const ( const (
// MsgTypeNews 图文消息 //MsgTypeNews 图文消息
MsgTypeNews MsgType = "mpnews" MsgTypeNews MsgType = "mpnews"
// MsgTypeText 文本 //MsgTypeText 文本
MsgTypeText MsgType = "text" MsgTypeText MsgType = "text"
// MsgTypeVoice 语音/音频 //MsgTypeVoice 语音/音频
MsgTypeVoice MsgType = "voice" MsgTypeVoice MsgType = "voice"
// MsgTypeImage 图片 //MsgTypeImage 图片
MsgTypeImage MsgType = "image" MsgTypeImage MsgType = "image"
// MsgTypeVideo 视频 //MsgTypeVideo 视频
MsgTypeVideo MsgType = "mpvideo" MsgTypeVideo MsgType = "mpvideo"
// MsgTypeWxCard 卡券 //MsgTypeWxCard 卡券
MsgTypeWxCard MsgType = "wxcard" MsgTypeWxCard MsgType = "wxcard"
) )
// Broadcast 群发消息 //Broadcast 群发消息
type Broadcast struct { type Broadcast struct {
*context.Context *context.Context
preview bool preview bool
} }
// NewBroadcast new //NewBroadcast new
func NewBroadcast(ctx *context.Context) *Broadcast { func NewBroadcast(ctx *context.Context) *Broadcast {
return &Broadcast{ctx, false} return &Broadcast{ctx, false}
} }
// User 发送的用户 //User 发送的用户
type User struct { type User struct {
TagID int64 TagID int64
OpenID []string OpenID []string
} }
// Result 群发返回结果 //Result 群发返回结果
type Result struct { type Result struct {
util.CommonError util.CommonError
MsgID int64 `json:"msg_id"` MsgID int64 `json:"msg_id"`
@@ -60,34 +60,34 @@ type Result struct {
MsgStatus string `json:"msg_status"` MsgStatus string `json:"msg_status"`
} }
// SpeedResult 群发速度返回结果 //SpeedResult 群发速度返回结果
type SpeedResult struct { type SpeedResult struct {
util.CommonError util.CommonError
Speed int64 `json:"speed"` Speed int64 `json:"speed"`
RealSpeed int64 `json:"realspeed"` RealSpeed int64 `json:"realspeed"`
} }
// sendRequest 发送请求的数据 //sendRequest 发送请求的数据
type sendRequest struct { type sendRequest struct {
// 根据tag获全部发送 //根据tag获全部发送
Filter map[string]interface{} `json:"filter,omitempty"` Filter map[string]interface{} `json:"filter,omitempty"`
// 根据OpenID发送 //根据OpenID发送
ToUser interface{} `json:"touser,omitempty"` ToUser interface{} `json:"touser,omitempty"`
// 发送文本 //发送文本
Text map[string]interface{} `json:"text,omitempty"` Text map[string]interface{} `json:"text,omitempty"`
// 发送图文消息 //发送图文消息
Mpnews map[string]interface{} `json:"mpnews,omitempty"` Mpnews map[string]interface{} `json:"mpnews,omitempty"`
// 发送语音 //发送语音
Voice map[string]interface{} `json:"voice,omitempty"` Voice map[string]interface{} `json:"voice,omitempty"`
// 发送图片 //发送图片
Images *Image `json:"images,omitempty"` Images *Image `json:"images,omitempty"`
// 发送卡券 //发送卡券
WxCard map[string]interface{} `json:"wxcard,omitempty"` WxCard map[string]interface{} `json:"wxcard,omitempty"`
MsgType MsgType `json:"msgtype"` MsgType MsgType `json:"msgtype"`
SendIgnoreReprint int32 `json:"send_ignore_reprint,omitempty"` SendIgnoreReprint int32 `json:"send_ignore_reprint,omitempty"`
} }
// Image 发送图片 //Image 发送图片
type Image struct { type Image struct {
MediaIDs []string `json:"media_ids"` MediaIDs []string `json:"media_ids"`
Recommend string `json:"recommend"` Recommend string `json:"recommend"`
@@ -95,10 +95,10 @@ type Image struct {
OnlyFansCanComment int32 `json:"only_fans_can_comment"` OnlyFansCanComment int32 `json:"only_fans_can_comment"`
} }
// SendText 群发文本 //SendText 群发文本
// user 为nil表示全员发送 //user 为nil表示全员发送
// &User{TagID:2} 根据tag发送 //&User{TagID:2} 根据tag发送
// &User{OpenID:[]string("xxx","xxx")} 根据openid发送 //&User{OpenID:[]string("xxx","xxx")} 根据openid发送
func (broadcast *Broadcast) SendText(user *User, content string) (*Result, error) { func (broadcast *Broadcast) SendText(user *User, content string) (*Result, error) {
ak, err := broadcast.GetAccessToken() ak, err := broadcast.GetAccessToken()
if err != nil { if err != nil {
@@ -122,7 +122,7 @@ func (broadcast *Broadcast) SendText(user *User, content string) (*Result, error
return res, err return res, err
} }
// SendNews 发送图文 //SendNews 发送图文
func (broadcast *Broadcast) SendNews(user *User, mediaID string, ignoreReprint bool) (*Result, error) { func (broadcast *Broadcast) SendNews(user *User, mediaID string, ignoreReprint bool) (*Result, error) {
ak, err := broadcast.GetAccessToken() ak, err := broadcast.GetAccessToken()
if err != nil { if err != nil {
@@ -149,7 +149,7 @@ func (broadcast *Broadcast) SendNews(user *User, mediaID string, ignoreReprint b
return res, err return res, err
} }
// SendVoice 发送语音 //SendVoice 发送语音
func (broadcast *Broadcast) SendVoice(user *User, mediaID string) (*Result, error) { func (broadcast *Broadcast) SendVoice(user *User, mediaID string) (*Result, error) {
ak, err := broadcast.GetAccessToken() ak, err := broadcast.GetAccessToken()
if err != nil { if err != nil {
@@ -173,7 +173,7 @@ func (broadcast *Broadcast) SendVoice(user *User, mediaID string) (*Result, erro
return res, err return res, err
} }
// SendImage 发送图片 //SendImage 发送图片
func (broadcast *Broadcast) SendImage(user *User, images *Image) (*Result, error) { func (broadcast *Broadcast) SendImage(user *User, images *Image) (*Result, error) {
ak, err := broadcast.GetAccessToken() ak, err := broadcast.GetAccessToken()
if err != nil { if err != nil {
@@ -195,7 +195,7 @@ func (broadcast *Broadcast) SendImage(user *User, images *Image) (*Result, error
return res, err return res, err
} }
// SendVideo 发送视频 //SendVideo 发送视频
func (broadcast *Broadcast) SendVideo(user *User, mediaID string, title, description string) (*Result, error) { func (broadcast *Broadcast) SendVideo(user *User, mediaID string, title, description string) (*Result, error) {
ak, err := broadcast.GetAccessToken() ak, err := broadcast.GetAccessToken()
if err != nil { if err != nil {
@@ -221,7 +221,7 @@ func (broadcast *Broadcast) SendVideo(user *User, mediaID string, title, descrip
return res, err return res, err
} }
// SendWxCard 发送卡券 //SendWxCard 发送卡券
func (broadcast *Broadcast) SendWxCard(user *User, cardID string) (*Result, error) { func (broadcast *Broadcast) SendWxCard(user *User, cardID string) (*Result, error) {
ak, err := broadcast.GetAccessToken() ak, err := broadcast.GetAccessToken()
if err != nil { if err != nil {
@@ -245,7 +245,7 @@ func (broadcast *Broadcast) SendWxCard(user *User, cardID string) (*Result, erro
return res, err return res, err
} }
// Delete 删除群发消息 //Delete 删除群发消息
func (broadcast *Broadcast) Delete(msgID int64, articleIDx int64) error { func (broadcast *Broadcast) Delete(msgID int64, articleIDx int64) error {
ak, err := broadcast.GetAccessToken() ak, err := broadcast.GetAccessToken()
if err != nil { if err != nil {

View File

@@ -4,11 +4,11 @@ import (
"github.com/silenceper/wechat/v2/cache" "github.com/silenceper/wechat/v2/cache"
) )
// Config .config for 微信公众号 // Config config for 微信公众号
type Config struct { type Config struct {
AppID string `json:"app_id"` // appid AppID string `json:"app_id"` //appid
AppSecret string `json:"app_secret"` // appsecret AppSecret string `json:"app_secret"` //appsecret
Token string `json:"token"` // token Token string `json:"token"` //token
EncodingAESKey string `json:"encoding_aes_key"` // EncodingAESKey EncodingAESKey string `json:"encoding_aes_key"` //EncodingAESKey
Cache cache.Cache Cache cache.Cache
} }

View File

@@ -1,253 +0,0 @@
package customerservice
import (
"fmt"
"github.com/silenceper/wechat/v2/officialaccount/context"
"github.com/silenceper/wechat/v2/util"
)
// TypingStatus 输入状态类型
type TypingStatus string
const (
customerServiceListURL = "https://api.weixin.qq.com/cgi-bin/customservice/getkflist"
customerServiceOnlineListURL = "https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist"
customerServiceAddURL = "https://api.weixin.qq.com/customservice/kfaccount/add"
customerServiceUpdateURL = "https://api.weixin.qq.com/customservice/kfaccount/update"
customerServiceDeleteURL = "https://api.weixin.qq.com/customservice/kfaccount/del"
customerServiceInviteURL = "https://api.weixin.qq.com/customservice/kfaccount/inviteworker"
customerServiceUploadHeadImg = "https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg"
customerServiceTypingURL = "https://api.weixin.qq.com/cgi-bin/message/custom/typing"
)
const (
// Typing 表示正在输入状态
Typing TypingStatus = "Typing"
// CancelTyping 表示取消正在输入状态
CancelTyping TypingStatus = "CancelTyping"
)
// Manager 客服管理者,可以管理客服
type Manager struct {
*context.Context
}
// NewCustomerServiceManager 实例化客服管理
func NewCustomerServiceManager(ctx *context.Context) *Manager {
csm := new(Manager)
csm.Context = ctx
return csm
}
// KeFuInfo 客服基本信息
type KeFuInfo struct {
KfAccount string `json:"kf_account"` // 完整客服帐号,格式为:帐号前缀@公众号微信号
KfNick string `json:"kf_nick"` // 客服昵称
KfID int `json:"kf_id"` // 客服编号
KfHeadImgURL string `json:"kf_headimgurl"` // 客服头像
KfWX string `json:"kf_wx"` // 如果客服帐号已绑定了客服人员微信号, 则此处显示微信号
InviteWX string `json:"invite_wx"` // 如果客服帐号尚未绑定微信号,但是已经发起了一个绑定邀请, 则此处显示绑定邀请的微信号
InviteExpTime int `json:"invite_expire_time"` // 如果客服帐号尚未绑定微信号,但是已经发起过一个绑定邀请, 邀请的过期时间为unix 时间戳
InviteStatus string `json:"invite_status"` // 邀请的状态有等待确认“waiting”被拒绝“rejected” 过期“expired”
}
type resKeFuList struct {
util.CommonError
KfList []*KeFuInfo `json:"kf_list"`
}
// List 获取所有客服基本信息
func (csm *Manager) List() (customerServiceList []*KeFuInfo, err error) {
var accessToken string
accessToken, err = csm.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", customerServiceListURL, accessToken)
var response []byte
response, err = util.HTTPGet(uri)
if err != nil {
return
}
var res resKeFuList
err = util.DecodeWithError(response, &res, "ListCustomerService")
if err != nil {
return
}
customerServiceList = res.KfList
return
}
// KeFuOnlineInfo 客服在线信息
type KeFuOnlineInfo struct {
KfAccount string `json:"kf_account"`
Status int `json:"status"`
KfID int `json:"kf_id"`
AcceptedCase int `json:"accepted_case"`
}
type resKeFuOnlineList struct {
util.CommonError
KfOnlineList []*KeFuOnlineInfo `json:"kf_online_list"`
}
// OnlineList 获取在线客服列表
func (csm *Manager) OnlineList() (customerServiceOnlineList []*KeFuOnlineInfo, err error) {
var accessToken string
accessToken, err = csm.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", customerServiceOnlineListURL, accessToken)
var response []byte
response, err = util.HTTPGet(uri)
if err != nil {
return
}
var res resKeFuOnlineList
err = util.DecodeWithError(response, &res, "ListOnlineCustomerService")
if err != nil {
return
}
customerServiceOnlineList = res.KfOnlineList
return
}
// Add 添加客服账号
func (csm *Manager) Add(kfAccount, nickName string) (err error) {
// kfAccount完整客服帐号格式为帐号前缀@公众号微信号帐号前缀最多10个字符必须是英文、数字字符或者下划线后缀为公众号微信号长度不超过30个字符
// nickName客服昵称最长16个字
// 参数此处均不做校验
var accessToken string
accessToken, err = csm.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", customerServiceAddURL, accessToken)
data := struct {
KfAccount string `json:"kf_account"`
NickName string `json:"nickname"`
}{
KfAccount: kfAccount,
NickName: nickName,
}
var response []byte
response, err = util.PostJSON(uri, data)
if err != nil {
return
}
err = util.DecodeWithCommonError(response, "AddCustomerService")
return
}
// Update 修改客服账号
func (csm *Manager) Update(kfAccount, nickName string) (err error) {
var accessToken string
accessToken, err = csm.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", customerServiceUpdateURL, accessToken)
data := struct {
KfAccount string `json:"kf_account"`
NickName string `json:"nickname"`
}{
KfAccount: kfAccount,
NickName: nickName,
}
var response []byte
response, err = util.PostJSON(uri, data)
if err != nil {
return
}
err = util.DecodeWithCommonError(response, "UpdateCustomerService")
return
}
// Delete 删除客服帐号
func (csm *Manager) Delete(kfAccount string) (err error) {
var accessToken string
accessToken, err = csm.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", customerServiceDeleteURL, accessToken)
data := struct {
KfAccount string `json:"kf_account"`
}{
KfAccount: kfAccount,
}
var response []byte
response, err = util.PostJSON(uri, data)
if err != nil {
return
}
err = util.DecodeWithCommonError(response, "DeleteCustomerService")
return
}
// InviteBind 邀请绑定客服帐号和微信号
func (csm *Manager) InviteBind(kfAccount, inviteWX string) (err error) {
var accessToken string
accessToken, err = csm.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", customerServiceInviteURL, accessToken)
data := struct {
KfAccount string `json:"kf_account"`
InviteWX string `json:"invite_wx"`
}{
KfAccount: kfAccount,
InviteWX: inviteWX,
}
var response []byte
response, err = util.PostJSON(uri, data)
if err != nil {
return
}
err = util.DecodeWithCommonError(response, "InviteBindCustomerService")
return
}
// UploadHeadImg 上传客服头像
func (csm *Manager) UploadHeadImg(kfAccount, fileName string) (err error) {
var accessToken string
accessToken, err = csm.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s&kf_account=%s", customerServiceUploadHeadImg, accessToken, kfAccount)
var response []byte
response, err = util.PostFile("media", fileName, uri)
if err != nil {
return
}
err = util.DecodeWithCommonError(response, "UploadCustomerServiceHeadImg")
return
}
// SendTypingStatus 下发客服输入状态给用户
func (csm *Manager) SendTypingStatus(openid string, cmd TypingStatus) (err error) {
var accessToken string
accessToken, err = csm.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", customerServiceTypingURL, accessToken)
data := struct {
ToUser string `json:"touser"`
Command string `json:"command"`
}{
ToUser: openid,
Command: string(cmd),
}
var response []byte
response, err = util.PostJSON(uri, data)
if err != nil {
return
}
err = util.DecodeWithCommonError(response, "SendTypingStatus")
return
}

View File

@@ -15,7 +15,7 @@ const (
getUserShareHour = "https://api.weixin.qq.com/datacube/getusersharehour" getUserShareHour = "https://api.weixin.qq.com/datacube/getusersharehour"
) )
// ResArticleSummary 获取图文群发每日数据响应 //ResArticleSummary 获取图文群发每日数据响应
type ResArticleSummary struct { type ResArticleSummary struct {
util.CommonError util.CommonError
@@ -34,7 +34,7 @@ type ResArticleSummary struct {
} `json:"list"` } `json:"list"`
} }
// ResArticleTotal 获取图文群发总数据响应 //ResArticleTotal 获取图文群发总数据响应
type ResArticleTotal struct { type ResArticleTotal struct {
util.CommonError util.CommonError
@@ -46,7 +46,7 @@ type ResArticleTotal struct {
} `json:"list"` } `json:"list"`
} }
// ArticleTotalDetails 获取图文群发总数据响应文字详情 //ArticleTotalDetails 获取图文群发总数据响应文字详情
type ArticleTotalDetails struct { type ArticleTotalDetails struct {
StatDate string `json:"stat_date"` StatDate string `json:"stat_date"`
TargetUser int `json:"target_user"` TargetUser int `json:"target_user"`
@@ -76,7 +76,7 @@ type ArticleTotalDetails struct {
FeedShareFromOtherCnt int `json:"feed_share_from_other_cnt"` FeedShareFromOtherCnt int `json:"feed_share_from_other_cnt"`
} }
// ResUserRead 获取图文统计数据响应 //ResUserRead 获取图文统计数据响应
type ResUserRead struct { type ResUserRead struct {
util.CommonError util.CommonError
@@ -94,7 +94,7 @@ type ResUserRead struct {
} `json:"list"` } `json:"list"`
} }
// ResUserReadHour 获取图文统计分时数据 //ResUserReadHour 获取图文统计分时数据
type ResUserReadHour struct { type ResUserReadHour struct {
util.CommonError util.CommonError
@@ -113,7 +113,7 @@ type ResUserReadHour struct {
} `json:"list"` } `json:"list"`
} }
// ResUserShare 获取图文分享转发数据 //ResUserShare 获取图文分享转发数据
type ResUserShare struct { type ResUserShare struct {
util.CommonError util.CommonError
@@ -125,7 +125,7 @@ type ResUserShare struct {
} `json:"list"` } `json:"list"`
} }
// ResUserShareHour 获取图文分享转发分时数据 //ResUserShareHour 获取图文分享转发分时数据
type ResUserShareHour struct { type ResUserShareHour struct {
util.CommonError util.CommonError
@@ -138,7 +138,7 @@ type ResUserShareHour struct {
} `json:"list"` } `json:"list"`
} }
// GetArticleSummary 获取图文群发每日数据 //GetArticleSummary 获取图文群发每日数据
func (cube *DataCube) GetArticleSummary(s string, e string) (resArticleSummary ResArticleSummary, err error) { func (cube *DataCube) GetArticleSummary(s string, e string) (resArticleSummary ResArticleSummary, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {
@@ -160,7 +160,7 @@ func (cube *DataCube) GetArticleSummary(s string, e string) (resArticleSummary R
return return
} }
// GetArticleTotal 获取图文群发总数据 //GetArticleTotal 获取图文群发总数据
func (cube *DataCube) GetArticleTotal(s string, e string) (resArticleTotal ResArticleTotal, err error) { func (cube *DataCube) GetArticleTotal(s string, e string) (resArticleTotal ResArticleTotal, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {
@@ -182,7 +182,7 @@ func (cube *DataCube) GetArticleTotal(s string, e string) (resArticleTotal ResAr
return return
} }
// GetUserRead 获取图文统计数据 //GetUserRead 获取图文统计数据
func (cube *DataCube) GetUserRead(s string, e string) (resUserRead ResUserRead, err error) { func (cube *DataCube) GetUserRead(s string, e string) (resUserRead ResUserRead, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {
@@ -204,7 +204,7 @@ func (cube *DataCube) GetUserRead(s string, e string) (resUserRead ResUserRead,
return return
} }
// GetUserReadHour 获取图文统计分时数据 //GetUserReadHour 获取图文统计分时数据
func (cube *DataCube) GetUserReadHour(s string, e string) (resUserReadHour ResUserReadHour, err error) { func (cube *DataCube) GetUserReadHour(s string, e string) (resUserReadHour ResUserReadHour, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {
@@ -226,7 +226,7 @@ func (cube *DataCube) GetUserReadHour(s string, e string) (resUserReadHour ResUs
return return
} }
// GetUserShare 获取图文分享转发数据 //GetUserShare 获取图文分享转发数据
func (cube *DataCube) GetUserShare(s string, e string) (resUserShare ResUserShare, err error) { func (cube *DataCube) GetUserShare(s string, e string) (resUserShare ResUserShare, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {
@@ -248,7 +248,7 @@ func (cube *DataCube) GetUserShare(s string, e string) (resUserShare ResUserShar
return return
} }
// GetUserShareHour 获取图文分享转发分时数据 //GetUserShareHour 获取图文分享转发分时数据
func (cube *DataCube) GetUserShareHour(s string, e string) (resUserShareHour ResUserShareHour, err error) { func (cube *DataCube) GetUserShareHour(s string, e string) (resUserShareHour ResUserShareHour, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {

View File

@@ -9,12 +9,12 @@ type reqDate struct {
EndDate string `json:"end_date"` EndDate string `json:"end_date"`
} }
// DataCube 数据统计 //DataCube 数据统计
type DataCube struct { type DataCube struct {
*context.Context *context.Context
} }
// NewCube 数据统计 //NewCube 数据统计
func NewCube(context *context.Context) *DataCube { func NewCube(context *context.Context) *DataCube {
dataCube := new(DataCube) dataCube := new(DataCube)
dataCube.Context = context dataCube.Context = context

View File

@@ -11,7 +11,7 @@ const (
getInterfaceSummaryHour = "https://api.weixin.qq.com/datacube/getinterfacesummaryhour" getInterfaceSummaryHour = "https://api.weixin.qq.com/datacube/getinterfacesummaryhour"
) )
// ResInterfaceSummary 接口分析数据响应 //ResInterfaceSummary 接口分析数据响应
type ResInterfaceSummary struct { type ResInterfaceSummary struct {
util.CommonError util.CommonError
@@ -24,7 +24,7 @@ type ResInterfaceSummary struct {
} `json:"list"` } `json:"list"`
} }
// ResInterfaceSummaryHour 接口分析分时数据响应 //ResInterfaceSummaryHour 接口分析分时数据响应
type ResInterfaceSummaryHour struct { type ResInterfaceSummaryHour struct {
util.CommonError util.CommonError
@@ -38,7 +38,7 @@ type ResInterfaceSummaryHour struct {
} `json:"list"` } `json:"list"`
} }
// GetInterfaceSummary 获取接口分析数据 //GetInterfaceSummary 获取接口分析数据
func (cube *DataCube) GetInterfaceSummary(s string, e string) (resInterfaceSummary ResInterfaceSummary, err error) { func (cube *DataCube) GetInterfaceSummary(s string, e string) (resInterfaceSummary ResInterfaceSummary, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {
@@ -60,7 +60,7 @@ func (cube *DataCube) GetInterfaceSummary(s string, e string) (resInterfaceSumma
return return
} }
// GetInterfaceSummaryHour 获取接口分析分时数据 //GetInterfaceSummaryHour 获取接口分析分时数据
func (cube *DataCube) GetInterfaceSummaryHour(s string, e string) (resInterfaceSummaryHour ResInterfaceSummaryHour, err error) { func (cube *DataCube) GetInterfaceSummaryHour(s string, e string) (resInterfaceSummaryHour ResInterfaceSummaryHour, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {

View File

@@ -16,7 +16,7 @@ const (
getUpstreamMsgDistMonth = "https://api.weixin.qq.com/datacube/getupstreammsgdistmonth" getUpstreamMsgDistMonth = "https://api.weixin.qq.com/datacube/getupstreammsgdistmonth"
) )
// ResUpstreamMsg 获取消息发送概况数据响应 //ResUpstreamMsg 获取消息发送概况数据响应
type ResUpstreamMsg struct { type ResUpstreamMsg struct {
util.CommonError util.CommonError
@@ -28,7 +28,7 @@ type ResUpstreamMsg struct {
} `json:"list"` } `json:"list"`
} }
// ResUpstreamMsgHour 获取消息分送分时数据响应 //ResUpstreamMsgHour 获取消息分送分时数据响应
type ResUpstreamMsgHour struct { type ResUpstreamMsgHour struct {
util.CommonError util.CommonError
@@ -41,7 +41,7 @@ type ResUpstreamMsgHour struct {
} `json:"list"` } `json:"list"`
} }
// ResUpstreamMsgWeek 获取消息发送周数据响应 //ResUpstreamMsgWeek 获取消息发送周数据响应
type ResUpstreamMsgWeek struct { type ResUpstreamMsgWeek struct {
util.CommonError util.CommonError
@@ -53,7 +53,7 @@ type ResUpstreamMsgWeek struct {
} `json:"list"` } `json:"list"`
} }
// ResUpstreamMsgMonth 获取消息发送月数据响应 //ResUpstreamMsgMonth 获取消息发送月数据响应
type ResUpstreamMsgMonth struct { type ResUpstreamMsgMonth struct {
util.CommonError util.CommonError
@@ -65,7 +65,7 @@ type ResUpstreamMsgMonth struct {
} `json:"list"` } `json:"list"`
} }
// ResUpstreamMsgDist 获取消息发送分布数据响应 //ResUpstreamMsgDist 获取消息发送分布数据响应
type ResUpstreamMsgDist struct { type ResUpstreamMsgDist struct {
util.CommonError util.CommonError
@@ -76,7 +76,7 @@ type ResUpstreamMsgDist struct {
} `json:"list"` } `json:"list"`
} }
// ResUpstreamMsgDistWeek 获取消息发送分布周数据响应 //ResUpstreamMsgDistWeek 获取消息发送分布周数据响应
type ResUpstreamMsgDistWeek struct { type ResUpstreamMsgDistWeek struct {
util.CommonError util.CommonError
@@ -87,7 +87,7 @@ type ResUpstreamMsgDistWeek struct {
} `json:"list"` } `json:"list"`
} }
// ResUpstreamMsgDistMonth 获取消息发送分布月数据响应 //ResUpstreamMsgDistMonth 获取消息发送分布月数据响应
type ResUpstreamMsgDistMonth struct { type ResUpstreamMsgDistMonth struct {
util.CommonError util.CommonError
@@ -98,7 +98,7 @@ type ResUpstreamMsgDistMonth struct {
} `json:"list"` } `json:"list"`
} }
// GetUpstreamMsg 获取消息发送概况数据 //GetUpstreamMsg 获取消息发送概况数据
func (cube *DataCube) GetUpstreamMsg(s string, e string) (resUpstreamMsg ResUpstreamMsg, err error) { func (cube *DataCube) GetUpstreamMsg(s string, e string) (resUpstreamMsg ResUpstreamMsg, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {
@@ -120,7 +120,7 @@ func (cube *DataCube) GetUpstreamMsg(s string, e string) (resUpstreamMsg ResUpst
return return
} }
// GetUpstreamMsgHour 获取消息分送分时数据 //GetUpstreamMsgHour 获取消息分送分时数据
func (cube *DataCube) GetUpstreamMsgHour(s string, e string) (resUpstreamMsgHour ResUpstreamMsgHour, err error) { func (cube *DataCube) GetUpstreamMsgHour(s string, e string) (resUpstreamMsgHour ResUpstreamMsgHour, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {
@@ -142,7 +142,7 @@ func (cube *DataCube) GetUpstreamMsgHour(s string, e string) (resUpstreamMsgHour
return return
} }
// GetUpstreamMsgWeek 获取消息发送周数据 //GetUpstreamMsgWeek 获取消息发送周数据
func (cube *DataCube) GetUpstreamMsgWeek(s string, e string) (resUpstreamMsgWeek ResUpstreamMsgWeek, err error) { func (cube *DataCube) GetUpstreamMsgWeek(s string, e string) (resUpstreamMsgWeek ResUpstreamMsgWeek, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {
@@ -164,7 +164,7 @@ func (cube *DataCube) GetUpstreamMsgWeek(s string, e string) (resUpstreamMsgWeek
return return
} }
// GetUpstreamMsgMonth 获取消息发送月数据 //GetUpstreamMsgMonth 获取消息发送月数据
func (cube *DataCube) GetUpstreamMsgMonth(s string, e string) (resUpstreamMsgMonth ResUpstreamMsgMonth, err error) { func (cube *DataCube) GetUpstreamMsgMonth(s string, e string) (resUpstreamMsgMonth ResUpstreamMsgMonth, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {
@@ -186,7 +186,7 @@ func (cube *DataCube) GetUpstreamMsgMonth(s string, e string) (resUpstreamMsgMon
return return
} }
// GetUpstreamMsgDist 获取消息发送分布数据 //GetUpstreamMsgDist 获取消息发送分布数据
func (cube *DataCube) GetUpstreamMsgDist(s string, e string) (resUpstreamMsgDist ResUpstreamMsgDist, err error) { func (cube *DataCube) GetUpstreamMsgDist(s string, e string) (resUpstreamMsgDist ResUpstreamMsgDist, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {
@@ -208,7 +208,7 @@ func (cube *DataCube) GetUpstreamMsgDist(s string, e string) (resUpstreamMsgDist
return return
} }
// GetUpstreamMsgDistWeek 获取消息发送分布周数据 //GetUpstreamMsgDistWeek 获取消息发送分布周数据
func (cube *DataCube) GetUpstreamMsgDistWeek(s string, e string) (resUpstreamMsgDistWeek ResUpstreamMsgDistWeek, err error) { func (cube *DataCube) GetUpstreamMsgDistWeek(s string, e string) (resUpstreamMsgDistWeek ResUpstreamMsgDistWeek, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {
@@ -230,7 +230,7 @@ func (cube *DataCube) GetUpstreamMsgDistWeek(s string, e string) (resUpstreamMsg
return return
} }
// GetUpstreamMsgDistMonth 获取消息发送分布月数据 //GetUpstreamMsgDistMonth 获取消息发送分布月数据
func (cube *DataCube) GetUpstreamMsgDistMonth(s string, e string) (resUpstreamMsgDistMonth ResUpstreamMsgDistMonth, err error) { func (cube *DataCube) GetUpstreamMsgDistMonth(s string, e string) (resUpstreamMsgDistMonth ResUpstreamMsgDistMonth, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {

View File

@@ -8,31 +8,31 @@ import (
"github.com/silenceper/wechat/v2/util" "github.com/silenceper/wechat/v2/util"
) )
// AdSlot 广告位类型 //AdSlot 广告位类型
type AdSlot string type AdSlot string
const ( const (
// SlotIDBizBottom 公众号底部广告 //SlotIDBizBottom 公众号底部广告
SlotIDBizBottom AdSlot = "SLOT_ID_BIZ_BOTTOM" SlotIDBizBottom AdSlot = "SLOT_ID_BIZ_BOTTOM"
// SlotIDBizMidContext 公众号文中广告 //SlotIDBizMidContext 公众号文中广告
SlotIDBizMidContext AdSlot = "SLOT_ID_BIZ_MID_CONTEXT" SlotIDBizMidContext AdSlot = "SLOT_ID_BIZ_MID_CONTEXT"
// SlotIDBizVideoEnd 公众号视频后贴 //SlotIDBizVideoEnd 公众号视频后贴
SlotIDBizVideoEnd AdSlot = "SLOT_ID_BIZ_VIDEO_END" SlotIDBizVideoEnd AdSlot = "SLOT_ID_BIZ_VIDEO_END"
// SlotIDBizSponsor 公众号互选广告 //SlotIDBizSponsor 公众号互选广告
SlotIDBizSponsor AdSlot = "SLOT_ID_BIZ_SPONSOR" SlotIDBizSponsor AdSlot = "SLOT_ID_BIZ_SPONSOR"
// SlotIDBizCps 公众号返佣商品 //SlotIDBizCps 公众号返佣商品
SlotIDBizCps AdSlot = "SLOT_ID_BIZ_CPS" SlotIDBizCps AdSlot = "SLOT_ID_BIZ_CPS"
// SlotIDWeappBanner 小程序banner //SlotIDWeappBanner 小程序banner
SlotIDWeappBanner AdSlot = "SLOT_ID_WEAPP_BANNER" SlotIDWeappBanner AdSlot = "SLOT_ID_WEAPP_BANNER"
// SlotIDWeappRewardVideo 小程序激励视频 //SlotIDWeappRewardVideo 小程序激励视频
SlotIDWeappRewardVideo AdSlot = "SLOT_ID_WEAPP_REWARD_VIDEO" SlotIDWeappRewardVideo AdSlot = "SLOT_ID_WEAPP_REWARD_VIDEO"
// SlotIDWeappInterstitial 小程序插屏广告 //SlotIDWeappInterstitial 小程序插屏广告
SlotIDWeappInterstitial AdSlot = "SLOT_ID_WEAPP_INTERSTITIAL" SlotIDWeappInterstitial AdSlot = "SLOT_ID_WEAPP_INTERSTITIAL"
// SlotIDWeappVideoFeeds 小程序视频广告 //SlotIDWeappVideoFeeds 小程序视频广告
SlotIDWeappVideoFeeds AdSlot = "SLOT_ID_WEAPP_VIDEO_FEEDS" SlotIDWeappVideoFeeds AdSlot = "SLOT_ID_WEAPP_VIDEO_FEEDS"
// SlotIDWeappVideoBegin 小程序视频前贴 //SlotIDWeappVideoBegin 小程序视频前贴
SlotIDWeappVideoBegin AdSlot = "SLOT_ID_WEAPP_VIDEO_BEGIN" SlotIDWeappVideoBegin AdSlot = "SLOT_ID_WEAPP_VIDEO_BEGIN"
// SlotIDWeappBox 小程序格子广告 //SlotIDWeappBox 小程序格子广告
SlotIDWeappBox AdSlot = "SLOT_ID_WEAPP_BOX" SlotIDWeappBox AdSlot = "SLOT_ID_WEAPP_BOX"
) )
@@ -46,13 +46,13 @@ const (
actionPublisherSettlement = "publisher_settlement" actionPublisherSettlement = "publisher_settlement"
) )
// BaseResp 错误信息 //BaseResp 错误信息
type BaseResp struct { type BaseResp struct {
ErrMsg string `json:"err_msg"` ErrMsg string `json:"err_msg"`
Ret int `json:"ret"` Ret int `json:"ret"`
} }
// ResPublisherAdPos 公众号分广告位数据响应 //ResPublisherAdPos 公众号分广告位数据响应
type ResPublisherAdPos struct { type ResPublisherAdPos struct {
util.CommonError util.CommonError
@@ -62,7 +62,7 @@ type ResPublisherAdPos struct {
TotalNum int `json:"total_num"` TotalNum int `json:"total_num"`
} }
// ResAdPosList 公众号分广告位列表 //ResAdPosList 公众号分广告位列表
type ResAdPosList struct { type ResAdPosList struct {
SlotID int64 `json:"slot_id"` SlotID int64 `json:"slot_id"`
AdSlot string `json:"ad_slot"` AdSlot string `json:"ad_slot"`
@@ -76,7 +76,7 @@ type ResAdPosList struct {
Ecpm float64 `json:"ecpm"` Ecpm float64 `json:"ecpm"`
} }
// ResAdPosSummary 公众号分广告位概览 //ResAdPosSummary 公众号分广告位概览
type ResAdPosSummary struct { type ResAdPosSummary struct {
ReqSuccCount int `json:"req_succ_count"` ReqSuccCount int `json:"req_succ_count"`
ExposureCount int `json:"exposure_count"` ExposureCount int `json:"exposure_count"`
@@ -87,7 +87,7 @@ type ResAdPosSummary struct {
Ecpm float64 `json:"ecpm"` Ecpm float64 `json:"ecpm"`
} }
// ResPublisherCps 公众号返佣商品数据响应 //ResPublisherCps 公众号返佣商品数据响应
type ResPublisherCps struct { type ResPublisherCps struct {
util.CommonError util.CommonError
@@ -97,7 +97,7 @@ type ResPublisherCps struct {
TotalNum int `json:"total_num"` TotalNum int `json:"total_num"`
} }
// ResCpsList 公众号返佣商品列表 //ResCpsList 公众号返佣商品列表
type ResCpsList struct { type ResCpsList struct {
Date string `json:"date"` Date string `json:"date"`
ExposureCount int `json:"exposure_count"` ExposureCount int `json:"exposure_count"`
@@ -109,7 +109,7 @@ type ResCpsList struct {
TotalCommission int `json:"total_commission"` TotalCommission int `json:"total_commission"`
} }
// ResCpsSummary 公众号返佣概览 //ResCpsSummary 公众号返佣概览
type ResCpsSummary struct { type ResCpsSummary struct {
ExposureCount int `json:"exposure_count"` ExposureCount int `json:"exposure_count"`
ClickCount int `json:"click_count"` ClickCount int `json:"click_count"`
@@ -120,7 +120,7 @@ type ResCpsSummary struct {
TotalCommission int `json:"total_commission"` TotalCommission int `json:"total_commission"`
} }
// ResPublisherSettlement 公众号结算收入数据及结算主体信息响应 //ResPublisherSettlement 公众号结算收入数据及结算主体信息响应
type ResPublisherSettlement struct { type ResPublisherSettlement struct {
util.CommonError util.CommonError
@@ -133,7 +133,7 @@ type ResPublisherSettlement struct {
TotalNum int `json:"total_num"` TotalNum int `json:"total_num"`
} }
// SettlementList 结算单列表 //SettlementList 结算单列表
type SettlementList struct { type SettlementList struct {
Date string `json:"date"` Date string `json:"date"`
Zone string `json:"zone"` Zone string `json:"zone"`
@@ -146,13 +146,13 @@ type SettlementList struct {
SlotRevenue []SlotRevenue `json:"slot_revenue"` SlotRevenue []SlotRevenue `json:"slot_revenue"`
} }
// SlotRevenue 产生收入的广告 //SlotRevenue 产生收入的广告
type SlotRevenue struct { type SlotRevenue struct {
SlotID string `json:"slot_id"` SlotID string `json:"slot_id"`
SlotSettledRevenue int `json:"slot_settled_revenue"` SlotSettledRevenue int `json:"slot_settled_revenue"`
} }
// ParamsPublisher 拉取数据参数 //ParamsPublisher 拉取数据参数
type ParamsPublisher struct { type ParamsPublisher struct {
Action string `json:"action"` Action string `json:"action"`
StartDate string `json:"start_date"` StartDate string `json:"start_date"`
@@ -189,7 +189,7 @@ func (cube *DataCube) fetchData(params ParamsPublisher) (response []byte, err er
return return
} }
// GetPublisherAdPosGeneral 获取公众号分广告位数据 //GetPublisherAdPosGeneral 获取公众号分广告位数据
func (cube *DataCube) GetPublisherAdPosGeneral(startDate, endDate string, page, pageSize int, adSlot AdSlot) (resPublisherAdPos ResPublisherAdPos, err error) { func (cube *DataCube) GetPublisherAdPosGeneral(startDate, endDate string, page, pageSize int, adSlot AdSlot) (resPublisherAdPos ResPublisherAdPos, err error) {
params := ParamsPublisher{ params := ParamsPublisher{
Action: actionPublisherAdPosGeneral, Action: actionPublisherAdPosGeneral,
@@ -217,7 +217,7 @@ func (cube *DataCube) GetPublisherAdPosGeneral(startDate, endDate string, page,
return return
} }
// GetPublisherCpsGeneral 获取公众号返佣商品数据 //GetPublisherCpsGeneral 获取公众号返佣商品数据
func (cube *DataCube) GetPublisherCpsGeneral(startDate, endDate string, page, pageSize int) (resPublisherCps ResPublisherCps, err error) { func (cube *DataCube) GetPublisherCpsGeneral(startDate, endDate string, page, pageSize int) (resPublisherCps ResPublisherCps, err error) {
params := ParamsPublisher{ params := ParamsPublisher{
Action: actionPublisherCpsGeneral, Action: actionPublisherCpsGeneral,
@@ -244,7 +244,7 @@ func (cube *DataCube) GetPublisherCpsGeneral(startDate, endDate string, page, pa
return return
} }
// GetPublisherSettlement 获取公众号结算收入数据及结算主体信息 //GetPublisherSettlement 获取公众号结算收入数据及结算主体信息
func (cube *DataCube) GetPublisherSettlement(startDate, endDate string, page, pageSize int) (resPublisherSettlement ResPublisherSettlement, err error) { func (cube *DataCube) GetPublisherSettlement(startDate, endDate string, page, pageSize int) (resPublisherSettlement ResPublisherSettlement, err error) {
params := ParamsPublisher{ params := ParamsPublisher{
Action: actionPublisherSettlement, Action: actionPublisherSettlement,

View File

@@ -11,7 +11,7 @@ const (
getUserAccumulate = "https://api.weixin.qq.com/datacube/getusercumulate" getUserAccumulate = "https://api.weixin.qq.com/datacube/getusercumulate"
) )
// ResUserSummary 获取用户增减数据响应 //ResUserSummary 获取用户增减数据响应
type ResUserSummary struct { type ResUserSummary struct {
util.CommonError util.CommonError
@@ -23,7 +23,7 @@ type ResUserSummary struct {
} `json:"list"` } `json:"list"`
} }
// ResUserAccumulate 获取累计用户数据响应 //ResUserAccumulate 获取累计用户数据响应
type ResUserAccumulate struct { type ResUserAccumulate struct {
util.CommonError util.CommonError
@@ -33,7 +33,7 @@ type ResUserAccumulate struct {
} `json:"list"` } `json:"list"`
} }
// GetUserSummary 获取用户增减数据 //GetUserSummary 获取用户增减数据
func (cube *DataCube) GetUserSummary(s string, e string) (resUserSummary ResUserSummary, err error) { func (cube *DataCube) GetUserSummary(s string, e string) (resUserSummary ResUserSummary, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {
@@ -55,7 +55,7 @@ func (cube *DataCube) GetUserSummary(s string, e string) (resUserSummary ResUser
return return
} }
// GetUserAccumulate 获取累计用户数据 //GetUserAccumulate 获取累计用户数据
func (cube *DataCube) GetUserAccumulate(s string, e string) (resUserAccumulate ResUserAccumulate, err error) { func (cube *DataCube) GetUserAccumulate(s string, e string) (resUserAccumulate ResUserAccumulate, err error) {
accessToken, err := cube.GetAccessToken() accessToken, err := cube.GetAccessToken()
if err != nil { if err != nil {

View File

@@ -1,4 +1,4 @@
// Package device 设备相关接口 //Package device 设备相关接口
package device package device
import ( import (
@@ -23,13 +23,13 @@ type reqDeviceAuthorize struct {
// 请求操作的类型限定取值为0设备授权缺省值为0 1设备更新更新已授权设备的各属性值 // 请求操作的类型限定取值为0设备授权缺省值为0 1设备更新更新已授权设备的各属性值
OpType string `json:"op_type,omitempty"` OpType string `json:"op_type,omitempty"`
// 设备的产品编号(由微信硬件平台分配)。可在公众号设备功能管理页面查询。 // 设备的产品编号(由微信硬件平台分配)。可在公众号设备功能管理页面查询。
// 当 op_type 为0product_id 为1不要填写 product_id 字段(会引起不必要错误); //当 op_type 为0product_id 为1不要填写 product_id 字段(会引起不必要错误);
// 当 op_typy 为0product_id 不为1必须填写 product_id 字段; //当 op_typy 为0product_id 不为1必须填写 product_id 字段;
// 当 op_type 为 1 时,不要填写 product_id 字段。 //当 op_type 为 1 时,不要填写 product_id 字段。
ProductID string `json:"product_id,omitempty"` ProductID string `json:"product_id,omitempty"`
} }
// ReqDevice 设备授权实体 //ReqDevice 设备授权实体
type ReqDevice struct { type ReqDevice struct {
// 设备的 device id // 设备的 device id
ID string `json:"id"` ID string `json:"id"`
@@ -45,13 +45,13 @@ type ReqDevice struct {
// 1表示设备仅支持andiod classic bluetooth 1|2表示设备支持android 和ios 两种classic bluetooth但是客户端优先选择android classic bluetooth 协议如果android classic bluetooth协议连接失败再选择ios classic bluetooth协议进行连接 // 1表示设备仅支持andiod classic bluetooth 1|2表示设备支持android 和ios 两种classic bluetooth但是客户端优先选择android classic bluetooth 协议如果android classic bluetooth协议连接失败再选择ios classic bluetooth协议进行连接
// 安卓平台不同时支持BLE和classic类型 // 安卓平台不同时支持BLE和classic类型
ConnectProtocol string `json:"connect_protocol"` ConnectProtocol string `json:"connect_protocol"`
// auth及通信的加密key第三方需要将key烧制在设备上128bit格式采用16进制串的方式长度为32字节不需要0X前缀 1234567890ABCDEF1234567890ABCDEF //auth及通信的加密key第三方需要将key烧制在设备上128bit格式采用16进制串的方式长度为32字节不需要0X前缀 1234567890ABCDEF1234567890ABCDEF
AuthKey string `json:"auth_key"` AuthKey string `json:"auth_key"`
// 断开策略,目前支持: 1退出公众号页面时即断开连接 2退出公众号之后保持连接不断开 // 断开策略,目前支持: 1退出公众号页面时即断开连接 2退出公众号之后保持连接不断开
CloseStrategy string `json:"close_strategy"` CloseStrategy string `json:"close_strategy"`
// 连接策略32位整型按bit位置位目前仅第1bit和第3bit位有效bit置0为无效1为有效第2bit已被废弃且bit位可以按或置位如1|4=5各bit置位含义说明如下 //连接策略32位整型按bit位置位目前仅第1bit和第3bit位有效bit置0为无效1为有效第2bit已被废弃且bit位可以按或置位如1|4=5各bit置位含义说明如下
// 1第1bit置位在公众号对话页面不停的尝试连接设备 //1第1bit置位在公众号对话页面不停的尝试连接设备
// 4第3bit置位处于非公众号页面如主界面等微信自动连接。当用户切换微信到前台时可能尝试去连接设备连上后一定时间会断开 //4第3bit置位处于非公众号页面如主界面等微信自动连接。当用户切换微信到前台时可能尝试去连接设备连上后一定时间会断开
ConnStrategy string `json:"conn_strategy"` ConnStrategy string `json:"conn_strategy"`
// auth version设备和微信进行auth时会根据该版本号来确认auth buf和auth key的格式各version对应的auth buf及key的具体格式可以参看“客户端蓝牙外设协议”该字段目前支持取值 // auth version设备和微信进行auth时会根据该版本号来确认auth buf和auth key的格式各version对应的auth buf及key的具体格式可以参看“客户端蓝牙外设协议”该字段目前支持取值
// 0不加密的version // 0不加密的version
@@ -69,7 +69,7 @@ type ReqDevice struct {
BleSimpleProtocol string `json:"ble_simple_protocol,omitempty"` BleSimpleProtocol string `json:"ble_simple_protocol,omitempty"`
} }
// ResBaseInfo 授权回调实体 //ResBaseInfo 授权回调实体
type ResBaseInfo struct { type ResBaseInfo struct {
BaseInfo struct { BaseInfo struct {
DeviceType string `json:"device_type"` DeviceType string `json:"device_type"`

View File

@@ -19,12 +19,12 @@ const (
uriState = "https://api.weixin.qq.com/device/get_stat" uriState = "https://api.weixin.qq.com/device/get_stat"
) )
// Device struct //Device struct
type Device struct { type Device struct {
*context.Context *context.Context
} }
// NewDevice 实例 //NewDevice 实例
func NewDevice(context *context.Context) *Device { func NewDevice(context *context.Context) *Device {
device := new(Device) device := new(Device)
device.Context = context device.Context = context

View File

@@ -1,6 +1,6 @@
package device package device
// MsgDevice 设备消息响应 //MsgDevice 设备消息响应
type MsgDevice struct { type MsgDevice struct {
DeviceType string DeviceType string
DeviceID string DeviceID string

View File

@@ -7,7 +7,7 @@ import (
"github.com/silenceper/wechat/v2/util" "github.com/silenceper/wechat/v2/util"
) )
// ResCreateQRCode 获取二维码的返回实体 //ResCreateQRCode 获取二维码的返回实体
type ResCreateQRCode struct { type ResCreateQRCode struct {
util.CommonError util.CommonError
DeviceNum int `json:"device_num"` DeviceNum int `json:"device_num"`
@@ -42,7 +42,7 @@ func (d *Device) CreateQRCode(devices []string) (res ResCreateQRCode, err error)
return return
} }
// ResVerifyQRCode 验证授权结果实体 //ResVerifyQRCode 验证授权结果实体
type ResVerifyQRCode struct { type ResVerifyQRCode struct {
util.CommonError util.CommonError
DeviceType string `json:"device_type"` DeviceType string `json:"device_type"`

View File

@@ -1,228 +0,0 @@
package draft
import (
"fmt"
"github.com/silenceper/wechat/v2/officialaccount/context"
"github.com/silenceper/wechat/v2/util"
)
const (
addURL = "https://api.weixin.qq.com/cgi-bin/draft/add" // 新建草稿
getURL = "https://api.weixin.qq.com/cgi-bin/draft/get" // 获取草稿
deleteURL = "https://api.weixin.qq.com/cgi-bin/draft/delete" // 删除草稿
updateURL = "https://api.weixin.qq.com/cgi-bin/draft/update" // 修改草稿
countURL = "https://api.weixin.qq.com/cgi-bin/draft/count" // 获取草稿总数
paginateURL = "https://api.weixin.qq.com/cgi-bin/draft/batchget" // 获取草稿列表
)
// Draft 草稿箱
type Draft struct {
*context.Context
}
// NewDraft init
func NewDraft(ctx *context.Context) *Draft {
return &Draft{
Context: ctx,
}
}
// Article 草稿
type Article struct {
Title string `json:"title"` // 标题
Author string `json:"author"` // 作者
Digest string `json:"digest"` // 图文消息的摘要,仅有单图文消息才有摘要,多图文此处为空。
Content string `json:"content"` // 图文消息的具体内容支持HTML标签必须少于2万字符小于1M且去除JS
ContentSourceURL string `json:"content_source_url"` // 图文消息的原文地址即点击“阅读原文”后的URL
ThumbMediaID string `json:"thumb_media_id"` // 图文消息的封面图片素材id必须是永久MediaID
ShowCoverPic uint `json:"show_cover_pic"` // 是否显示封面0为false即不显示1为true即显示(默认)
NeedOpenComment uint `json:"need_open_comment"` // 是否打开评论0不打开(默认)1打开
OnlyFansCanComment uint `json:"only_fans_can_comment"` // 是否粉丝才可评论0所有人可评论(默认)1粉丝才可评论
}
// AddDraft 新建草稿
func (draft *Draft) AddDraft(articles []*Article) (mediaID string, err error) {
accessToken, err := draft.GetAccessToken()
if err != nil {
return
}
var req struct {
Articles []*Article `json:"articles"`
}
req.Articles = articles
uri := fmt.Sprintf("%s?access_token=%s", addURL, accessToken)
response, err := util.PostJSON(uri, req)
if err != nil {
return
}
var res struct {
util.CommonError
MediaID string `json:"media_id"`
}
err = util.DecodeWithError(response, &res, "AddDraft")
if err != nil {
return
}
mediaID = res.MediaID
return
}
// GetDraft 获取草稿
func (draft *Draft) GetDraft(mediaID string) (articles []*Article, err error) {
accessToken, err := draft.GetAccessToken()
if err != nil {
return
}
var req struct {
MediaID string `json:"media_id"`
}
req.MediaID = mediaID
uri := fmt.Sprintf("%s?access_token=%s", getURL, accessToken)
response, err := util.PostJSON(uri, req)
if err != nil {
return
}
var res struct {
util.CommonError
NewsItem []*Article `json:"news_item"`
}
err = util.DecodeWithError(response, &res, "GetDraft")
if err != nil {
return
}
articles = res.NewsItem
return
}
// DeleteDraft 删除草稿
func (draft *Draft) DeleteDraft(mediaID string) (err error) {
accessToken, err := draft.GetAccessToken()
if err != nil {
return
}
var req struct {
MediaID string `json:"media_id"`
}
req.MediaID = mediaID
var response []byte
uri := fmt.Sprintf("%s?access_token=%s", deleteURL, accessToken)
response, err = util.PostJSON(uri, req)
if err != nil {
return
}
err = util.DecodeWithCommonError(response, "DeleteDraft")
return
}
// UpdateDraft 修改草稿
// index 要更新的文章在图文消息中的位置多图文消息时此字段才有意义第一篇为0
func (draft *Draft) UpdateDraft(article *Article, mediaID string, index uint) (err error) {
accessToken, err := draft.GetAccessToken()
if err != nil {
return
}
var req struct {
MediaID string `json:"media_id"`
Index uint `json:"index"`
Article *Article `json:"articles"`
}
req.MediaID = mediaID
req.Index = index
req.Article = article
uri := fmt.Sprintf("%s?access_token=%s", updateURL, accessToken)
var response []byte
response, err = util.PostJSON(uri, req)
if err != nil {
return
}
err = util.DecodeWithCommonError(response, "UpdateDraft")
return
}
// CountDraft 获取草稿总数
func (draft *Draft) CountDraft() (total uint, err error) {
accessToken, err := draft.GetAccessToken()
if err != nil {
return
}
var response []byte
uri := fmt.Sprintf("%s?access_token=%s", countURL, accessToken)
response, err = util.HTTPGet(uri)
if err != nil {
return
}
var res struct {
util.CommonError
Total uint `json:"total_count"`
}
err = util.DecodeWithError(response, &res, "CountDraft")
if nil != err {
return
}
total = res.Total
return
}
// ArticleList 草稿列表
type ArticleList struct {
util.CommonError
TotalCount int64 `json:"total_count"` // 草稿素材的总数
ItemCount int64 `json:"item_count"` // 本次调用获取的素材的数量
Item []ArticleListItem `json:"item"`
}
// ArticleListItem 用于 ArticleList 的 item 节点
type ArticleListItem struct {
MediaID string `json:"media_id"` // 图文消息的id
Content ArticleListContent `json:"content"` // 内容
UpdateTime int64 `json:"update_time"` // 这篇图文消息素材的最后更新时间
}
// ArticleListContent 用于 ArticleListItem 的 content 节点
type ArticleListContent struct {
NewsItem []Article `json:"news_item"` // 这篇图文消息素材的内容
}
// PaginateDraft 获取草稿列表
func (draft *Draft) PaginateDraft(offset, count int64, noReturnContent bool) (list ArticleList, err error) {
accessToken, err := draft.GetAccessToken()
if err != nil {
return
}
var req struct {
Count int64 `json:"count"`
Offset int64 `json:"offset"`
NoReturnContent bool `json:"no_content"`
}
req.Count = count
req.Offset = offset
req.NoReturnContent = noReturnContent
var response []byte
uri := fmt.Sprintf("%s?access_token=%s", paginateURL, accessToken)
response, err = util.PostJSON(uri, req)
if err != nil {
return
}
err = util.DecodeWithError(response, &list, "PaginateDraft")
return
}

View File

@@ -1,248 +0,0 @@
package freepublish
import (
"fmt"
"github.com/silenceper/wechat/v2/officialaccount/context"
"github.com/silenceper/wechat/v2/util"
)
const (
publishURL = "https://api.weixin.qq.com/cgi-bin/freepublish/submit" // 发布接口
selectStateURL = "https://api.weixin.qq.com/cgi-bin/freepublish/get" // 发布状态轮询接口
deleteURL = "https://api.weixin.qq.com/cgi-bin/freepublish/delete" // 删除发布
firstArticleURL = "https://api.weixin.qq.com/cgi-bin/freepublish/getarticle" // 通过 article_id 获取已发布文章
paginateURL = "https://api.weixin.qq.com/cgi-bin/freepublish/batchget" // 获取成功发布列表
)
// PublishStatus 发布状态
type PublishStatus uint
const (
// PublishStatusSuccess 0:成功
PublishStatusSuccess PublishStatus = iota
// PublishStatusPublishing 1:发布中
PublishStatusPublishing
// PublishStatusOriginalFail 2:原创失败
PublishStatusOriginalFail
// PublishStatusFail 3:常规失败
PublishStatusFail
// PublishStatusAuditRefused 4:平台审核不通过
PublishStatusAuditRefused
// PublishStatusUserDeleted 5:成功后用户删除所有文章
PublishStatusUserDeleted
// PublishStatusSystemBanned 6:成功后系统封禁所有文章
PublishStatusSystemBanned
)
// FreePublish 发布能力
type FreePublish struct {
*context.Context
}
// NewFreePublish init
func NewFreePublish(ctx *context.Context) *FreePublish {
return &FreePublish{
Context: ctx,
}
}
// Publish 发布接口。需要先将图文素材以草稿的形式保存(见“草稿箱/新建草稿”,
// 如需从已保存的草稿中选择,见“草稿箱/获取草稿列表”),选择要发布的草稿 media_id 进行发布
func (freePublish *FreePublish) Publish(mediaID string) (publishID int64, err error) {
var accessToken string
accessToken, err = freePublish.GetAccessToken()
if err != nil {
return
}
var req struct {
MediaID string `json:"media_id"`
}
req.MediaID = mediaID
var response []byte
uri := fmt.Sprintf("%s?access_token=%s", publishURL, accessToken)
response, err = util.PostJSON(uri, req)
if err != nil {
return
}
var res struct {
util.CommonError
PublishID int64 `json:"publish_id"`
}
err = util.DecodeWithError(response, &res, "SubmitFreePublish")
if err != nil {
return
}
publishID = res.PublishID
return
}
// PublishStatusList 发布任务状态列表
type PublishStatusList struct {
util.CommonError
PublishID int64 `json:"publish_id"` // 发布任务id
PublishStatus PublishStatus `json:"publish_status"` // 发布状态
ArticleID string `json:"article_id"` // 当发布状态为0时即成功返回图文的 article_id可用于“客服消息”场景
ArticleDetail PublishArticleDetail `json:"article_detail"` // 发布任务文章成功状态详情
FailIndex []uint `json:"fail_idx"` // 当发布状态为2或4时返回不通过的文章编号第一篇为 1其他发布状态则为空
}
// PublishArticleDetail 发布任务成功详情
type PublishArticleDetail struct {
Count uint `json:"count"` // 当发布状态为0时即成功返回文章数量
Items []PublishArticleItem `json:"item"`
}
// PublishArticleItem 发布任务成功的文章内容
type PublishArticleItem struct {
Index uint `json:"idx"` // 当发布状态为0时即成功返回文章对应的编号
ArticleURL string `json:"article_url"` // 当发布状态为0时即成功返回图文的永久链接
}
// SelectStatus 发布状态轮询接口
func (freePublish *FreePublish) SelectStatus(publishID int64) (list PublishStatusList, err error) {
accessToken, err := freePublish.GetAccessToken()
if err != nil {
return
}
var req struct {
PublishID int64 `json:"publish_id"`
}
req.PublishID = publishID
var response []byte
uri := fmt.Sprintf("%s?access_token=%s", selectStateURL, accessToken)
response, err = util.PostJSON(uri, req)
if err != nil {
return
}
err = util.DecodeWithError(response, &list, "SelectStatusFreePublish")
return
}
// Delete 删除发布。
// index 要删除的文章在图文消息中的位置第一篇编号为1该字段不填或填0会删除全部文章
// !!!此操作不可逆,请谨慎操作!!!删除后微信公众号后台仍然会有记录!!!
func (freePublish *FreePublish) Delete(articleID string, index uint) (err error) {
accessToken, err := freePublish.GetAccessToken()
if err != nil {
return err
}
var req struct {
ArticleID string `json:"article_id"`
Index uint `json:"index"`
}
req.ArticleID = articleID
req.Index = index
var response []byte
uri := fmt.Sprintf("%s?access_token=%s", deleteURL, accessToken)
response, err = util.PostJSON(uri, req)
if err != nil {
return err
}
return util.DecodeWithCommonError(response, "DeleteFreePublish")
}
// Article 图文信息内容
type Article struct {
Title string `json:"title"` // 标题
Author string `json:"author"` // 作者
Digest string `json:"digest"` // 图文消息的摘要,仅有单图文消息才有摘要,多图文此处为空
Content string `json:"content"` // 图文消息的具体内容支持HTML标签必须少于2万字符小于1M且此处会去除JS
ContentSourceURL string `json:"content_source_url"` // 图文消息的原文地址即点击“阅读原文”后的URL
ThumbMediaID string `json:"thumb_media_id"` // 图文消息的封面图片素材id一定是永久MediaID
ShowCoverPic uint `json:"show_cover_pic"` // 是否显示封面0为false即不显示1为true即显示(默认)
NeedOpenComment uint `json:"need_open_comment"` // 是否打开评论0不打开(默认)1打开
OnlyFansCanComment uint `json:"only_fans_can_comment"` // 是否粉丝才可评论0所有人可评论(默认)1粉丝才可评论
URL string `json:"url"` // 图文消息的URL
IsDeleted bool `json:"is_deleted"` // 该图文是否被删除
}
// First 通过 article_id 获取已发布文章
func (freePublish *FreePublish) First(articleID string) (list []Article, err error) {
accessToken, err := freePublish.GetAccessToken()
if err != nil {
return
}
var req struct {
ArticleID string `json:"article_id"`
}
req.ArticleID = articleID
var response []byte
uri := fmt.Sprintf("%s?access_token=%s", firstArticleURL, accessToken)
response, err = util.PostJSON(uri, req)
if err != nil {
return
}
var res struct {
util.CommonError
NewsItem []Article `json:"news_item"`
}
err = util.DecodeWithError(response, &res, "FirstFreePublish")
if err != nil {
return
}
list = res.NewsItem
return
}
// ArticleList 发布列表
type ArticleList struct {
util.CommonError
TotalCount int64 `json:"total_count"` // 成功发布素材的总数
ItemCount int64 `json:"item_count"` // 本次调用获取的素材的数量
Item []ArticleListItem `json:"item"`
}
// ArticleListItem 用于 ArticleList 的 item 节点
type ArticleListItem struct {
ArticleID string `json:"article_id"` // 成功发布的图文消息id
Content ArticleListContent `json:"content"` // 内容
UpdateTime int64 `json:"update_time"` // 这篇图文消息素材的最后更新时间
}
// ArticleListContent 用于 ArticleListItem 的 content 节点
type ArticleListContent struct {
NewsItem []Article `json:"news_item"` // 这篇图文消息素材的内容
}
// Paginate 获取成功发布列表
func (freePublish *FreePublish) Paginate(offset, count int64, noReturnContent bool) (list ArticleList, err error) {
var accessToken string
accessToken, err = freePublish.GetAccessToken()
if err != nil {
return
}
var req struct {
Count int64 `json:"count"`
Offset int64 `json:"offset"`
NoReturnContent bool `json:"no_content"`
}
req.Count = count
req.Offset = offset
req.NoReturnContent = noReturnContent
var response []byte
uri := fmt.Sprintf("%s?access_token=%s", paginateURL, accessToken)
response, err = util.PostJSON(uri, req)
if err != nil {
return
}
err = util.DecodeWithError(response, &list, "PaginateFreePublish")
return
}

View File

@@ -22,7 +22,7 @@ type Config struct {
Signature string `json:"signature"` Signature string `json:"signature"`
} }
// NewJs init //NewJs init
func NewJs(context *context.Context) *Js { func NewJs(context *context.Context) *Js {
js := new(Js) js := new(Js)
js.Context = context js.Context = context
@@ -31,13 +31,13 @@ func NewJs(context *context.Context) *Js {
return js return js
} }
// SetJsTicketHandle 自定义js ticket取值方式 //SetJsTicketHandle 自定义js ticket取值方式
func (js *Js) SetJsTicketHandle(ticketHandle credential.JsTicketHandle) { func (js *Js) SetJsTicketHandle(ticketHandle credential.JsTicketHandle) {
js.JsTicketHandle = ticketHandle js.JsTicketHandle = ticketHandle
} }
// GetConfig 获取jssdk需要的配置参数 //GetConfig 获取jssdk需要的配置参数
// uri 为当前网页地址 //uri 为当前网页地址
func (js *Js) GetConfig(uri string) (config *Config, err error) { func (js *Js) GetConfig(uri string) (config *Config, err error) {
config = new(Config) config = new(Config)
var accessToken string var accessToken string

View File

@@ -19,33 +19,33 @@ const (
batchGetMaterialURL = "https://api.weixin.qq.com/cgi-bin/material/batchget_material" batchGetMaterialURL = "https://api.weixin.qq.com/cgi-bin/material/batchget_material"
) )
// PermanentMaterialType 永久素材类型 //PermanentMaterialType 永久素材类型
type PermanentMaterialType string type PermanentMaterialType string
const ( const (
// PermanentMaterialTypeImage 永久素材图片类型image //PermanentMaterialTypeImage 永久素材图片类型image
PermanentMaterialTypeImage PermanentMaterialType = "image" PermanentMaterialTypeImage PermanentMaterialType = "image"
// PermanentMaterialTypeVideo 永久素材视频类型video //PermanentMaterialTypeVideo 永久素材视频类型video
PermanentMaterialTypeVideo PermanentMaterialType = "video" PermanentMaterialTypeVideo PermanentMaterialType = "video"
// PermanentMaterialTypeVoice 永久素材语音类型 voice //PermanentMaterialTypeVoice 永久素材语音类型 voice
PermanentMaterialTypeVoice PermanentMaterialType = "voice" PermanentMaterialTypeVoice PermanentMaterialType = "voice"
// PermanentMaterialTypeNews 永久素材图文类型news //PermanentMaterialTypeNews 永久素材图文类型news
PermanentMaterialTypeNews PermanentMaterialType = "news" PermanentMaterialTypeNews PermanentMaterialType = "news"
) )
// Material 素材管理 //Material 素材管理
type Material struct { type Material struct {
*context.Context *context.Context
} }
// NewMaterial init //NewMaterial init
func NewMaterial(context *context.Context) *Material { func NewMaterial(context *context.Context) *Material {
material := new(Material) material := new(Material)
material.Context = context material.Context = context
return material return material
} }
// Article 永久图文素材 //Article 永久图文素材
type Article struct { type Article struct {
Title string `json:"title"` Title string `json:"title"`
ThumbMediaID string `json:"thumb_media_id"` ThumbMediaID string `json:"thumb_media_id"`
@@ -87,19 +87,19 @@ func (material *Material) GetNews(id string) ([]*Article, error) {
return res.NewsItem, nil return res.NewsItem, nil
} }
// reqArticles 永久性图文素材请求信息 //reqArticles 永久性图文素材请求信息
type reqArticles struct { type reqArticles struct {
Articles []*Article `json:"articles"` Articles []*Article `json:"articles"`
} }
// resArticles 永久性图文素材返回结果 //resArticles 永久性图文素材返回结果
type resArticles struct { type resArticles struct {
util.CommonError util.CommonError
MediaID string `json:"media_id"` MediaID string `json:"media_id"`
} }
// AddNews 新增永久图文素材 //AddNews 新增永久图文素材
func (material *Material) AddNews(articles []*Article) (mediaID string, err error) { func (material *Material) AddNews(articles []*Article) (mediaID string, err error) {
req := &reqArticles{articles} req := &reqArticles{articles}
@@ -126,7 +126,7 @@ func (material *Material) AddNews(articles []*Article) (mediaID string, err erro
return return
} }
// reqUpdateArticle 更新永久性图文素材请求信息 //reqUpdateArticle 更新永久性图文素材请求信息
type reqUpdateArticle struct { type reqUpdateArticle struct {
MediaID string `json:"media_id"` MediaID string `json:"media_id"`
Index int64 `json:"index"` Index int64 `json:"index"`
@@ -152,7 +152,7 @@ func (material *Material) UpdateNews(article *Article, mediaID string, index int
return util.DecodeWithCommonError(response, "UpdateNews") return util.DecodeWithCommonError(response, "UpdateNews")
} }
// resAddMaterial 永久性素材上传返回的结果 //resAddMaterial 永久性素材上传返回的结果
type resAddMaterial struct { type resAddMaterial struct {
util.CommonError util.CommonError
@@ -160,7 +160,7 @@ type resAddMaterial struct {
URL string `json:"url"` URL string `json:"url"`
} }
// AddMaterial 上传永久性素材(处理视频需要单独上传) //AddMaterial 上传永久性素材(处理视频需要单独上传)
func (material *Material) AddMaterial(mediaType MediaType, filename string) (mediaID string, url string, err error) { func (material *Material) AddMaterial(mediaType MediaType, filename string) (mediaID string, url string, err error) {
if mediaType == MediaTypeVideo { if mediaType == MediaTypeVideo {
err = errors.New("永久视频素材上传使用 AddVideo 方法") err = errors.New("永久视频素材上传使用 AddVideo 方法")
@@ -197,7 +197,7 @@ type reqVideo struct {
Introduction string `json:"introduction"` Introduction string `json:"introduction"`
} }
// AddVideo 永久视频素材文件上传 //AddVideo 永久视频素材文件上传
func (material *Material) AddVideo(filename, title, introduction string) (mediaID string, url string, err error) { func (material *Material) AddVideo(filename, title, introduction string) (mediaID string, url string, err error) {
var accessToken string var accessToken string
accessToken, err = material.GetAccessToken() accessToken, err = material.GetAccessToken()
@@ -254,7 +254,7 @@ type reqDeleteMaterial struct {
MediaID string `json:"media_id"` MediaID string `json:"media_id"`
} }
// DeleteMaterial 删除永久素材 //DeleteMaterial 删除永久素材
func (material *Material) DeleteMaterial(mediaID string) error { func (material *Material) DeleteMaterial(mediaID string) error {
accessToken, err := material.GetAccessToken() accessToken, err := material.GetAccessToken()
if err != nil { if err != nil {
@@ -270,7 +270,7 @@ func (material *Material) DeleteMaterial(mediaID string) error {
return util.DecodeWithCommonError(response, "DeleteMaterial") return util.DecodeWithCommonError(response, "DeleteMaterial")
} }
// ArticleList 永久素材列表 //ArticleList 永久素材列表
type ArticleList struct { type ArticleList struct {
util.CommonError util.CommonError
TotalCount int64 `json:"total_count"` TotalCount int64 `json:"total_count"`
@@ -278,7 +278,7 @@ type ArticleList struct {
Item []ArticleListItem `json:"item"` Item []ArticleListItem `json:"item"`
} }
// ArticleListItem 用于ArticleList的item节点 //ArticleListItem 用于ArticleList的item节点
type ArticleListItem struct { type ArticleListItem struct {
MediaID string `json:"media_id"` MediaID string `json:"media_id"`
Content ArticleListContent `json:"content"` Content ArticleListContent `json:"content"`
@@ -287,14 +287,14 @@ type ArticleListItem struct {
UpdateTime int64 `json:"update_time"` UpdateTime int64 `json:"update_time"`
} }
// ArticleListContent 用于ArticleListItem的content节点 //ArticleListContent 用于ArticleListItem的content节点
type ArticleListContent struct { type ArticleListContent struct {
NewsItem []Article `json:"news_item"` NewsItem []Article `json:"news_item"`
UpdateTime int64 `json:"update_time"` UpdateTime int64 `json:"update_time"`
CreateTime int64 `json:"create_time"` CreateTime int64 `json:"create_time"`
} }
// reqBatchGetMaterial BatchGetMaterial请求参数 //reqBatchGetMaterial BatchGetMaterial请求参数
type reqBatchGetMaterial struct { type reqBatchGetMaterial struct {
Type PermanentMaterialType `json:"type"` Type PermanentMaterialType `json:"type"`
Count int64 `json:"count"` Count int64 `json:"count"`
@@ -302,7 +302,6 @@ type reqBatchGetMaterial struct {
} }
// BatchGetMaterial 批量获取永久素材 // BatchGetMaterial 批量获取永久素材
//
//reference:https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Get_materials_list.html //reference:https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Get_materials_list.html
func (material *Material) BatchGetMaterial(permanentMaterialType PermanentMaterialType, offset, count int64) (list ArticleList, err error) { func (material *Material) BatchGetMaterial(permanentMaterialType PermanentMaterialType, offset, count int64) (list ArticleList, err error) {
var accessToken string var accessToken string

View File

@@ -7,17 +7,17 @@ import (
"github.com/silenceper/wechat/v2/util" "github.com/silenceper/wechat/v2/util"
) )
// MediaType 媒体文件类型 //MediaType 媒体文件类型
type MediaType string type MediaType string
const ( const (
// MediaTypeImage 媒体文件:图片 //MediaTypeImage 媒体文件:图片
MediaTypeImage MediaType = "image" MediaTypeImage MediaType = "image"
// MediaTypeVoice 媒体文件:声音 //MediaTypeVoice 媒体文件:声音
MediaTypeVoice MediaType = "voice" MediaTypeVoice MediaType = "voice"
// MediaTypeVideo 媒体文件:视频 //MediaTypeVideo 媒体文件:视频
MediaTypeVideo MediaType = "video" MediaTypeVideo MediaType = "video"
// MediaTypeThumb 媒体文件:缩略图 //MediaTypeThumb 媒体文件:缩略图
MediaTypeThumb MediaType = "thumb" MediaTypeThumb MediaType = "thumb"
) )
@@ -27,7 +27,7 @@ const (
mediaGetURL = "https://api.weixin.qq.com/cgi-bin/media/get" mediaGetURL = "https://api.weixin.qq.com/cgi-bin/media/get"
) )
// Media 临时素材上传返回信息 //Media 临时素材上传返回信息
type Media struct { type Media struct {
util.CommonError util.CommonError
@@ -37,7 +37,7 @@ type Media struct {
CreatedAt int64 `json:"created_at"` CreatedAt int64 `json:"created_at"`
} }
// MediaUpload 临时素材上传 //MediaUpload 临时素材上传
func (material *Material) MediaUpload(mediaType MediaType, filename string) (media Media, err error) { func (material *Material) MediaUpload(mediaType MediaType, filename string) (media Media, err error) {
var accessToken string var accessToken string
accessToken, err = material.GetAccessToken() accessToken, err = material.GetAccessToken()
@@ -62,8 +62,8 @@ func (material *Material) MediaUpload(mediaType MediaType, filename string) (med
return return
} }
// GetMediaURL 返回临时素材的下载地址供用户自己处理 //GetMediaURL 返回临时素材的下载地址供用户自己处理
// NOTICE: URL 不可公开因为含access_token 需要立即另存文件 //NOTICE: URL 不可公开因为含access_token 需要立即另存文件
func (material *Material) GetMediaURL(mediaID string) (mediaURL string, err error) { func (material *Material) GetMediaURL(mediaID string) (mediaURL string, err error) {
var accessToken string var accessToken string
accessToken, err = material.GetAccessToken() accessToken, err = material.GetAccessToken()
@@ -74,14 +74,14 @@ func (material *Material) GetMediaURL(mediaID string) (mediaURL string, err erro
return return
} }
// resMediaImage 图片上传返回结果 //resMediaImage 图片上传返回结果
type resMediaImage struct { type resMediaImage struct {
util.CommonError util.CommonError
URL string `json:"url"` URL string `json:"url"`
} }
// ImageUpload 图片上传 //ImageUpload 图片上传
func (material *Material) ImageUpload(filename string) (url string, err error) { func (material *Material) ImageUpload(filename string) (url string, err error) {
var accessToken string var accessToken string
accessToken, err = material.GetAccessToken() accessToken, err = material.GetAccessToken()

View File

@@ -1,6 +1,6 @@
package menu package menu
// Button 菜单按钮 //Button 菜单按钮
type Button struct { type Button struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
@@ -12,7 +12,7 @@ type Button struct {
SubButtons []*Button `json:"sub_button,omitempty"` SubButtons []*Button `json:"sub_button,omitempty"`
} }
// SetSubButton 设置二级菜单 //SetSubButton 设置二级菜单
func (btn *Button) SetSubButton(name string, subButtons []*Button) *Button { func (btn *Button) SetSubButton(name string, subButtons []*Button) *Button {
btn.Name = name btn.Name = name
btn.SubButtons = subButtons btn.SubButtons = subButtons
@@ -23,7 +23,7 @@ func (btn *Button) SetSubButton(name string, subButtons []*Button) *Button {
return btn return btn
} }
// SetClickButton btn 为click类型 //SetClickButton btn 为click类型
func (btn *Button) SetClickButton(name, key string) *Button { func (btn *Button) SetClickButton(name, key string) *Button {
btn.Type = "click" btn.Type = "click"
btn.Name = name btn.Name = name
@@ -34,7 +34,7 @@ func (btn *Button) SetClickButton(name, key string) *Button {
return btn return btn
} }
// SetViewButton view类型 //SetViewButton view类型
func (btn *Button) SetViewButton(name, url string) *Button { func (btn *Button) SetViewButton(name, url string) *Button {
btn.Type = "view" btn.Type = "view"
btn.Name = name btn.Name = name
@@ -45,7 +45,7 @@ func (btn *Button) SetViewButton(name, url string) *Button {
return btn return btn
} }
// SetScanCodePushButton 扫码推事件 //SetScanCodePushButton 扫码推事件
func (btn *Button) SetScanCodePushButton(name, key string) *Button { func (btn *Button) SetScanCodePushButton(name, key string) *Button {
btn.Type = "scancode_push" btn.Type = "scancode_push"
btn.Name = name btn.Name = name
@@ -56,7 +56,7 @@ func (btn *Button) SetScanCodePushButton(name, key string) *Button {
return btn return btn
} }
// SetScanCodeWaitMsgButton 设置 扫码推事件且弹出"消息接收中"提示框 //SetScanCodeWaitMsgButton 设置 扫码推事件且弹出"消息接收中"提示框
func (btn *Button) SetScanCodeWaitMsgButton(name, key string) *Button { func (btn *Button) SetScanCodeWaitMsgButton(name, key string) *Button {
btn.Type = "scancode_waitmsg" btn.Type = "scancode_waitmsg"
btn.Name = name btn.Name = name
@@ -68,7 +68,7 @@ func (btn *Button) SetScanCodeWaitMsgButton(name, key string) *Button {
return btn return btn
} }
// SetPicSysPhotoButton 设置弹出系统拍照发图按钮 //SetPicSysPhotoButton 设置弹出系统拍照发图按钮
func (btn *Button) SetPicSysPhotoButton(name, key string) *Button { func (btn *Button) SetPicSysPhotoButton(name, key string) *Button {
btn.Type = "pic_sysphoto" btn.Type = "pic_sysphoto"
btn.Name = name btn.Name = name
@@ -80,7 +80,7 @@ func (btn *Button) SetPicSysPhotoButton(name, key string) *Button {
return btn return btn
} }
// SetPicPhotoOrAlbumButton 设置弹出拍照或者相册发图类型按钮 //SetPicPhotoOrAlbumButton 设置弹出拍照或者相册发图类型按钮
func (btn *Button) SetPicPhotoOrAlbumButton(name, key string) *Button { func (btn *Button) SetPicPhotoOrAlbumButton(name, key string) *Button {
btn.Type = "pic_photo_or_album" btn.Type = "pic_photo_or_album"
btn.Name = name btn.Name = name
@@ -92,7 +92,7 @@ func (btn *Button) SetPicPhotoOrAlbumButton(name, key string) *Button {
return btn return btn
} }
// SetPicWeixinButton 设置弹出微信相册发图器类型按钮 //SetPicWeixinButton 设置弹出微信相册发图器类型按钮
func (btn *Button) SetPicWeixinButton(name, key string) *Button { func (btn *Button) SetPicWeixinButton(name, key string) *Button {
btn.Type = "pic_weixin" btn.Type = "pic_weixin"
btn.Name = name btn.Name = name
@@ -104,7 +104,7 @@ func (btn *Button) SetPicWeixinButton(name, key string) *Button {
return btn return btn
} }
// SetLocationSelectButton 设置 弹出地理位置选择器 类型按钮 //SetLocationSelectButton 设置 弹出地理位置选择器 类型按钮
func (btn *Button) SetLocationSelectButton(name, key string) *Button { func (btn *Button) SetLocationSelectButton(name, key string) *Button {
btn.Type = "location_select" btn.Type = "location_select"
btn.Name = name btn.Name = name
@@ -116,7 +116,7 @@ func (btn *Button) SetLocationSelectButton(name, key string) *Button {
return btn return btn
} }
// SetMediaIDButton 设置 下发消息(除文本消息) 类型按钮 //SetMediaIDButton 设置 下发消息(除文本消息) 类型按钮
func (btn *Button) SetMediaIDButton(name, mediaID string) *Button { func (btn *Button) SetMediaIDButton(name, mediaID string) *Button {
btn.Type = "media_id" btn.Type = "media_id"
btn.Name = name btn.Name = name
@@ -128,7 +128,7 @@ func (btn *Button) SetMediaIDButton(name, mediaID string) *Button {
return btn return btn
} }
// SetViewLimitedButton 设置 跳转图文消息URL 类型按钮 //SetViewLimitedButton 设置 跳转图文消息URL 类型按钮
func (btn *Button) SetViewLimitedButton(name, mediaID string) *Button { func (btn *Button) SetViewLimitedButton(name, mediaID string) *Button {
btn.Type = "view_limited" btn.Type = "view_limited"
btn.Name = name btn.Name = name
@@ -140,7 +140,7 @@ func (btn *Button) SetViewLimitedButton(name, mediaID string) *Button {
return btn return btn
} }
// SetMiniprogramButton 设置 跳转小程序 类型按钮 (公众号后台必须已经关联小程序) //SetMiniprogramButton 设置 跳转小程序 类型按钮 (公众号后台必须已经关联小程序)
func (btn *Button) SetMiniprogramButton(name, url, appID, pagePath string) *Button { func (btn *Button) SetMiniprogramButton(name, url, appID, pagePath string) *Button {
btn.Type = "miniprogram" btn.Type = "miniprogram"
btn.Name = name btn.Name = name
@@ -154,62 +154,62 @@ func (btn *Button) SetMiniprogramButton(name, url, appID, pagePath string) *Butt
return btn return btn
} }
// NewSubButton 二级菜单 //NewSubButton 二级菜单
func NewSubButton(name string, subButtons []*Button) *Button { func NewSubButton(name string, subButtons []*Button) *Button {
return (&Button{}).SetSubButton(name, subButtons) return (&Button{}).SetSubButton(name, subButtons)
} }
// NewClickButton btn 为click类型 //NewClickButton btn 为click类型
func NewClickButton(name, key string) *Button { func NewClickButton(name, key string) *Button {
return (&Button{}).SetClickButton(name, key) return (&Button{}).SetClickButton(name, key)
} }
// NewViewButton view类型 //NewViewButton view类型
func NewViewButton(name, url string) *Button { func NewViewButton(name, url string) *Button {
return (&Button{}).SetViewButton(name, url) return (&Button{}).SetViewButton(name, url)
} }
// NewScanCodePushButton 扫码推事件 //NewScanCodePushButton 扫码推事件
func NewScanCodePushButton(name, key string) *Button { func NewScanCodePushButton(name, key string) *Button {
return (&Button{}).SetScanCodePushButton(name, key) return (&Button{}).SetScanCodePushButton(name, key)
} }
// NewScanCodeWaitMsgButton 扫码推事件且弹出"消息接收中"提示框 //NewScanCodeWaitMsgButton 扫码推事件且弹出"消息接收中"提示框
func NewScanCodeWaitMsgButton(name, key string) *Button { func NewScanCodeWaitMsgButton(name, key string) *Button {
return (&Button{}).SetScanCodeWaitMsgButton(name, key) return (&Button{}).SetScanCodeWaitMsgButton(name, key)
} }
// NewPicSysPhotoButton 弹出系统拍照发图按钮 //NewPicSysPhotoButton 弹出系统拍照发图按钮
func NewPicSysPhotoButton(name, key string) *Button { func NewPicSysPhotoButton(name, key string) *Button {
return (&Button{}).SetPicSysPhotoButton(name, key) return (&Button{}).SetPicSysPhotoButton(name, key)
} }
// NewPicPhotoOrAlbumButton 弹出拍照或者相册发图类型按钮 //NewPicPhotoOrAlbumButton 弹出拍照或者相册发图类型按钮
func NewPicPhotoOrAlbumButton(name, key string) *Button { func NewPicPhotoOrAlbumButton(name, key string) *Button {
return (&Button{}).SetPicPhotoOrAlbumButton(name, key) return (&Button{}).SetPicPhotoOrAlbumButton(name, key)
} }
// NewPicWeixinButton 弹出微信相册发图器类型按钮 //NewPicWeixinButton 弹出微信相册发图器类型按钮
func NewPicWeixinButton(name, key string) *Button { func NewPicWeixinButton(name, key string) *Button {
return (&Button{}).SetPicWeixinButton(name, key) return (&Button{}).SetPicWeixinButton(name, key)
} }
// NewLocationSelectButton 弹出地理位置选择器 类型按钮 //NewLocationSelectButton 弹出地理位置选择器 类型按钮
func NewLocationSelectButton(name, key string) *Button { func NewLocationSelectButton(name, key string) *Button {
return (&Button{}).SetLocationSelectButton(name, key) return (&Button{}).SetLocationSelectButton(name, key)
} }
// NewMediaIDButton 下发消息(除文本消息) 类型按钮 //NewMediaIDButton 下发消息(除文本消息) 类型按钮
func NewMediaIDButton(name, mediaID string) *Button { func NewMediaIDButton(name, mediaID string) *Button {
return (&Button{}).SetMediaIDButton(name, mediaID) return (&Button{}).SetMediaIDButton(name, mediaID)
} }
// NewViewLimitedButton 跳转图文消息URL 类型按钮 //NewViewLimitedButton 跳转图文消息URL 类型按钮
func NewViewLimitedButton(name, mediaID string) *Button { func NewViewLimitedButton(name, mediaID string) *Button {
return (&Button{}).SetViewLimitedButton(name, mediaID) return (&Button{}).SetViewLimitedButton(name, mediaID)
} }
// NewMiniprogramButton 跳转小程序 类型按钮 (公众号后台必须已经关联小程序) //NewMiniprogramButton 跳转小程序 类型按钮 (公众号后台必须已经关联小程序)
func NewMiniprogramButton(name, url, appID, pagePath string) *Button { func NewMiniprogramButton(name, url, appID, pagePath string) *Button {
return (&Button{}).SetMiniprogramButton(name, url, appID, pagePath) return (&Button{}).SetMiniprogramButton(name, url, appID, pagePath)
} }

View File

@@ -18,42 +18,42 @@ const (
menuSelfMenuInfoURL = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info" menuSelfMenuInfoURL = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info"
) )
// Menu struct //Menu struct
type Menu struct { type Menu struct {
*context.Context *context.Context
} }
// reqMenu 设置菜单请求数据 //reqMenu 设置菜单请求数据
type reqMenu struct { type reqMenu struct {
Button []*Button `json:"button,omitempty"` Button []*Button `json:"button,omitempty"`
MatchRule *MatchRule `json:"matchrule,omitempty"` MatchRule *MatchRule `json:"matchrule,omitempty"`
} }
// reqDeleteConditional 删除个性化菜单请求数据 //reqDeleteConditional 删除个性化菜单请求数据
type reqDeleteConditional struct { type reqDeleteConditional struct {
MenuID int64 `json:"menuid"` MenuID int64 `json:"menuid"`
} }
// reqMenuTryMatch 菜单匹配请求 //reqMenuTryMatch 菜单匹配请求
type reqMenuTryMatch struct { type reqMenuTryMatch struct {
UserID string `json:"user_id"` UserID string `json:"user_id"`
} }
// resConditionalMenu 个性化菜单返回结果 //resConditionalMenu 个性化菜单返回结果
type resConditionalMenu struct { type resConditionalMenu struct {
Button []Button `json:"button"` Button []Button `json:"button"`
MatchRule MatchRule `json:"matchrule"` MatchRule MatchRule `json:"matchrule"`
MenuID int64 `json:"menuid"` MenuID int64 `json:"menuid"`
} }
// resMenuTryMatch 菜单匹配请求结果 //resMenuTryMatch 菜单匹配请求结果
type resMenuTryMatch struct { type resMenuTryMatch struct {
util.CommonError util.CommonError
Button []Button `json:"button"` Button []Button `json:"button"`
} }
// ResMenu 查询菜单的返回数据 //ResMenu 查询菜单的返回数据
type ResMenu struct { type ResMenu struct {
util.CommonError util.CommonError
@@ -64,7 +64,7 @@ type ResMenu struct {
Conditionalmenu []resConditionalMenu `json:"conditionalmenu"` Conditionalmenu []resConditionalMenu `json:"conditionalmenu"`
} }
// ResSelfMenuInfo 自定义菜单配置返回结果 //ResSelfMenuInfo 自定义菜单配置返回结果
type ResSelfMenuInfo struct { type ResSelfMenuInfo struct {
util.CommonError util.CommonError
@@ -74,7 +74,7 @@ type ResSelfMenuInfo struct {
} `json:"selfmenu_info"` } `json:"selfmenu_info"`
} }
// SelfMenuButton 自定义菜单配置详情 //SelfMenuButton 自定义菜单配置详情
type SelfMenuButton struct { type SelfMenuButton struct {
Type string `json:"type"` Type string `json:"type"`
Name string `json:"name"` Name string `json:"name"`
@@ -89,7 +89,7 @@ type SelfMenuButton struct {
} `json:"news_info,omitempty"` } `json:"news_info,omitempty"`
} }
// ButtonNew 图文消息菜单 //ButtonNew 图文消息菜单
type ButtonNew struct { type ButtonNew struct {
Title string `json:"title"` Title string `json:"title"`
Author string `json:"author"` Author string `json:"author"`
@@ -100,7 +100,7 @@ type ButtonNew struct {
SourceURL string `json:"source_url"` SourceURL string `json:"source_url"`
} }
// MatchRule 个性化菜单规则 //MatchRule 个性化菜单规则
type MatchRule struct { type MatchRule struct {
GroupID string `json:"group_id,omitempty"` GroupID string `json:"group_id,omitempty"`
Sex string `json:"sex,omitempty"` Sex string `json:"sex,omitempty"`
@@ -111,14 +111,14 @@ type MatchRule struct {
Language string `json:"language,omitempty"` Language string `json:"language,omitempty"`
} }
// NewMenu 实例 //NewMenu 实例
func NewMenu(context *context.Context) *Menu { func NewMenu(context *context.Context) *Menu {
menu := new(Menu) menu := new(Menu)
menu.Context = context menu.Context = context
return menu return menu
} }
// SetMenu 设置按钮 //SetMenu 设置按钮
func (menu *Menu) SetMenu(buttons []*Button) error { func (menu *Menu) SetMenu(buttons []*Button) error {
accessToken, err := menu.GetAccessToken() accessToken, err := menu.GetAccessToken()
if err != nil { if err != nil {
@@ -138,7 +138,7 @@ func (menu *Menu) SetMenu(buttons []*Button) error {
return util.DecodeWithCommonError(response, "SetMenu") return util.DecodeWithCommonError(response, "SetMenu")
} }
// SetMenuByJSON 设置按钮 //SetMenuByJSON 设置按钮
func (menu *Menu) SetMenuByJSON(jsonInfo string) error { func (menu *Menu) SetMenuByJSON(jsonInfo string) error {
accessToken, err := menu.GetAccessToken() accessToken, err := menu.GetAccessToken()
if err != nil { if err != nil {
@@ -155,7 +155,7 @@ func (menu *Menu) SetMenuByJSON(jsonInfo string) error {
return util.DecodeWithCommonError(response, "SetMenuByJSON") return util.DecodeWithCommonError(response, "SetMenuByJSON")
} }
// GetMenu 获取菜单配置 //GetMenu 获取菜单配置
func (menu *Menu) GetMenu() (resMenu ResMenu, err error) { func (menu *Menu) GetMenu() (resMenu ResMenu, err error) {
var accessToken string var accessToken string
accessToken, err = menu.GetAccessToken() accessToken, err = menu.GetAccessToken()
@@ -179,7 +179,7 @@ func (menu *Menu) GetMenu() (resMenu ResMenu, err error) {
return return
} }
// DeleteMenu 删除菜单 //DeleteMenu 删除菜单
func (menu *Menu) DeleteMenu() error { func (menu *Menu) DeleteMenu() error {
accessToken, err := menu.GetAccessToken() accessToken, err := menu.GetAccessToken()
if err != nil { if err != nil {
@@ -194,7 +194,7 @@ func (menu *Menu) DeleteMenu() error {
return util.DecodeWithCommonError(response, "GetMenu") return util.DecodeWithCommonError(response, "GetMenu")
} }
// AddConditional 添加个性化菜单 //AddConditional 添加个性化菜单
func (menu *Menu) AddConditional(buttons []*Button, matchRule *MatchRule) error { func (menu *Menu) AddConditional(buttons []*Button, matchRule *MatchRule) error {
accessToken, err := menu.GetAccessToken() accessToken, err := menu.GetAccessToken()
if err != nil { if err != nil {
@@ -215,7 +215,7 @@ func (menu *Menu) AddConditional(buttons []*Button, matchRule *MatchRule) error
return util.DecodeWithCommonError(response, "AddConditional") return util.DecodeWithCommonError(response, "AddConditional")
} }
// AddConditionalByJSON 添加个性化菜单 //AddConditionalByJSON 添加个性化菜单
func (menu *Menu) AddConditionalByJSON(jsonInfo string) error { func (menu *Menu) AddConditionalByJSON(jsonInfo string) error {
accessToken, err := menu.GetAccessToken() accessToken, err := menu.GetAccessToken()
if err != nil { if err != nil {
@@ -231,7 +231,7 @@ func (menu *Menu) AddConditionalByJSON(jsonInfo string) error {
return util.DecodeWithCommonError(response, "AddConditional") return util.DecodeWithCommonError(response, "AddConditional")
} }
// DeleteConditional 删除个性化菜单 //DeleteConditional 删除个性化菜单
func (menu *Menu) DeleteConditional(menuID int64) error { func (menu *Menu) DeleteConditional(menuID int64) error {
accessToken, err := menu.GetAccessToken() accessToken, err := menu.GetAccessToken()
if err != nil { if err != nil {
@@ -251,7 +251,7 @@ func (menu *Menu) DeleteConditional(menuID int64) error {
return util.DecodeWithCommonError(response, "DeleteConditional") return util.DecodeWithCommonError(response, "DeleteConditional")
} }
// MenuTryMatch 菜单匹配 //MenuTryMatch 菜单匹配
func (menu *Menu) MenuTryMatch(userID string) (buttons []Button, err error) { func (menu *Menu) MenuTryMatch(userID string) (buttons []Button, err error) {
var accessToken string var accessToken string
accessToken, err = menu.GetAccessToken() accessToken, err = menu.GetAccessToken()
@@ -278,7 +278,7 @@ func (menu *Menu) MenuTryMatch(userID string) (buttons []Button, err error) {
return return
} }
// GetCurrentSelfMenuInfo 获取自定义菜单配置接口 //GetCurrentSelfMenuInfo 获取自定义菜单配置接口
func (menu *Menu) GetCurrentSelfMenuInfo() (resSelfMenuInfo ResSelfMenuInfo, err error) { func (menu *Menu) GetCurrentSelfMenuInfo() (resSelfMenuInfo ResSelfMenuInfo, err error) {
var accessToken string var accessToken string
accessToken, err = menu.GetAccessToken() accessToken, err = menu.GetAccessToken()

View File

@@ -12,35 +12,35 @@ const (
customerSendMessage = "https://api.weixin.qq.com/cgi-bin/message/custom/send" customerSendMessage = "https://api.weixin.qq.com/cgi-bin/message/custom/send"
) )
// Manager 消息管理者,可以发送消息 //Manager 消息管理者,可以发送消息
type Manager struct { type Manager struct {
*context.Context *context.Context
} }
// NewMessageManager 实例化消息管理者 //NewMessageManager 实例化消息管理者
func NewMessageManager(context *context.Context) *Manager { func NewMessageManager(context *context.Context) *Manager {
return &Manager{ return &Manager{
context, context,
} }
} }
// CustomerMessage 客服消息 //CustomerMessage 客服消息
type CustomerMessage struct { type CustomerMessage struct {
ToUser string `json:"touser"` // 接受者OpenID ToUser string `json:"touser"` //接受者OpenID
Msgtype MsgType `json:"msgtype"` // 客服消息类型 Msgtype MsgType `json:"msgtype"` //客服消息类型
Text *MediaText `json:"text,omitempty"` // 可选 Text *MediaText `json:"text,omitempty"` //可选
Image *MediaResource `json:"image,omitempty"` // 可选 Image *MediaResource `json:"image,omitempty"` //可选
Voice *MediaResource `json:"voice,omitempty"` // 可选 Voice *MediaResource `json:"voice,omitempty"` //可选
Video *MediaVideo `json:"video,omitempty"` // 可选 Video *MediaVideo `json:"video,omitempty"` //可选
Music *MediaMusic `json:"music,omitempty"` // 可选 Music *MediaMusic `json:"music,omitempty"` //可选
News *MediaNews `json:"news,omitempty"` // 可选 News *MediaNews `json:"news,omitempty"` //可选
Mpnews *MediaResource `json:"mpnews,omitempty"` // 可选 Mpnews *MediaResource `json:"mpnews,omitempty"` //可选
Wxcard *MediaWxcard `json:"wxcard,omitempty"` // 可选 Wxcard *MediaWxcard `json:"wxcard,omitempty"` //可选
Msgmenu *MediaMsgmenu `json:"msgmenu,omitempty"` // 可选 Msgmenu *MediaMsgmenu `json:"msgmenu,omitempty"` //可选
Miniprogrampage *MediaMiniprogrampage `json:"miniprogrampage,omitempty"` // 可选 Miniprogrampage *MediaMiniprogrampage `json:"miniprogrampage,omitempty"` //可选
} }
// NewCustomerTextMessage 文本消息结构体构造方法 //NewCustomerTextMessage 文本消息结构体构造方法
func NewCustomerTextMessage(toUser, text string) *CustomerMessage { func NewCustomerTextMessage(toUser, text string) *CustomerMessage {
return &CustomerMessage{ return &CustomerMessage{
ToUser: toUser, ToUser: toUser,
@@ -51,7 +51,7 @@ func NewCustomerTextMessage(toUser, text string) *CustomerMessage {
} }
} }
// NewCustomerImgMessage 图片消息的构造方法 //NewCustomerImgMessage 图片消息的构造方法
func NewCustomerImgMessage(toUser, mediaID string) *CustomerMessage { func NewCustomerImgMessage(toUser, mediaID string) *CustomerMessage {
return &CustomerMessage{ return &CustomerMessage{
ToUser: toUser, ToUser: toUser,
@@ -62,7 +62,7 @@ func NewCustomerImgMessage(toUser, mediaID string) *CustomerMessage {
} }
} }
// NewCustomerVoiceMessage 语音消息的构造方法 //NewCustomerVoiceMessage 语音消息的构造方法
func NewCustomerVoiceMessage(toUser, mediaID string) *CustomerMessage { func NewCustomerVoiceMessage(toUser, mediaID string) *CustomerMessage {
return &CustomerMessage{ return &CustomerMessage{
ToUser: toUser, ToUser: toUser,
@@ -73,7 +73,7 @@ func NewCustomerVoiceMessage(toUser, mediaID string) *CustomerMessage {
} }
} }
// NewCustomerMiniprogrampageMessage 小程序卡片消息的构造方法 //NewCustomerMiniprogrampageMessage 小程序卡片消息的构造方法
func NewCustomerMiniprogrampageMessage(toUser, title, appID, pagePath, thumbMediaID string) *CustomerMessage { func NewCustomerMiniprogrampageMessage(toUser, title, appID, pagePath, thumbMediaID string) *CustomerMessage {
return &CustomerMessage{ return &CustomerMessage{
ToUser: toUser, ToUser: toUser,
@@ -87,17 +87,17 @@ func NewCustomerMiniprogrampageMessage(toUser, title, appID, pagePath, thumbMedi
} }
} }
// MediaText 文本消息的文字 //MediaText 文本消息的文字
type MediaText struct { type MediaText struct {
Content string `json:"content"` Content string `json:"content"`
} }
// MediaResource 消息使用的永久素材id //MediaResource 消息使用的永久素材id
type MediaResource struct { type MediaResource struct {
MediaID string `json:"media_id"` MediaID string `json:"media_id"`
} }
// MediaVideo 视频消息包含的内容 //MediaVideo 视频消息包含的内容
type MediaVideo struct { type MediaVideo struct {
MediaID string `json:"media_id"` MediaID string `json:"media_id"`
ThumbMediaID string `json:"thumb_media_id"` ThumbMediaID string `json:"thumb_media_id"`
@@ -105,7 +105,7 @@ type MediaVideo struct {
Description string `json:"description"` Description string `json:"description"`
} }
// MediaMusic 音乐消息包括的内容 //MediaMusic 音乐消息包括的内容
type MediaMusic struct { type MediaMusic struct {
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
@@ -114,12 +114,12 @@ type MediaMusic struct {
ThumbMediaID string `json:"thumb_media_id"` ThumbMediaID string `json:"thumb_media_id"`
} }
// MediaNews 图文消息的内容 //MediaNews 图文消息的内容
type MediaNews struct { type MediaNews struct {
Articles []MediaArticles `json:"articles"` Articles []MediaArticles `json:"articles"`
} }
// MediaArticles 图文消息的内容的文章列表中的单独一条 //MediaArticles 图文消息的内容的文章列表中的单独一条
type MediaArticles struct { type MediaArticles struct {
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
@@ -127,25 +127,25 @@ type MediaArticles struct {
Picurl string `json:"picurl"` Picurl string `json:"picurl"`
} }
// MediaMsgmenu 菜单消息的内容 //MediaMsgmenu 菜单消息的内容
type MediaMsgmenu struct { type MediaMsgmenu struct {
HeadContent string `json:"head_content"` HeadContent string `json:"head_content"`
List []MsgmenuItem `json:"list"` List []MsgmenuItem `json:"list"`
TailContent string `json:"tail_content"` TailContent string `json:"tail_content"`
} }
// MsgmenuItem 菜单消息的菜单按钮 //MsgmenuItem 菜单消息的菜单按钮
type MsgmenuItem struct { type MsgmenuItem struct {
ID string `json:"id"` ID string `json:"id"`
Content string `json:"content"` Content string `json:"content"`
} }
// MediaWxcard 卡券的id //MediaWxcard 卡券的id
type MediaWxcard struct { type MediaWxcard struct {
CardID string `json:"card_id"` CardID string `json:"card_id"`
} }
// MediaMiniprogrampage 小程序消息 //MediaMiniprogrampage 小程序消息
type MediaMiniprogrampage struct { type MediaMiniprogrampage struct {
Title string `json:"title"` Title string `json:"title"`
AppID string `json:"appid"` AppID string `json:"appid"`
@@ -153,7 +153,7 @@ type MediaMiniprogrampage struct {
ThumbMediaID string `json:"thumb_media_id"` ThumbMediaID string `json:"thumb_media_id"`
} }
// Send 发送客服消息 //Send 发送客服消息
func (manager *Manager) Send(msg *CustomerMessage) error { func (manager *Manager) Send(msg *CustomerMessage) error {
accessToken, err := manager.Context.GetAccessToken() accessToken, err := manager.Context.GetAccessToken()
if err != nil { if err != nil {

View File

@@ -1,6 +1,6 @@
package message package message
// Image 图片消息 //Image 图片消息
type Image struct { type Image struct {
CommonToken CommonToken
@@ -9,7 +9,7 @@ type Image struct {
} `xml:"Image"` } `xml:"Image"`
} }
// NewImage 回复图片消息 //NewImage 回复图片消息
func NewImage(mediaID string) *Image { func NewImage(mediaID string) *Image {
image := new(Image) image := new(Image)
image.Image.MediaID = mediaID image.Image.MediaID = mediaID

View File

@@ -4,7 +4,6 @@ import (
"encoding/xml" "encoding/xml"
"github.com/silenceper/wechat/v2/officialaccount/device" "github.com/silenceper/wechat/v2/officialaccount/device"
"github.com/silenceper/wechat/v2/officialaccount/freepublish"
) )
// MsgType 基本消息类型 // MsgType 基本消息类型
@@ -17,93 +16,85 @@ type EventType string
type InfoType string type InfoType string
const ( const (
// MsgTypeText 表示文本消息 //MsgTypeText 表示文本消息
MsgTypeText MsgType = "text" MsgTypeText MsgType = "text"
// MsgTypeImage 表示图片消息 //MsgTypeImage 表示图片消息
MsgTypeImage MsgType = "image" MsgTypeImage = "image"
// MsgTypeVoice 表示语音消息 //MsgTypeVoice 表示语音消息
MsgTypeVoice MsgType = "voice" MsgTypeVoice = "voice"
// MsgTypeVideo 表示视频消息 //MsgTypeVideo 表示视频消息
MsgTypeVideo MsgType = "video" MsgTypeVideo = "video"
// MsgTypeMiniprogrampage 表示小程序卡片消息 //MsgTypeMiniprogrampage 表示小程序卡片消息
MsgTypeMiniprogrampage MsgType = "miniprogrampage" MsgTypeMiniprogrampage = "miniprogrampage"
// MsgTypeShortVideo 表示短视频消息[限接收] //MsgTypeShortVideo 表示短视频消息[限接收]
MsgTypeShortVideo MsgType = "shortvideo" MsgTypeShortVideo = "shortvideo"
// MsgTypeLocation 表示坐标消息[限接收] //MsgTypeLocation 表示坐标消息[限接收]
MsgTypeLocation MsgType = "location" MsgTypeLocation = "location"
// MsgTypeLink 表示链接消息[限接收] //MsgTypeLink 表示链接消息[限接收]
MsgTypeLink MsgType = "link" MsgTypeLink = "link"
// MsgTypeMusic 表示音乐消息[限回复] //MsgTypeMusic 表示音乐消息[限回复]
MsgTypeMusic MsgType = "music" MsgTypeMusic = "music"
// MsgTypeNews 表示图文消息[限回复] //MsgTypeNews 表示图文消息[限回复]
MsgTypeNews MsgType = "news" MsgTypeNews = "news"
// MsgTypeTransfer 表示消息消息转发到客服 //MsgTypeTransfer 表示消息消息转发到客服
MsgTypeTransfer MsgType = "transfer_customer_service" MsgTypeTransfer = "transfer_customer_service"
// MsgTypeEvent 表示事件推送消息 //MsgTypeEvent 表示事件推送消息
MsgTypeEvent MsgType = "event" MsgTypeEvent = "event"
) )
const ( const (
// EventSubscribe 订阅 //EventSubscribe 订阅
EventSubscribe EventType = "subscribe" EventSubscribe EventType = "subscribe"
// EventUnsubscribe 取消订阅 //EventUnsubscribe 取消订阅
EventUnsubscribe EventType = "unsubscribe" EventUnsubscribe = "unsubscribe"
// EventScan 用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者 //EventScan 用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者
EventScan EventType = "SCAN" EventScan = "SCAN"
// EventLocation 上报地理位置事件 //EventLocation 上报地理位置事件
EventLocation EventType = "LOCATION" EventLocation = "LOCATION"
// EventClick 点击菜单拉取消息时的事件推送 //EventClick 点击菜单拉取消息时的事件推送
EventClick EventType = "CLICK" EventClick = "CLICK"
// EventView 点击菜单跳转链接时的事件推送 //EventView 点击菜单跳转链接时的事件推送
EventView EventType = "VIEW" EventView = "VIEW"
// EventScancodePush 扫码推事件的事件推送 //EventScancodePush 扫码推事件的事件推送
EventScancodePush EventType = "scancode_push" EventScancodePush = "scancode_push"
// EventScancodeWaitmsg 扫码推事件且弹出“消息接收中”提示框的事件推送 //EventScancodeWaitmsg 扫码推事件且弹出“消息接收中”提示框的事件推送
EventScancodeWaitmsg EventType = "scancode_waitmsg" EventScancodeWaitmsg = "scancode_waitmsg"
// EventPicSysphoto 弹出系统拍照发图的事件推送 //EventPicSysphoto 弹出系统拍照发图的事件推送
EventPicSysphoto EventType = "pic_sysphoto" EventPicSysphoto = "pic_sysphoto"
// EventPicPhotoOrAlbum 弹出拍照或者相册发图的事件推送 //EventPicPhotoOrAlbum 弹出拍照或者相册发图的事件推送
EventPicPhotoOrAlbum EventType = "pic_photo_or_album" EventPicPhotoOrAlbum = "pic_photo_or_album"
// EventPicWeixin 弹出微信相册发图器的事件推送 //EventPicWeixin 弹出微信相册发图器的事件推送
EventPicWeixin EventType = "pic_weixin" EventPicWeixin = "pic_weixin"
// EventLocationSelect 弹出地理位置选择器的事件推送 //EventLocationSelect 弹出地理位置选择器的事件推送
EventLocationSelect EventType = "location_select" EventLocationSelect = "location_select"
// EventViewMiniprogram 点击菜单跳转小程序的事件推送 //EventTemplateSendJobFinish 发送模板消息推送通知
EventViewMiniprogram EventType = "view_miniprogram" EventTemplateSendJobFinish = "TEMPLATESENDJOBFINISH"
// EventTemplateSendJobFinish 发送模板消息推送通知 //EventMassSendJobFinish 发消息推送通知
EventTemplateSendJobFinish EventType = "TEMPLATESENDJOBFINISH" EventMassSendJobFinish = "MASSSENDJOBFINISH"
// EventMassSendJobFinish 群发消息推送通知 //EventWxaMediaCheck 异步校验图片/音频是否含有违法违规内容推送事件
EventMassSendJobFinish EventType = "MASSSENDJOBFINISH" EventWxaMediaCheck = "wxa_media_check"
// EventWxaMediaCheck 异步校验图片/音频是否含有违法违规内容推送事件
EventWxaMediaCheck EventType = "wxa_media_check"
// EventSubscribeMsgPopupEvent 订阅通知事件推送
EventSubscribeMsgPopupEvent EventType = "subscribe_msg_popup_event"
// EventPublishJobFinish 发布任务完成
EventPublishJobFinish EventType = "PUBLISHJOBFINISH"
) )
const ( const (
// 微信开放平台需要用到 //微信开放平台需要用到
// InfoTypeVerifyTicket 返回ticket // InfoTypeVerifyTicket 返回ticket
InfoTypeVerifyTicket InfoType = "component_verify_ticket" InfoTypeVerifyTicket InfoType = "component_verify_ticket"
// InfoTypeAuthorized 授权 // InfoTypeAuthorized 授权
InfoTypeAuthorized InfoType = "authorized" InfoTypeAuthorized = "authorized"
// InfoTypeUnauthorized 取消授权 // InfoTypeUnauthorized 取消授权
InfoTypeUnauthorized InfoType = "unauthorized" InfoTypeUnauthorized = "unauthorized"
// InfoTypeUpdateAuthorized 更新授权 // InfoTypeUpdateAuthorized 更新授权
InfoTypeUpdateAuthorized InfoType = "updateauthorized" InfoTypeUpdateAuthorized = "updateauthorized"
// InfoTypeNotifyThirdFasterRegister 注册审核事件推送
InfoTypeNotifyThirdFasterRegister InfoType = "notify_third_fasteregister"
) )
// MixMessage 存放所有微信发送过来的消息和事件 //MixMessage 存放所有微信发送过来的消息和事件
type MixMessage struct { type MixMessage struct {
CommonToken CommonToken
// 基本消息 //基本消息
MsgID int64 `xml:"MsgId"` // 其他消息推送过来是MsgId MsgID int64 `xml:"MsgId"` //其他消息推送过来是MsgId
TemplateMsgID int64 `xml:"MsgID"` // 模板消息推送成功的消息是MsgID TemplateMsgID int64 `xml:"MsgID"` //模板消息推送成功的消息是MsgID
Content string `xml:"Content"` Content string `xml:"Content"`
Recognition string `xml:"Recognition"` Recognition string `xml:"Recognition"`
PicURL string `xml:"PicUrl"` PicURL string `xml:"PicUrl"`
@@ -118,8 +109,8 @@ type MixMessage struct {
Description string `xml:"Description"` Description string `xml:"Description"`
URL string `xml:"Url"` URL string `xml:"Url"`
// 事件相关 //事件相关
Event EventType `xml:"Event" json:"Event"` Event EventType `xml:"Event"`
EventKey string `xml:"EventKey"` EventKey string `xml:"EventKey"`
Ticket string `xml:"Ticket"` Ticket string `xml:"Ticket"`
Latitude string `xml:"Latitude"` Latitude string `xml:"Latitude"`
@@ -151,27 +142,6 @@ type MixMessage struct {
Poiname string `xml:"Poiname"` Poiname string `xml:"Poiname"`
} }
subscribeMsgPopupEventList []SubscribeMsgPopupEvent `json:"-"`
SubscribeMsgPopupEvent []struct {
List SubscribeMsgPopupEvent `xml:"List"`
} `xml:"SubscribeMsgPopupEvent"`
// 事件相关:发布能力
PublishEventInfo struct {
PublishID int64 `xml:"publish_id"` // 发布任务id
PublishStatus freepublish.PublishStatus `xml:"publish_status"` // 发布状态
ArticleID string `xml:"article_id"` // 当发布状态为0时即成功返回图文的 article_id可用于“客服消息”场景
ArticleDetail struct {
Count uint `xml:"count"` // 文章数量
Item []struct {
Index uint `xml:"idx"` // 文章对应的编号
ArticleURL string `xml:"article_url"` // 图文的永久链接
} `xml:"item"`
} `xml:"article_detail"` // 当发布状态为0时即成功返回内容
FailIndex []uint `xml:"fail_idx"` // 当发布状态为2或4时返回不通过的文章编号第一篇为 1其他发布状态则为空
} `xml:"PublishEventInfo"`
// 第三方平台相关 // 第三方平台相关
InfoType InfoType `xml:"InfoType"` InfoType InfoType `xml:"InfoType"`
AppID string `xml:"AppId"` AppID string `xml:"AppId"`
@@ -180,15 +150,6 @@ type MixMessage struct {
AuthorizationCode string `xml:"AuthorizationCode"` AuthorizationCode string `xml:"AuthorizationCode"`
AuthorizationCodeExpiredTime int64 `xml:"AuthorizationCodeExpiredTime"` AuthorizationCodeExpiredTime int64 `xml:"AuthorizationCodeExpiredTime"`
PreAuthCode string `xml:"PreAuthCode"` PreAuthCode string `xml:"PreAuthCode"`
AuthCode string `xml:"auth_code"`
Info struct {
Name string `xml:"name"`
Code string `xml:"code"`
CodeType int `xml:"code_type"`
LegalPersonaWechat string `xml:"legal_persona_wechat"`
LegalPersonaName string `xml:"legal_persona_name"`
ComponentPhone string `xml:"component_phone"`
} `xml:"info"`
// 卡券相关 // 卡券相关
CardID string `xml:"CardId"` CardID string `xml:"CardId"`
@@ -207,47 +168,23 @@ type MixMessage struct {
TraceID string `xml:"trace_id"` TraceID string `xml:"trace_id"`
StatusCode int `xml:"status_code"` StatusCode int `xml:"status_code"`
// 设备相关 //设备相关
device.MsgDevice device.MsgDevice
} }
// SubscribeMsgPopupEvent 订阅通知事件推送的消息体 //EventPic 发图事件推送
type SubscribeMsgPopupEvent struct {
TemplateID string `xml:"TemplateId" json:"TemplateId"`
SubscribeStatusString string `xml:"SubscribeStatusString" json:"SubscribeStatusString"`
PopupScene int `xml:"PopupScene" json:"PopupScene,string"`
}
// SetSubscribeMsgPopupEvents 设置订阅消息事件
func (s *MixMessage) SetSubscribeMsgPopupEvents(list []SubscribeMsgPopupEvent) {
s.subscribeMsgPopupEventList = list
}
// GetSubscribeMsgPopupEvents 获取订阅消息事件数据
func (s *MixMessage) GetSubscribeMsgPopupEvents() []SubscribeMsgPopupEvent {
if s.subscribeMsgPopupEventList != nil {
return s.subscribeMsgPopupEventList
}
list := make([]SubscribeMsgPopupEvent, len(s.SubscribeMsgPopupEvent))
for i, item := range s.SubscribeMsgPopupEvent {
list[i] = item.List
}
return list
}
// EventPic 发图事件推送
type EventPic struct { type EventPic struct {
PicMd5Sum string `xml:"PicMd5Sum"` PicMd5Sum string `xml:"PicMd5Sum"`
} }
// EncryptedXMLMsg 安全模式下的消息体 //EncryptedXMLMsg 安全模式下的消息体
type EncryptedXMLMsg struct { type EncryptedXMLMsg struct {
XMLName struct{} `xml:"xml" json:"-"` XMLName struct{} `xml:"xml" json:"-"`
ToUserName string `xml:"ToUserName" json:"ToUserName"` ToUserName string `xml:"ToUserName" json:"ToUserName"`
EncryptedMsg string `xml:"Encrypt" json:"Encrypt"` EncryptedMsg string `xml:"Encrypt" json:"Encrypt"`
} }
// ResponseEncryptedXMLMsg 需要返回的消息体 //ResponseEncryptedXMLMsg 需要返回的消息体
type ResponseEncryptedXMLMsg struct { type ResponseEncryptedXMLMsg struct {
XMLName struct{} `xml:"xml" json:"-"` XMLName struct{} `xml:"xml" json:"-"`
EncryptedMsg string `xml:"Encrypt" json:"Encrypt"` EncryptedMsg string `xml:"Encrypt" json:"Encrypt"`
@@ -269,33 +206,33 @@ func (c CDATA) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
// CommonToken 消息中通用的结构 // CommonToken 消息中通用的结构
type CommonToken struct { type CommonToken struct {
XMLName xml.Name `xml:"xml"` XMLName xml.Name `xml:"xml"`
ToUserName CDATA `xml:"ToUserName" json:"ToUserName"` ToUserName CDATA `xml:"ToUserName"`
FromUserName CDATA `xml:"FromUserName" json:"FromUserName"` FromUserName CDATA `xml:"FromUserName"`
CreateTime int64 `xml:"CreateTime" json:"CreateTime"` CreateTime int64 `xml:"CreateTime"`
MsgType MsgType `xml:"MsgType" json:"MsgType"` MsgType MsgType `xml:"MsgType"`
} }
// SetToUserName set ToUserName //SetToUserName set ToUserName
func (msg *CommonToken) SetToUserName(toUserName CDATA) { func (msg *CommonToken) SetToUserName(toUserName CDATA) {
msg.ToUserName = toUserName msg.ToUserName = toUserName
} }
// SetFromUserName set FromUserName //SetFromUserName set FromUserName
func (msg *CommonToken) SetFromUserName(fromUserName CDATA) { func (msg *CommonToken) SetFromUserName(fromUserName CDATA) {
msg.FromUserName = fromUserName msg.FromUserName = fromUserName
} }
// SetCreateTime set createTime //SetCreateTime set createTime
func (msg *CommonToken) SetCreateTime(createTime int64) { func (msg *CommonToken) SetCreateTime(createTime int64) {
msg.CreateTime = createTime msg.CreateTime = createTime
} }
// SetMsgType set MsgType //SetMsgType set MsgType
func (msg *CommonToken) SetMsgType(msgType MsgType) { func (msg *CommonToken) SetMsgType(msgType MsgType) {
msg.MsgType = msgType msg.MsgType = msgType
} }
// GetOpenID get the FromUserName value //GetOpenID get the FromUserName value
func (msg *CommonToken) GetOpenID() string { func (msg *CommonToken) GetOpenID() string {
return string(msg.FromUserName) return string(msg.FromUserName)
} }

View File

@@ -1,6 +1,6 @@
package message package message
// Music 音乐消息 //Music 音乐消息
type Music struct { type Music struct {
CommonToken CommonToken
@@ -13,7 +13,7 @@ type Music struct {
} `xml:"Music"` } `xml:"Music"`
} }
// NewMusic 回复音乐消息 //NewMusic 回复音乐消息
func NewMusic(title, description, musicURL, hQMusicURL, thumbMediaID string) *Music { func NewMusic(title, description, musicURL, hQMusicURL, thumbMediaID string) *Music {
music := new(Music) music := new(Music)
music.Music.Title = title music.Music.Title = title

View File

@@ -1,6 +1,6 @@
package message package message
// News 图文消息 //News 图文消息
type News struct { type News struct {
CommonToken CommonToken
@@ -8,7 +8,7 @@ type News struct {
Articles []*Article `xml:"Articles>item,omitempty"` Articles []*Article `xml:"Articles>item,omitempty"`
} }
// NewNews 初始化图文消息 //NewNews 初始化图文消息
func NewNews(articles []*Article) *News { func NewNews(articles []*Article) *News {
news := new(News) news := new(News)
news.ArticleCount = len(articles) news.ArticleCount = len(articles)
@@ -16,7 +16,7 @@ func NewNews(articles []*Article) *News {
return news return news
} }
// Article 单篇文章 //Article 单篇文章
type Article struct { type Article struct {
Title string `xml:"Title,omitempty"` Title string `xml:"Title,omitempty"`
Description string `xml:"Description,omitempty"` Description string `xml:"Description,omitempty"`
@@ -24,7 +24,7 @@ type Article struct {
URL string `xml:"Url,omitempty"` URL string `xml:"Url,omitempty"`
} }
// NewArticle 初始化文章 //NewArticle 初始化文章
func NewArticle(title, description, picURL, url string) *Article { func NewArticle(title, description, picURL, url string) *Article {
article := new(Article) article := new(Article)
article.Title = title article.Title = title

View File

@@ -2,13 +2,13 @@ package message
import "errors" import "errors"
// ErrInvalidReply 无效的回复 //ErrInvalidReply 无效的回复
var ErrInvalidReply = errors.New("无效的回复消息") var ErrInvalidReply = errors.New("无效的回复消息")
// ErrUnsupportReply 不支持的回复类型 //ErrUnsupportReply 不支持的回复类型
var ErrUnsupportReply = errors.New("不支持的回复消息") var ErrUnsupportReply = errors.New("不支持的回复消息")
// Reply 消息回复 //Reply 消息回复
type Reply struct { type Reply struct {
MsgType MsgType MsgType MsgType
MsgData interface{} MsgData interface{}

View File

@@ -8,45 +8,42 @@ import (
) )
const ( const (
subscribeSendURL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/bizsend" subscribeSendURL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/bizsend"
subscribeTemplateListURL = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate" subscribeTemplateListURL = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate"
subscribeTemplateAddURL = "https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate" subscribeTemplateAddURL = "https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate"
subscribeTemplateDelURL = "https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate" subscribeTemplateDelURL = "https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate"
subscribeTemplateGetCategoryURL = "https://api.weixin.qq.com/wxaapi/newtmpl/getcategory"
subscribeTemplateGetPubTplKeyWorksURL = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatekeywords"
subscribeTemplateGetPubTplTitles = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatetitles"
) )
// Subscribe 订阅消息 //Subscribe 订阅消息
type Subscribe struct { type Subscribe struct {
*context.Context *context.Context
} }
// NewSubscribe 实例化 //NewSubscribe 实例化
func NewSubscribe(context *context.Context) *Subscribe { func NewSubscribe(context *context.Context) *Subscribe {
tpl := new(Subscribe) tpl := new(Subscribe)
tpl.Context = context tpl.Context = context
return tpl return tpl
} }
// SubscribeMessage 发送的订阅消息内容 //SubscribeMessage 发送的订阅消息内容
type SubscribeMessage struct { type SubscribeMessage struct {
ToUser string `json:"touser"` // 必须, 接受者OpenID ToUser string `json:"touser"` // 必须, 接受者OpenID
TemplateID string `json:"template_id"` // 必须, 模版ID TemplateID string `json:"template_id"` // 必须, 模版ID
Page string `json:"page,omitempty"` // 可选, 跳转网页时填写 Page string `json:"page,omitempty"` // 可选, 跳转网页时填写
Data map[string]*SubscribeDataItem `json:"data"` // 必须, 模板数据 Data map[string]*SubscribeDataItem `json:"data"` // 必须, 模板数据
MiniProgram struct { MiniProgram struct {
AppID string `json:"appid"` // 所需跳转到的小程序appid该小程序appid必须与发模板消息的公众号是绑定关联关系 AppID string `json:"appid"` //所需跳转到的小程序appid该小程序appid必须与发模板消息的公众号是绑定关联关系
PagePath string `json:"pagepath"` // 所需跳转到小程序的具体页面路径,支持带参数,示例index?foo=bar PagePath string `json:"pagepath"` //所需跳转到小程序的具体页面路径,支持带参数,示例index?foo=bar
} `json:"miniprogram"` // 可选,跳转至小程序地址 } `json:"miniprogram"` //可选,跳转至小程序地址
} }
// SubscribeDataItem 模版内某个 .DATA 的值 //SubscribeDataItem 模版内某个 .DATA 的值
type SubscribeDataItem struct { type SubscribeDataItem struct {
Value string `json:"value"` Value string `json:"value"`
} }
// Send 发送订阅消息 //Send 发送订阅消息
func (tpl *Subscribe) Send(msg *SubscribeMessage) (err error) { func (tpl *Subscribe) Send(msg *SubscribeMessage) (err error) {
var accessToken string var accessToken string
accessToken, err = tpl.GetAccessToken() accessToken, err = tpl.GetAccessToken()
@@ -64,10 +61,10 @@ func (tpl *Subscribe) Send(msg *SubscribeMessage) (err error) {
// PrivateSubscribeItem 私有订阅消息模板 // PrivateSubscribeItem 私有订阅消息模板
type PrivateSubscribeItem struct { type PrivateSubscribeItem struct {
PriTmplID string `json:"priTmplId"` // 添加至帐号下的模板 id发送订阅通知时所需 PriTmplID string `json:"priTmplId"` // 添加至帐号下的模板 id发送订阅通知时所需
Title string `json:"title"` // 模版标题 Title string `json:"title"` //模版标题
Content string `json:"content"` // 模版内容 Content string `json:"content"` //模版内容
Example string `json:"example"` // 模板内容示例 Example string `json:"example"` //模板内容示例
SubType int `json:"type"` // 模版类型2 为一次性订阅3 为长期订阅 SubType int `json:"type"` //模版类型2 为一次性订阅3 为长期订阅
} }
type resPrivateSubscribeList struct { type resPrivateSubscribeList struct {
@@ -75,7 +72,7 @@ type resPrivateSubscribeList struct {
SubscriptionList []*PrivateSubscribeItem `json:"data"` SubscriptionList []*PrivateSubscribeItem `json:"data"`
} }
// List 获取私有订阅消息模板列表 //List 获取私有订阅消息模板列表
func (tpl *Subscribe) List() (templateList []*PrivateSubscribeItem, err error) { func (tpl *Subscribe) List() (templateList []*PrivateSubscribeItem, err error) {
var accessToken string var accessToken string
accessToken, err = tpl.GetAccessToken() accessToken, err = tpl.GetAccessToken()
@@ -148,108 +145,3 @@ func (tpl *Subscribe) Delete(templateID string) (err error) {
} }
return util.DecodeWithCommonError(response, "DeleteSubscribe") return util.DecodeWithCommonError(response, "DeleteSubscribe")
} }
// PublicTemplateCategory 公众号类目
type PublicTemplateCategory struct {
ID int `json:"id"` // 类目ID
Name string `json:"name"` // 类目的中文名
}
type resSubscribeCategoryList struct {
util.CommonError
CategoryList []*PublicTemplateCategory `json:"data"`
}
// GetCategory 获取公众号类目
func (tpl *Subscribe) GetCategory() (categoryList []*PublicTemplateCategory, err error) {
var accessToken string
accessToken, err = tpl.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", subscribeTemplateGetCategoryURL, accessToken)
var response []byte
response, err = util.HTTPGet(uri)
if err != nil {
return
}
var result resSubscribeCategoryList
err = util.DecodeWithError(response, &result, "GetCategory")
if err != nil {
return
}
categoryList = result.CategoryList
return
}
// PublicTemplateKeyWords 模板中的关键词
type PublicTemplateKeyWords struct {
KeyWordsID int `json:"kid"` // 关键词 id
Name string `json:"name"` // 关键词内容
Example string `json:"example"` // 关键词内容对应的示例
Rule string `json:"rule"` // 参数类型
}
type resPublicTemplateKeyWordsList struct {
util.CommonError
KeyWordsList []*PublicTemplateKeyWords `json:"data"` // 关键词列表
}
// GetPubTplKeyWordsByID 获取模板中的关键词
func (tpl *Subscribe) GetPubTplKeyWordsByID(titleID string) (keyWordsList []*PublicTemplateKeyWords, err error) {
var accessToken string
accessToken, err = tpl.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s&tid=%s", subscribeTemplateGetPubTplKeyWorksURL, accessToken, titleID)
var response []byte
response, err = util.HTTPGet(uri)
if err != nil {
return
}
var result resPublicTemplateKeyWordsList
err = util.DecodeWithError(response, &result, "GetPublicTemplateKeyWords")
if err != nil {
return
}
keyWordsList = result.KeyWordsList
return
}
// PublicTemplateTitle 类目下的公共模板
type PublicTemplateTitle struct {
TitleID int `json:"tid"` // 模版标题 id
Title string `json:"title"` // 模版标题
Type int `json:"type"` // 模版类型2 为一次性订阅3 为长期订阅
CategoryID string `json:"categoryId"` // 模版所属类目 id
}
type resPublicTemplateTitleList struct {
util.CommonError
Count int `json:"count"` // 公共模板列表总数
TemplateTitleList []*PublicTemplateTitle `json:"data"` // 模板标题列表
}
// GetPublicTemplateTitleList 获取类目下的公共模板
func (tpl *Subscribe) GetPublicTemplateTitleList(ids string, start int, limit int) (count int, templateTitleList []*PublicTemplateTitle, err error) {
var accessToken string
accessToken, err = tpl.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s&ids=%s&start=%d&limit=%d", subscribeTemplateGetPubTplTitles, accessToken, ids, start, limit)
var response []byte
response, err = util.HTTPGet(uri)
if err != nil {
return
}
var result resPublicTemplateTitleList
err = util.DecodeWithError(response, &result, "GetPublicTemplateTitle")
if err != nil {
return
}
count = result.Count
templateTitleList = result.TemplateTitleList
return
}

View File

@@ -15,19 +15,19 @@ const (
templateDelURL = "https://api.weixin.qq.com/cgi-bin/template/del_private_template" templateDelURL = "https://api.weixin.qq.com/cgi-bin/template/del_private_template"
) )
// Template 模板消息 //Template 模板消息
type Template struct { type Template struct {
*context.Context *context.Context
} }
// NewTemplate 实例化 //NewTemplate 实例化
func NewTemplate(context *context.Context) *Template { func NewTemplate(context *context.Context) *Template {
tpl := new(Template) tpl := new(Template)
tpl.Context = context tpl.Context = context
return tpl return tpl
} }
// TemplateMessage 发送的模板消息内容 //TemplateMessage 发送的模板消息内容
type TemplateMessage struct { type TemplateMessage struct {
ToUser string `json:"touser"` // 必须, 接受者OpenID ToUser string `json:"touser"` // 必须, 接受者OpenID
TemplateID string `json:"template_id"` // 必须, 模版ID TemplateID string `json:"template_id"` // 必须, 模版ID
@@ -36,12 +36,12 @@ type TemplateMessage struct {
Data map[string]*TemplateDataItem `json:"data"` // 必须, 模板数据 Data map[string]*TemplateDataItem `json:"data"` // 必须, 模板数据
MiniProgram struct { MiniProgram struct {
AppID string `json:"appid"` // 所需跳转到的小程序appid该小程序appid必须与发模板消息的公众号是绑定关联关系 AppID string `json:"appid"` //所需跳转到的小程序appid该小程序appid必须与发模板消息的公众号是绑定关联关系
PagePath string `json:"pagepath"` // 所需跳转到小程序的具体页面路径,支持带参数,示例index?foo=bar PagePath string `json:"pagepath"` //所需跳转到小程序的具体页面路径,支持带参数,示例index?foo=bar
} `json:"miniprogram"` // 可选,跳转至小程序地址 } `json:"miniprogram"` //可选,跳转至小程序地址
} }
// TemplateDataItem 模版内某个 .DATA 的值 //TemplateDataItem 模版内某个 .DATA 的值
type TemplateDataItem struct { type TemplateDataItem struct {
Value string `json:"value"` Value string `json:"value"`
Color string `json:"color,omitempty"` Color string `json:"color,omitempty"`
@@ -53,7 +53,7 @@ type resTemplateSend struct {
MsgID int64 `json:"msgid"` MsgID int64 `json:"msgid"`
} }
// Send 发送模板消息 //Send 发送模板消息
func (tpl *Template) Send(msg *TemplateMessage) (msgID int64, err error) { func (tpl *Template) Send(msg *TemplateMessage) (msgID int64, err error) {
var accessToken string var accessToken string
accessToken, err = tpl.GetAccessToken() accessToken, err = tpl.GetAccessToken()
@@ -95,7 +95,7 @@ type resTemplateList struct {
TemplateList []*TemplateItem `json:"template_list"` TemplateList []*TemplateItem `json:"template_list"`
} }
// List 获取模板列表 //List 获取模板列表
func (tpl *Template) List() (templateList []*TemplateItem, err error) { func (tpl *Template) List() (templateList []*TemplateItem, err error) {
var accessToken string var accessToken string
accessToken, err = tpl.GetAccessToken() accessToken, err = tpl.GetAccessToken()

View File

@@ -1,12 +1,12 @@
package message package message
// Text 文本消息 //Text 文本消息
type Text struct { type Text struct {
CommonToken CommonToken
Content CDATA `xml:"Content"` Content CDATA `xml:"Content"`
} }
// NewText 初始化文本消息 //NewText 初始化文本消息
func NewText(content string) *Text { func NewText(content string) *Text {
text := new(Text) text := new(Text)
text.Content = CDATA(content) text.Content = CDATA(content)

View File

@@ -1,18 +1,18 @@
package message package message
// TransferCustomer 转发客服消息 //TransferCustomer 转发客服消息
type TransferCustomer struct { type TransferCustomer struct {
CommonToken CommonToken
TransInfo *TransInfo `xml:"TransInfo,omitempty"` TransInfo *TransInfo `xml:"TransInfo,omitempty"`
} }
// TransInfo 转发到指定客服 //TransInfo 转发到指定客服
type TransInfo struct { type TransInfo struct {
KfAccount string `xml:"KfAccount"` KfAccount string `xml:"KfAccount"`
} }
// NewTransferCustomer 实例化 //NewTransferCustomer 实例化
func NewTransferCustomer(kfAccount string) *TransferCustomer { func NewTransferCustomer(kfAccount string) *TransferCustomer {
tc := new(TransferCustomer) tc := new(TransferCustomer)
if kfAccount != "" { if kfAccount != "" {

View File

@@ -1,6 +1,6 @@
package message package message
// Video 视频消息 //Video 视频消息
type Video struct { type Video struct {
CommonToken CommonToken
@@ -11,7 +11,7 @@ type Video struct {
} `xml:"Video"` } `xml:"Video"`
} }
// NewVideo 回复图片消息 //NewVideo 回复图片消息
func NewVideo(mediaID, title, description string) *Video { func NewVideo(mediaID, title, description string) *Video {
video := new(Video) video := new(Video)
video.Video.MediaID = mediaID video.Video.MediaID = mediaID

View File

@@ -1,6 +1,6 @@
package message package message
// Voice 语音消息 //Voice 语音消息
type Voice struct { type Voice struct {
CommonToken CommonToken
@@ -9,7 +9,7 @@ type Voice struct {
} `xml:"Voice"` } `xml:"Voice"`
} }
// NewVoice 回复语音消息 //NewVoice 回复语音消息
func NewVoice(mediaID string) *Voice { func NewVoice(mediaID string) *Voice {
voice := new(Voice) voice := new(Voice)
voice.Voice.MediaID = mediaID voice.Voice.MediaID = mediaID

View File

@@ -19,32 +19,32 @@ const (
checkAccessTokenURL = "https://api.weixin.qq.com/sns/auth?access_token=%s&openid=%s" checkAccessTokenURL = "https://api.weixin.qq.com/sns/auth?access_token=%s&openid=%s"
) )
// Oauth 保存用户授权信息 //Oauth 保存用户授权信息
type Oauth struct { type Oauth struct {
*context.Context *context.Context
} }
// NewOauth 实例化授权信息 //NewOauth 实例化授权信息
func NewOauth(context *context.Context) *Oauth { func NewOauth(context *context.Context) *Oauth {
auth := new(Oauth) auth := new(Oauth)
auth.Context = context auth.Context = context
return auth return auth
} }
// GetRedirectURL 获取跳转的url地址 //GetRedirectURL 获取跳转的url地址
func (oauth *Oauth) GetRedirectURL(redirectURI, scope, state string) (string, error) { func (oauth *Oauth) GetRedirectURL(redirectURI, scope, state string) (string, error) {
// url encode //url encode
urlStr := url.QueryEscape(redirectURI) urlStr := url.QueryEscape(redirectURI)
return fmt.Sprintf(redirectOauthURL, oauth.AppID, urlStr, scope, state), nil return fmt.Sprintf(redirectOauthURL, oauth.AppID, urlStr, scope, state), nil
} }
// GetWebAppRedirectURL 获取网页应用跳转的url地址 //GetWebAppRedirectURL 获取网页应用跳转的url地址
func (oauth *Oauth) GetWebAppRedirectURL(redirectURI, scope, state string) (string, error) { func (oauth *Oauth) GetWebAppRedirectURL(redirectURI, scope, state string) (string, error) {
urlStr := url.QueryEscape(redirectURI) urlStr := url.QueryEscape(redirectURI)
return fmt.Sprintf(webAppRedirectOauthURL, oauth.AppID, urlStr, scope, state), nil return fmt.Sprintf(webAppRedirectOauthURL, oauth.AppID, urlStr, scope, state), nil
} }
// Redirect 跳转到网页授权 //Redirect 跳转到网页授权
func (oauth *Oauth) Redirect(writer http.ResponseWriter, req *http.Request, redirectURI, scope, state string) error { func (oauth *Oauth) Redirect(writer http.ResponseWriter, req *http.Request, redirectURI, scope, state string) error {
location, err := oauth.GetRedirectURL(redirectURI, scope, state) location, err := oauth.GetRedirectURL(redirectURI, scope, state)
if err != nil { if err != nil {
@@ -88,7 +88,7 @@ func (oauth *Oauth) GetUserAccessToken(code string) (result ResAccessToken, err
return return
} }
// RefreshAccessToken 刷新access_token //RefreshAccessToken 刷新access_token
func (oauth *Oauth) RefreshAccessToken(refreshToken string) (result ResAccessToken, err error) { func (oauth *Oauth) RefreshAccessToken(refreshToken string) (result ResAccessToken, err error) {
urlStr := fmt.Sprintf(refreshAccessTokenURL, oauth.AppID, refreshToken) urlStr := fmt.Sprintf(refreshAccessTokenURL, oauth.AppID, refreshToken)
var response []byte var response []byte
@@ -107,7 +107,7 @@ func (oauth *Oauth) RefreshAccessToken(refreshToken string) (result ResAccessTok
return return
} }
// CheckAccessToken 检验access_token是否有效 //CheckAccessToken 检验access_token是否有效
func (oauth *Oauth) CheckAccessToken(accessToken, openID string) (b bool, err error) { func (oauth *Oauth) CheckAccessToken(accessToken, openID string) (b bool, err error) {
urlStr := fmt.Sprintf(checkAccessTokenURL, accessToken, openID) urlStr := fmt.Sprintf(checkAccessTokenURL, accessToken, openID)
var response []byte var response []byte
@@ -128,7 +128,7 @@ func (oauth *Oauth) CheckAccessToken(accessToken, openID string) (b bool, err er
return return
} }
// UserInfo 用户授权获取到用户信息 //UserInfo 用户授权获取到用户信息
type UserInfo struct { type UserInfo struct {
util.CommonError util.CommonError
@@ -143,7 +143,7 @@ type UserInfo struct {
Unionid string `json:"unionid"` Unionid string `json:"unionid"`
} }
// GetUserInfo 如果scope为 snsapi_userinfo 则可以通过此方法获取到用户基本信息 //GetUserInfo 如果scope为 snsapi_userinfo 则可以通过此方法获取到用户基本信息
func (oauth *Oauth) GetUserInfo(accessToken, openID, lang string) (result UserInfo, err error) { func (oauth *Oauth) GetUserInfo(accessToken, openID, lang string) (result UserInfo, err error) {
if lang == "" { if lang == "" {
lang = "zh_CN" lang = "zh_CN"

View File

@@ -18,18 +18,18 @@ const (
ocrPlateNumberURL = "https://api.weixin.qq.com/cv/ocr/platenum" ocrPlateNumberURL = "https://api.weixin.qq.com/cv/ocr/platenum"
) )
// OCR struct //OCR struct
type OCR struct { type OCR struct {
*context.Context *context.Context
} }
// coordinate 坐标 //coordinate 坐标
type coordinate struct { type coordinate struct {
X int64 `json:"x,omitempty"` X int64 `json:"x,omitempty"`
Y int64 `json:"y,omitempty"` Y int64 `json:"y,omitempty"`
} }
// position 位置 //position 位置
type position struct { type position struct {
LeftTop coordinate `json:"left_top"` LeftTop coordinate `json:"left_top"`
RightTop coordinate `json:"right_top"` RightTop coordinate `json:"right_top"`
@@ -37,13 +37,13 @@ type position struct {
LeftBottom coordinate `json:"left_bottom"` LeftBottom coordinate `json:"left_bottom"`
} }
// imageSize 图片尺寸 //imageSize 图片尺寸
type imageSize struct { type imageSize struct {
Width int64 `json:"w,omitempty"` Width int64 `json:"w,omitempty"`
Height int64 `json:"h,omitempty"` Height int64 `json:"h,omitempty"`
} }
// ResDriving 行驶证返回结果 //ResDriving 行驶证返回结果
type ResDriving struct { type ResDriving struct {
util.CommonError util.CommonError
@@ -68,7 +68,7 @@ type ResDriving struct {
ImageSize imageSize `json:"img_size,omitempty"` ImageSize imageSize `json:"img_size,omitempty"`
} }
// ResIDCard 身份证返回结果 //ResIDCard 身份证返回结果
type ResIDCard struct { type ResIDCard struct {
util.CommonError util.CommonError
@@ -81,14 +81,14 @@ type ResIDCard struct {
ValidDate string `json:"valid_date,omitempty"` ValidDate string `json:"valid_date,omitempty"`
} }
// ResBankCard 银行卡返回结果 //ResBankCard 银行卡返回结果
type ResBankCard struct { type ResBankCard struct {
util.CommonError util.CommonError
Number string `json:"number,omitempty"` Number string `json:"number,omitempty"`
} }
// ResDrivingLicense 驾驶证返回结果 //ResDrivingLicense 驾驶证返回结果
type ResDrivingLicense struct { type ResDrivingLicense struct {
util.CommonError util.CommonError
@@ -105,7 +105,7 @@ type ResDrivingLicense struct {
OfficialSeal string `json:"official_seal,omitempty"` OfficialSeal string `json:"official_seal,omitempty"`
} }
// ResBizLicense 营业执照返回结果 //ResBizLicense 营业执照返回结果
type ResBizLicense struct { type ResBizLicense struct {
util.CommonError util.CommonError
@@ -125,7 +125,7 @@ type ResBizLicense struct {
ImageSize imageSize `json:"img_size,omitempty"` ImageSize imageSize `json:"img_size,omitempty"`
} }
// ResCommon 公共印刷品返回结果 //ResCommon 公共印刷品返回结果
type ResCommon struct { type ResCommon struct {
util.CommonError util.CommonError
@@ -133,27 +133,27 @@ type ResCommon struct {
ImageSize imageSize `json:"img_size,omitempty"` ImageSize imageSize `json:"img_size,omitempty"`
} }
// commonItem 公共元素 //commonItem 公共元素
type commonItem struct { type commonItem struct {
Position position `json:"pos"` Position position `json:"pos"`
Text string `json:"text"` Text string `json:"text"`
} }
// ResPlateNumber 车牌号返回结果 //ResPlateNumber 车牌号返回结果
type ResPlateNumber struct { type ResPlateNumber struct {
util.CommonError util.CommonError
Number string `json:"number"` Number string `json:"number"`
} }
// NewOCR 实例 //NewOCR 实例
func NewOCR(c *context.Context) *OCR { func NewOCR(c *context.Context) *OCR {
ocr := new(OCR) ocr := new(OCR)
ocr.Context = c ocr.Context = c
return ocr return ocr
} }
// IDCard 身份证OCR识别接口 //IDCard 身份证OCR识别接口
func (ocr *OCR) IDCard(path string) (ResIDCard ResIDCard, err error) { func (ocr *OCR) IDCard(path string) (ResIDCard ResIDCard, err error) {
accessToken, err := ocr.GetAccessToken() accessToken, err := ocr.GetAccessToken()
if err != nil { if err != nil {
@@ -172,7 +172,7 @@ func (ocr *OCR) IDCard(path string) (ResIDCard ResIDCard, err error) {
return return
} }
// BankCard 银行卡OCR识别接口 //BankCard 银行卡OCR识别接口
func (ocr *OCR) BankCard(path string) (ResBankCard ResBankCard, err error) { func (ocr *OCR) BankCard(path string) (ResBankCard ResBankCard, err error) {
accessToken, err := ocr.GetAccessToken() accessToken, err := ocr.GetAccessToken()
if err != nil { if err != nil {
@@ -191,7 +191,7 @@ func (ocr *OCR) BankCard(path string) (ResBankCard ResBankCard, err error) {
return return
} }
// Driving 行驶证OCR识别接口 //Driving 行驶证OCR识别接口
func (ocr *OCR) Driving(path string) (ResDriving ResDriving, err error) { func (ocr *OCR) Driving(path string) (ResDriving ResDriving, err error) {
accessToken, err := ocr.GetAccessToken() accessToken, err := ocr.GetAccessToken()
if err != nil { if err != nil {
@@ -210,7 +210,7 @@ func (ocr *OCR) Driving(path string) (ResDriving ResDriving, err error) {
return return
} }
// DrivingLicense 驾驶证OCR识别接口 //DrivingLicense 驾驶证OCR识别接口
func (ocr *OCR) DrivingLicense(path string) (ResDrivingLicense ResDrivingLicense, err error) { func (ocr *OCR) DrivingLicense(path string) (ResDrivingLicense ResDrivingLicense, err error) {
accessToken, err := ocr.GetAccessToken() accessToken, err := ocr.GetAccessToken()
if err != nil { if err != nil {
@@ -229,7 +229,7 @@ func (ocr *OCR) DrivingLicense(path string) (ResDrivingLicense ResDrivingLicense
return return
} }
// BizLicense 营业执照OCR识别接口 //BizLicense 营业执照OCR识别接口
func (ocr *OCR) BizLicense(path string) (ResBizLicense ResBizLicense, err error) { func (ocr *OCR) BizLicense(path string) (ResBizLicense ResBizLicense, err error) {
accessToken, err := ocr.GetAccessToken() accessToken, err := ocr.GetAccessToken()
if err != nil { if err != nil {
@@ -248,7 +248,7 @@ func (ocr *OCR) BizLicense(path string) (ResBizLicense ResBizLicense, err error)
return return
} }
// Common 通用印刷体OCR识别接口 //Common 通用印刷体OCR识别接口
func (ocr *OCR) Common(path string) (ResCommon ResCommon, err error) { func (ocr *OCR) Common(path string) (ResCommon ResCommon, err error) {
accessToken, err := ocr.GetAccessToken() accessToken, err := ocr.GetAccessToken()
if err != nil { if err != nil {
@@ -267,7 +267,7 @@ func (ocr *OCR) Common(path string) (ResCommon ResCommon, err error) {
return return
} }
// PlateNumber 车牌OCR识别接口 //PlateNumber 车牌OCR识别接口
func (ocr *OCR) PlateNumber(path string) (ResPlateNumber ResPlateNumber, err error) { func (ocr *OCR) PlateNumber(path string) (ResPlateNumber ResPlateNumber, err error) {
accessToken, err := ocr.GetAccessToken() accessToken, err := ocr.GetAccessToken()
if err != nil { if err != nil {

View File

@@ -1,11 +1,8 @@
package officialaccount package officialaccount
import ( import (
stdcontext "context"
"net/http" "net/http"
"github.com/silenceper/wechat/v2/officialaccount/draft"
"github.com/silenceper/wechat/v2/officialaccount/freepublish"
"github.com/silenceper/wechat/v2/officialaccount/ocr" "github.com/silenceper/wechat/v2/officialaccount/ocr"
"github.com/silenceper/wechat/v2/officialaccount/datacube" "github.com/silenceper/wechat/v2/officialaccount/datacube"
@@ -15,7 +12,6 @@ import (
"github.com/silenceper/wechat/v2/officialaccount/broadcast" "github.com/silenceper/wechat/v2/officialaccount/broadcast"
"github.com/silenceper/wechat/v2/officialaccount/config" "github.com/silenceper/wechat/v2/officialaccount/config"
"github.com/silenceper/wechat/v2/officialaccount/context" "github.com/silenceper/wechat/v2/officialaccount/context"
"github.com/silenceper/wechat/v2/officialaccount/customerservice"
"github.com/silenceper/wechat/v2/officialaccount/device" "github.com/silenceper/wechat/v2/officialaccount/device"
"github.com/silenceper/wechat/v2/officialaccount/js" "github.com/silenceper/wechat/v2/officialaccount/js"
"github.com/silenceper/wechat/v2/officialaccount/material" "github.com/silenceper/wechat/v2/officialaccount/material"
@@ -26,27 +22,12 @@ import (
"github.com/silenceper/wechat/v2/officialaccount/user" "github.com/silenceper/wechat/v2/officialaccount/user"
) )
// OfficialAccount 微信公众号相关API //OfficialAccount 微信公众号相关API
type OfficialAccount struct { type OfficialAccount struct {
ctx *context.Context ctx *context.Context
basic *basic.Basic
menu *menu.Menu
oauth *oauth.Oauth
material *material.Material
draft *draft.Draft
freepublish *freepublish.FreePublish
js *js.Js
user *user.User
templateMsg *message.Template
managerMsg *message.Manager
device *device.Device
broadcast *broadcast.Broadcast
datacube *datacube.DataCube
ocr *ocr.OCR
subscribeMsg *message.Subscribe
} }
// NewOfficialAccount 实例化公众号API //NewOfficialAccount 实例化公众号API
func NewOfficialAccount(cfg *config.Config) *OfficialAccount { func NewOfficialAccount(cfg *config.Config) *OfficialAccount {
defaultAkHandle := credential.NewDefaultAccessToken(cfg.AppID, cfg.AppSecret, credential.CacheKeyOfficialAccountPrefix, cfg.Cache) defaultAkHandle := credential.NewDefaultAccessToken(cfg.AppID, cfg.AppSecret, credential.CacheKeyOfficialAccountPrefix, cfg.Cache)
ctx := &context.Context{ ctx := &context.Context{
@@ -56,7 +37,7 @@ func NewOfficialAccount(cfg *config.Config) *OfficialAccount {
return &OfficialAccount{ctx: ctx} return &OfficialAccount{ctx: ctx}
} }
// SetAccessTokenHandle 自定义access_token获取方式 //SetAccessTokenHandle 自定义access_token获取方式
func (officialAccount *OfficialAccount) SetAccessTokenHandle(accessTokenHandle credential.AccessTokenHandle) { func (officialAccount *OfficialAccount) SetAccessTokenHandle(accessTokenHandle credential.AccessTokenHandle) {
officialAccount.ctx.AccessTokenHandle = accessTokenHandle officialAccount.ctx.AccessTokenHandle = accessTokenHandle
} }
@@ -68,18 +49,12 @@ func (officialAccount *OfficialAccount) GetContext() *context.Context {
// GetBasic qr/url 相关配置 // GetBasic qr/url 相关配置
func (officialAccount *OfficialAccount) GetBasic() *basic.Basic { func (officialAccount *OfficialAccount) GetBasic() *basic.Basic {
if officialAccount.basic == nil { return basic.NewBasic(officialAccount.ctx)
officialAccount.basic = basic.NewBasic(officialAccount.ctx)
}
return officialAccount.basic
} }
// GetMenu 菜单管理接口 // GetMenu 菜单管理接口
func (officialAccount *OfficialAccount) GetMenu() *menu.Menu { func (officialAccount *OfficialAccount) GetMenu() *menu.Menu {
if officialAccount.menu == nil { return menu.NewMenu(officialAccount.ctx)
officialAccount.menu = menu.NewMenu(officialAccount.ctx)
}
return officialAccount.menu
} }
// GetServer 消息管理:接收事件,被动回复消息管理 // GetServer 消息管理:接收事件,被动回复消息管理
@@ -90,125 +65,63 @@ func (officialAccount *OfficialAccount) GetServer(req *http.Request, writer http
return srv return srv
} }
// GetAccessToken 获取access_token //GetAccessToken 获取access_token
func (officialAccount *OfficialAccount) GetAccessToken() (string, error) { func (officialAccount *OfficialAccount) GetAccessToken() (string, error) {
return officialAccount.ctx.GetAccessToken() return officialAccount.ctx.GetAccessToken()
} }
// GetAccessTokenContext 获取access_token
func (officialAccount *OfficialAccount) GetAccessTokenContext(ctx stdcontext.Context) (string, error) {
if c, ok := officialAccount.ctx.AccessTokenHandle.(credential.AccessTokenContextHandle); ok {
return c.GetAccessTokenContext(ctx)
}
return officialAccount.ctx.GetAccessToken()
}
// GetOauth oauth2网页授权 // GetOauth oauth2网页授权
func (officialAccount *OfficialAccount) GetOauth() *oauth.Oauth { func (officialAccount *OfficialAccount) GetOauth() *oauth.Oauth {
if officialAccount.oauth == nil { return oauth.NewOauth(officialAccount.ctx)
officialAccount.oauth = oauth.NewOauth(officialAccount.ctx)
}
return officialAccount.oauth
} }
// GetMaterial 素材管理 // GetMaterial 素材管理
func (officialAccount *OfficialAccount) GetMaterial() *material.Material { func (officialAccount *OfficialAccount) GetMaterial() *material.Material {
if officialAccount.material == nil { return material.NewMaterial(officialAccount.ctx)
officialAccount.material = material.NewMaterial(officialAccount.ctx)
}
return officialAccount.material
}
// GetDraft 草稿箱
func (officialAccount *OfficialAccount) GetDraft() *draft.Draft {
if officialAccount.draft == nil {
officialAccount.draft = draft.NewDraft(officialAccount.ctx)
}
return officialAccount.draft
}
// GetFreePublish 发布能力
func (officialAccount *OfficialAccount) GetFreePublish() *freepublish.FreePublish {
if officialAccount.freepublish == nil {
officialAccount.freepublish = freepublish.NewFreePublish(officialAccount.ctx)
}
return officialAccount.freepublish
} }
// GetJs js-sdk配置 // GetJs js-sdk配置
func (officialAccount *OfficialAccount) GetJs() *js.Js { func (officialAccount *OfficialAccount) GetJs() *js.Js {
if officialAccount.js == nil { return js.NewJs(officialAccount.ctx)
officialAccount.js = js.NewJs(officialAccount.ctx)
}
return officialAccount.js
} }
// GetUser 用户管理接口 // GetUser 用户管理接口
func (officialAccount *OfficialAccount) GetUser() *user.User { func (officialAccount *OfficialAccount) GetUser() *user.User {
if officialAccount.user == nil { return user.NewUser(officialAccount.ctx)
officialAccount.user = user.NewUser(officialAccount.ctx)
}
return officialAccount.user
} }
// GetTemplate 模板消息接口 // GetTemplate 模板消息接口
func (officialAccount *OfficialAccount) GetTemplate() *message.Template { func (officialAccount *OfficialAccount) GetTemplate() *message.Template {
if officialAccount.templateMsg == nil { return message.NewTemplate(officialAccount.ctx)
officialAccount.templateMsg = message.NewTemplate(officialAccount.ctx)
}
return officialAccount.templateMsg
} }
// GetCustomerMessageManager 客服消息接口 // GetCustomerMessageManager 客服消息接口
func (officialAccount *OfficialAccount) GetCustomerMessageManager() *message.Manager { func (officialAccount *OfficialAccount) GetCustomerMessageManager() *message.Manager {
if officialAccount.managerMsg == nil { return message.NewMessageManager(officialAccount.ctx)
officialAccount.managerMsg = message.NewMessageManager(officialAccount.ctx)
}
return officialAccount.managerMsg
} }
// GetDevice 获取智能设备的实例 // GetDevice 获取智能设备的实例
func (officialAccount *OfficialAccount) GetDevice() *device.Device { func (officialAccount *OfficialAccount) GetDevice() *device.Device {
if officialAccount.device == nil { return device.NewDevice(officialAccount.ctx)
officialAccount.device = device.NewDevice(officialAccount.ctx)
}
return officialAccount.device
} }
// GetBroadcast 群发消息 //GetBroadcast 群发消息
// TODO 待完善 //TODO 待完善
func (officialAccount *OfficialAccount) GetBroadcast() *broadcast.Broadcast { func (officialAccount *OfficialAccount) GetBroadcast() *broadcast.Broadcast {
if officialAccount.broadcast == nil { return broadcast.NewBroadcast(officialAccount.ctx)
officialAccount.broadcast = broadcast.NewBroadcast(officialAccount.ctx)
}
return officialAccount.broadcast
} }
// GetDataCube 数据统计 //GetDataCube 数据统计
func (officialAccount *OfficialAccount) GetDataCube() *datacube.DataCube { func (officialAccount *OfficialAccount) GetDataCube() *datacube.DataCube {
if officialAccount.datacube == nil { return datacube.NewCube(officialAccount.ctx)
officialAccount.datacube = datacube.NewCube(officialAccount.ctx)
}
return officialAccount.datacube
} }
// GetOCR OCR接口 //GetOCR OCR接口
func (officialAccount *OfficialAccount) GetOCR() *ocr.OCR { func (officialAccount *OfficialAccount) GetOCR() *ocr.OCR {
if officialAccount.ocr == nil { return ocr.NewOCR(officialAccount.ctx)
officialAccount.ocr = ocr.NewOCR(officialAccount.ctx)
}
return officialAccount.ocr
} }
// GetSubscribe 公众号订阅消息 //GetSubscribe 公众号订阅消息
func (officialAccount *OfficialAccount) GetSubscribe() *message.Subscribe { func (officialAccount *OfficialAccount) GetSubscribe() *message.Subscribe {
if officialAccount.subscribeMsg == nil { return message.NewSubscribe(officialAccount.ctx)
officialAccount.subscribeMsg = message.NewSubscribe(officialAccount.ctx)
}
return officialAccount.subscribeMsg
}
// GetCustomerServiceManager 客服管理
func (officialAccount *OfficialAccount) GetCustomerServiceManager() *customerservice.Manager {
return customerservice.NewCustomerServiceManager(officialAccount.ctx)
} }

View File

@@ -1,26 +1,23 @@
package server package server
import ( import (
"encoding/json"
"encoding/xml" "encoding/xml"
"errors" "errors"
"fmt" "fmt"
"io" "io/ioutil"
"net/http" "net/http"
"reflect" "reflect"
"runtime/debug" "runtime/debug"
"strconv" "strconv"
"strings"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/silenceper/wechat/v2/officialaccount/context" "github.com/silenceper/wechat/v2/officialaccount/context"
"github.com/silenceper/wechat/v2/officialaccount/message" "github.com/silenceper/wechat/v2/officialaccount/message"
log "github.com/sirupsen/logrus"
"github.com/silenceper/wechat/v2/util" "github.com/silenceper/wechat/v2/util"
) )
// Server struct //Server struct
type Server struct { type Server struct {
*context.Context *context.Context
Writer http.ResponseWriter Writer http.ResponseWriter
@@ -37,14 +34,13 @@ type Server struct {
ResponseRawXMLMsg []byte ResponseRawXMLMsg []byte
ResponseMsg interface{} ResponseMsg interface{}
isSafeMode bool isSafeMode bool
isJSONContent bool random []byte
random []byte nonce string
nonce string timestamp int64
timestamp int64
} }
// NewServer init //NewServer init
func NewServer(context *context.Context) *Server { func NewServer(context *context.Context) *Server {
srv := new(Server) srv := new(Server)
srv.Context = context srv.Context = context
@@ -56,7 +52,7 @@ func (srv *Server) SkipValidate(skip bool) {
srv.skipValidate = skip srv.skipValidate = skip
} }
// Serve 处理微信的请求消息 //Serve 处理微信的请求消息
func (srv *Server) Serve() error { func (srv *Server) Serve() error {
if !srv.Validate() { if !srv.Validate() {
log.Error("Validate Signature Failed.") log.Error("Validate Signature Failed.")
@@ -73,19 +69,14 @@ func (srv *Server) Serve() error {
if err != nil { if err != nil {
return err return err
} }
// 非安全模式下请求处理方法返回为nil则直接回复success给微信服务器
if response == nil && !srv.isSafeMode {
srv.String("success")
return nil
}
// debug print request msg //debug print request msg
log.Debugf("request msg =%s", string(srv.RequestRawXMLMsg)) log.Debugf("request msg =%s", string(srv.RequestRawXMLMsg))
return srv.buildResponse(response) return srv.buildResponse(response)
} }
// Validate 校验请求是否合法 //Validate 校验请求是否合法
func (srv *Server) Validate() bool { func (srv *Server) Validate() bool {
if srv.skipValidate { if srv.skipValidate {
return true return true
@@ -97,20 +88,16 @@ func (srv *Server) Validate() bool {
return signature == util.Signature(srv.Token, timestamp, nonce) return signature == util.Signature(srv.Token, timestamp, nonce)
} }
// HandleRequest 处理微信的请求 //HandleRequest 处理微信的请求
func (srv *Server) handleRequest() (reply *message.Reply, err error) { func (srv *Server) handleRequest() (reply *message.Reply, err error) {
// set isSafeMode //set isSafeMode
srv.isSafeMode = false srv.isSafeMode = false
encryptType := srv.Query("encrypt_type") encryptType := srv.Query("encrypt_type")
if encryptType == "aes" { if encryptType == "aes" {
srv.isSafeMode = true srv.isSafeMode = true
} }
// set request contentType //set openID
contentType := srv.Request.Header.Get("Content-Type")
srv.isJSONContent = strings.Contains(contentType, "application/json")
// set openID
srv.openID = srv.Query("openid") srv.openID = srv.Query("openid")
var msg interface{} var msg interface{}
@@ -127,22 +114,22 @@ func (srv *Server) handleRequest() (reply *message.Reply, err error) {
return return
} }
// GetOpenID return openID //GetOpenID return openID
func (srv *Server) GetOpenID() string { func (srv *Server) GetOpenID() string {
return srv.openID return srv.openID
} }
// getMessage 解析微信返回的消息 //getMessage 解析微信返回的消息
func (srv *Server) getMessage() (interface{}, error) { func (srv *Server) getMessage() (interface{}, error) {
var rawXMLMsgBytes []byte var rawXMLMsgBytes []byte
var err error var err error
if srv.isSafeMode { if srv.isSafeMode {
encryptedXMLMsg, dataErr := srv.getEncryptBody() var encryptedXMLMsg message.EncryptedXMLMsg
if dataErr != nil { if err := xml.NewDecoder(srv.Request.Body).Decode(&encryptedXMLMsg); err != nil {
return nil, dataErr return nil, fmt.Errorf("从body中解析xml失败,err=%v", err)
} }
// 验证消息签名 //验证消息签名
timestamp := srv.Query("timestamp") timestamp := srv.Query("timestamp")
srv.timestamp, err = strconv.ParseInt(timestamp, 10, 32) srv.timestamp, err = strconv.ParseInt(timestamp, 10, 32)
if err != nil { if err != nil {
@@ -156,13 +143,13 @@ func (srv *Server) getMessage() (interface{}, error) {
return nil, fmt.Errorf("消息不合法,验证签名失败") return nil, fmt.Errorf("消息不合法,验证签名失败")
} }
// 解密 //解密
srv.random, rawXMLMsgBytes, err = util.DecryptMsg(srv.AppID, encryptedXMLMsg.EncryptedMsg, srv.EncodingAESKey) srv.random, rawXMLMsgBytes, err = util.DecryptMsg(srv.AppID, encryptedXMLMsg.EncryptedMsg, srv.EncodingAESKey)
if err != nil { if err != nil {
return nil, fmt.Errorf("消息解密失败, err=%v", err) return nil, fmt.Errorf("消息解密失败, err=%v", err)
} }
} else { } else {
rawXMLMsgBytes, err = io.ReadAll(srv.Request.Body) rawXMLMsgBytes, err = ioutil.ReadAll(srv.Request.Body)
if err != nil { if err != nil {
return nil, fmt.Errorf("从body中解析xml失败, err=%v", err) return nil, fmt.Errorf("从body中解析xml失败, err=%v", err)
} }
@@ -173,52 +160,13 @@ func (srv *Server) getMessage() (interface{}, error) {
return srv.parseRequestMessage(rawXMLMsgBytes) return srv.parseRequestMessage(rawXMLMsgBytes)
} }
func (srv *Server) getEncryptBody() (*message.EncryptedXMLMsg, error) {
var encryptedXMLMsg = &message.EncryptedXMLMsg{}
if srv.isJSONContent {
if err := json.NewDecoder(srv.Request.Body).Decode(encryptedXMLMsg); err != nil {
return nil, fmt.Errorf("从body中解析json失败,err=%v", err)
}
} else {
if err := xml.NewDecoder(srv.Request.Body).Decode(encryptedXMLMsg); err != nil {
return nil, fmt.Errorf("从body中解析xml失败,err=%v", err)
}
}
return encryptedXMLMsg, nil
}
func (srv *Server) parseRequestMessage(rawXMLMsgBytes []byte) (msg *message.MixMessage, err error) { func (srv *Server) parseRequestMessage(rawXMLMsgBytes []byte) (msg *message.MixMessage, err error) {
msg = &message.MixMessage{} msg = &message.MixMessage{}
if !srv.isJSONContent { err = xml.Unmarshal(rawXMLMsgBytes, msg)
err = xml.Unmarshal(rawXMLMsgBytes, msg)
return
}
// parse json
err = json.Unmarshal(rawXMLMsgBytes, msg)
if err != nil {
return
}
// nonstandard json, 目前小程序订阅消息返回数据格式不标准订阅消息模板单个List返回是对象多个List返回是数组。
if msg.MsgType == message.MsgTypeEvent {
listData := gjson.Get(string(rawXMLMsgBytes), "List")
if listData.IsObject() {
listItem := message.SubscribeMsgPopupEvent{}
if parseErr := json.Unmarshal([]byte(listData.Raw), &listItem); parseErr != nil {
return msg, parseErr
}
msg.SetSubscribeMsgPopupEvents([]message.SubscribeMsgPopupEvent{listItem})
} else if listData.IsArray() {
listItems := make([]message.SubscribeMsgPopupEvent, 0)
if parseErr := json.Unmarshal([]byte(listData.Raw), &listItems); parseErr != nil {
return msg, parseErr
}
msg.SetSubscribeMsgPopupEvents(listItems)
}
}
return return
} }
// SetMessageHandler 设置用户自定义的回调方法 //SetMessageHandler 设置用户自定义的回调方法
func (srv *Server) SetMessageHandler(handler func(*message.MixMessage) *message.Reply) { func (srv *Server) SetMessageHandler(handler func(*message.MixMessage) *message.Reply) {
srv.messageHandler = handler srv.messageHandler = handler
} }
@@ -230,7 +178,7 @@ func (srv *Server) buildResponse(reply *message.Reply) (err error) {
} }
}() }()
if reply == nil { if reply == nil {
// do nothing //do nothing
return nil return nil
} }
msgType := reply.MsgType msgType := reply.MsgType
@@ -249,7 +197,7 @@ func (srv *Server) buildResponse(reply *message.Reply) (err error) {
msgData := reply.MsgData msgData := reply.MsgData
value := reflect.ValueOf(msgData) value := reflect.ValueOf(msgData)
// msgData must be a ptr //msgData must be a ptr
kind := value.Kind().String() kind := value.Kind().String()
if kind != "ptr" { if kind != "ptr" {
return message.ErrUnsupportReply return message.ErrUnsupportReply
@@ -273,18 +221,18 @@ func (srv *Server) buildResponse(reply *message.Reply) (err error) {
return return
} }
// Send 将自定义的消息发送 //Send 将自定义的消息发送
func (srv *Server) Send() (err error) { func (srv *Server) Send() (err error) {
replyMsg := srv.ResponseMsg replyMsg := srv.ResponseMsg
log.Debugf("response msg =%+v", replyMsg) log.Debugf("response msg =%+v", replyMsg)
if srv.isSafeMode { if srv.isSafeMode {
// 安全模式下对消息进行加密 //安全模式下对消息进行加密
var encryptedMsg []byte var encryptedMsg []byte
encryptedMsg, err = util.EncryptMsg(srv.random, srv.ResponseRawXMLMsg, srv.AppID, srv.EncodingAESKey) encryptedMsg, err = util.EncryptMsg(srv.random, srv.ResponseRawXMLMsg, srv.AppID, srv.EncodingAESKey)
if err != nil { if err != nil {
return return
} }
// TODO 如果获取不到timestamp nonce 则自己生成 //TODO 如果获取不到timestamp nonce 则自己生成
timestamp := srv.timestamp timestamp := srv.timestamp
timestampStr := strconv.FormatInt(timestamp, 10) timestampStr := strconv.FormatInt(timestamp, 10)
msgSignature := util.Signature(srv.Token, timestampStr, srv.nonce, string(encryptedMsg)) msgSignature := util.Signature(srv.Token, timestampStr, srv.nonce, string(encryptedMsg))

View File

@@ -15,10 +15,10 @@ func writeContextType(w http.ResponseWriter, value []string) {
} }
} }
// Render render from bytes //Render render from bytes
func (srv *Server) Render(bytes []byte) { func (srv *Server) Render(bytes []byte) {
// debug //debug
// fmt.Println("response msg = ", string(bytes)) //fmt.Println("response msg = ", string(bytes))
srv.Writer.WriteHeader(200) srv.Writer.WriteHeader(200)
_, err := srv.Writer.Write(bytes) _, err := srv.Writer.Write(bytes)
if err != nil { if err != nil {
@@ -26,13 +26,13 @@ func (srv *Server) Render(bytes []byte) {
} }
} }
// String render from string //String render from string
func (srv *Server) String(str string) { func (srv *Server) String(str string) {
writeContextType(srv.Writer, plainContentType) writeContextType(srv.Writer, plainContentType)
srv.Render([]byte(str)) srv.Render([]byte(str))
} }
// XML render to xml //XML render to xml
func (srv *Server) XML(obj interface{}) { func (srv *Server) XML(obj interface{}) {
writeContextType(srv.Writer, xmlContentType) writeContextType(srv.Writer, xmlContentType)
bytes, err := xml.Marshal(obj) bytes, err := xml.Marshal(obj)

View File

@@ -1,116 +0,0 @@
// Package user blacklist 公众号用户黑名单管理
// 参考文档https://developers.weixin.qq.com/doc/offiaccount/User_Management/Manage_blacklist.html
package user
import (
"errors"
"fmt"
"github.com/silenceper/wechat/v2/util"
)
const (
// 获取公众号的黑名单列表
getblacklistURL = "https://api.weixin.qq.com/cgi-bin/tags/members/getblacklist?access_token=%s"
// 拉黑用户
batchblacklistURL = "https://api.weixin.qq.com/cgi-bin/tags/members/batchblacklist?access_token=%s"
// 取消拉黑用户
batchunblacklistURL = "https://api.weixin.qq.com/cgi-bin/tags/members/batchunblacklist?access_token=%s"
)
// GetBlackList 获取公众号的黑名单列表
// 该接口每次调用最多可拉取 1000 个OpenID当列表数较多时可以通过多次拉取的方式来满足需求。
// 参数 beginOpenid当 begin_openid 为空时,默认从开头拉取。
func (user *User) GetBlackList(beginOpenid ...string) (userlist *OpenidList, err error) {
if len(beginOpenid) > 1 {
return nil, errors.New("参数 beginOpenid 错误:请传递 1 个openID若需要从头开始拉取列表请留空。")
}
// 获取 AccessToken
var accessToken string
if accessToken, err = user.GetAccessToken(); err != nil {
return
}
// 处理 request 内容
request := map[string]string{"begin_openid": ""}
if len(beginOpenid) == 1 {
request["begin_openid"] = beginOpenid[0]
}
// 调用接口
var resp []byte
url := fmt.Sprintf(getblacklistURL, accessToken)
if resp, err = util.PostJSON(url, &request); err != nil {
return nil, err
}
// 处理返回
userlist = &OpenidList{}
if err = util.DecodeWithError(resp, userlist, "GetBlackList"); err != nil {
return nil, err
}
return
}
// GetAllBlackList 获取公众号的所有黑名单列表
func (user *User) GetAllBlackList() (openIDList []string, err error) {
var (
beginOpenid string
count int
userlist *OpenidList
)
for {
// 获取列表每次1k条
if userlist, err = user.GetBlackList(beginOpenid); err != nil {
return nil, err
}
openIDList = append(openIDList, userlist.Data.OpenIDs...) // 存储本次获得的OpenIDs
count += userlist.Count // 记录获得的总数量
beginOpenid = userlist.NextOpenID // 记录下次循环的起始openID
if count >= userlist.Total {
break // 获得的数量=total结束循环
}
}
return
}
// BatchBlackList 拉黑用户
// 参数 openidList需要拉入黑名单的用户的openid每次拉黑最多允许20个
func (user *User) BatchBlackList(openidList ...string) (err error) {
return user.batch(batchblacklistURL, "BatchBlackList", openidList...)
}
// BatchUnBlackList 取消拉黑用户
// 参数 openidList需要取消拉入黑名单的用户的openid每次拉黑最多允许20个
func (user *User) BatchUnBlackList(openidList ...string) (err error) {
return user.batch(batchunblacklistURL, "BatchUnBlackList", openidList...)
}
// batch 公共方法
func (user *User) batch(url, apiName string, openidList ...string) (err error) {
// 检查参数
if len(openidList) == 0 || len(openidList) > 20 {
return errors.New("参数 openidList 错误每次操作黑名单用户数量为1-20个。")
}
// 获取 AccessToken
var accessToken string
if accessToken, err = user.GetAccessToken(); err != nil {
return
}
// 处理 request 内容
request := map[string][]string{"openid_list": openidList}
// 调用接口
var resp []byte
url = fmt.Sprintf(url, accessToken)
if resp, err = util.PostJSON(url, &request); err != nil {
return
}
return util.DecodeWithCommonError(resp, apiName)
}

View File

@@ -1,5 +1,5 @@
// Package user migrate 用于微信公众号账号迁移获取openID变化 //Package user migrate 用于微信公众号账号迁移获取openID变化
// 参考文档https://kf.qq.com/faq/1901177NrqMr190117nqYJze.html //参考文档https://kf.qq.com/faq/1901177NrqMr190117nqYJze.html
package user package user
import ( import (
@@ -32,7 +32,7 @@ type ChangeOpenIDResultList struct {
// AccessToken 为新账号的AccessToken // AccessToken 为新账号的AccessToken
func (user *User) ListChangeOpenIDs(fromAppID string, openIDs ...string) (list *ChangeOpenIDResultList, err error) { func (user *User) ListChangeOpenIDs(fromAppID string, openIDs ...string) (list *ChangeOpenIDResultList, err error) {
list = &ChangeOpenIDResultList{} list = &ChangeOpenIDResultList{}
// list.List = make([]ChangeOpenIDResult, 0) //list.List = make([]ChangeOpenIDResult, 0)
if len(openIDs) > 100 { if len(openIDs) > 100 {
err = errors.New("openIDs length must be lt 100") err = errors.New("openIDs length must be lt 100")
return return

View File

@@ -18,7 +18,7 @@ const (
tagUserTidListURL = "https://api.weixin.qq.com/cgi-bin/tags/getidlist?access_token=%s" tagUserTidListURL = "https://api.weixin.qq.com/cgi-bin/tags/getidlist?access_token=%s"
) )
// TagInfo 标签信息 //TagInfo 标签信息
type TagInfo struct { type TagInfo struct {
ID int32 `json:"id"` ID int32 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
@@ -34,7 +34,7 @@ type TagOpenIDList struct {
NextOpenID string `json:"next_openid"` NextOpenID string `json:"next_openid"`
} }
// CreateTag 创建标签 //CreateTag 创建标签
func (user *User) CreateTag(tagName string) (tagInfo *TagInfo, err error) { func (user *User) CreateTag(tagName string) (tagInfo *TagInfo, err error) {
var accessToken string var accessToken string
accessToken, err = user.GetAccessToken() accessToken, err = user.GetAccessToken()
@@ -68,7 +68,7 @@ func (user *User) CreateTag(tagName string) (tagInfo *TagInfo, err error) {
return result.Tag, nil return result.Tag, nil
} }
// DeleteTag 删除标签 //DeleteTag 删除标签
func (user *User) DeleteTag(tagID int32) (err error) { func (user *User) DeleteTag(tagID int32) (err error) {
accessToken, err := user.GetAccessToken() accessToken, err := user.GetAccessToken()
if err != nil { if err != nil {
@@ -88,7 +88,7 @@ func (user *User) DeleteTag(tagID int32) (err error) {
return util.DecodeWithCommonError(resp, "DeleteTag") return util.DecodeWithCommonError(resp, "DeleteTag")
} }
// UpdateTag 编辑标签 //UpdateTag 编辑标签
func (user *User) UpdateTag(tagID int32, tagName string) (err error) { func (user *User) UpdateTag(tagID int32, tagName string) (err error) {
accessToken, err := user.GetAccessToken() accessToken, err := user.GetAccessToken()
if err != nil { if err != nil {
@@ -110,7 +110,7 @@ func (user *User) UpdateTag(tagID int32, tagName string) (err error) {
return util.DecodeWithCommonError(resp, "UpdateTag") return util.DecodeWithCommonError(resp, "UpdateTag")
} }
// GetTag 获取公众号已创建的标签 //GetTag 获取公众号已创建的标签
func (user *User) GetTag() (tags []*TagInfo, err error) { func (user *User) GetTag() (tags []*TagInfo, err error) {
accessToken, err := user.GetAccessToken() accessToken, err := user.GetAccessToken()
if err != nil { if err != nil {
@@ -132,7 +132,7 @@ func (user *User) GetTag() (tags []*TagInfo, err error) {
return result.Tags, nil return result.Tags, nil
} }
// OpenIDListByTag 获取标签下粉丝列表 //OpenIDListByTag 获取标签下粉丝列表
func (user *User) OpenIDListByTag(tagID int32, nextOpenID ...string) (userList *TagOpenIDList, err error) { func (user *User) OpenIDListByTag(tagID int32, nextOpenID ...string) (userList *TagOpenIDList, err error) {
accessToken, err := user.GetAccessToken() accessToken, err := user.GetAccessToken()
if err != nil { if err != nil {
@@ -160,7 +160,7 @@ func (user *User) OpenIDListByTag(tagID int32, nextOpenID ...string) (userList *
return return
} }
// BatchTag 批量为用户打标签 //BatchTag 批量为用户打标签
func (user *User) BatchTag(openIDList []string, tagID int32) (err error) { func (user *User) BatchTag(openIDList []string, tagID int32) (err error) {
accessToken, err := user.GetAccessToken() accessToken, err := user.GetAccessToken()
if err != nil { if err != nil {
@@ -184,7 +184,7 @@ func (user *User) BatchTag(openIDList []string, tagID int32) (err error) {
return util.DecodeWithCommonError(resp, "BatchTag") return util.DecodeWithCommonError(resp, "BatchTag")
} }
// BatchUntag 批量为用户取消标签 //BatchUntag 批量为用户取消标签
func (user *User) BatchUntag(openIDList []string, tagID int32) (err error) { func (user *User) BatchUntag(openIDList []string, tagID int32) (err error) {
if len(openIDList) == 0 { if len(openIDList) == 0 {
return return
@@ -208,7 +208,7 @@ func (user *User) BatchUntag(openIDList []string, tagID int32) (err error) {
return util.DecodeWithCommonError(resp, "BatchUntag") return util.DecodeWithCommonError(resp, "BatchUntag")
} }
// UserTidList 获取用户身上的标签列表 //UserTidList 获取用户身上的标签列表
func (user *User) UserTidList(openID string) (tagIDList []int32, err error) { func (user *User) UserTidList(openID string) (tagIDList []int32, err error) {
accessToken, err := user.GetAccessToken() accessToken, err := user.GetAccessToken()
if err != nil { if err != nil {

View File

@@ -15,19 +15,19 @@ const (
userListURL = "https://api.weixin.qq.com/cgi-bin/user/get" userListURL = "https://api.weixin.qq.com/cgi-bin/user/get"
) )
// User 用户管理 //User 用户管理
type User struct { type User struct {
*context.Context *context.Context
} }
// NewUser 实例化 //NewUser 实例化
func NewUser(context *context.Context) *User { func NewUser(context *context.Context) *User {
user := new(User) user := new(User)
user.Context = context user.Context = context
return user return user
} }
// Info 用户基本信息 //Info 用户基本信息
type Info struct { type Info struct {
util.CommonError util.CommonError
@@ -62,7 +62,7 @@ type OpenidList struct {
NextOpenID string `json:"next_openid"` NextOpenID string `json:"next_openid"`
} }
// GetUserInfo 获取用户基本信息 //GetUserInfo 获取用户基本信息
func (user *User) GetUserInfo(openID string) (userInfo *Info, err error) { func (user *User) GetUserInfo(openID string) (userInfo *Info, err error) {
var accessToken string var accessToken string
accessToken, err = user.GetAccessToken() accessToken, err = user.GetAccessToken()

View File

@@ -1,11 +1,11 @@
# 微信开放平台 # 微信开放平台
[官方文档](https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Third_party_platform_appid.html) [官方文档](https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Third_party_platform_appid.html)
## 快速入门 ## 快速入门
### 服务端处理 ### 服务端处理
```go ```go
wc := wechat.NewWechat() wc := wechat.NewWechat()
memory := cache.NewMemory() memory := cache.NewMemory()
@@ -52,9 +52,7 @@ server.Send()
``` ```
### 待授权处理消息 ### 待授权处理消息
```go ```go
//授权的第三方公众号的appID //授权的第三方公众号的appID

View File

@@ -2,33 +2,33 @@ package account
import "github.com/silenceper/wechat/v2/openplatform/context" import "github.com/silenceper/wechat/v2/openplatform/context"
// Account 开放平台张哈管理 //Account 开放平台张哈管理
// TODO 实现方法 //TODO 实现方法
type Account struct { type Account struct {
*context.Context *context.Context
} }
// NewAccount new //NewAccount new
func NewAccount(ctx *context.Context) *Account { func NewAccount(ctx *context.Context) *Account {
return &Account{ctx} return &Account{ctx}
} }
// Create 创建开放平台帐号并绑定公众号/小程序 //Create 创建开放平台帐号并绑定公众号/小程序
func (account *Account) Create(appID string) (string, error) { func (account *Account) Create(appID string) (string, error) {
return "", nil return "", nil
} }
// Bind 将公众号/小程序绑定到开放平台帐号下 //Bind 将公众号/小程序绑定到开放平台帐号下
func (account *Account) Bind(appID string) error { func (account *Account) Bind(appID string) error {
return nil return nil
} }
// Unbind 将公众号/小程序从开放平台帐号下解绑 //Unbind 将公众号/小程序从开放平台帐号下解绑
func (account *Account) Unbind(appID string, openAppID string) error { func (account *Account) Unbind(appID string, openAppID string) error {
return nil return nil
} }
// Get 获取公众号/小程序所绑定的开放平台帐号 //Get 获取公众号/小程序所绑定的开放平台帐号
func (account *Account) Get(appID string) (string, error) { func (account *Account) Get(appID string) (string, error) {
return "", nil return "", nil
} }

View File

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

View File

@@ -1,4 +1,4 @@
// Package context 开放平台相关context //Package context 开放平台相关context
package context package context
import ( import (
@@ -18,15 +18,14 @@ const (
getComponentInfoURL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=%s" getComponentInfoURL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=%s"
componentLoginURL = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=%s&pre_auth_code=%s&redirect_uri=%s&auth_type=%d&biz_appid=%s" componentLoginURL = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=%s&pre_auth_code=%s&redirect_uri=%s&auth_type=%d&biz_appid=%s"
bindComponentURL = "https://mp.weixin.qq.com/safe/bindcomponent?action=bindcomponent&auth_type=%d&no_scan=1&component_appid=%s&pre_auth_code=%s&redirect_uri=%s&biz_appid=%s#wechat_redirect" bindComponentURL = "https://mp.weixin.qq.com/safe/bindcomponent?action=bindcomponent&auth_type=%d&no_scan=1&component_appid=%s&pre_auth_code=%s&redirect_uri=%s&biz_appid=%s#wechat_redirect"
// TODO 获取授权方选项信息 //TODO 获取授权方选项信息
// getComponentConfigURL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option?component_access_token=%s" //getComponentConfigURL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option?component_access_token=%s"
// TODO 获取已授权的账号信息 //TODO 获取已授权的账号信息
// getuthorizerListURL = "POST https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_list?component_access_token=%s" //getuthorizerListURL = "POST https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_list?component_access_token=%s"
) )
// ComponentAccessToken 第三方平台 // ComponentAccessToken 第三方平台
type ComponentAccessToken struct { type ComponentAccessToken struct {
util.CommonError
AccessToken string `json:"component_access_token"` AccessToken string `json:"component_access_token"`
ExpiresIn int64 `json:"expires_in"` ExpiresIn int64 `json:"expires_in"`
} }
@@ -58,10 +57,6 @@ func (ctx *Context) SetComponentAccessToken(verifyTicket string) (*ComponentAcce
return nil, err return nil, err
} }
if at.ErrCode != 0 {
return nil, fmt.Errorf("SetComponentAccessToken Error , errcode=%d , errmsg=%s", at.ErrCode, at.ErrMsg)
}
accessTokenCacheKey := fmt.Sprintf("component_access_token_%s", ctx.AppID) accessTokenCacheKey := fmt.Sprintf("component_access_token_%s", ctx.AppID)
expires := at.ExpiresIn - 1500 expires := at.ExpiresIn - 1500
if err := ctx.Cache.Set(accessTokenCacheKey, at.AccessToken, time.Duration(expires)*time.Second); err != nil { if err := ctx.Cache.Set(accessTokenCacheKey, at.AccessToken, time.Duration(expires)*time.Second); err != nil {
@@ -226,36 +221,6 @@ type AuthorizerInfo struct {
} }
Alias string `json:"alias"` Alias string `json:"alias"`
QrcodeURL string `json:"qrcode_url"` QrcodeURL string `json:"qrcode_url"`
MiniProgramInfo *MiniProgramInfo `json:"MiniProgramInfo"`
RegisterType int `json:"register_type"`
AccountStatus int `json:"account_status"`
BasicConfig *AuthorizerBasicConfig `json:"basic_config"`
}
// AuthorizerBasicConfig 授权账号的基础配置结构体
type AuthorizerBasicConfig struct {
IsPhoneConfigured bool `json:"isPhoneConfigured"`
IsEmailConfigured bool `json:"isEmailConfigured"`
}
// MiniProgramInfo 授权账号小程序配置 授权账号为小程序时存在
type MiniProgramInfo struct {
Network struct {
RequestDomain []string `json:"RequestDomain"`
WsRequestDomain []string `json:"WsRequestDomain"`
UploadDomain []string `json:"UploadDomain"`
DownloadDomain []string `json:"DownloadDomain"`
BizDomain []string `json:"BizDomain"`
UDPDomain []string `json:"UDPDomain"`
} `json:"network"`
Categories []CategoriesInfo `json:"categories"`
}
// CategoriesInfo 授权账号小程序配置的类目信息
type CategoriesInfo struct {
First string `wx:"first"`
Second string `wx:"second"`
} }
// GetAuthrInfo 获取授权方的帐号基本信息 // GetAuthrInfo 获取授权方的帐号基本信息

View File

@@ -11,24 +11,23 @@ const (
getAccountBasicInfoURL = "https://api.weixin.qq.com/cgi-bin/account/getaccountbasicinfo" getAccountBasicInfoURL = "https://api.weixin.qq.com/cgi-bin/account/getaccountbasicinfo"
) )
// Basic 基础信息设置 //Basic 基础信息设置
type Basic struct { type Basic struct {
*openContext.Context *openContext.Context
appID string appID string
} }
// NewBasic new //NewBasic new
func NewBasic(opContext *openContext.Context, appID string) *Basic { func NewBasic(opContext *openContext.Context, appID string) *Basic {
return &Basic{Context: opContext, appID: appID} return &Basic{Context: opContext, appID: appID}
} }
// AccountBasicInfo 基础信息 //AccountBasicInfo 基础信息
type AccountBasicInfo struct { type AccountBasicInfo struct {
util.CommonError util.CommonError
} }
// GetAccountBasicInfo 获取小程序基础信息 //GetAccountBasicInfo 获取小程序基础信息
//
//reference:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/Mini_Program_Information_Settings.html //reference:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/Mini_Program_Information_Settings.html
func (basic *Basic) GetAccountBasicInfo() (*AccountBasicInfo, error) { func (basic *Basic) GetAccountBasicInfo() (*AccountBasicInfo, error) {
ak, err := basic.GetAuthrAccessToken(basic.AppID) ak, err := basic.GetAuthrAccessToken(basic.AppID)
@@ -47,7 +46,7 @@ func (basic *Basic) GetAccountBasicInfo() (*AccountBasicInfo, error) {
return result, nil return result, nil
} }
// modify_domain设置服务器域名 //modify_domain设置服务器域名
// TODO //TODO
// func (encryptor *Basic) modifyDomain() { //func (encryptor *Basic) modifyDomain() {
// } //}

Some files were not shown because too many files have changed in this diff Show More