增加vendor
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
||||
# 微信小程序
|
||||
|
||||
[官方文档](https://developers.weixin.qq.com/miniprogram/dev/framework/)
|
||||
|
||||
|
||||
## 包说明
|
||||
- analysis 数据分析相关API
|
||||
|
||||
## 快速入门
|
||||
```go
|
||||
wc := wechat.NewWechat()
|
||||
memory := cache.NewMemory()
|
||||
cfg := &miniConfig.Config{
|
||||
AppID: "xxx",
|
||||
AppSecret: "xxx",
|
||||
Cache: memory,
|
||||
}
|
||||
miniprogram := wc.GetMiniProgram(cfg)
|
||||
miniprogram.GetAnalysis().GetAnalysisDailyRetain()
|
||||
```
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
package analysis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/silenceper/wechat/v2/miniprogram/context"
|
||||
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// 获取用户访问小程序日留存
|
||||
getAnalysisDailyRetainURL = "https://api.weixin.qq.com/datacube/getweanalysisappiddailyretaininfo?access_token=%s"
|
||||
// 获取用户访问小程序月留存
|
||||
getAnalysisMonthlyRetainURL = "https://api.weixin.qq.com/datacube/getweanalysisappidmonthlyretaininfo?access_token=%s"
|
||||
// 获取用户访问小程序周留存
|
||||
getAnalysisWeeklyRetainURL = "https://api.weixin.qq.com/datacube/getweanalysisappidweeklyretaininfo?access_token=%s"
|
||||
// 获取用户访问小程序数据概况
|
||||
getAnalysisDailySummaryURL = "https://api.weixin.qq.com/datacube/getweanalysisappiddailysummarytrend?access_token=%s"
|
||||
// 获取用户访问小程序数据日趋势
|
||||
getAnalysisDailyVisitTrendURL = "https://api.weixin.qq.com/datacube/getweanalysisappiddailyvisittrend?access_token=%s"
|
||||
// 获取用户访问小程序数据月趋势
|
||||
getAnalysisMonthlyVisitTrendURL = "https://api.weixin.qq.com/datacube/getweanalysisappidmonthlyvisittrend?access_token=%s"
|
||||
// 获取用户访问小程序数据周趋势
|
||||
getAnalysisWeeklyVisitTrendURL = "https://api.weixin.qq.com/datacube/getweanalysisappidweeklyvisittrend?access_token=%s"
|
||||
// 获取小程序新增或活跃用户的画像分布数据
|
||||
getAnalysisUserPortraitURL = "https://api.weixin.qq.com/datacube/getweanalysisappiduserportrait?access_token=%s"
|
||||
// 获取用户小程序访问分布数据
|
||||
getAnalysisVisitDistributionURL = "https://api.weixin.qq.com/datacube/getweanalysisappidvisitdistribution?access_token=%s"
|
||||
// 访问页面
|
||||
getAnalysisVisitPageURL = "https://api.weixin.qq.com/datacube/getweanalysisappidvisitpage?access_token=%s"
|
||||
)
|
||||
|
||||
// Analysis analyis 数据分析
|
||||
type Analysis struct {
|
||||
*context.Context
|
||||
}
|
||||
|
||||
// NewAnalysis new
|
||||
func NewAnalysis(ctx *context.Context) *Analysis {
|
||||
return &Analysis{ctx}
|
||||
}
|
||||
|
||||
// fetchData 拉取统计数据
|
||||
func (analysis *Analysis) fetchData(urlStr string, body interface{}) (response []byte, err error) {
|
||||
var accessToken string
|
||||
accessToken, err = analysis.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
urlStr = fmt.Sprintf(urlStr, accessToken)
|
||||
response, err = util.PostJSON(urlStr, body)
|
||||
return
|
||||
}
|
||||
|
||||
// RetainItem 留存项结构
|
||||
type RetainItem struct {
|
||||
Key int `json:"key"` // 标识,0开始表示当天,1表示1甜后,以此类推
|
||||
Value int `json:"value"` // key对应日期的新增用户数/活跃用户数(key=0时)或留存用户数(k>0时)
|
||||
}
|
||||
|
||||
// ResAnalysisRetain 小程序留存数据返回
|
||||
type ResAnalysisRetain struct {
|
||||
util.CommonError
|
||||
RefDate string `json:"ref_date"` // 日期
|
||||
VisitUVNew []RetainItem `json:"visit_uv_new"` // 新增用户留存
|
||||
VisitUV []RetainItem `json:"visit_uv"` // 活跃用户留存
|
||||
}
|
||||
|
||||
// getAnalysisRetain 获取用户访问小程序留存数据(日、月、周)
|
||||
func (analysis *Analysis) getAnalysisRetain(urlStr string, beginDate, endDate string) (result ResAnalysisRetain, err error) {
|
||||
body := map[string]string{
|
||||
"begin_date": beginDate,
|
||||
"end_date": endDate,
|
||||
}
|
||||
response, err := analysis.fetchData(urlStr, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(response, &result)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if result.ErrCode != 0 {
|
||||
err = fmt.Errorf("getAnalysisRetain error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetAnalysisDailyRetain 获取用户访问小程序日留存
|
||||
func (analysis *Analysis) GetAnalysisDailyRetain(beginDate, endDate string) (result ResAnalysisRetain, err error) {
|
||||
return analysis.getAnalysisRetain(getAnalysisDailyRetainURL, beginDate, endDate)
|
||||
}
|
||||
|
||||
// GetAnalysisMonthlyRetain 获取用户访问小程序月留存
|
||||
func (analysis *Analysis) GetAnalysisMonthlyRetain(beginDate, endDate string) (result ResAnalysisRetain, err error) {
|
||||
return analysis.getAnalysisRetain(getAnalysisMonthlyRetainURL, beginDate, endDate)
|
||||
}
|
||||
|
||||
// GetAnalysisWeeklyRetain 获取用户访问小程序周留存
|
||||
func (analysis *Analysis) GetAnalysisWeeklyRetain(beginDate, endDate string) (result ResAnalysisRetain, err error) {
|
||||
return analysis.getAnalysisRetain(getAnalysisWeeklyRetainURL, beginDate, endDate)
|
||||
}
|
||||
|
||||
// ResAnalysisDailySummary 小程序访问数据概况
|
||||
type ResAnalysisDailySummary struct {
|
||||
util.CommonError
|
||||
List []struct {
|
||||
RefDate string `json:"ref_date"` // 日期
|
||||
VisitTotal int `json:"visit_total"` // 累计用户数
|
||||
SharePV int `json:"share_pv"` // 转发次数
|
||||
ShareUV int `json:"share_uv"` // 转发人数
|
||||
} `json:"list"`
|
||||
}
|
||||
|
||||
// GetAnalysisDailySummary 获取用户访问小程序数据概况
|
||||
func (analysis *Analysis) GetAnalysisDailySummary(beginDate, endDate string) (result ResAnalysisDailySummary, err error) {
|
||||
body := map[string]string{
|
||||
"begin_date": beginDate,
|
||||
"end_date": endDate,
|
||||
}
|
||||
response, err := analysis.fetchData(getAnalysisDailySummaryURL, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = json.Unmarshal(response, &result)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if result.ErrCode != 0 {
|
||||
err = fmt.Errorf("GetAnalysisDailySummary error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ResAnalysisVisitTrend 小程序访问数据趋势(日、月、周)
|
||||
type ResAnalysisVisitTrend struct {
|
||||
util.CommonError
|
||||
List []struct {
|
||||
RefDate string `json:"ref_date"` // 日期
|
||||
SessionCnt int `json:"session_cnt"` // 打开次数
|
||||
VisitPV int `json:"visit_pv"` // 访问次数
|
||||
VisitUV int `json:"visit_uv"` // 访问人数
|
||||
VisitUVNew int `json:"visit_uv_new"` // 新用户数
|
||||
StayTimeUV float64 `json:"stay_time_uv"` // 人均停留时长
|
||||
StayTimeSession float64 `json:"stay_time_session"` // 次均停留时常
|
||||
VisitDepth float64 `json:"visit_depth"` // 平均访问深度
|
||||
} `json:"list"`
|
||||
}
|
||||
|
||||
// getAnalysisRetain 获取小程序访问数据趋势(日、月、周)
|
||||
func (analysis *Analysis) getAnalysisVisitTrend(urlStr string, beginDate, endDate string) (result ResAnalysisVisitTrend, err error) {
|
||||
body := map[string]string{
|
||||
"begin_date": beginDate,
|
||||
"end_date": endDate,
|
||||
}
|
||||
response, err := analysis.fetchData(urlStr, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(response, &result)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if result.ErrCode != 0 {
|
||||
err = fmt.Errorf("getAnalysisVisitTrend error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetAnalysisDailyVisitTrend 获取用户访问小程序数据日趋势
|
||||
func (analysis *Analysis) GetAnalysisDailyVisitTrend(beginDate, endDate string) (result ResAnalysisVisitTrend, err error) {
|
||||
return analysis.getAnalysisVisitTrend(getAnalysisDailyVisitTrendURL, beginDate, endDate)
|
||||
}
|
||||
|
||||
// GetAnalysisMonthlyVisitTrend 获取用户访问小程序数据月趋势
|
||||
func (analysis *Analysis) GetAnalysisMonthlyVisitTrend(beginDate, endDate string) (result ResAnalysisVisitTrend, err error) {
|
||||
return analysis.getAnalysisVisitTrend(getAnalysisMonthlyVisitTrendURL, beginDate, endDate)
|
||||
}
|
||||
|
||||
// GetAnalysisWeeklyVisitTrend 获取用户访问小程序数据周趋势
|
||||
func (analysis *Analysis) GetAnalysisWeeklyVisitTrend(beginDate, endDate string) (result ResAnalysisVisitTrend, err error) {
|
||||
return analysis.getAnalysisVisitTrend(getAnalysisWeeklyVisitTrendURL, beginDate, endDate)
|
||||
}
|
||||
|
||||
// UserPortraitItem 用户画像项目
|
||||
type UserPortraitItem struct {
|
||||
ID int `json:"id"` // 属性值id
|
||||
Name string `json:"name"` // 属性值名称
|
||||
AccessSourceVisitUV int `json:"access_source_visit_uv"` // 该场景访问uv
|
||||
}
|
||||
|
||||
// UserPortrait 用户画像
|
||||
type UserPortrait struct {
|
||||
Index int `json:"index"` // 分布类型
|
||||
Province []UserPortraitItem `json:"province"` // 省份,如北京、广东等
|
||||
City []UserPortraitItem `json:"city"` // 城市,如北京、广州等
|
||||
Genders []UserPortraitItem `json:"genders"` // 性别,包括男、女、未知
|
||||
Platforms []UserPortraitItem `json:"platforms"` // 终端类型,包括iPhone, android, 其他
|
||||
Devices []UserPortraitItem `json:"devices"` // 机型,如苹果iPhone 6, OPPO R9等
|
||||
Ages []UserPortraitItem `json:"ages"` // 年龄,包括17岁以下、18-24对等区间
|
||||
}
|
||||
|
||||
// ResAnalysisUserPortrait 小程序新增或活跃用户的画像分布数据返回
|
||||
type ResAnalysisUserPortrait struct {
|
||||
util.CommonError
|
||||
RefDate string `json:"ref_date"` // 日期
|
||||
VisitUVNew UserPortrait `json:"visit_uv_new"` // 新用户画像
|
||||
VisitUV UserPortrait `json:"visit_uv"` // 活跃用户画像
|
||||
}
|
||||
|
||||
// GetAnalysisUserPortrait 获取小程序新增或活跃用户的画像分布数据
|
||||
func (analysis *Analysis) GetAnalysisUserPortrait(beginDate, endDate string) (result ResAnalysisUserPortrait, err error) {
|
||||
body := map[string]string{
|
||||
"begin_date": beginDate,
|
||||
"end_date": endDate,
|
||||
}
|
||||
response, err := analysis.fetchData(getAnalysisUserPortraitURL, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(response, &result)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if result.ErrCode != 0 {
|
||||
err = fmt.Errorf("GetAnalysisUserPortrait error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// VisitDistributionIndexItem 访问分数数据结构
|
||||
type VisitDistributionIndexItem struct {
|
||||
Key int `json:"key"` // 场景id
|
||||
Value int `json:"value"` // 该场景id访问pv
|
||||
AccessSourceVisitUV int `json:"access_source_visit_uv"` // 该场景id访问uv
|
||||
}
|
||||
|
||||
// VisitDistributionIndex 访问分布单分布类型数据
|
||||
type VisitDistributionIndex struct {
|
||||
Index string `json:"index"` // 分布类型
|
||||
ItemList []VisitDistributionIndexItem `json:"item_list"` // 分布数据列表
|
||||
}
|
||||
|
||||
// ResAnalysisVisitDistribution 小程序访问分布数据返回
|
||||
type ResAnalysisVisitDistribution struct {
|
||||
util.CommonError
|
||||
RefDate string `json:"ref_date"` // 日期
|
||||
List []VisitDistributionIndex `json:"list"` // 数据列表
|
||||
}
|
||||
|
||||
// GetAnalysisVisitDistribution 获取用户小程序访问分布数据
|
||||
func (analysis *Analysis) GetAnalysisVisitDistribution(beginDate, endDate string) (result ResAnalysisVisitDistribution, err error) {
|
||||
body := map[string]string{
|
||||
"begin_date": beginDate,
|
||||
"end_date": endDate,
|
||||
}
|
||||
response, err := analysis.fetchData(getAnalysisVisitDistributionURL, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(response, &result)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if result.ErrCode != 0 {
|
||||
err = fmt.Errorf("GetAnalysisVisitDistribution error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// VisitPageItem 访问单个页面的数据结构
|
||||
type VisitPageItem struct {
|
||||
PagePath string `json:"page_path"` // 页面路径
|
||||
PageVisitPV int `json:"page_visit_pv"` // 访问次数
|
||||
PageVisitUV int `json:"page_visit_uv"` // 访问人数
|
||||
PageStaytimePV float64 `json:"page_staytime_pv"` // 次均停留时常
|
||||
EntrypagePV int `json:"entrypage_pv"` // 进入页次数
|
||||
ExitpagePV int `json:"exitpage_pv"` // 退出页次数
|
||||
PageSharePV int `json:"page_share_pv"` // 转发次数
|
||||
PageShareUV int `json:"page_share_uv"` // 转发人数
|
||||
}
|
||||
|
||||
// ResAnalysisVisitPage 访问小程序页面访问数据返回
|
||||
type ResAnalysisVisitPage struct {
|
||||
util.CommonError
|
||||
RefDate string `json:"ref_date"` // 日期
|
||||
List []VisitPageItem `json:"list"` // 数据列表
|
||||
}
|
||||
|
||||
// GetAnalysisVisitPage 获取小程序页面访问数据
|
||||
func (analysis *Analysis) GetAnalysisVisitPage(beginDate, endDate string) (result ResAnalysisVisitPage, err error) {
|
||||
body := map[string]string{
|
||||
"begin_date": beginDate,
|
||||
"end_date": endDate,
|
||||
}
|
||||
response, err := analysis.fetchData(getAnalysisVisitPageURL, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(response, &result)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if result.ErrCode != 0 {
|
||||
err = fmt.Errorf("GetAnalysisVisitPage error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
context2 "context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/silenceper/wechat/v2/miniprogram/context"
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
const (
|
||||
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 登录/用户信息
|
||||
type Auth struct {
|
||||
*context.Context
|
||||
}
|
||||
|
||||
// NewAuth new auth
|
||||
func NewAuth(ctx *context.Context) *Auth {
|
||||
return &Auth{ctx}
|
||||
}
|
||||
|
||||
// ResCode2Session 登录凭证校验的返回结果
|
||||
type ResCode2Session struct {
|
||||
util.CommonError
|
||||
|
||||
OpenID string `json:"openid"` // 用户唯一标识
|
||||
SessionKey string `json:"session_key"` // 会话密钥
|
||||
UnionID string `json:"unionid"` // 用户在开放平台的唯一标识符,在满足UnionID下发条件的情况下会返回
|
||||
}
|
||||
|
||||
// RspCheckEncryptedData .
|
||||
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) {
|
||||
return auth.Code2SessionContext(context2.Background(), jsCode)
|
||||
}
|
||||
|
||||
// Code2SessionContext 登录凭证校验。
|
||||
func (auth *Auth) Code2SessionContext(ctx context2.Context, jsCode string) (result ResCode2Session, err error) {
|
||||
var response []byte
|
||||
if response, err = util.HTTPGetContext(ctx, fmt.Sprintf(code2SessionURL, auth.AppID, auth.AppSecret, jsCode)); err != nil {
|
||||
return
|
||||
}
|
||||
if err = json.Unmarshal(response, &result); err != nil {
|
||||
return
|
||||
}
|
||||
if result.ErrCode != 0 {
|
||||
err = fmt.Errorf("Code2Session error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetPaidUnionID 用户支付完成后,获取该用户的 UnionId,无需用户授权
|
||||
func (auth *Auth) GetPaidUnionID() {
|
||||
// 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
|
||||
}
|
||||
if response, err = util.HTTPPostContext(ctx, fmt.Sprintf(checkEncryptedDataURL, at), "encrypted_msg_hash="+encryptedMsgHash); 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"` // 数据水印
|
||||
}
|
||||
|
||||
// GetPhoneNumber 小程序通过code获取用户手机号
|
||||
func (auth *Auth) GetPhoneNumber(code string) (result GetPhoneNumberResponse, err error) {
|
||||
var response []byte
|
||||
var (
|
||||
at string
|
||||
)
|
||||
if at, err = auth.GetAccessToken(); err != nil {
|
||||
return
|
||||
}
|
||||
body := map[string]interface{}{
|
||||
"code": code,
|
||||
}
|
||||
if response, err = util.PostJSON(fmt.Sprintf(getPhoneNumber, at), body); err != nil {
|
||||
return
|
||||
}
|
||||
if err = util.DecodeWithError(response, &result, "phonenumber.getPhoneNumber"); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// Package config 小程序config配置
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/silenceper/wechat/v2/cache"
|
||||
)
|
||||
|
||||
// Config .config for 小程序
|
||||
type Config struct {
|
||||
AppID string `json:"app_id"` // appid
|
||||
AppSecret string `json:"app_secret"` // appsecret
|
||||
Cache cache.Cache
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package content
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/silenceper/wechat/v2/miniprogram/context"
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
const (
|
||||
checkTextURL = "https://api.weixin.qq.com/wxa/msg_sec_check?access_token=%s"
|
||||
checkImageURL = "https://api.weixin.qq.com/wxa/img_sec_check?access_token=%s"
|
||||
)
|
||||
|
||||
// Content 内容安全
|
||||
type Content struct {
|
||||
*context.Context
|
||||
}
|
||||
|
||||
// NewContent 内容安全接口
|
||||
func NewContent(ctx *context.Context) *Content {
|
||||
return &Content{ctx}
|
||||
}
|
||||
|
||||
// CheckText 检测文字
|
||||
// @text 需要检测的文字
|
||||
func (content *Content) CheckText(text string) error {
|
||||
accessToken, err := content.GetAccessToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response, err := util.PostJSON(
|
||||
fmt.Sprintf(checkTextURL, accessToken),
|
||||
map[string]string{
|
||||
"content": text,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return util.DecodeWithCommonError(response, "ContentCheckText")
|
||||
}
|
||||
|
||||
// CheckImage 检测图片
|
||||
// 所传参数为要检测的图片文件的绝对路径,图片格式支持PNG、JPEG、JPG、GIF, 像素不超过 750 x 1334,同时文件大小以不超过 300K 为宜,否则可能报错
|
||||
// @media 图片文件的绝对路径
|
||||
func (content *Content) CheckImage(media string) error {
|
||||
accessToken, err := content.GetAccessToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response, err := util.PostFile(
|
||||
"media",
|
||||
media,
|
||||
fmt.Sprintf(checkImageURL, accessToken),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return util.DecodeWithCommonError(response, "ContentCheckImage")
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/silenceper/wechat/v2/credential"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/config"
|
||||
)
|
||||
|
||||
// Context struct
|
||||
type Context struct {
|
||||
*config.Config
|
||||
credential.AccessTokenHandle
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
package encryptor
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/silenceper/wechat/v2/miniprogram/context"
|
||||
)
|
||||
|
||||
// Encryptor struct
|
||||
type Encryptor struct {
|
||||
*context.Context
|
||||
}
|
||||
|
||||
// NewEncryptor 实例
|
||||
func NewEncryptor(context *context.Context) *Encryptor {
|
||||
basic := new(Encryptor)
|
||||
basic.Context = context
|
||||
return basic
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrAppIDNotMatch appid不匹配
|
||||
ErrAppIDNotMatch = errors.New("app id not match")
|
||||
// ErrInvalidBlockSize block size不合法
|
||||
ErrInvalidBlockSize = errors.New("invalid block size")
|
||||
// ErrInvalidPKCS7Data PKCS7数据不合法
|
||||
ErrInvalidPKCS7Data = errors.New("invalid PKCS7 data")
|
||||
// ErrInvalidPKCS7Padding 输入padding失败
|
||||
ErrInvalidPKCS7Padding = errors.New("invalid padding on input")
|
||||
)
|
||||
|
||||
// PlainData 用户信息/手机号信息
|
||||
type PlainData struct {
|
||||
OpenID string `json:"openId"`
|
||||
UnionID string `json:"unionId"`
|
||||
NickName string `json:"nickName"`
|
||||
Gender int `json:"gender"`
|
||||
City string `json:"city"`
|
||||
Province string `json:"province"`
|
||||
Country string `json:"country"`
|
||||
AvatarURL string `json:"avatarUrl"`
|
||||
Language string `json:"language"`
|
||||
PhoneNumber string `json:"phoneNumber"`
|
||||
OpenGID string `json:"openGId"`
|
||||
MsgTicket string `json:"msgTicket"`
|
||||
PurePhoneNumber string `json:"purePhoneNumber"`
|
||||
CountryCode string `json:"countryCode"`
|
||||
Watermark struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
AppID string `json:"appid"`
|
||||
} `json:"watermark"`
|
||||
}
|
||||
|
||||
// pkcs7Unpad returns slice of the original data without padding
|
||||
func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
|
||||
if blockSize <= 0 {
|
||||
return nil, ErrInvalidBlockSize
|
||||
}
|
||||
if len(data)%blockSize != 0 || len(data) == 0 {
|
||||
return nil, ErrInvalidPKCS7Data
|
||||
}
|
||||
c := data[len(data)-1]
|
||||
n := int(c)
|
||||
if n == 0 || n > len(data) {
|
||||
return nil, ErrInvalidPKCS7Padding
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
if data[len(data)-n+i] != c {
|
||||
return nil, ErrInvalidPKCS7Padding
|
||||
}
|
||||
}
|
||||
return data[:len(data)-n], nil
|
||||
}
|
||||
|
||||
// GetCipherText returns slice of the cipher text
|
||||
func GetCipherText(sessionKey, encryptedData, iv string) ([]byte, error) {
|
||||
aesKey, err := base64.StdEncoding.DecodeString(sessionKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cipherText, err := base64.StdEncoding.DecodeString(encryptedData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ivBytes, err := base64.StdEncoding.DecodeString(iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ivBytes) != aes.BlockSize {
|
||||
return nil, fmt.Errorf("bad iv length %d", len(ivBytes))
|
||||
}
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mode := cipher.NewCBCDecrypter(block, ivBytes)
|
||||
mode.CryptBlocks(cipherText, cipherText)
|
||||
cipherText, err = pkcs7Unpad(cipherText, block.BlockSize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cipherText, nil
|
||||
}
|
||||
|
||||
// Decrypt 解密数据
|
||||
func (encryptor *Encryptor) Decrypt(sessionKey, encryptedData, iv string) (*PlainData, error) {
|
||||
cipherText, err := GetCipherText(sessionKey, encryptedData, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var plainData PlainData
|
||||
err = json.Unmarshal(cipherText, &plainData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if plainData.Watermark.AppID != encryptor.AppID {
|
||||
return nil, ErrAppIDNotMatch
|
||||
}
|
||||
return &plainData, nil
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package message
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
// MsgType 基本消息类型
|
||||
type MsgType string
|
||||
|
||||
// EventType 事件类型
|
||||
type EventType string
|
||||
|
||||
// InfoType 第三方平台授权事件类型
|
||||
type InfoType string
|
||||
|
||||
const (
|
||||
// MsgTypeText 文本消息
|
||||
MsgTypeText MsgType = "text"
|
||||
// MsgTypeImage 图片消息
|
||||
MsgTypeImage = "image"
|
||||
// MsgTypeLink 图文链接
|
||||
MsgTypeLink = "link"
|
||||
// MsgTypeMiniProgramPage 小程序卡片
|
||||
MsgTypeMiniProgramPage = "miniprogrampage"
|
||||
)
|
||||
|
||||
// CommonToken 消息中通用的结构
|
||||
type CommonToken struct {
|
||||
XMLName xml.Name `xml:"xml"`
|
||||
ToUserName string `xml:"ToUserName"`
|
||||
FromUserName string `xml:"FromUserName"`
|
||||
CreateTime int64 `xml:"CreateTime"`
|
||||
MsgType MsgType `xml:"MsgType"`
|
||||
}
|
||||
|
||||
// MiniProgramMixMessage 小程序回调的消息结构
|
||||
type MiniProgramMixMessage struct {
|
||||
CommonToken
|
||||
|
||||
MsgID int64 `xml:"MsgId"`
|
||||
|
||||
// 文本消息
|
||||
Content string `xml:"Content"`
|
||||
|
||||
// 图片消息
|
||||
PicURL string `xml:"PicUrl"`
|
||||
MediaID string `xml:"MediaId"`
|
||||
|
||||
// 小程序卡片消息
|
||||
Title string `xml:"Title"`
|
||||
AppID string `xml:"AppId"`
|
||||
PagePath string `xml:"PagePath"`
|
||||
ThumbURL string `xml:"ThumbUrl"`
|
||||
ThumbMediaID string `xml:"ThumbMediaId"`
|
||||
|
||||
// 进入会话事件
|
||||
Event string `xml:"Event"`
|
||||
SessionFrom string `xml:"SessionFrom"`
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/silenceper/wechat/v2/miniprogram/context"
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
const (
|
||||
customerSendMessage = "https://api.weixin.qq.com/cgi-bin/message/custom/send"
|
||||
)
|
||||
|
||||
// Manager 消息管理者,可以发送消息
|
||||
type Manager struct {
|
||||
*context.Context
|
||||
}
|
||||
|
||||
// NewCustomerMessageManager 实例化消息管理者
|
||||
func NewCustomerMessageManager(context *context.Context) *Manager {
|
||||
return &Manager{
|
||||
context,
|
||||
}
|
||||
}
|
||||
|
||||
// MediaText 文本消息的文字
|
||||
type MediaText struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// MediaResource 消息使用的临时素材id
|
||||
type MediaResource struct {
|
||||
MediaID string `json:"media_id"`
|
||||
}
|
||||
|
||||
// MediaMiniprogrampage 小程序卡片
|
||||
type MediaMiniprogrampage struct {
|
||||
Title string `json:"title"`
|
||||
Appid string `json:"appid"`
|
||||
Pagepath string `json:"pagepath"`
|
||||
ThumbMediaID string `json:"thumb_media_id"`
|
||||
}
|
||||
|
||||
// MediaLink 发送图文链接
|
||||
type MediaLink struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
URL string `json:"url"`
|
||||
ThumbURL string `json:"thumb_url"`
|
||||
}
|
||||
|
||||
// CustomerMessage 客服消息
|
||||
type CustomerMessage struct {
|
||||
ToUser string `json:"touser"` // 接受者OpenID
|
||||
Msgtype MsgType `json:"msgtype"` // 客服消息类型
|
||||
Text *MediaText `json:"text,omitempty"` // 可选
|
||||
Image *MediaResource `json:"image,omitempty"` // 可选
|
||||
Link *MediaLink `json:"link,omitempty"` // 可选
|
||||
Miniprogrampage *MediaMiniprogrampage `json:"miniprogrampage,omitempty"` // 可选
|
||||
}
|
||||
|
||||
// NewCustomerTextMessage 文本消息结构体构造方法
|
||||
func NewCustomerTextMessage(toUser, text string) *CustomerMessage {
|
||||
return &CustomerMessage{
|
||||
ToUser: toUser,
|
||||
Msgtype: MsgTypeText,
|
||||
Text: &MediaText{
|
||||
Content: text,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewCustomerImgMessage 图片消息的构造方法
|
||||
func NewCustomerImgMessage(toUser, mediaID string) *CustomerMessage {
|
||||
return &CustomerMessage{
|
||||
ToUser: toUser,
|
||||
Msgtype: MsgTypeImage,
|
||||
Image: &MediaResource{
|
||||
MediaID: mediaID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewCustomerLinkMessage 图文链接消息的构造方法
|
||||
func NewCustomerLinkMessage(toUser, title, description, url, thumbURL string) *CustomerMessage {
|
||||
return &CustomerMessage{
|
||||
ToUser: toUser,
|
||||
Msgtype: MsgTypeLink,
|
||||
Link: &MediaLink{
|
||||
Title: title,
|
||||
Description: description,
|
||||
URL: url,
|
||||
ThumbURL: thumbURL,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewCustomerMiniprogrampageMessage 小程序卡片消息的构造方法
|
||||
func NewCustomerMiniprogrampageMessage(toUser, title, pagepath, thumbMediaID string) *CustomerMessage {
|
||||
return &CustomerMessage{
|
||||
ToUser: toUser,
|
||||
Msgtype: MsgTypeMiniProgramPage,
|
||||
Miniprogrampage: &MediaMiniprogrampage{
|
||||
Title: title,
|
||||
Pagepath: pagepath,
|
||||
ThumbMediaID: thumbMediaID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Send 发送客服消息
|
||||
func (manager *Manager) Send(msg *CustomerMessage) error {
|
||||
accessToken, err := manager.Context.GetAccessToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", customerSendMessage, accessToken)
|
||||
response, err := util.PostJSON(uri, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return util.DecodeWithCommonError(response, "SendCustomerMessage")
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
package miniprogram
|
||||
|
||||
import (
|
||||
"github.com/silenceper/wechat/v2/credential"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/analysis"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/auth"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/config"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/content"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/context"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/encryptor"
|
||||
"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/shortlink"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/subscribe"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/tcb"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/urllink"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/werun"
|
||||
)
|
||||
|
||||
// MiniProgram 微信小程序相关API
|
||||
type MiniProgram struct {
|
||||
ctx *context.Context
|
||||
}
|
||||
|
||||
// NewMiniProgram 实例化小程序API
|
||||
func NewMiniProgram(cfg *config.Config) *MiniProgram {
|
||||
defaultAkHandle := credential.NewDefaultAccessToken(cfg.AppID, cfg.AppSecret, credential.CacheKeyMiniProgramPrefix, cfg.Cache)
|
||||
ctx := &context.Context{
|
||||
Config: cfg,
|
||||
AccessTokenHandle: defaultAkHandle,
|
||||
}
|
||||
return &MiniProgram{ctx}
|
||||
}
|
||||
|
||||
// SetAccessTokenHandle 自定义access_token获取方式
|
||||
func (miniProgram *MiniProgram) SetAccessTokenHandle(accessTokenHandle credential.AccessTokenHandle) {
|
||||
miniProgram.ctx.AccessTokenHandle = accessTokenHandle
|
||||
}
|
||||
|
||||
// GetContext get Context
|
||||
func (miniProgram *MiniProgram) GetContext() *context.Context {
|
||||
return miniProgram.ctx
|
||||
}
|
||||
|
||||
// GetEncryptor 小程序加解密
|
||||
func (miniProgram *MiniProgram) GetEncryptor() *encryptor.Encryptor {
|
||||
return encryptor.NewEncryptor(miniProgram.ctx)
|
||||
}
|
||||
|
||||
// GetAuth 登录/用户信息相关接口
|
||||
func (miniProgram *MiniProgram) GetAuth() *auth.Auth {
|
||||
return auth.NewAuth(miniProgram.ctx)
|
||||
}
|
||||
|
||||
// GetAnalysis 数据分析
|
||||
func (miniProgram *MiniProgram) GetAnalysis() *analysis.Analysis {
|
||||
return analysis.NewAnalysis(miniProgram.ctx)
|
||||
}
|
||||
|
||||
// GetPrivacy 小程序隐私协议相关API
|
||||
func (miniProgram *MiniProgram) GetPrivacy() *privacy.Privacy {
|
||||
return privacy.NewPrivacy(miniProgram.ctx)
|
||||
}
|
||||
|
||||
// GetQRCode 小程序码相关API
|
||||
func (miniProgram *MiniProgram) GetQRCode() *qrcode.QRCode {
|
||||
return qrcode.NewQRCode(miniProgram.ctx)
|
||||
}
|
||||
|
||||
// GetTcb 小程序云开发API
|
||||
func (miniProgram *MiniProgram) GetTcb() *tcb.Tcb {
|
||||
return tcb.NewTcb(miniProgram.ctx)
|
||||
}
|
||||
|
||||
// GetSubscribe 小程序订阅消息
|
||||
func (miniProgram *MiniProgram) GetSubscribe() *subscribe.Subscribe {
|
||||
return subscribe.NewSubscribe(miniProgram.ctx)
|
||||
}
|
||||
|
||||
// GetCustomerMessage 客服消息接口
|
||||
func (miniProgram *MiniProgram) GetCustomerMessage() *message.Manager {
|
||||
return message.NewCustomerMessageManager(miniProgram.ctx)
|
||||
}
|
||||
|
||||
// GetWeRun 微信运动接口
|
||||
func (miniProgram *MiniProgram) GetWeRun() *werun.WeRun {
|
||||
return werun.NewWeRun(miniProgram.ctx)
|
||||
}
|
||||
|
||||
// GetContentSecurity 内容安全接口
|
||||
func (miniProgram *MiniProgram) GetContentSecurity() *content.Content {
|
||||
return content.NewContent(miniProgram.ctx)
|
||||
}
|
||||
|
||||
// GetURLLink 小程序URL Link接口
|
||||
func (miniProgram *MiniProgram) GetURLLink() *urllink.URLLink {
|
||||
return urllink.NewURLLink(miniProgram.ctx)
|
||||
}
|
||||
|
||||
// GetShortLink 小程序短链接口
|
||||
func (miniProgram *MiniProgram) GetShortLink() *shortlink.ShortLink {
|
||||
return shortlink.NewShortLink(miniProgram.ctx)
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
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
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package qrcode
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/silenceper/wechat/v2/miniprogram/context"
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
const (
|
||||
createWXAQRCodeURL = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=%s"
|
||||
getWXACodeURL = "https://api.weixin.qq.com/wxa/getwxacode?access_token=%s"
|
||||
getWXACodeUnlimitURL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s"
|
||||
)
|
||||
|
||||
// QRCode struct
|
||||
type QRCode struct {
|
||||
*context.Context
|
||||
}
|
||||
|
||||
// NewQRCode 实例
|
||||
func NewQRCode(context *context.Context) *QRCode {
|
||||
qrCode := new(QRCode)
|
||||
qrCode.Context = context
|
||||
return qrCode
|
||||
}
|
||||
|
||||
// Color QRCode color
|
||||
type Color struct {
|
||||
R string `json:"r"`
|
||||
G string `json:"g"`
|
||||
B string `json:"b"`
|
||||
}
|
||||
|
||||
// QRCoder 小程序码参数
|
||||
type QRCoder struct {
|
||||
// page 必须是已经发布的小程序存在的页面,根路径前不要填加 /,不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面
|
||||
Page string `json:"page,omitempty"`
|
||||
// path 扫码进入的小程序页面路径
|
||||
Path string `json:"path,omitempty"`
|
||||
// checkPath 检查page 是否存在,为 true 时 page 必须是已经发布的小程序存在的页面(否则报错);为 false 时允许小程序未发布或者 page 不存在, 但page 有数量上限(60000个)请勿滥用
|
||||
CheckPath bool `json:"check_path,omitempty"`
|
||||
// width 图片宽度
|
||||
Width int `json:"width,omitempty"`
|
||||
// scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式)
|
||||
Scene string `json:"scene,omitempty"`
|
||||
// autoColor 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调
|
||||
AutoColor bool `json:"auto_color,omitempty"`
|
||||
// lineColor AutoColor 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"},十进制表示
|
||||
LineColor Color `json:"line_color,omitempty"`
|
||||
// isHyaline 是否需要透明底色
|
||||
IsHyaline bool `json:"is_hyaline,omitempty"`
|
||||
// envVersion 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop"
|
||||
EnvVersion string `json:"env_version,omitempty"`
|
||||
}
|
||||
|
||||
// fetchCode 请求并返回二维码二进制数据
|
||||
func (qrCode *QRCode) fetchCode(urlStr string, body interface{}) (response []byte, err error) {
|
||||
var accessToken string
|
||||
accessToken, err = qrCode.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
urlStr = fmt.Sprintf(urlStr, accessToken)
|
||||
var contentType string
|
||||
response, contentType, err = util.PostJSONWithRespContentType(urlStr, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(contentType, "application/json") {
|
||||
// 返回错误信息
|
||||
var result util.CommonError
|
||||
err = json.Unmarshal(response, &result)
|
||||
if err == nil && result.ErrCode != 0 {
|
||||
err = fmt.Errorf("fetchCode error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if contentType == "image/jpeg" {
|
||||
// 返回文件
|
||||
return response, nil
|
||||
}
|
||||
err = fmt.Errorf("fetchCode error : unknown response content type - %v", contentType)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// CreateWXAQRCode 获取小程序二维码,适用于需要的码数量较少的业务场景
|
||||
// 文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/createWXAQRCode.html
|
||||
func (qrCode *QRCode) CreateWXAQRCode(coderParams QRCoder) (response []byte, err error) {
|
||||
return qrCode.fetchCode(createWXAQRCodeURL, coderParams)
|
||||
}
|
||||
|
||||
// GetWXACode 获取小程序码,适用于需要的码数量较少的业务场景
|
||||
// 文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/getWXACode.html
|
||||
func (qrCode *QRCode) GetWXACode(coderParams QRCoder) (response []byte, err error) {
|
||||
return qrCode.fetchCode(getWXACodeURL, coderParams)
|
||||
}
|
||||
|
||||
// GetWXACodeUnlimit 获取小程序码,适用于需要的码数量极多的业务场景
|
||||
// 文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/getWXACodeUnlimit.html
|
||||
func (qrCode *QRCode) GetWXACodeUnlimit(coderParams QRCoder) (response []byte, err error) {
|
||||
return qrCode.fetchCode(getWXACodeUnlimitURL, coderParams)
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
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,
|
||||
})
|
||||
}
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
package subscribe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/silenceper/wechat/v2/miniprogram/context"
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// 发送订阅消息
|
||||
// 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"
|
||||
|
||||
// 获取当前帐号下的个人模板列表
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getTemplateList.html
|
||||
getTemplateURL = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate"
|
||||
|
||||
// 添加订阅模板
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.addTemplate.html
|
||||
addTemplateURL = "https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate"
|
||||
|
||||
// 删除私有模板
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.deleteTemplate.html
|
||||
delTemplateURL = "https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate"
|
||||
|
||||
// 统一服务消息
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/uniform-message/uniformMessage.send.html
|
||||
uniformMessageSend = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send"
|
||||
)
|
||||
|
||||
// Subscribe 订阅消息
|
||||
type Subscribe struct {
|
||||
*context.Context
|
||||
}
|
||||
|
||||
// NewSubscribe 实例化
|
||||
func NewSubscribe(ctx *context.Context) *Subscribe {
|
||||
return &Subscribe{Context: ctx}
|
||||
}
|
||||
|
||||
// Message 订阅消息请求参数
|
||||
type Message struct {
|
||||
ToUser string `json:"touser"` // 必选,接收者(用户)的 openid
|
||||
TemplateID string `json:"template_id"` // 必选,所需下发的订阅模板id
|
||||
Page string `json:"page"` // 可选,点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。
|
||||
Data map[string]*DataItem `json:"data"` // 必选, 模板内容
|
||||
MiniprogramState string `json:"miniprogram_state"` // 可选,跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
|
||||
Lang string `json:"lang"` // 入小程序查看”的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN
|
||||
}
|
||||
|
||||
// DataItem 模版内某个 .DATA 的值
|
||||
type DataItem struct {
|
||||
Value interface{} `json:"value"`
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
||||
// TemplateItem template item
|
||||
type TemplateItem struct {
|
||||
PriTmplID string `json:"priTmplId"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Example string `json:"example"`
|
||||
Type int64 `json:"type"`
|
||||
}
|
||||
|
||||
// TemplateList template list
|
||||
type TemplateList struct {
|
||||
util.CommonError
|
||||
Data []TemplateItem `json:"data"`
|
||||
}
|
||||
|
||||
// Send 发送订阅消息
|
||||
func (s *Subscribe) Send(msg *Message) (err error) {
|
||||
var accessToken string
|
||||
accessToken, err = s.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", subscribeSendURL, accessToken)
|
||||
response, err := util.PostJSON(uri, msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return util.DecodeWithCommonError(response, "Send")
|
||||
}
|
||||
|
||||
// ListTemplates 获取当前帐号下的个人模板列表
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getTemplateList.html
|
||||
func (s *Subscribe) ListTemplates() (*TemplateList, error) {
|
||||
accessToken, err := s.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", getTemplateURL, accessToken)
|
||||
response, err := util.HTTPGet(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
templateList := TemplateList{}
|
||||
err = util.DecodeWithError(response, &templateList, "ListTemplates")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &templateList, nil
|
||||
}
|
||||
|
||||
// UniformMessage 统一服务消息
|
||||
type UniformMessage struct {
|
||||
ToUser string `json:"touser"`
|
||||
WeappTemplateMsg struct {
|
||||
TemplateID string `json:"template_id"`
|
||||
Page string `json:"page"`
|
||||
FormID string `json:"form_id"`
|
||||
Data map[string]*DataItem `json:"data"`
|
||||
EmphasisKeyword string `json:"emphasis_keyword"`
|
||||
} `json:"weapp_template_msg"`
|
||||
MpTemplateMsg struct {
|
||||
Appid string `json:"appid"`
|
||||
TemplateID string `json:"template_id"`
|
||||
URL string `json:"url"`
|
||||
Miniprogram struct {
|
||||
Appid string `json:"appid"`
|
||||
Pagepath string `json:"pagepath"`
|
||||
} `json:"miniprogram"`
|
||||
Data map[string]*DataItem `json:"data"`
|
||||
} `json:"mp_template_msg"`
|
||||
}
|
||||
|
||||
// UniformSend 发送统一服务消息
|
||||
func (s *Subscribe) UniformSend(msg *UniformMessage) (err error) {
|
||||
var accessToken string
|
||||
accessToken, err = s.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", uniformMessageSend, accessToken)
|
||||
response, err := util.PostJSON(uri, msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return util.DecodeWithCommonError(response, "UniformSend")
|
||||
}
|
||||
|
||||
type resSubscribeAdd struct {
|
||||
util.CommonError
|
||||
|
||||
TemplateID string `json:"priTmplId"`
|
||||
}
|
||||
|
||||
// Add 添加订阅消息模板
|
||||
func (s *Subscribe) Add(ShortID string, kidList []int, sceneDesc string) (templateID string, err error) {
|
||||
var accessToken string
|
||||
accessToken, err = s.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var msg = struct {
|
||||
TemplateIDShort string `json:"tid"`
|
||||
SceneDesc string `json:"sceneDesc"`
|
||||
KidList []int `json:"kidList"`
|
||||
}{TemplateIDShort: ShortID, SceneDesc: sceneDesc, KidList: kidList}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", addTemplateURL, accessToken)
|
||||
var response []byte
|
||||
response, err = util.PostJSON(uri, msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var result resSubscribeAdd
|
||||
err = util.DecodeWithError(response, &result, "AddSubscribe")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
templateID = result.TemplateID
|
||||
return
|
||||
}
|
||||
|
||||
// Delete 删除私有模板
|
||||
func (s *Subscribe) Delete(templateID string) (err error) {
|
||||
var accessToken string
|
||||
accessToken, err = s.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var msg = struct {
|
||||
TemplateID string `json:"priTmplId"`
|
||||
}{TemplateID: templateID}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", delTemplateURL, accessToken)
|
||||
var response []byte
|
||||
response, err = util.PostJSON(uri, msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return util.DecodeWithCommonError(response, "DeleteSubscribe")
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
# 小程序-云开发 SDK
|
||||
|
||||
Tencent Cloud Base [文档](https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/)
|
||||
|
||||
## 使用说明
|
||||
|
||||
**初始化配置**
|
||||
|
||||
```golang
|
||||
//使用memcache保存access_token,也可选择redis或自定义cache
|
||||
memCache=cache.NewMemcache("127.0.0.1:11211")
|
||||
|
||||
//配置小程序参数
|
||||
config := &wechat.Config{
|
||||
AppID: "your app id",
|
||||
AppSecret: "your app secret",
|
||||
Cache: memCache,
|
||||
}
|
||||
wc := wechat.NewWechat(config)
|
||||
wcTcb := wc.GetTcb()
|
||||
```
|
||||
|
||||
### 举例
|
||||
#### 触发云函数
|
||||
```golang
|
||||
res, err := wcTcb.InvokeCloudFunction("test-xxxx", "add", `{"a":1,"b":2}`)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
```
|
||||
|
||||
更多使用方法参考[PKG.DEV](https://pkg.go.dev/github.com/silenceper/wechat/v2/miniprogram/tcb)
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package tcb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// 触发云函数
|
||||
invokeCloudFunctionURL = "https://api.weixin.qq.com/tcb/invokecloudfunction"
|
||||
)
|
||||
|
||||
// InvokeCloudFunctionRes 云函数调用返回结果
|
||||
type InvokeCloudFunctionRes struct {
|
||||
util.CommonError
|
||||
RespData string `json:"resp_data"` // 云函数返回的buffer
|
||||
}
|
||||
|
||||
// InvokeCloudFunction 云函数调用
|
||||
//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) {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s&env=%s&name=%s", invokeCloudFunctionURL, accessToken, env, name)
|
||||
response, err := util.HTTPPost(uri, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
invokeCloudFunctionRes := &InvokeCloudFunctionRes{}
|
||||
err = util.DecodeWithError(response, invokeCloudFunctionRes, "InvokeCloudFunction")
|
||||
return invokeCloudFunctionRes, err
|
||||
}
|
||||
+418
@@ -0,0 +1,418 @@
|
||||
package tcb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// 数据库导入
|
||||
databaseMigrateImportURL = "https://api.weixin.qq.com/tcb/databasemigrateimport"
|
||||
// 数据库导出
|
||||
databaseMigrateExportURL = "https://api.weixin.qq.com/tcb/databasemigrateexport"
|
||||
// 数据库迁移状态查询
|
||||
databaseMigrateQueryInfoURL = "https://api.weixin.qq.com/tcb/databasemigratequeryinfo"
|
||||
// 变更数据库索引
|
||||
updateIndexURL = "https://api.weixin.qq.com/tcb/updateindex"
|
||||
// 新增集合
|
||||
databaseCollectionAddURL = "https://api.weixin.qq.com/tcb/databasecollectionadd"
|
||||
// 删除集合
|
||||
databaseCollectionDeleteURL = "https://api.weixin.qq.com/tcb/databasecollectiondelete"
|
||||
// 获取特定云环境下集合信息
|
||||
databaseCollectionGetURL = "https://api.weixin.qq.com/tcb/databasecollectionget"
|
||||
// 数据库插入记录
|
||||
databaseAddURL = "https://api.weixin.qq.com/tcb/databaseadd"
|
||||
// 数据库删除记录
|
||||
databaseDeleteURL = "https://api.weixin.qq.com/tcb/databasedelete"
|
||||
// 数据库更新记录
|
||||
databaseUpdateURL = "https://api.weixin.qq.com/tcb/databaseupdate"
|
||||
// 数据库查询记录
|
||||
databaseQueryURL = "https://api.weixin.qq.com/tcb/databasequery"
|
||||
// 统计集合记录数或统计查询语句对应的结果记录数
|
||||
databaseCountURL = "https://api.weixin.qq.com/tcb/databasecount"
|
||||
|
||||
// ConflictModeInster 冲突处理模式 插入
|
||||
ConflictModeInster ConflictMode = 1
|
||||
// ConflictModeUpsert 冲突处理模式 更新
|
||||
ConflictModeUpsert ConflictMode = 2
|
||||
|
||||
// FileTypeJSON 的合法值 json
|
||||
FileTypeJSON FileType = 1
|
||||
// FileTypeCsv 的合法值 csv
|
||||
FileTypeCsv FileType = 2
|
||||
)
|
||||
|
||||
// ConflictMode 冲突处理模式
|
||||
type ConflictMode int
|
||||
|
||||
// FileType 文件上传和导出的允许文件类型
|
||||
type FileType int
|
||||
|
||||
// ValidDirections 合法的direction值
|
||||
var ValidDirections = []string{"1", "-1", "2dsphere"}
|
||||
|
||||
// DatabaseMigrateExportReq 数据库出 请求参数
|
||||
type DatabaseMigrateExportReq struct {
|
||||
Env string `json:"env,omitempty"` // 云环境ID
|
||||
FilePath string `json:"file_path,omitempty"` // 导出文件路径(导入文件需先上传到同环境的存储中,可使用开发者工具或 HTTP API的上传文件 API上传)
|
||||
FileType FileType `json:"file_type,omitempty"` // 导出文件类型,文件格式参考数据库导入指引中的文件格式部分 1:json 2:csv
|
||||
Query string `json:"query,omitempty"` // 导出条件
|
||||
}
|
||||
|
||||
// DatabaseMigrateExportRes 数据库导出 返回结果
|
||||
type DatabaseMigrateExportRes struct {
|
||||
util.CommonError
|
||||
JobID int64 `json:"job_id"` // 导出任务ID,可使用数据库迁移进度查询 API 查询导入进度及结果
|
||||
}
|
||||
|
||||
// DatabaseMigrateImportReq 数据库导入 请求参数
|
||||
type DatabaseMigrateImportReq struct {
|
||||
Env string `json:"env,omitempty"` // 云环境ID
|
||||
CollectionName string `json:"collection_name,omitempty"` // 集合名称
|
||||
FilePath string `json:"file_path,omitempty"` // 导出文件路径(文件会导出到同环境的云存储中,可使用获取下载链接 API 获取下载链接)
|
||||
FileType FileType `json:"file_type,omitempty"` // 导入文件类型,文件格式参考数据库导入指引中的文件格式部分 1:json 2:csv
|
||||
StopOnError bool `json:"stop_on_error,omitempty"` // 是否在遇到错误时停止导入
|
||||
ConflictMode ConflictMode `json:"conflict_mode,omitempty"` // 冲突处理模式 1:inster 2:UPSERT
|
||||
}
|
||||
|
||||
// DatabaseMigrateImportRes 数据库导入 返回结果
|
||||
type DatabaseMigrateImportRes struct {
|
||||
util.CommonError
|
||||
JobID int64 `json:"job_id"` // 导入任务ID,可使用数据库迁移进度查询 API 查询导入进度及结果
|
||||
}
|
||||
|
||||
// DatabaseMigrateQueryInfoRes 数据库迁移状态查询
|
||||
type DatabaseMigrateQueryInfoRes struct {
|
||||
util.CommonError
|
||||
Status string `json:"status"` // 导出状态
|
||||
RecordSuccess int64 `json:"record_success"` // 导出成功记录数
|
||||
RecordFail int64 `json:"record_fail"` // 导出失败记录数
|
||||
ErrMsg string `json:"err_msg"` // 导出错误信息
|
||||
FileURL string `json:"file_url"` // 导出文件下载地址
|
||||
}
|
||||
|
||||
// UpdateIndexReq 变更数据库索引 请求参数
|
||||
type UpdateIndexReq struct {
|
||||
Env string `json:"env,omitempty"` // 云环境ID
|
||||
CollectionName string `json:"collection_name,omitempty"` // 集合名称
|
||||
CreateIndexes []CreateIndex `json:"create_indexes,omitempty"` // 新增索引
|
||||
DropIndexes []DropIndex `json:"drop_indexes,omitempty"` // 删除索引
|
||||
}
|
||||
|
||||
// CreateIndex 新增索引
|
||||
type CreateIndex struct {
|
||||
Name string `json:"name,omitempty"` // 索引名
|
||||
Unique bool `json:"unique,omitempty"` // 是否唯一
|
||||
Keys []CreateIndexKey `json:"keys,omitempty"` // 索引字段
|
||||
}
|
||||
|
||||
// CreateIndexKey create index key
|
||||
type CreateIndexKey struct {
|
||||
Name string `json:"name,omitempty"` // 字段名
|
||||
Direction string `json:"direction,omitempty"` // 字段排序
|
||||
}
|
||||
|
||||
// DropIndex 删除索引
|
||||
type DropIndex struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// DatabaseCollectionReq 新增/删除集合请求参数
|
||||
type DatabaseCollectionReq struct {
|
||||
Env string `json:"env,omitempty"` // 云环境ID
|
||||
CollectionName string `json:"collection_name,omitempty"` // 集合名称
|
||||
}
|
||||
|
||||
// DatabaseCollectionGetReq 获取特定云环境下集合信息请求
|
||||
type DatabaseCollectionGetReq struct {
|
||||
Env string `json:"env,omitempty"` // 云环境ID
|
||||
Limit int64 `json:"limit,omitempty"` // 获取数量限制
|
||||
Offset int64 `json:"offset,omitempty"` // 偏移量
|
||||
}
|
||||
|
||||
// DatabaseCollectionGetRes 获取特定云环境下集合信息结果
|
||||
type DatabaseCollectionGetRes struct {
|
||||
util.CommonError
|
||||
Pager struct {
|
||||
Limit int64 `json:"limit"` // 单次查询限制
|
||||
Offset int64 `json:"offset"` // 偏移量
|
||||
Total int64 `json:"total"` // 符合查询条件的记录总数
|
||||
} `json:"pager"`
|
||||
Collections []struct {
|
||||
Name string `json:"name"` // 集合名
|
||||
Count int64 `json:"count"` // 表中文档数量
|
||||
Size int64 `json:"size"` // 表的大小(即表中文档总大小),单位:字节
|
||||
IndexCount int64 `json:"index_count"` // 索引数量
|
||||
IndexSize int64 `json:"index_size"` // 索引占用大小,单位:字节
|
||||
} `json:"collections"`
|
||||
}
|
||||
|
||||
// DatabaseReq 数据库插入/删除/更新/查询/统计记录请求参数
|
||||
type DatabaseReq struct {
|
||||
Env string `json:"env,omitempty"` // 云环境ID
|
||||
Query string `json:"query,omitempty"` // 数据库操作语句
|
||||
}
|
||||
|
||||
// DatabaseAddRes 数据库插入记录返回结果
|
||||
type DatabaseAddRes struct {
|
||||
util.CommonError
|
||||
IDList []string `json:"id_list"` // 插入成功的数据集合主键_id。
|
||||
}
|
||||
|
||||
// DatabaseDeleteRes 数据库删除记录返回结果
|
||||
type DatabaseDeleteRes struct {
|
||||
util.CommonError
|
||||
Deleted int64 `json:"deleted"` // 删除记录数量
|
||||
}
|
||||
|
||||
// DatabaseUpdateRes 数据库更新记录返回结果
|
||||
type DatabaseUpdateRes struct {
|
||||
util.CommonError
|
||||
Matched int64 `json:"matched"` // 更新条件匹配到的结果数
|
||||
Modified int64 `json:"modified"` // 修改的记录数,注意:使用set操作新插入的数据不计入修改数目
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// DatabaseQueryRes 数据库查询记录 返回结果
|
||||
type DatabaseQueryRes struct {
|
||||
util.CommonError
|
||||
Pager struct {
|
||||
Limit int64 `json:"limit"` // 单次查询限制
|
||||
Offset int64 `json:"offset"` // 偏移量
|
||||
Total int64 `json:"total"` // 符合查询条件的记录总数
|
||||
} `json:"pager"`
|
||||
Data []string `json:"data"`
|
||||
}
|
||||
|
||||
// DatabaseCountRes 统计集合记录数或统计查询语句对应的结果记录数 返回结果
|
||||
type DatabaseCountRes struct {
|
||||
util.CommonError
|
||||
Count int64 `json:"count"` // 记录数量
|
||||
}
|
||||
|
||||
// DatabaseMigrateImport 数据库导入
|
||||
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateImport.html
|
||||
func (tcb *Tcb) DatabaseMigrateImport(req *DatabaseMigrateImportReq) (*DatabaseMigrateImportRes, error) {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", databaseMigrateImportURL, accessToken)
|
||||
response, err := util.PostJSON(uri, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
databaseMigrateImportRes := &DatabaseMigrateImportRes{}
|
||||
err = util.DecodeWithError(response, databaseMigrateImportRes, "DatabaseMigrateImport")
|
||||
return databaseMigrateImportRes, err
|
||||
}
|
||||
|
||||
// DatabaseMigrateExport 数据库导出
|
||||
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateExport.html
|
||||
func (tcb *Tcb) DatabaseMigrateExport(req *DatabaseMigrateExportReq) (*DatabaseMigrateExportRes, error) {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", databaseMigrateExportURL, accessToken)
|
||||
response, err := util.PostJSON(uri, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
databaseMigrateExportRes := &DatabaseMigrateExportRes{}
|
||||
err = util.DecodeWithError(response, databaseMigrateExportRes, "DatabaseMigrateExport")
|
||||
return databaseMigrateExportRes, err
|
||||
}
|
||||
|
||||
// DatabaseMigrateQueryInfo 数据库迁移状态查询
|
||||
//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) {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", databaseMigrateQueryInfoURL, accessToken)
|
||||
response, err := util.PostJSON(uri, map[string]interface{}{
|
||||
"env": env,
|
||||
"job_id": jobID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
databaseMigrateQueryInfoRes := &DatabaseMigrateQueryInfoRes{}
|
||||
err = util.DecodeWithError(response, databaseMigrateQueryInfoRes, "DatabaseMigrateQueryInfo")
|
||||
return databaseMigrateQueryInfoRes, err
|
||||
}
|
||||
|
||||
// UpdateIndex 变更数据库索引
|
||||
// https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/updateIndex.html
|
||||
func (tcb *Tcb) UpdateIndex(req *UpdateIndexReq) error {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", updateIndexURL, accessToken)
|
||||
response, err := util.PostJSON(uri, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return util.DecodeWithCommonError(response, "UpdateIndex")
|
||||
}
|
||||
|
||||
// DatabaseCollectionAdd 新增集合
|
||||
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionAdd.html
|
||||
func (tcb *Tcb) DatabaseCollectionAdd(env, collectionName string) error {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", databaseCollectionAddURL, accessToken)
|
||||
response, err := util.PostJSON(uri, &DatabaseCollectionReq{
|
||||
Env: env,
|
||||
CollectionName: collectionName,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return util.DecodeWithCommonError(response, "DatabaseCollectionAdd")
|
||||
}
|
||||
|
||||
// DatabaseCollectionDelete 删除集合
|
||||
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionDelete.html
|
||||
func (tcb *Tcb) DatabaseCollectionDelete(env, collectionName string) error {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", databaseCollectionDeleteURL, accessToken)
|
||||
response, err := util.PostJSON(uri, &DatabaseCollectionReq{
|
||||
Env: env,
|
||||
CollectionName: collectionName,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return util.DecodeWithCommonError(response, "DatabaseCollectionDelete")
|
||||
}
|
||||
|
||||
// DatabaseCollectionGet 获取特定云环境下集合信息
|
||||
//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) {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", databaseCollectionGetURL, accessToken)
|
||||
response, err := util.PostJSON(uri, &DatabaseCollectionGetReq{
|
||||
Env: env,
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
databaseCollectionGetRes := &DatabaseCollectionGetRes{}
|
||||
err = util.DecodeWithError(response, databaseCollectionGetRes, "DatabaseCollectionGet")
|
||||
return databaseCollectionGetRes, err
|
||||
}
|
||||
|
||||
// DatabaseAdd 数据库插入记录
|
||||
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseAdd.html
|
||||
func (tcb *Tcb) DatabaseAdd(env, query string) (*DatabaseAddRes, error) {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", databaseAddURL, accessToken)
|
||||
response, err := util.PostJSON(uri, &DatabaseReq{
|
||||
Env: env,
|
||||
Query: query,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
databaseAddRes := &DatabaseAddRes{}
|
||||
err = util.DecodeWithError(response, databaseAddRes, "DatabaseAdd")
|
||||
return databaseAddRes, err
|
||||
}
|
||||
|
||||
// DatabaseDelete 数据库插入记录
|
||||
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseDelete.html
|
||||
func (tcb *Tcb) DatabaseDelete(env, query string) (*DatabaseDeleteRes, error) {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", databaseDeleteURL, accessToken)
|
||||
response, err := util.PostJSON(uri, &DatabaseReq{
|
||||
Env: env,
|
||||
Query: query,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
databaseDeleteRes := &DatabaseDeleteRes{}
|
||||
err = util.DecodeWithError(response, databaseDeleteRes, "DatabaseDelete")
|
||||
return databaseDeleteRes, err
|
||||
}
|
||||
|
||||
// DatabaseUpdate 数据库插入记录
|
||||
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseUpdate.html
|
||||
func (tcb *Tcb) DatabaseUpdate(env, query string) (*DatabaseUpdateRes, error) {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", databaseUpdateURL, accessToken)
|
||||
response, err := util.PostJSON(uri, &DatabaseReq{
|
||||
Env: env,
|
||||
Query: query,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
databaseUpdateRes := &DatabaseUpdateRes{}
|
||||
err = util.DecodeWithError(response, databaseUpdateRes, "DatabaseUpdate")
|
||||
return databaseUpdateRes, err
|
||||
}
|
||||
|
||||
// DatabaseQuery 数据库查询记录
|
||||
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseQuery.html
|
||||
func (tcb *Tcb) DatabaseQuery(env, query string) (*DatabaseQueryRes, error) {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", databaseQueryURL, accessToken)
|
||||
response, err := util.PostJSON(uri, &DatabaseReq{
|
||||
Env: env,
|
||||
Query: query,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
databaseQueryRes := &DatabaseQueryRes{}
|
||||
err = util.DecodeWithError(response, databaseQueryRes, "DatabaseQuery")
|
||||
return databaseQueryRes, err
|
||||
}
|
||||
|
||||
// DatabaseCount 统计集合记录数或统计查询语句对应的结果记录数
|
||||
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCount.html
|
||||
func (tcb *Tcb) DatabaseCount(env, query string) (*DatabaseCountRes, error) {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", databaseCountURL, accessToken)
|
||||
response, err := util.PostJSON(uri, &DatabaseReq{
|
||||
Env: env,
|
||||
Query: query,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
databaseCountRes := &DatabaseCountRes{}
|
||||
err = util.DecodeWithError(response, databaseCountRes, "DatabaseCount")
|
||||
return databaseCountRes, err
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package tcb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// 获取文件上传链接
|
||||
uploadFilePathURL = "https://api.weixin.qq.com/tcb/uploadfile"
|
||||
// 获取文件下载链接
|
||||
batchDownloadFileURL = "https://api.weixin.qq.com/tcb/batchdownloadfile"
|
||||
// 删除文件链接
|
||||
batchDeleteFileURL = "https://api.weixin.qq.com/tcb/batchdeletefile"
|
||||
)
|
||||
|
||||
// UploadFileReq 上传文件请求值
|
||||
type UploadFileReq struct {
|
||||
Env string `json:"env,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
// UploadFileRes 上传文件返回结果
|
||||
type UploadFileRes struct {
|
||||
util.CommonError
|
||||
URL string `json:"url"` // 上传url
|
||||
Token string `json:"token"` // token
|
||||
Authorization string `json:"authorization"` // authorization
|
||||
FileID string `json:"file_id"` // 文件ID
|
||||
CosFileID string `json:"cos_file_id"` // cos文件ID
|
||||
}
|
||||
|
||||
// BatchDownloadFileReq 上传文件请求值
|
||||
type BatchDownloadFileReq struct {
|
||||
Env string `json:"env,omitempty"`
|
||||
FileList []*DownloadFile `json:"file_list,omitempty"`
|
||||
}
|
||||
|
||||
// DownloadFile 文件信息
|
||||
type DownloadFile struct {
|
||||
FileID string `json:"fileid"` // 文件ID
|
||||
MaxAge int64 `json:"max_age"` // 下载链接有效期
|
||||
}
|
||||
|
||||
// BatchDownloadFileRes 上传文件返回结果
|
||||
type BatchDownloadFileRes struct {
|
||||
util.CommonError
|
||||
FileList []struct {
|
||||
FileID string `json:"file_id"` // 文件ID
|
||||
DownloadURL string `json:"download_url"` // 下载链接
|
||||
Status int64 `json:"status"` // 状态码
|
||||
ErrMsg string `json:"errmsg"` // 该文件错误信息
|
||||
} `json:"file_list"`
|
||||
}
|
||||
|
||||
// BatchDeleteFileReq 批量删除文件请求参数
|
||||
type BatchDeleteFileReq struct {
|
||||
Env string `json:"env,omitempty"`
|
||||
FileIDList []string `json:"fileid_list,omitempty"`
|
||||
}
|
||||
|
||||
// BatchDeleteFileRes 批量删除文件返回结果
|
||||
type BatchDeleteFileRes struct {
|
||||
util.CommonError
|
||||
DeleteList []struct {
|
||||
FileID string `json:"fileid"`
|
||||
Status int64 `json:"status"`
|
||||
ErrMsg string `json:"errmsg"`
|
||||
} `json:"delete_list"`
|
||||
}
|
||||
|
||||
// UploadFile 上传文件
|
||||
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/uploadFile.html
|
||||
func (tcb *Tcb) UploadFile(env, path string) (*UploadFileRes, error) {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", uploadFilePathURL, accessToken)
|
||||
req := &UploadFileReq{
|
||||
Env: env,
|
||||
Path: path,
|
||||
}
|
||||
response, err := util.PostJSON(uri, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uploadFileRes := &UploadFileRes{}
|
||||
err = util.DecodeWithError(response, uploadFileRes, "UploadFile")
|
||||
return uploadFileRes, err
|
||||
}
|
||||
|
||||
// BatchDownloadFile 获取文件下载链接
|
||||
//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) {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", batchDownloadFileURL, accessToken)
|
||||
req := &BatchDownloadFileReq{
|
||||
Env: env,
|
||||
FileList: fileList,
|
||||
}
|
||||
response, err := util.PostJSON(uri, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
batchDownloadFileRes := &BatchDownloadFileRes{}
|
||||
err = util.DecodeWithError(response, batchDownloadFileRes, "BatchDownloadFile")
|
||||
return batchDownloadFileRes, err
|
||||
}
|
||||
|
||||
// BatchDeleteFile 批量删除文件
|
||||
//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) {
|
||||
accessToken, err := tcb.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := fmt.Sprintf("%s?access_token=%s", batchDeleteFileURL, accessToken)
|
||||
req := &BatchDeleteFileReq{
|
||||
Env: env,
|
||||
FileIDList: fileIDList,
|
||||
}
|
||||
response, err := util.PostJSON(uri, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
batchDeleteFileRes := &BatchDeleteFileRes{}
|
||||
err = util.DecodeWithError(response, batchDeleteFileRes, "BatchDeleteFile")
|
||||
return batchDeleteFileRes, err
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package tcb
|
||||
|
||||
import "github.com/silenceper/wechat/v2/miniprogram/context"
|
||||
|
||||
// Tcb Tencent Cloud Base
|
||||
type Tcb struct {
|
||||
*context.Context
|
||||
}
|
||||
|
||||
// NewTcb new Tencent Cloud Base
|
||||
func NewTcb(context *context.Context) *Tcb {
|
||||
return &Tcb{
|
||||
context,
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
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
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package werun
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/silenceper/wechat/v2/miniprogram/context"
|
||||
"github.com/silenceper/wechat/v2/miniprogram/encryptor"
|
||||
)
|
||||
|
||||
// WeRun 微信运动
|
||||
type WeRun struct {
|
||||
*context.Context
|
||||
}
|
||||
|
||||
// Data 微信运动数据
|
||||
type Data struct {
|
||||
StepInfoList []struct {
|
||||
Timestamp int `json:"timestamp"`
|
||||
Step int `json:"step"`
|
||||
} `json:"stepInfoList"`
|
||||
}
|
||||
|
||||
// NewWeRun 实例化
|
||||
func NewWeRun(ctx *context.Context) *WeRun {
|
||||
return &WeRun{Context: ctx}
|
||||
}
|
||||
|
||||
// GetWeRunData 解密数据
|
||||
func (werun *WeRun) GetWeRunData(sessionKey, encryptedData, iv string) (*Data, error) {
|
||||
cipherText, err := encryptor.GetCipherText(sessionKey, encryptedData, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var weRunData Data
|
||||
err = json.Unmarshal(cipherText, &weRunData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &weRunData, nil
|
||||
}
|
||||
Reference in New Issue
Block a user