ソースを参照

增加部分小程序接口

jefferwang(王俊锋) 6 年 前
コミット
eda287070d
5 ファイル変更459 行追加0 行削除
  1. 305 0
      miniprogram/analysis.go
  2. 17 0
      miniprogram/miniprogram.go
  3. 91 0
      miniprogram/qrcode.go
  4. 40 0
      miniprogram/sns.go
  5. 6 0
      wechat.go

+ 305 - 0
miniprogram/analysis.go

@@ -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
+}

+ 17 - 0
miniprogram/miniprogram.go

@@ -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 - 0
miniprogram/qrcode.go

@@ -0,0 +1,91 @@
+package miniprogram
+
+import (
+	"encoding/json"
+	"fmt"
+	"strings"
+
+	"github.com/silenceper/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 - 0
miniprogram/sns.go

@@ -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
+}

+ 6 - 0
wechat.go

@@ -4,6 +4,7 @@ import (
 	"net/http"
 	"sync"
 
+	"github.com/JefferyWang/wechat/miniprogram"
 	"github.com/silenceper/wechat/cache"
 	"github.com/silenceper/wechat/context"
 	"github.com/silenceper/wechat/js"
@@ -99,3 +100,8 @@ func (wc *Wechat) GetTemplate() *template.Template {
 func (wc *Wechat) GetPay() *pay.Pay {
 	return pay.NewPay(wc.Context)
 }
+
+// GetMiniProgram 获取小程序的实例
+func (wc *Wechat) GetMiniProgram() *miniprogram.MiniProgram {
+	return miniprogram.NewMiniProgram(wc.Context)
+}