-
Notifications
You must be signed in to change notification settings - Fork 280
/
weapp.go
307 lines (250 loc) · 7.32 KB
/
weapp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
package weapp
import (
"log"
"net/http"
"os"
"time"
"github.com/medivhzhan/weapp/v3/order"
"github.com/medivhzhan/weapp/v3/auth"
"github.com/medivhzhan/weapp/v3/cache"
"github.com/medivhzhan/weapp/v3/livebroadcast"
"github.com/medivhzhan/weapp/v3/logger"
"github.com/medivhzhan/weapp/v3/ocr"
"github.com/medivhzhan/weapp/v3/openapi"
"github.com/medivhzhan/weapp/v3/operation"
"github.com/medivhzhan/weapp/v3/phonenumber"
"github.com/medivhzhan/weapp/v3/request"
"github.com/medivhzhan/weapp/v3/search"
"github.com/medivhzhan/weapp/v3/security"
"github.com/medivhzhan/weapp/v3/server"
"github.com/medivhzhan/weapp/v3/subscribemessage"
"github.com/medivhzhan/weapp/v3/updatablemessage"
"github.com/medivhzhan/weapp/v3/wxacode"
"github.com/mitchellh/mapstructure"
)
const (
// baseURL 微信请求基础URL
baseURL = "https://api.weixin.qq.com"
)
type Client struct {
// HTTP请求客户端
request *request.Request
// 数据缓存器
cache cache.Cache
// 日志记录器
logger logger.Logger
// 小程序后台配置: 小程序ID
appid string
// 小程序后台配置: 小程序密钥
secret string
// 用户自定义获取access_token的方法
accessTokenGetter AccessTokenGetter
}
// 用户自定义获取access_token的方法
type AccessTokenGetter func(appid, secret string) (token string, expireIn uint)
// 初始化客户端并用自定义配置替换默认配置
func NewClient(appid, secret string, opts ...func(*Client)) *Client {
cli := &Client{
appid: appid,
secret: secret,
}
// 执行额外的配置函数
for _, fn := range opts {
fn(cli)
}
if cli.cache == nil {
cli.cache = cache.NewMemoryCache()
}
if cli.request == nil {
cli.request = request.NewRequest(http.DefaultClient, request.ContentTypeJSON, cli.Logger)
}
if cli.logger == nil {
cli.logger = logger.NewLogger(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Info, true)
}
return cli
}
// 自定义 HTTP Client
func WithHttpClient(hc *http.Client) func(*Client) {
return func(cli *Client) {
cli.request = request.NewRequest(hc, request.ContentTypeJSON, cli.Logger)
}
}
// 自定义缓存
func WithCache(cc cache.Cache) func(*Client) {
return func(cli *Client) {
cli.cache = cc
}
}
// 自定义获取access_token的方法
func WithAccessTokenSetter(getter AccessTokenGetter) func(*Client) {
return func(cli *Client) {
cli.accessTokenGetter = getter
}
}
// 自定义日志
func WithLogger(logger logger.Logger) func(*Client) {
return func(cli *Client) {
cli.logger = logger
}
}
// POST 参数
type requestParams map[string]interface{}
// URL 参数
type requestQueries map[string]interface{}
// tokenAPI 获取带 token 的 API 地址
func tokenAPI(api, token string) (string, error) {
queries := requestQueries{
"access_token": token,
}
return request.EncodeURL(api, queries)
}
// convert bool to int
func bool2int(ok bool) uint8 {
if ok {
return 1
}
return 0
}
// 获取日志记录器
func (cli *Client) Logger() logger.Logger { return cli.logger }
// 设置日志等级
func (cli *Client) SetLogLevel(lv logger.Level) {
if cli.logger != nil {
cli.logger.SetLevel(lv)
}
}
// 获取小程序全局唯一后台接口调用凭据(access_token)。
// 调调用绝大多数后台接口时都需使用 access_token,开发者需要进行妥善保存,注意缓存。
func (cli *Client) AccessToken() (string, error) {
key := cli.tokenCacheKey()
data, ok := cli.cache.Get(key)
if ok {
return data.(string), nil
}
if cli.accessTokenGetter != nil {
token, expireIn := cli.accessTokenGetter(cli.appid, cli.secret)
cli.cache.Set(key, token, time.Duration(expireIn)*time.Second)
return token, nil
} else {
req := auth.GetStableAccessTokenRequest{
Appid: cli.appid,
Secret: cli.secret,
GrantType: "client_credential",
}
rsp, err := cli.NewAuth().GetStableAccessToken(&req)
if err != nil {
return "", err
}
if err := rsp.GetResponseError(); err != nil {
return "", err
}
cli.cache.Set(key, rsp.AccessToken, time.Duration(rsp.ExpiresIn)*time.Second)
return rsp.AccessToken, nil
}
}
// 获取稳定版接口调用凭据
func (cli *Client) StableAccessToken(forceRefresh bool) (string, error) {
key := cli.tokenCacheKey()
data, ok := cli.cache.Get(key)
if ok {
return data.(string), nil
}
if !forceRefresh && cli.accessTokenGetter != nil {
token, expireIn := cli.accessTokenGetter(cli.appid, cli.secret)
cli.cache.Set(key, token, time.Duration(expireIn)*time.Second)
return token, nil
} else {
req := auth.GetStableAccessTokenRequest{
Appid: cli.appid,
Secret: cli.secret,
GrantType: "client_credential",
ForceRefresh: forceRefresh,
}
rsp, err := cli.NewAuth().GetStableAccessToken(&req)
if err != nil {
return "", err
}
if err := rsp.GetResponseError(); err != nil {
return "", err
}
cli.cache.Set(key, rsp.AccessToken, time.Duration(rsp.ExpiresIn)*time.Second)
return rsp.AccessToken, nil
}
}
// 拼凑完整的 URI
func (cli *Client) combineURI(url string, req interface{}, withToken bool) (string, error) {
output := make(map[string]interface{})
config := &mapstructure.DecoderConfig{
Metadata: nil,
Result: &output,
TagName: "query",
}
decoder, err := mapstructure.NewDecoder(config)
if err != nil {
return "", err
}
err = decoder.Decode(req)
if err != nil {
return "", err
}
if withToken {
token, err := cli.AccessToken()
if err != nil {
return "", err
}
output["access_token"] = token
}
return request.EncodeURL(baseURL+url, output)
}
// 用户信息
func (cli *Client) NewAuth() *auth.Auth {
return auth.NewAuth(cli.request, cli.combineURI)
}
// 微信通知监听服务
func (cli *Client) NewServer(token, aesKey, mchID, apiKey string, validate bool, handler func(map[string]interface{}) map[string]interface{}) (*server.Server, error) {
return server.NewServer(cli.appid, token, aesKey, mchID, apiKey, validate, handler)
}
// 订阅消息
func (cli *Client) NewSubscribeMessage() *subscribemessage.SubscribeMessage {
return subscribemessage.NewSubscribeMessage(cli.request, cli.combineURI)
}
// 运维中心
func (cli *Client) NewOperation() *operation.Operation {
return operation.NewOperation(cli.request, cli.combineURI)
}
// openApi管理
func (cli *Client) NewOpenApi() *openapi.OpenApi {
return openapi.NewOpenApi(cli.request, cli.combineURI)
}
// 小程序码
func (cli *Client) NewWXACode() *wxacode.WXACode {
return wxacode.NewWXACode(cli.request, cli.combineURI)
}
// OCR
func (cli *Client) NewOCR() *ocr.OCR {
return ocr.NewOCR(cli.request, cli.combineURI)
}
// 动态消息
func (cli *Client) NewUpdatableMessage() *updatablemessage.UpdatableMessage {
return updatablemessage.NewUpdatableMessage(cli.request, cli.combineURI)
}
// 小程序搜索
func (cli *Client) NewSearch() *search.Search {
return search.NewSearch(cli.request, cli.combineURI)
}
// 直播
func (cli *Client) NewLiveBroadcast() *livebroadcast.LiveBroadcast {
return livebroadcast.NewLiveBroadcast(cli.request, cli.combineURI)
}
// 内容安全
func (cli *Client) NewSecurity() *security.Security {
return security.NewSecurity(cli.request, cli.combineURI)
}
// 手机号
func (cli *Client) NewPhonenumber() *phonenumber.Phonenumber {
return phonenumber.NewPhonenumber(cli.request, cli.combineURI)
}
// NewOrder 发货内容管理
func (cli *Client) NewOrder() *order.Order {
return order.NewOrder(cli.request, cli.combineURI)
}