mirror of
https://github.com/silenceper/wechat.git
synced 2026-02-09 07:02:27 +08:00
105
README.md
105
README.md
@@ -96,6 +96,7 @@ Cache主要用来保存全局access_token以及js-sdk中的ticket:
|
|||||||
- 检验access_token是否有效
|
- 检验access_token是否有效
|
||||||
- 获取js-sdk配置
|
- 获取js-sdk配置
|
||||||
- [素材管理](#素材管理)
|
- [素材管理](#素材管理)
|
||||||
|
- [小程序开发](#小程序开发)
|
||||||
|
|
||||||
## 消息管理
|
## 消息管理
|
||||||
|
|
||||||
@@ -529,6 +530,110 @@ type Config struct {
|
|||||||
|
|
||||||
[素材管理API](https://godoc.org/github.com/silenceper/wechat/material#Material)
|
[素材管理API](https://godoc.org/github.com/silenceper/wechat/material#Material)
|
||||||
|
|
||||||
|
## 小程序开发
|
||||||
|
|
||||||
|
获取小程序操作对象
|
||||||
|
|
||||||
|
``` go
|
||||||
|
memCache=cache.NewMemcache("127.0.0.1:11211")
|
||||||
|
config := &wechat.Config{
|
||||||
|
AppID: "xxx",
|
||||||
|
AppSecret: "xxx",
|
||||||
|
Cache: memCache=cache.NewMemcache("127.0.0.1:11211"),
|
||||||
|
}
|
||||||
|
wc := wechat.NewWechat(config)
|
||||||
|
|
||||||
|
wxa := wc.GetMiniProgram()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 小程序登录凭证校验
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (wxa *MiniProgram) Code2Session(jsCode string) (result ResCode2Session, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 小程序数据统计
|
||||||
|
|
||||||
|
**获取用户访问小程序日留存**
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (wxa *MiniProgram) GetAnalysisDailyRetain(beginDate, endDate string) (result ResAnalysisRetain, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
**获取用户访问小程序月留存**
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (wxa *MiniProgram) GetAnalysisMonthlyRetain(beginDate, endDate string) (result ResAnalysisRetain, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
**获取用户访问小程序周留存**
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (wxa *MiniProgram) GetAnalysisWeeklyRetain(beginDate, endDate string) (result ResAnalysisRetain, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
**获取用户访问小程序数据概况**
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (wxa *MiniProgram) GetAnalysisDailySummary(beginDate, endDate string) (result ResAnalysisDailySummary, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
**获取用户访问小程序数据日趋势**
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (wxa *MiniProgram) GetAnalysisDailyVisitTrend(beginDate, endDate string) (result ResAnalysisVisitTrend, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
**获取用户访问小程序数据月趋势**
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (wxa *MiniProgram) GetAnalysisMonthlyVisitTrend(beginDate, endDate string) (result ResAnalysisVisitTrend, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
**获取用户访问小程序数据周趋势**
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (wxa *MiniProgram) GetAnalysisWeeklyVisitTrend(beginDate, endDate string) (result ResAnalysisVisitTrend, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
**获取小程序新增或活跃用户的画像分布数据**
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (wxa *MiniProgram) GetAnalysisUserPortrait(beginDate, endDate string) (result ResAnalysisUserPortrait, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
**获取用户小程序访问分布数据**
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (wxa *MiniProgram) GetAnalysisVisitDistribution(beginDate, endDate string) (result ResAnalysisVisitDistribution, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
**获取小程序页面访问数据**
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (wxa *MiniProgram) GetAnalysisVisitPage(beginDate, endDate string) (result ResAnalysisVisitPage, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 小程序二维码生成
|
||||||
|
|
||||||
|
**获取小程序二维码,适用于需要的码数量较少的业务场景**
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (wxa *MiniProgram) CreateWXAQRCode(coderParams QRCoder) (response []byte, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
**获取小程序码,适用于需要的码数量较少的业务场景**
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (wxa *MiniProgram) GetWXACode(coderParams QRCoder) (response []byte, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
**获取小程序码,适用于需要的码数量极多的业务场景**
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func (wxa *MiniProgram) GetWXACodeUnlimit(coderParams QRCoder) (response []byte, err error)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
更多API使用请参考 godoc :
|
更多API使用请参考 godoc :
|
||||||
[https://godoc.org/github.com/silenceper/wechat](https://godoc.org/github.com/silenceper/wechat)
|
[https://godoc.org/github.com/silenceper/wechat](https://godoc.org/github.com/silenceper/wechat)
|
||||||
|
|||||||
305
miniprogram/analysis.go
Normal file
305
miniprogram/analysis.go
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
package miniprogram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/silenceper/wechat/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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// fetchData 拉取统计数据
|
||||||
|
func (wxa *MiniProgram) fetchData(urlStr string, body interface{}) (response []byte, err error) {
|
||||||
|
var accessToken string
|
||||||
|
accessToken, err = wxa.GetAccessToken()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
urlStr = fmt.Sprintf(urlStr, accessToken)
|
||||||
|
response, err = util.PostJSON(urlStr, body)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnalysisRetainItem 留存项结构
|
||||||
|
type AnalysisRetainItem 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 []AnalysisRetainItem `json:"visit_uv_new"` // 新增用户留存
|
||||||
|
VisitUV []AnalysisRetainItem `json:"visit_uv"` // 活跃用户留存
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAnalysisRetain 获取用户访问小程序留存数据(日、月、周)
|
||||||
|
func (wxa *MiniProgram) getAnalysisRetain(urlStr string, beginDate, endDate string) (result ResAnalysisRetain, err error) {
|
||||||
|
body := map[string]string{
|
||||||
|
"begin_date": beginDate,
|
||||||
|
"end_date": endDate,
|
||||||
|
}
|
||||||
|
response, err := wxa.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 (wxa *MiniProgram) GetAnalysisDailyRetain(beginDate, endDate string) (result ResAnalysisRetain, err error) {
|
||||||
|
return wxa.getAnalysisRetain(getAnalysisDailyRetainURL, beginDate, endDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAnalysisMonthlyRetain 获取用户访问小程序月留存
|
||||||
|
func (wxa *MiniProgram) GetAnalysisMonthlyRetain(beginDate, endDate string) (result ResAnalysisRetain, err error) {
|
||||||
|
return wxa.getAnalysisRetain(getAnalysisMonthlyRetainURL, beginDate, endDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAnalysisWeeklyRetain 获取用户访问小程序周留存
|
||||||
|
func (wxa *MiniProgram) GetAnalysisWeeklyRetain(beginDate, endDate string) (result ResAnalysisRetain, err error) {
|
||||||
|
return wxa.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 (wxa *MiniProgram) GetAnalysisDailySummary(beginDate, endDate string) (result ResAnalysisDailySummary, err error) {
|
||||||
|
body := map[string]string{
|
||||||
|
"begin_date": beginDate,
|
||||||
|
"end_date": endDate,
|
||||||
|
}
|
||||||
|
response, err := wxa.fetchData(getAnalysisDailySummaryURL, body)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(string(response))
|
||||||
|
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 (wxa *MiniProgram) getAnalysisVisitTrend(urlStr string, beginDate, endDate string) (result ResAnalysisVisitTrend, err error) {
|
||||||
|
body := map[string]string{
|
||||||
|
"begin_date": beginDate,
|
||||||
|
"end_date": endDate,
|
||||||
|
}
|
||||||
|
response, err := wxa.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 (wxa *MiniProgram) GetAnalysisDailyVisitTrend(beginDate, endDate string) (result ResAnalysisVisitTrend, err error) {
|
||||||
|
return wxa.getAnalysisVisitTrend(getAnalysisDailyVisitTrendURL, beginDate, endDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAnalysisMonthlyVisitTrend 获取用户访问小程序数据月趋势
|
||||||
|
func (wxa *MiniProgram) GetAnalysisMonthlyVisitTrend(beginDate, endDate string) (result ResAnalysisVisitTrend, err error) {
|
||||||
|
return wxa.getAnalysisVisitTrend(getAnalysisMonthlyVisitTrendURL, beginDate, endDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAnalysisWeeklyVisitTrend 获取用户访问小程序数据周趋势
|
||||||
|
func (wxa *MiniProgram) GetAnalysisWeeklyVisitTrend(beginDate, endDate string) (result ResAnalysisVisitTrend, err error) {
|
||||||
|
return wxa.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 (wxa *MiniProgram) GetAnalysisUserPortrait(beginDate, endDate string) (result ResAnalysisUserPortrait, err error) {
|
||||||
|
body := map[string]string{
|
||||||
|
"begin_date": beginDate,
|
||||||
|
"end_date": endDate,
|
||||||
|
}
|
||||||
|
response, err := wxa.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 (wxa *MiniProgram) GetAnalysisVisitDistribution(beginDate, endDate string) (result ResAnalysisVisitDistribution, err error) {
|
||||||
|
body := map[string]string{
|
||||||
|
"begin_date": beginDate,
|
||||||
|
"end_date": endDate,
|
||||||
|
}
|
||||||
|
response, err := wxa.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 (wxa *MiniProgram) GetAnalysisVisitPage(beginDate, endDate string) (result ResAnalysisVisitPage, err error) {
|
||||||
|
body := map[string]string{
|
||||||
|
"begin_date": beginDate,
|
||||||
|
"end_date": endDate,
|
||||||
|
}
|
||||||
|
response, err := wxa.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
|
||||||
|
}
|
||||||
93
miniprogram/decrypt.go
Normal file
93
miniprogram/decrypt.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package miniprogram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserInfo 用户信息
|
||||||
|
type UserInfo 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"`
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt 解密数据
|
||||||
|
func (wxa *MiniProgram) Decrypt(sessionKey, encryptedData, iv string) (*UserInfo, 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
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
var userInfo UserInfo
|
||||||
|
err = json.Unmarshal(cipherText, &userInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if userInfo.Watermark.AppID != wxa.AppID {
|
||||||
|
return nil, ErrAppIDNotMatch
|
||||||
|
}
|
||||||
|
return &userInfo, nil
|
||||||
|
}
|
||||||
17
miniprogram/miniprogram.go
Normal file
17
miniprogram/miniprogram.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package miniprogram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/silenceper/wechat/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MiniProgram struct extends context
|
||||||
|
type MiniProgram struct {
|
||||||
|
*context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMiniProgram 实例化小程序接口
|
||||||
|
func NewMiniProgram(context *context.Context) *MiniProgram {
|
||||||
|
miniProgram := new(MiniProgram)
|
||||||
|
miniProgram.Context = context
|
||||||
|
return miniProgram
|
||||||
|
}
|
||||||
91
miniprogram/qrcode.go
Normal file
91
miniprogram/qrcode.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package miniprogram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/JefferyWang/wechat/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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// QRCoder 小程序码参数
|
||||||
|
type QRCoder struct {
|
||||||
|
// page 必须是已经发布的小程序存在的页面,根路径前不要填加 /,不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面
|
||||||
|
Page string `json:"page,omitempty"`
|
||||||
|
// path 扫码进入的小程序页面路径
|
||||||
|
Path string `json:"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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color QRCode color
|
||||||
|
type Color struct {
|
||||||
|
R string `json:"r"`
|
||||||
|
G string `json:"g"`
|
||||||
|
B string `json:"b"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchCode 请求并返回二维码二进制数据
|
||||||
|
func (wxa *MiniProgram) fetchCode(urlStr string, body interface{}) (response []byte, err error) {
|
||||||
|
var accessToken string
|
||||||
|
accessToken, err = wxa.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
|
||||||
|
}
|
||||||
|
} else if contentType == "image/jpeg" {
|
||||||
|
// 返回文件
|
||||||
|
return response, nil
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("fetchCode error : unknown response content type - %v", contentType)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateWXAQRCode 获取小程序二维码,适用于需要的码数量较少的业务场景
|
||||||
|
// 文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/createWXAQRCode.html
|
||||||
|
func (wxa *MiniProgram) CreateWXAQRCode(coderParams QRCoder) (response []byte, err error) {
|
||||||
|
return wxa.fetchCode(createWXAQRCodeURL, coderParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWXACode 获取小程序码,适用于需要的码数量较少的业务场景
|
||||||
|
// 文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/getWXACode.html
|
||||||
|
func (wxa *MiniProgram) GetWXACode(coderParams QRCoder) (response []byte, err error) {
|
||||||
|
return wxa.fetchCode(getWXACodeURL, coderParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWXACodeUnlimit 获取小程序码,适用于需要的码数量极多的业务场景
|
||||||
|
// 文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/getWXACodeUnlimit.html
|
||||||
|
func (wxa *MiniProgram) GetWXACodeUnlimit(coderParams QRCoder) (response []byte, err error) {
|
||||||
|
return wxa.fetchCode(getWXACodeUnlimitURL, coderParams)
|
||||||
|
}
|
||||||
40
miniprogram/sns.go
Normal file
40
miniprogram/sns.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package miniprogram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/silenceper/wechat/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
code2SessionURL = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResCode2Session 登录凭证校验的返回结果
|
||||||
|
type ResCode2Session struct {
|
||||||
|
util.CommonError
|
||||||
|
|
||||||
|
OpenID string `json:"openid"` // 用户唯一标识
|
||||||
|
SessionKey string `json:"session_key"` // 会话密钥
|
||||||
|
UnionID string `json:"unionid"` // 用户在开放平台的唯一标识符,在满足UnionID下发条件的情况下会返回
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code2Session 登录凭证校验
|
||||||
|
func (wxa *MiniProgram) Code2Session(jsCode string) (result ResCode2Session, err error) {
|
||||||
|
urlStr := fmt.Sprintf(code2SessionURL, wxa.AppID, wxa.AppSecret, jsCode)
|
||||||
|
var response []byte
|
||||||
|
response, err = util.HTTPGet(urlStr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(response, &result)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if result.ErrCode != 0 {
|
||||||
|
err = fmt.Errorf("Code2Session error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
26
util/http.go
26
util/http.go
@@ -50,6 +50,32 @@ func PostJSON(uri string, obj interface{}) ([]byte, error) {
|
|||||||
return ioutil.ReadAll(response.Body)
|
return ioutil.ReadAll(response.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostJSONWithRespContentType post json数据请求,且返回数据类型
|
||||||
|
func PostJSONWithRespContentType(uri string, obj interface{}) ([]byte, string, error) {
|
||||||
|
jsonData, err := json.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData = bytes.Replace(jsonData, []byte("\\u003c"), []byte("<"), -1)
|
||||||
|
jsonData = bytes.Replace(jsonData, []byte("\\u003e"), []byte(">"), -1)
|
||||||
|
jsonData = bytes.Replace(jsonData, []byte("\\u0026"), []byte("&"), -1)
|
||||||
|
|
||||||
|
body := bytes.NewBuffer(jsonData)
|
||||||
|
response, err := http.Post(uri, "application/json;charset=utf-8", body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
if response.StatusCode != http.StatusOK {
|
||||||
|
return nil, "", fmt.Errorf("http get error : uri=%v , statusCode=%v", uri, response.StatusCode)
|
||||||
|
}
|
||||||
|
responseData, err := ioutil.ReadAll(response.Body)
|
||||||
|
contentType := response.Header.Get("Content-Type")
|
||||||
|
return responseData, contentType, err
|
||||||
|
}
|
||||||
|
|
||||||
//PostFile 上传文件
|
//PostFile 上传文件
|
||||||
func PostFile(fieldname, filename, uri string) ([]byte, error) {
|
func PostFile(fieldname, filename, uri string) ([]byte, error) {
|
||||||
fields := []MultipartFormField{
|
fields := []MultipartFormField{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/JefferyWang/wechat/miniprogram"
|
||||||
"github.com/silenceper/wechat/cache"
|
"github.com/silenceper/wechat/cache"
|
||||||
"github.com/silenceper/wechat/context"
|
"github.com/silenceper/wechat/context"
|
||||||
"github.com/silenceper/wechat/js"
|
"github.com/silenceper/wechat/js"
|
||||||
@@ -99,3 +100,8 @@ func (wc *Wechat) GetTemplate() *template.Template {
|
|||||||
func (wc *Wechat) GetPay() *pay.Pay {
|
func (wc *Wechat) GetPay() *pay.Pay {
|
||||||
return pay.NewPay(wc.Context)
|
return pay.NewPay(wc.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMiniProgram 获取小程序的实例
|
||||||
|
func (wc *Wechat) GetMiniProgram() *miniprogram.MiniProgram {
|
||||||
|
return miniprogram.NewMiniProgram(wc.Context)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user