commit
7cdf21084d
@ -0,0 +1,17 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: clone
|
||||
|
||||
steps:
|
||||
- name: Test
|
||||
image: golang:1.18
|
||||
commands:
|
||||
- go env -w GO111MODULE=on
|
||||
- go env -w GOPROXY=https://goproxy.cn,direct
|
||||
- go test -v ./...
|
||||
- name: Benchmark
|
||||
image: golang:1.18
|
||||
commands:
|
||||
- go env -w GO111MODULE=on
|
||||
- go env -w GOPROXY=https://goproxy.cn,direct
|
||||
- go test -bench=. -benchmem
|
@ -0,0 +1,10 @@
|
||||
.env
|
||||
.git
|
||||
.svn
|
||||
.idea
|
||||
.vscode
|
||||
*.log
|
||||
goinit.sh
|
||||
gomod.sh
|
||||
*_test.go
|
||||
/vendor/
|
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 李光春
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -0,0 +1,17 @@
|
||||
<h1>
|
||||
<a href="https://www.dtapp.net/">Golang wechatminiprogram</a>
|
||||
</h1>
|
||||
|
||||
📦 Golang wechatminiprogram
|
||||
|
||||
[comment]: <> (go)
|
||||
[![godoc](https://pkg.go.dev/badge/go.dtapp.net/wechatminiprogram?status.svg)](https://pkg.go.dev/go.dtapp.net/wechatminiprogram)
|
||||
[![goproxy.cn](https://goproxy.cn/stats/go.dtapp.net/wechatminiprogram/badges/download-count.svg)](https://goproxy.cn/stats/go.dtapp.net/wechatminiprogram)
|
||||
[![goreportcard.com](https://goreportcard.com/badge/go.dtapp.net/wechatminiprogram)](https://goreportcard.com/report/go.dtapp.net/wechatminiprogram)
|
||||
[![deps.dev](https://img.shields.io/badge/deps-go-red.svg)](https://deps.dev/go/go.dtapp.net%2Fwechatminiprogram)
|
||||
|
||||
#### 安装
|
||||
|
||||
```shell
|
||||
go get -v -u go.dtapp.net/wechatminiprogram
|
||||
```
|
@ -0,0 +1,35 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go.dtapp.net/gorequest"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type GetCallBackIpResponse struct {
|
||||
IpList []string `json:"ip_list"`
|
||||
}
|
||||
|
||||
type GetCallBackIpResult struct {
|
||||
Result GetCallBackIpResponse // 结果
|
||||
Body []byte // 内容
|
||||
Http gorequest.Response // 请求
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func newGetCallBackIpResult(result GetCallBackIpResponse, body []byte, http gorequest.Response, err error) *GetCallBackIpResult {
|
||||
return &GetCallBackIpResult{Result: result, Body: body, Http: http, Err: err}
|
||||
}
|
||||
|
||||
// GetCallBackIp 获取微信callback IP地址
|
||||
// callback IP即微信调用开发者服务器所使用的出口IP。
|
||||
// https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_the_WeChat_server_IP_address.html#2.%20%E8%8E%B7%E5%8F%96%E5%BE%AE%E4%BF%A1callback%20IP%E5%9C%B0%E5%9D%80
|
||||
func (c *Client) GetCallBackIp() *GetCallBackIpResult {
|
||||
// 请求
|
||||
request, err := c.request(fmt.Sprintf(apiUrl+"/cgi-bin/getcallbackip?access_token=%s", c.getAccessToken()), map[string]interface{}{}, http.MethodGet)
|
||||
// 定义
|
||||
var response GetCallBackIpResponse
|
||||
err = json.Unmarshal(request.ResponseBody, &response)
|
||||
return newGetCallBackIpResult(response, request.ResponseBody, request, err)
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go.dtapp.net/gorequest"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type SubscribeMessageSendResponse struct {
|
||||
Errcode int // 错误码
|
||||
Errmsg string // 错误信息
|
||||
}
|
||||
|
||||
type SubscribeMessageSendResult struct {
|
||||
Result SubscribeMessageSendResponse // 结果
|
||||
Body []byte // 内容
|
||||
Http gorequest.Response // 请求
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func newSubscribeMessageSendResult(result SubscribeMessageSendResponse, body []byte, http gorequest.Response, err error) *SubscribeMessageSendResult {
|
||||
return &SubscribeMessageSendResult{Result: result, Body: body, Http: http, Err: err}
|
||||
}
|
||||
|
||||
// SubscribeMessageSend 发送订阅消息
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html
|
||||
func (c *Client) SubscribeMessageSend(notMustParams ...gorequest.Params) *SubscribeMessageSendResult {
|
||||
// 参数
|
||||
params := gorequest.NewParamsWith(notMustParams...)
|
||||
// 请求
|
||||
request, err := c.request(fmt.Sprintf(apiUrl+"/cgi-bin/message/subscribe/send?access_token=%s", c.getAccessToken()), params, http.MethodPost)
|
||||
// 定义
|
||||
var response SubscribeMessageSendResponse
|
||||
err = json.Unmarshal(request.ResponseBody, &response)
|
||||
return newSubscribeMessageSendResult(response, request.ResponseBody, request, err)
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (c *Client) GetAccessTokenMonitor() (string, error) {
|
||||
if c.config.RedisClient.Db == nil {
|
||||
return "", errors.New("驱动没有初始化")
|
||||
}
|
||||
result := c.GetCallBackIp()
|
||||
if len(result.Result.IpList) <= 0 {
|
||||
token := c.CgiBinToken()
|
||||
c.config.RedisClient.Db.Set(context.Background(), c.getAccessTokenCacheKeyName(), token.Result.AccessToken, time.Second*7000)
|
||||
return token.Result.AccessToken, nil
|
||||
}
|
||||
return c.config.AccessToken, nil
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (c *Client) GetAccessToken() string {
|
||||
if c.config.RedisClient.Db == nil {
|
||||
return c.config.AccessToken
|
||||
}
|
||||
newCache := c.config.RedisClient.NewSimpleStringCache(c.config.RedisClient.NewStringOperation(), time.Second*7000)
|
||||
newCache.DBGetter = func() string {
|
||||
token := c.CgiBinToken()
|
||||
return token.Result.AccessToken
|
||||
}
|
||||
return newCache.GetCache(c.getAccessTokenCacheKeyName())
|
||||
}
|
||||
|
||||
func (c *Client) getAccessTokenCacheKeyName() string {
|
||||
return fmt.Sprintf("wechat_access_token:%v", c.config.AppId)
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go.dtapp.net/gorequest"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CgiBinWxaAppCreateWxaQrCodeResponse struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
ContentType string `json:"contentType"`
|
||||
Buffer interface{} `json:"buffer"`
|
||||
}
|
||||
|
||||
type CgiBinWxaAppCreateWxaQrCodeResult struct {
|
||||
Result CgiBinWxaAppCreateWxaQrCodeResponse // 结果
|
||||
Body []byte // 内容
|
||||
Http gorequest.Response // 请求
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func newCgiBinWxaAppCreateWxaQrCodeResult(result CgiBinWxaAppCreateWxaQrCodeResponse, body []byte, http gorequest.Response, err error) *CgiBinWxaAppCreateWxaQrCodeResult {
|
||||
return &CgiBinWxaAppCreateWxaQrCodeResult{Result: result, Body: body, Http: http, Err: err}
|
||||
}
|
||||
|
||||
// CgiBinWxaAppCreateWxaQrCode 获取小程序二维码,适用于需要的码数量较少的业务场景。通过该接口生成的小程序码,永久有效,有数量限制
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/qr-code/wxacode.createQRCode.html
|
||||
func (c *Client) CgiBinWxaAppCreateWxaQrCode(notMustParams ...gorequest.Params) *CgiBinWxaAppCreateWxaQrCodeResult {
|
||||
// 参数
|
||||
params := gorequest.NewParamsWith(notMustParams...)
|
||||
// 请求
|
||||
request, err := c.request(fmt.Sprintf(apiUrl+"/cgi-bin/wxaapp/createwxaqrcode?access_token=%s", c.getAccessToken()), params, http.MethodPost)
|
||||
// 定义
|
||||
var response CgiBinWxaAppCreateWxaQrCodeResponse
|
||||
err = json.Unmarshal(request.ResponseBody, &response)
|
||||
return newCgiBinWxaAppCreateWxaQrCodeResult(response, request.ResponseBody, request, err)
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import (
|
||||
"go.dtapp.net/dorm"
|
||||
"go.dtapp.net/golog"
|
||||
"go.dtapp.net/gorequest"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ConfigClient struct {
|
||||
AppId string // 小程序唯一凭证,即 appId
|
||||
AppSecret string // 小程序唯一凭证密钥,即 appSecret
|
||||
AccessToken string // 接口调用凭证
|
||||
JsapiTicket string // 签名凭证
|
||||
RedisClient *dorm.RedisClient // 缓存数据库
|
||||
TokenDb *gorm.DB // 令牌数据库
|
||||
MongoDb *dorm.MongoClient // 日志数据库
|
||||
PgsqlDb *gorm.DB // 日志数据库
|
||||
DatabaseName string // 库名
|
||||
|
||||
}
|
||||
|
||||
// Client 微信小程序服务
|
||||
type Client struct {
|
||||
client *gorequest.App // 请求客户端
|
||||
log *golog.ApiClient // 日志服务
|
||||
config *ConfigClient // 配置
|
||||
}
|
||||
|
||||
func NewClient(config *ConfigClient) (*Client, error) {
|
||||
|
||||
var err error
|
||||
c := &Client{config: config}
|
||||
|
||||
c.client = gorequest.NewHttp()
|
||||
|
||||
if c.config.PgsqlDb != nil {
|
||||
c.log, err = golog.NewApiClient(
|
||||
golog.WithGormClient(c.config.PgsqlDb),
|
||||
golog.WithTableName(logTable),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if c.config.MongoDb != nil {
|
||||
c.log, err = golog.NewApiClient(
|
||||
golog.WithMongoClient(c.config.MongoDb),
|
||||
golog.WithDatabaseName(c.config.DatabaseName),
|
||||
golog.WithCollectionName(logTable),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// ConfigApp 配置
|
||||
func (c *Client) ConfigApp(appId, appSecret string) *Client {
|
||||
c.config.AppId = appId
|
||||
c.config.AppSecret = appSecret
|
||||
return c
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package wechatminiprogram
|
||||
|
||||
const (
|
||||
apiUrl = "https://api.weixin.qq.com"
|
||||
)
|
||||
|
||||
const (
|
||||
logTable = "wechatminiprogram"
|
||||
)
|
||||
|
||||
const Version = "1.0.0"
|
@ -0,0 +1,18 @@
|
||||
package wechatminiprogram
|
||||
|
||||
func (c *Client) getAppId() string {
|
||||
return c.config.AppId
|
||||
}
|
||||
|
||||
func (c *Client) getAppSecret() string {
|
||||
return c.config.AppSecret
|
||||
}
|
||||
|
||||
func (c *Client) getAccessToken() string {
|
||||
c.config.AccessToken = c.GetAccessToken()
|
||||
return c.config.AccessToken
|
||||
}
|
||||
|
||||
func (c *Client) GetAppId() string {
|
||||
return c.config.AppId
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
module go.dtapp.net/wechatminiprogram
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
go.dtapp.net/dorm v1.0.11
|
||||
go.dtapp.net/golog v1.0.16
|
||||
go.dtapp.net/gorequest v1.0.23
|
||||
go.dtapp.net/gostorage v1.0.11
|
||||
gorm.io/gorm v1.23.8
|
||||
)
|
||||
|
||||
require (
|
||||
gitee.com/chunanyong/dm v1.8.5 // indirect
|
||||
gitee.com/chunanyong/zorm v1.5.9 // indirect
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.4+incompatible // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.70 // indirect
|
||||
github.com/baidubce/bce-sdk-go v0.9.131 // indirect
|
||||
github.com/basgys/goxml2json v1.1.0 // indirect
|
||||
github.com/beego/beego/v2 v2.0.5 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/clbanning/mxj v1.8.4 // indirect
|
||||
github.com/deckarep/golang-set v1.8.0 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.12.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/edsrzf/mmap-go v1.1.0 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.8.1 // indirect
|
||||
github.com/glebarez/go-sqlite v1.18.0 // indirect
|
||||
github.com/glebarez/sqlite v1.4.6 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.0 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/go-redis/redis/v9 v9.0.0-beta.2 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/goccy/go-json v0.9.10 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.12+incompatible // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.12.1 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jackc/pgtype v1.11.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.16.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.15.9 // indirect
|
||||
github.com/ks3sdklib/aws-sdk-go v1.1.6 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/lib/pq v1.10.6 // indirect
|
||||
github.com/mailru/go-clickhouse/v2 v2.0.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.14 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/montanaflynn/stats v0.6.6 // indirect
|
||||
github.com/mozillazg/go-httpheader v0.3.1 // indirect
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/qiniu/go-sdk/v7 v7.13.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda // indirect
|
||||
github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f // indirect
|
||||
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.36 // indirect
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
github.com/upper/db/v4 v4.5.4 // indirect
|
||||
github.com/uptrace/bun v1.1.7 // indirect
|
||||
github.com/uptrace/bun/dialect/mssqldialect v1.1.7 // indirect
|
||||
github.com/uptrace/bun/dialect/mysqldialect v1.1.7 // indirect
|
||||
github.com/uptrace/bun/dialect/pgdialect v1.1.7 // indirect
|
||||
github.com/uptrace/bun/driver/pgdriver v1.1.7 // indirect
|
||||
github.com/upyun/go-sdk/v3 v3.0.3 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.1 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.3 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
|
||||
go.dtapp.net/goip v1.0.23 // indirect
|
||||
go.dtapp.net/gojson v1.0.1 // indirect
|
||||
go.dtapp.net/gorandom v1.0.1 // indirect
|
||||
go.dtapp.net/gostring v1.0.6 // indirect
|
||||
go.dtapp.net/gotime v1.0.5 // indirect
|
||||
go.dtapp.net/goxml v1.0.1 // indirect
|
||||
go.mongodb.org/mongo-driver v1.10.1 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48 // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gorm.io/datatypes v1.0.7 // indirect
|
||||
gorm.io/driver/mysql v1.3.5 // indirect
|
||||
gorm.io/driver/postgres v1.3.8 // indirect
|
||||
gorm.io/driver/sqlite v1.3.6 // indirect
|
||||
gorm.io/driver/sqlserver v1.3.2 // indirect
|
||||
mellium.im/sasl v0.2.1 // indirect
|
||||
modernc.org/b v1.0.2 // indirect
|
||||
modernc.org/db v1.0.4 // indirect
|
||||
modernc.org/file v1.0.3 // indirect
|
||||
modernc.org/fileutil v1.1.0 // indirect
|
||||
modernc.org/golex v1.0.1 // indirect
|
||||
modernc.org/internal v1.0.4 // indirect
|
||||
modernc.org/libc v1.16.19 // indirect
|
||||
modernc.org/lldb v1.0.4 // indirect
|
||||
modernc.org/mathutil v1.4.1 // indirect
|
||||
modernc.org/memory v1.1.1 // indirect
|
||||
modernc.org/ql v1.4.1 // indirect
|
||||
modernc.org/sortutil v1.1.0 // indirect
|
||||
modernc.org/sqlite v1.18.0 // indirect
|
||||
modernc.org/strutil v1.1.2 // indirect
|
||||
modernc.org/zappy v1.0.5 // indirect
|
||||
xorm.io/builder v0.3.12 // indirect
|
||||
xorm.io/xorm v1.3.1 // indirect
|
||||
)
|
@ -0,0 +1,38 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import "go.dtapp.net/gorequest"
|
||||
|
||||
// 请求接口
|
||||
func (c *Client) request(url string, params map[string]interface{}, method string) (gorequest.Response, error) {
|
||||
|
||||
// 创建请求
|
||||
client := c.client
|
||||
|
||||
// 设置请求地址
|
||||
client.SetUri(url)
|
||||
|
||||
// 设置方式
|
||||
client.SetMethod(method)
|
||||
|
||||
// 设置格式
|
||||
client.SetContentTypeJson()
|
||||
|
||||
// 设置参数
|
||||
client.SetParams(params)
|
||||
|
||||
// 发起请求
|
||||
request, err := client.Request()
|
||||
if err != nil {
|
||||
return gorequest.Response{}, err
|
||||
}
|
||||
|
||||
// 日志
|
||||
if c.config.PgsqlDb != nil {
|
||||
go c.log.GormMiddleware(request)
|
||||
}
|
||||
if c.config.MongoDb != nil {
|
||||
go c.log.MongoMiddleware(request)
|
||||
}
|
||||
|
||||
return request, err
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import "errors"
|
||||
|
||||
func (c *Client) pkcs7Unpaid(data []byte, blockSize int) ([]byte, error) {
|
||||
if blockSize <= 0 {
|
||||
return nil, errors.New("invalid block size")
|
||||
}
|
||||
if len(data)%blockSize != 0 || len(data) == 0 {
|
||||
return nil, errors.New("invalid PKCS7 data")
|
||||
}
|
||||
d := data[len(data)-1]
|
||||
n := int(d)
|
||||
if n == 0 || n > len(data) {
|
||||
return nil, errors.New("invalid padding on input")
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
if data[len(data)-n+i] != d {
|
||||
return nil, errors.New("invalid padding on input")
|
||||
}
|
||||
}
|
||||
return data[:len(data)-n], nil
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go.dtapp.net/gorequest"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type SnsJsCode2sessionResponse struct {
|
||||
OpenId string `json:"openid"` // 用户唯一标识
|
||||
SessionKey string `json:"session_key"` // 会话密钥
|
||||
Unionid string `json:"unionid"` // 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回
|
||||
Errcode string `json:"errcode"` // 错误码
|
||||
Errmsg string `json:"errmsg"` // 错误信息
|
||||
}
|
||||
|
||||
type SnsJsCode2sessionResult struct {
|
||||
Result SnsJsCode2sessionResponse // 结果
|
||||
Body []byte // 内容
|
||||
Http gorequest.Response // 请求
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func newSnsJsCode2sessionResult(result SnsJsCode2sessionResponse, body []byte, http gorequest.Response, err error) *SnsJsCode2sessionResult {
|
||||
return &SnsJsCode2sessionResult{Result: result, Body: body, Http: http, Err: err}
|
||||
}
|
||||
|
||||
// SnsJsCode2session 登录凭证校验
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
|
||||
func (c *Client) SnsJsCode2session(jsCode string) *SnsJsCode2sessionResult {
|
||||
// 请求
|
||||
request, err := c.request(fmt.Sprintf(apiUrl+"/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", c.getAppId(), c.getAppSecret(), jsCode), map[string]interface{}{}, http.MethodGet)
|
||||
// 定义
|
||||
var response SnsJsCode2sessionResponse
|
||||
err = json.Unmarshal(request.ResponseBody, &response)
|
||||
return newSnsJsCode2sessionResult(response, request.ResponseBody, request, err)
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UserInfo struct {
|
||||
SessionKey string `json:"session_key"`
|
||||
EncryptedData string `json:"encrypted_data"`
|
||||
Iv string `json:"iv"`
|
||||
}
|
||||
|
||||
type UserInfoResponse struct {
|
||||
OpenID string `json:"openId"`
|
||||
NickName string `json:"nickName"`
|
||||
Gender int `json:"gender"`
|
||||
City string `json:"city"`
|
||||
Province string `json:"province"`
|
||||
Country string `json:"country"`
|
||||
AvatarUrl string `json:"avatarUrl"`
|
||||
UnionId string `json:"unionId"`
|
||||
Watermark struct {
|
||||
AppID string `json:"appid"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
} `json:"watermark"`
|
||||
}
|
||||
|
||||
type UserInfoResult struct {
|
||||
Result UserInfoResponse // 结果
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func newUserInfoResult(result UserInfoResponse, err error) *UserInfoResult {
|
||||
return &UserInfoResult{Result: result, Err: err}
|
||||
}
|
||||
|
||||
// UserInfo 解密用户信息
|
||||
func (c *Client) UserInfo(param UserInfo) *UserInfoResult {
|
||||
var response UserInfoResponse
|
||||
aesKey, err := base64.StdEncoding.DecodeString(param.SessionKey)
|
||||
if err != nil {
|
||||
return newUserInfoResult(response, err)
|
||||
}
|
||||
cipherText, err := base64.StdEncoding.DecodeString(param.EncryptedData)
|
||||
if err != nil {
|
||||
return newUserInfoResult(response, err)
|
||||
}
|
||||
ivBytes, err := base64.StdEncoding.DecodeString(param.Iv)
|
||||
if err != nil {
|
||||
return newUserInfoResult(response, err)
|
||||
}
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
return newUserInfoResult(response, err)
|
||||
}
|
||||
mode := cipher.NewCBCDecrypter(block, ivBytes)
|
||||
mode.CryptBlocks(cipherText, cipherText)
|
||||
cipherText, err = c.pkcs7Unpaid(cipherText, block.BlockSize())
|
||||
if err != nil {
|
||||
return newUserInfoResult(response, err)
|
||||
}
|
||||
err = json.Unmarshal(cipherText, &response)
|
||||
if err != nil {
|
||||
return newUserInfoResult(response, err)
|
||||
}
|
||||
if response.Watermark.AppID != c.getAppId() {
|
||||
return newUserInfoResult(response, errors.New("c id not match"))
|
||||
}
|
||||
return newUserInfoResult(response, err)
|
||||
}
|
||||
|
||||
func (u *UserInfoResponse) UserInfoAvatarUrlReal() string {
|
||||
return UserInfoAvatarUrlReal(u.AvatarUrl)
|
||||
}
|
||||
|
||||
func UserInfoAvatarUrlReal(avatarUrl string) string {
|
||||
return strings.Replace(avatarUrl, "/132", "/0", -1)
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type UserPhone struct {
|
||||
SessionKey string `json:"session_key"`
|
||||
EncryptedData string `json:"encrypted_data"`
|
||||
Iv string `json:"iv"`
|
||||
}
|
||||
|
||||
type UserPhoneResponse struct {
|
||||
PhoneNumber string `json:"phoneNumber"` // 用户绑定的手机号(国外手机号会有区号)
|
||||
PurePhoneNumber string `json:"purePhoneNumber"` // 没有区号的手机号
|
||||
CountryCode string `json:"countryCode"` // 区号
|
||||
Watermark struct {
|
||||
AppID string `json:"appid"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
} `json:"watermark"`
|
||||
}
|
||||
|
||||
type UserPhoneResult struct {
|
||||
Result UserPhoneResponse // 结果
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func newUserPhoneResult(result UserPhoneResponse, err error) *UserPhoneResult {
|
||||
return &UserPhoneResult{Result: result, Err: err}
|
||||
}
|
||||
|
||||
// UserPhone 解密手机号信息
|
||||
func (c *Client) UserPhone(param UserPhone) *UserPhoneResult {
|
||||
var response UserPhoneResponse
|
||||
aesKey, err := base64.StdEncoding.DecodeString(param.SessionKey)
|
||||
if err != nil {
|
||||
return newUserPhoneResult(response, err)
|
||||
}
|
||||
cipherText, err := base64.StdEncoding.DecodeString(param.EncryptedData)
|
||||
if err != nil {
|
||||
return newUserPhoneResult(response, err)
|
||||
}
|
||||
ivBytes, err := base64.StdEncoding.DecodeString(param.Iv)
|
||||
if err != nil {
|
||||
return newUserPhoneResult(response, err)
|
||||
}
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
return newUserPhoneResult(response, err)
|
||||
}
|
||||
mode := cipher.NewCBCDecrypter(block, ivBytes)
|
||||
mode.CryptBlocks(cipherText, cipherText)
|
||||
cipherText, err = c.pkcs7Unpaid(cipherText, block.BlockSize())
|
||||
if err != nil {
|
||||
return newUserPhoneResult(response, err)
|
||||
}
|
||||
err = json.Unmarshal(cipherText, &response)
|
||||
if err != nil {
|
||||
return newUserPhoneResult(response, err)
|
||||
}
|
||||
if response.Watermark.AppID != c.getAppId() {
|
||||
return newUserPhoneResult(response, errors.New("c id not match"))
|
||||
}
|
||||
return newUserPhoneResult(response, err)
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go.dtapp.net/gorequest"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WxaGenerateSchemeResponse struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
UrlLink interface{} `json:"url_link"`
|
||||
}
|
||||
|
||||
type WxaGenerateSchemeResult struct {
|
||||
Result WxaGenerateSchemeResponse // 结果
|
||||
Body []byte // 内容
|
||||
Http gorequest.Response // 请求
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func newWxaGenerateSchemeResult(result WxaGenerateSchemeResponse, body []byte, http gorequest.Response, err error) *WxaGenerateSchemeResult {
|
||||
return &WxaGenerateSchemeResult{Result: result, Body: body, Http: http, Err: err}
|
||||
}
|
||||
|
||||
// WxaGenerateScheme 获取小程序 scheme 码,适用于短信、邮件、外部网页、微信内等拉起小程序的业务场景。通过该接口,可以选择生成到期失效和永久有效的小程序码,有数量限制,目前仅针对国内非个人主体的小程序开放
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-scheme/urlscheme.generate.html
|
||||
func (c *Client) WxaGenerateScheme(notMustParams ...gorequest.Params) *WxaGenerateSchemeResult {
|
||||
// 参数
|
||||
params := gorequest.NewParamsWith(notMustParams...)
|
||||
// 请求
|
||||
request, err := c.request(fmt.Sprintf(apiUrl+"/wxa/generatescheme?access_token=%s", c.getAccessToken()), params, http.MethodPost)
|
||||
// 定义
|
||||
var response WxaGenerateSchemeResponse
|
||||
err = json.Unmarshal(request.ResponseBody, &response)
|
||||
return newWxaGenerateSchemeResult(response, request.ResponseBody, request, err)
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go.dtapp.net/gorequest"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WxaQueryUrlLinkResponse struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
UrlLinkInfo struct {
|
||||
Appid string `json:"appid"`
|
||||
Path string `json:"path"`
|
||||
Query string `json:"query"`
|
||||
CreateTime int `json:"create_time"`
|
||||
ExpireTime int `json:"expire_time"`
|
||||
EnvVersion string `json:"env_version"`
|
||||
CloudBase struct {
|
||||
Env string `json:"env"`
|
||||
Doamin string `json:"doamin"`
|
||||
Path string `json:"path"`
|
||||
Query string `json:"query"`
|
||||
ResourceAppid string `json:"resource_appid"`
|
||||
} `json:"cloud_base"`
|
||||
} `json:"url_link_info"`
|
||||
UrlLinkQuota struct {
|
||||
LongTimeUsed int `json:"long_time_used"`
|
||||
LongTimeLimit int `json:"long_time_limit"`
|
||||
} `json:"url_link_quota"`
|
||||
}
|
||||
|
||||
type WxaQueryUrlLinkResult struct {
|
||||
Result WxaQueryUrlLinkResponse // 结果
|
||||
Body []byte // 内容
|
||||
Http gorequest.Response // 请求
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func newWxaQueryUrlLinkResult(result WxaQueryUrlLinkResponse, body []byte, http gorequest.Response, err error) *WxaQueryUrlLinkResult {
|
||||
return &WxaQueryUrlLinkResult{Result: result, Body: body, Http: http, Err: err}
|
||||
}
|
||||
|
||||
// WxaQueryUrlLink 查询小程序 url_link 配置,及长期有效 quota
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-link/urllink.query.html
|
||||
func (c *Client) WxaQueryUrlLink(urlLink string) *WxaQueryUrlLinkResult {
|
||||
// 参数
|
||||
param := gorequest.NewParams()
|
||||
param.Set("url_link", urlLink)
|
||||
params := gorequest.NewParamsWith(param)
|
||||
// 请求
|
||||
request, err := c.request(fmt.Sprintf(apiUrl+"/wxa/query_urllink?access_token=%s", c.getAccessToken()), params, http.MethodPost)
|
||||
// 定义
|
||||
var response WxaQueryUrlLinkResponse
|
||||
err = json.Unmarshal(request.ResponseBody, &response)
|
||||
return newWxaQueryUrlLinkResult(response, request.ResponseBody, request, err)
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go.dtapp.net/gorequest"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WxaQuerySchemeResponse struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
SchemeInfo struct {
|
||||
Appid string `json:"appid"`
|
||||
Path string `json:"path"`
|
||||
Query string `json:"query"`
|
||||
CreateTime int `json:"create_time"`
|
||||
ExpireTime int `json:"expire_time"`
|
||||
EnvVersion string `json:"env_version"`
|
||||
} `json:"scheme_info"`
|
||||
SchemeQuota struct {
|
||||
LongTimeUsed int `json:"long_time_used"`
|
||||
LongTimeLimit int `json:"long_time_limit"`
|
||||
} `json:"scheme_quota"`
|
||||
}
|
||||
|
||||
type WxaQuerySchemeResult struct {
|
||||
Result WxaQuerySchemeResponse // 结果
|
||||
Body []byte // 内容
|
||||
Http gorequest.Response // 请求
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func newWxaQuerySchemeResult(result WxaQuerySchemeResponse, body []byte, http gorequest.Response, err error) *WxaQuerySchemeResult {
|
||||
return &WxaQuerySchemeResult{Result: result, Body: body, Http: http, Err: err}
|
||||
}
|
||||
|
||||
// WxaQueryScheme 查询小程序 scheme 码,及长期有效 quota
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-scheme/urlscheme.query.html
|
||||
func (c *Client) WxaQueryScheme(notMustParams ...gorequest.Params) *WxaQuerySchemeResult {
|
||||
// 参数
|
||||
params := gorequest.NewParamsWith(notMustParams...)
|
||||
// 请求
|
||||
request, err := c.request(fmt.Sprintf(apiUrl+"/wxa/queryscheme?access_token=%s", c.getAccessToken()), params, http.MethodPost)
|
||||
// 定义
|
||||
var response WxaQuerySchemeResponse
|
||||
err = json.Unmarshal(request.ResponseBody, &response)
|
||||
return newWxaQuerySchemeResult(response, request.ResponseBody, request, err)
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package wechatminiprogram
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"go.dtapp.net/gorequest"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WxaApiFeedbackListResponse struct {
|
||||
List []struct {
|
||||
RecordId int `json:"record_id"`
|
||||
CreateTime int `json:"create_time"`
|
||||
Content string `json:"content"`
|
||||
Phone string `json:"phone"`
|
||||
Openid string `json:"openid"`
|
||||
Nickname string `json:"nickname"`
|
||||
HeadUrl string `json:"head_url"`
|
||||
Type int `json:"type"`
|
||||
MediaIds []string `json:"mediaIds"`
|
||||
} `json:"list"`
|
||||
TotalNum int `json:"total_num"`
|
||||
Errcode int `json:"errcode"`
|
||||
RpcCount int `json:"__rpcCount"`
|
||||
}
|
||||
|
||||
type WxaApiFeedbackListResult struct {
|
||||
Result WxaApiFeedbackListResponse // 结果
|
||||
Body []byte // 内容
|
||||
Http gorequest.Response // 请求
|
||||
Err error // 错误
|
||||
}
|
||||
|
||||
func newWxaApiFeedbackListResult(result WxaApiFeedbackListResponse, body []byte, http gorequest.Response, err error) *WxaApiFeedbackListResult {
|
||||
return &WxaApiFeedbackListResult{Result: result, Body: body, Http: http, Err: err}
|
||||
}
|
||||
|
||||
// WxaApiFeedbackList 获取用户反馈列表
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/operation/operation.getFeedback.html
|
||||
func (c *Client) WxaApiFeedbackList(notMustParams ...gorequest.Params) *WxaApiFeedbackListResult {
|
||||
// 参数
|
||||
params := gorequest.NewParamsWith(notMustParams...)
|
||||
params.Set("access_token", c.getAccessToken())
|
||||
// 请求
|
||||
request, err := c.request(apiUrl+"/wxaapi/feedback/list", params, http.MethodGet)
|
||||
// 定义
|
||||
var response WxaApiFeedbackListResponse
|
||||
err = json.Unmarshal(request.ResponseBody, &response)
|
||||
return newWxaApiFeedbackListResult(response, request.ResponseBody, request, err)
|
||||
}
|
Loading…
Reference in new issue