Просмотр исходного кода

增加微信公众号数据统计功能 (#279)

* * fix: 移除代码中的Println

* 增加公众号数据统计

* fix code style
Liangwt 6 лет назад
Родитель
Сommit
fe31f04640

+ 1 - 1
miniprogram/analysis/analysis.go

@@ -125,7 +125,7 @@ func (analysis *Analysis) GetAnalysisDailySummary(beginDate, endDate string) (re
 	if err != nil {
 		return
 	}
-	fmt.Println(string(response))
+
 	err = json.Unmarshal(response, &result)
 	if err != nil {
 		return

+ 270 - 0
officialaccount/datacube/broadcast.go

@@ -0,0 +1,270 @@
+package datacube
+
+import (
+	"fmt"
+	"github.com/silenceper/wechat/v2/util"
+)
+
+const (
+	getArticleSummary = "https://api.weixin.qq.com/datacube/getarticlesummary"
+	getArticleTotal   = "https://api.weixin.qq.com/datacube/getarticletotal"
+	getUserRead       = "https://api.weixin.qq.com/datacube/getuserread"
+	getUserReadHour   = "https://api.weixin.qq.com/datacube/getuserreadhour"
+	getUserShare      = "https://api.weixin.qq.com/datacube/getusershare"
+	getUserShareHour  = "https://api.weixin.qq.com/datacube/getusersharehour"
+)
+
+//ResArticleSummary 获取图文群发每日数据响应
+type ResArticleSummary struct {
+	util.CommonError
+
+	List []struct {
+		RefDate          string `json:"ref_date"`
+		MsgID            string `json:"msgid"`
+		Title            string `json:"title"`
+		IntPageReadUser  int    `json:"int_page_read_user"`
+		IntPageReadCount int    `json:"int_page_read_count"`
+		OriPageReadUser  int    `json:"ori_page_read_user"`
+		OriPageReadCount int    `json:"ori_page_read_count"`
+		ShareUser        int    `json:"share_user"`
+		ShareCount       int    `json:"share_count"`
+		AddToFavUser     int    `json:"add_to_fav_user"`
+		AddToFavCount    int    `json:"add_to_fav_count"`
+	} `json:"list"`
+}
+
+//ResArticleTotal 获取图文群发总数据响应
+type ResArticleTotal struct {
+	util.CommonError
+
+	List []struct {
+		RefDate string                `json:"ref_date"`
+		MsgID   string                `json:"msgid"`
+		Title   string                `json:"title"`
+		Details []ArticleTotalDetails `json:"details"`
+	} `json:"list"`
+}
+
+//ArticleTotalDetails 获取图文群发总数据响应文字详情
+type ArticleTotalDetails struct {
+	StatDate                    string `json:"stat_date"`
+	TargetUser                  int    `json:"target_user"`
+	IntPageReadUser             int    `json:"int_page_read_user"`
+	IntPageReadCount            int    `json:"int_page_read_count"`
+	OriPageReadUser             int    `json:"ori_page_read_user"`
+	OriPageReadCount            int    `json:"ori_page_read_count"`
+	ShareUser                   int    `json:"share_user"`
+	ShareCount                  int    `json:"share_count"`
+	AddToFavUser                int    `json:"add_to_fav_user"`
+	AddToFavCount               int    `json:"add_to_fav_count"`
+	IntPageFromSessionReadUser  int    `json:"int_page_from_session_read_user"`
+	IntPageFromSessionReadCount int    `json:"int_page_from_session_read_count"`
+	IntPageFromHistMsgReadUser  int    `json:"int_page_from_hist_msg_read_user"`
+	IntPageFromHistMsgReadCount int    `json:"int_page_from_hist_msg_read_count"`
+	IntPageFromFeedReadUser     int    `json:"int_page_from_feed_read_user"`
+	IntPageFromFeedReadCount    int    `json:"int_page_from_feed_read_count"`
+	IntPageFromFriendsReadUser  int    `json:"int_page_from_friends_read_user"`
+	IntPageFromFriendsReadCount int    `json:"int_page_from_friends_read_count"`
+	IntPageFromOtherReadUser    int    `json:"int_page_from_other_read_user"`
+	IntPageFromOtherReadCount   int    `json:"int_page_from_other_read_count"`
+	FeedShareFromSessionUser    int    `json:"feed_share_from_session_user"`
+	FeedShareFromSessionCnt     int    `json:"feed_share_from_session_cnt"`
+	FeedShareFromFeedUser       int    `json:"feed_share_from_feed_user"`
+	FeedShareFromFeedCnt        int    `json:"feed_share_from_feed_cnt"`
+	FeedShareFromOtherUser      int    `json:"feed_share_from_other_user"`
+	FeedShareFromOtherCnt       int    `json:"feed_share_from_other_cnt"`
+}
+
+//ResUserRead 获取图文统计数据响应
+type ResUserRead struct {
+	util.CommonError
+
+	List []struct {
+		RefDate          string `json:"ref_date"`
+		UserSource       int    `json:"user_source"`
+		IntPageReadUser  int    `json:"int_page_read_user"`
+		IntPageReadCount int    `json:"int_page_read_count"`
+		OriPageReadUser  int    `json:"ori_page_read_user"`
+		OriPageReadCount int    `json:"ori_page_read_count"`
+		ShareUser        int    `json:"share_user"`
+		ShareCount       int    `json:"share_count"`
+		AddToFavUser     int    `json:"add_to_fav_user"`
+		AddToFavCount    int    `json:"add_to_fav_count"`
+	} `json:"list"`
+}
+
+//ResUserReadHour 获取图文统计分时数据
+type ResUserReadHour struct {
+	util.CommonError
+
+	List []struct {
+		RefDate          string `json:"ref_date"`
+		RefHour          int    `json:"ref_hour"`
+		UserSource       int    `json:"user_source"`
+		IntPageReadUser  int    `json:"int_page_read_user"`
+		IntPageReadCount int    `json:"int_page_read_count"`
+		OriPageReadUser  int    `json:"ori_page_read_user"`
+		OriPageReadCount int    `json:"ori_page_read_count"`
+		ShareUser        int    `json:"share_user"`
+		ShareCount       int    `json:"share_count"`
+		AddToFavUser     int    `json:"add_to_fav_user"`
+		AddToFavCount    int    `json:"add_to_fav_count"`
+	} `json:"list"`
+}
+
+//ResUserShare 获取图文分享转发数据
+type ResUserShare struct {
+	util.CommonError
+
+	List []struct {
+		RefDate    string `json:"ref_date"`
+		ShareScene int    `json:"share_scene"`
+		ShareCount int    `json:"share_count"`
+		ShareUser  int    `json:"share_user"`
+	} `json:"list"`
+}
+
+//ResUserShareHour  获取图文分享转发分时数据
+type ResUserShareHour struct {
+	util.CommonError
+
+	List []struct {
+		RefDate    string `json:"ref_date"`
+		RefHour    int    `json:"ref_hour"`
+		ShareScene int    `json:"share_scene"`
+		ShareCount int    `json:"share_count"`
+		ShareUser  int    `json:"share_user"`
+	} `json:"list"`
+}
+
+//GetArticleSummary 获取图文群发每日数据
+func (cube *DataCube) GetArticleSummary(s string, e string) (resArticleSummary ResArticleSummary, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getArticleSummary, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resArticleSummary, "GetArticleSummary")
+	return
+}
+
+//GetArticleTotal 获取图文群发总数据
+func (cube *DataCube) GetArticleTotal(s string, e string) (resArticleTotal ResArticleTotal, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getArticleTotal, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resArticleTotal, "GetArticleTotal")
+	return
+}
+
+//GetUserRead 获取图文统计数据
+func (cube *DataCube) GetUserRead(s string, e string) (resUserRead ResUserRead, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getUserRead, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resUserRead, "GetUserRead")
+	return
+}
+
+//GetUserReadHour 获取图文统计分时数据
+func (cube *DataCube) GetUserReadHour(s string, e string) (resUserReadHour ResUserReadHour, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getUserReadHour, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resUserReadHour, "GetUserReadHour")
+	return
+}
+
+//GetUserShare 获取图文分享转发数据
+func (cube *DataCube) GetUserShare(s string, e string) (resUserShare ResUserShare, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getUserShare, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resUserShare, "GetUserShare")
+	return
+}
+
+//GetUserShareHour 获取图文分享转发分时数据
+func (cube *DataCube) GetUserShareHour(s string, e string) (resUserShareHour ResUserShareHour, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getUserShareHour, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resUserShareHour, "GetUserShareHour")
+	return
+}

+ 22 - 0
officialaccount/datacube/datacube.go

@@ -0,0 +1,22 @@
+package datacube
+
+import (
+	"github.com/silenceper/wechat/v2/officialaccount/context"
+)
+
+type reqDate struct {
+	BeginDate string `json:"begin_date"`
+	EndDate   string `json:"end_date"`
+}
+
+//DataCube 数据统计
+type DataCube struct {
+	*context.Context
+}
+
+//NewCube 数据统计
+func NewCube(context *context.Context) *DataCube {
+	dataCube := new(DataCube)
+	dataCube.Context = context
+	return dataCube
+}

+ 82 - 0
officialaccount/datacube/interface.go

@@ -0,0 +1,82 @@
+package datacube
+
+import (
+	"fmt"
+	"github.com/silenceper/wechat/v2/util"
+)
+
+const (
+	getInterfaceSummary     = "https://api.weixin.qq.com/datacube/getinterfacesummary"
+	getInterfaceSummaryHour = "https://api.weixin.qq.com/datacube/getinterfacesummaryhour"
+)
+
+//ResInterfaceSummary 接口分析数据响应
+type ResInterfaceSummary struct {
+	util.CommonError
+
+	List []struct {
+		RefDate       string `json:"ref_date"`
+		CallbackCount int    `json:"callback_count"`
+		FailCount     int    `json:"fail_count"`
+		TotalTimeCost int    `json:"total_time_cost"`
+		MaxTimeCost   int    `json:"max_time_cost"`
+	} `json:"list"`
+}
+
+//ResInterfaceSummaryHour 接口分析分时数据响应
+type ResInterfaceSummaryHour struct {
+	util.CommonError
+
+	List []struct {
+		RefDate       string `json:"ref_date"`
+		RefHour       int    `json:"ref_hour"`
+		CallbackCount int    `json:"callback_count"`
+		FailCount     int    `json:"fail_count"`
+		TotalTimeCost int    `json:"total_time_cost"`
+		MaxTimeCost   int    `json:"max_time_cost"`
+	} `json:"list"`
+}
+
+//GetInterfaceSummary 获取接口分析数据
+func (cube *DataCube) GetInterfaceSummary(s string, e string) (resInterfaceSummary ResInterfaceSummary, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getInterfaceSummary, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resInterfaceSummary, "GetInterfaceSummary")
+	return
+}
+
+//GetInterfaceSummaryHour 获取接口分析分时数据
+func (cube *DataCube) GetInterfaceSummaryHour(s string, e string) (resInterfaceSummaryHour ResInterfaceSummaryHour, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getInterfaceSummaryHour, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resInterfaceSummaryHour, "GetInterfaceSummaryHour")
+	return
+}

+ 252 - 0
officialaccount/datacube/message.go

@@ -0,0 +1,252 @@
+package datacube
+
+import (
+	"fmt"
+	"github.com/silenceper/wechat/v2/util"
+)
+
+const (
+	getUpstreamMsg          = "https://api.weixin.qq.com/datacube/getupstreammsg"
+	getUpstreamMsgHour      = "https://api.weixin.qq.com/datacube/getupstreammsghour"
+	getUpstreamMsgWeek      = "https://api.weixin.qq.com/datacube/getupstreammsgweek"
+	getUpstreamMsgMonth     = "https://api.weixin.qq.com/datacube/getupstreammsgmonth"
+	getUpstreamMsgDist      = "https://api.weixin.qq.com/datacube/getupstreammsgdist"
+	getUpstreamMsgDistWeek  = "https://api.weixin.qq.com/datacube/getupstreammsgdistweek"
+	getUpstreamMsgDistMonth = "https://api.weixin.qq.com/datacube/getupstreammsgdistmonth"
+)
+
+//ResUpstreamMsg 获取消息发送概况数据响应
+type ResUpstreamMsg struct {
+	util.CommonError
+
+	List []struct {
+		RefDate  string `json:"ref_date"`
+		MsgType  int    `json:"msg_type"`
+		MsgUser  int    `json:"msg_user"`
+		MsgCount int    `json:"msg_count"`
+	} `json:"list"`
+}
+
+//ResUpstreamMsgHour 获取消息分送分时数据响应
+type ResUpstreamMsgHour struct {
+	util.CommonError
+
+	List []struct {
+		RefDate  string `json:"ref_date"`
+		RefHour  int    `json:"ref_hour"`
+		MsgType  int    `json:"msg_type"`
+		MsgUser  int    `json:"msg_user"`
+		MsgCount int    `json:"msg_count"`
+	} `json:"list"`
+}
+
+//ResUpstreamMsgWeek 获取消息发送周数据响应
+type ResUpstreamMsgWeek struct {
+	util.CommonError
+
+	List []struct {
+		RefDate  string `json:"ref_date"`
+		MsgType  int    `json:"msg_type"`
+		MsgUser  int    `json:"msg_user"`
+		MsgCount int    `json:"msg_count"`
+	} `json:"list"`
+}
+
+//ResUpstreamMsgMonth 获取消息发送月数据响应
+type ResUpstreamMsgMonth struct {
+	util.CommonError
+
+	List []struct {
+		RefDate  string `json:"ref_date"`
+		MsgType  int    `json:"msg_type"`
+		MsgUser  int    `json:"msg_user"`
+		MsgCount int    `json:"msg_count"`
+	} `json:"list"`
+}
+
+//ResUpstreamMsgDist 获取消息发送分布数据响应
+type ResUpstreamMsgDist struct {
+	util.CommonError
+
+	List []struct {
+		RefDate       string `json:"ref_date"`
+		CountInterval int    `json:"count_interval"`
+		MsgUser       int    `json:"msg_user"`
+	} `json:"list"`
+}
+
+//ResUpstreamMsgDistWeek 获取消息发送分布周数据响应
+type ResUpstreamMsgDistWeek struct {
+	util.CommonError
+
+	List []struct {
+		RefDate       string `json:"ref_date"`
+		CountInterval int    `json:"count_interval"`
+		MsgUser       int    `json:"msg_user"`
+	} `json:"list"`
+}
+
+//ResUpstreamMsgDistMonth 获取消息发送分布月数据响应
+type ResUpstreamMsgDistMonth struct {
+	util.CommonError
+
+	List []struct {
+		RefDate       string `json:"ref_date"`
+		CountInterval int    `json:"count_interval"`
+		MsgUser       int    `json:"msg_user"`
+	} `json:"list"`
+}
+
+//GetUpstreamMsg 获取消息发送概况数据
+func (cube *DataCube) GetUpstreamMsg(s string, e string) (resUpstreamMsg ResUpstreamMsg, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getUpstreamMsg, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resUpstreamMsg, "GetUpstreamMsg")
+	return
+}
+
+//GetUpstreamMsgHour 获取消息分送分时数据
+func (cube *DataCube) GetUpstreamMsgHour(s string, e string) (resUpstreamMsgHour ResUpstreamMsgHour, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getUpstreamMsgHour, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resUpstreamMsgHour, "GetUpstreamMsgHour")
+	return
+}
+
+//GetUpstreamMsgWeek 获取消息发送周数据
+func (cube *DataCube) GetUpstreamMsgWeek(s string, e string) (resUpstreamMsgWeek ResUpstreamMsgWeek, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getUpstreamMsgWeek, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resUpstreamMsgWeek, "GetUpstreamMsgWeek")
+	return
+}
+
+//GetUpstreamMsgMonth 获取消息发送月数据
+func (cube *DataCube) GetUpstreamMsgMonth(s string, e string) (resUpstreamMsgMonth ResUpstreamMsgMonth, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getUpstreamMsgMonth, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resUpstreamMsgMonth, "GetUpstreamMsgMonth")
+	return
+}
+
+//GetUpstreamMsgDist 获取消息发送分布数据
+func (cube *DataCube) GetUpstreamMsgDist(s string, e string) (resUpstreamMsgDist ResUpstreamMsgDist, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getUpstreamMsgDist, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resUpstreamMsgDist, "GetUpstreamMsgDist")
+	return
+}
+
+//GetUpstreamMsgDistWeek 获取消息发送分布周数据
+func (cube *DataCube) GetUpstreamMsgDistWeek(s string, e string) (resUpstreamMsgDistWeek ResUpstreamMsgDistWeek, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getUpstreamMsgDistWeek, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resUpstreamMsgDistWeek, "GetUpstreamMsgDistWeek")
+	return
+}
+
+//GetUpstreamMsgDistMonth 获取消息发送分布月数据
+func (cube *DataCube) GetUpstreamMsgDistMonth(s string, e string) (resUpstreamMsgDistMonth ResUpstreamMsgDistMonth, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getUpstreamMsgDistMonth, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resUpstreamMsgDistMonth, "GetUpstreamMsgDistMonth")
+	return
+}

+ 306 - 0
officialaccount/datacube/publisher.go

@@ -0,0 +1,306 @@
+package datacube
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/silenceper/wechat/v2/util"
+	"net/url"
+	"strconv"
+)
+
+//AdSlot 广告位类型
+type AdSlot string
+
+const (
+	//SlotIDBizBottom 公众号底部广告
+	SlotIDBizBottom AdSlot = "SLOT_ID_BIZ_BOTTOM"
+	//SlotIDBizMidContext 公众号文中广告
+	SlotIDBizMidContext AdSlot = "SLOT_ID_BIZ_MID_CONTEXT"
+	//SlotIDBizVideoEnd 公众号视频后贴
+	SlotIDBizVideoEnd AdSlot = "SLOT_ID_BIZ_VIDEO_END"
+	//SlotIDBizSponsor 公众号互选广告
+	SlotIDBizSponsor AdSlot = "SLOT_ID_BIZ_SPONSOR"
+	//SlotIDBizCps 公众号返佣商品
+	SlotIDBizCps AdSlot = "SLOT_ID_BIZ_CPS"
+	//SlotIDWeappBanner 小程序banner
+	SlotIDWeappBanner AdSlot = "SLOT_ID_WEAPP_BANNER"
+	//SlotIDWeappRewardVideo 小程序激励视频
+	SlotIDWeappRewardVideo AdSlot = "SLOT_ID_WEAPP_REWARD_VIDEO"
+	//SlotIDWeappInterstitial 小程序插屏广告
+	SlotIDWeappInterstitial AdSlot = "SLOT_ID_WEAPP_INTERSTITIAL"
+	//SlotIDWeappVideoFeeds 小程序视频广告
+	SlotIDWeappVideoFeeds AdSlot = "SLOT_ID_WEAPP_VIDEO_FEEDS"
+	//SlotIDWeappVideoBegin 小程序视频前贴
+	SlotIDWeappVideoBegin AdSlot = "SLOT_ID_WEAPP_VIDEO_BEGIN"
+	//SlotIDWeappBox 小程序格子广告
+	SlotIDWeappBox AdSlot = "SLOT_ID_WEAPP_BOX"
+)
+
+const (
+	publisherURL = "https://api.weixin.qq.com/publisher/stat"
+)
+
+const (
+	actionPublisherAdPosGeneral = "publisher_adpos_general"
+	actionPublisherCpsGeneral   = "publisher_cps_general"
+	actionPublisherSettlement   = "publisher_settlement"
+)
+
+//BaseResp 错误信息
+type BaseResp struct {
+	ErrMsg string `json:"err_msg"`
+	Ret    int    `json:"ret"`
+}
+
+//ResPublisherAdPos 公众号分广告位数据响应
+type ResPublisherAdPos struct {
+	util.CommonError
+	BaseResp
+
+	Base     BaseResp        `json:"base_resp"`
+	List     []ResAdPosList  `json:"list"`
+	Summary  ResAdPosSummary `json:"summary"`
+	TotalNum int             `json:"total_num"`
+}
+
+//ResAdPosList 公众号分广告位列表
+type ResAdPosList struct {
+	SlotID        int64   `json:"slot_id"`
+	AdSlot        string  `json:"ad_slot"`
+	Date          string  `json:"date"`
+	ReqSuccCount  int     `json:"req_succ_count"`
+	ExposureCount int     `json:"exposure_count"`
+	ExposureRate  float64 `json:"exposure_rate"`
+	ClickCount    int     `json:"click_count"`
+	ClickRate     float64 `json:"click_rate"`
+	Income        int     `json:"income"`
+	Ecpm          float64 `json:"ecpm"`
+}
+
+//ResAdPosSummary 公众号分广告位概览
+type ResAdPosSummary struct {
+	ReqSuccCount  int     `json:"req_succ_count"`
+	ExposureCount int     `json:"exposure_count"`
+	ExposureRate  float64 `json:"exposure_rate"`
+	ClickCount    int     `json:"click_count"`
+	ClickRate     float64 `json:"click_rate"`
+	Income        int     `json:"income"`
+	Ecpm          float64 `json:"ecpm"`
+}
+
+//ResPublisherCps 公众号返佣商品数据响应
+type ResPublisherCps struct {
+	util.CommonError
+	BaseResp
+
+	Base     BaseResp      `json:"base_resp"`
+	List     []ResCpsList  `json:"list"`
+	Summary  ResCpsSummary `json:"summary"`
+	TotalNum int           `json:"total_num"`
+}
+
+//ResCpsList 公众号返佣商品列表
+type ResCpsList struct {
+	Date            string  `json:"date"`
+	ExposureCount   int     `json:"exposure_count"`
+	ClickCount      int     `json:"click_count"`
+	ClickRate       float64 `json:"click_rate"`
+	OrderCount      int     `json:"order_count"`
+	OrderRate       float64 `json:"order_rate"`
+	TotalFee        int     `json:"total_fee"`
+	TotalCommission int     `json:"total_commission"`
+}
+
+//ResCpsSummary 公众号返佣概览
+type ResCpsSummary struct {
+	ExposureCount   int     `json:"exposure_count"`
+	ClickCount      int     `json:"click_count"`
+	ClickRate       float64 `json:"click_rate"`
+	OrderCount      int     `json:"order_count"`
+	OrderRate       float64 `json:"order_rate"`
+	TotalFee        int     `json:"total_fee"`
+	TotalCommission int     `json:"total_commission"`
+}
+
+//ResPublisherSettlement 公众号结算收入数据及结算主体信息响应
+type ResPublisherSettlement struct {
+	util.CommonError
+	BaseResp
+
+	Base              BaseResp         `json:"base_resp"`
+	Body              string           `json:"body"`
+	PenaltyAll        int              `json:"penalty_all"`
+	RevenueAll        int64            `json:"revenue_all"`
+	SettledRevenueAll int64            `json:"settled_revenue_all"`
+	SettlementList    []SettlementList `json:"settlement_list"`
+	TotalNum          int              `json:"total_num"`
+}
+
+//SettlementList 结算单列表
+type SettlementList struct {
+	Date           string        `json:"date"`
+	Zone           string        `json:"zone"`
+	Month          string        `json:"month"`
+	Order          int           `json:"order"`
+	SettStatus     int           `json:"sett_status"`
+	SettledRevenue int           `json:"settled_revenue"`
+	SettNo         string        `json:"sett_no"`
+	MailSendCnt    string        `json:"mail_send_cnt"`
+	SlotRevenue    []SlotRevenue `json:"slot_revenue"`
+}
+
+//SlotRevenue 产生收入的广告
+type SlotRevenue struct {
+	SlotID             string `json:"slot_id"`
+	SlotSettledRevenue int    `json:"slot_settled_revenue"`
+}
+
+//ParamsPublisher 拉取数据参数
+type ParamsPublisher struct {
+	Action    string `json:"action"`
+	StartDate string `json:"start_date"`
+	EndDate   string `json:"end_date"`
+	Page      int    `json:"page"`
+	PageSize  int    `json:"page_size"`
+	AdSlot    AdSlot `json:"ad_slot"`
+}
+
+// fetchData 拉取统计数据
+func (cube *DataCube) fetchData(params ParamsPublisher) (response []byte, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	v := url.Values{}
+	v.Add("action", params.Action)
+	v.Add("access_token", accessToken)
+	v.Add("page", strconv.Itoa(params.Page))
+	v.Add("page_size", strconv.Itoa(params.PageSize))
+	v.Add("start_date", params.StartDate)
+	v.Add("end_date", params.EndDate)
+	if params.AdSlot != "" {
+		v.Add("ad_slot", string(params.AdSlot))
+	}
+
+	uri := fmt.Sprintf("%s?%s", publisherURL, v.Encode())
+
+	response, err = util.HTTPGet(uri)
+	if err != nil {
+		return
+	}
+	return
+}
+
+//GetPublisherAdPosGeneral 获取公众号分广告位数据
+func (cube *DataCube) GetPublisherAdPosGeneral(startDate, endDate string, page, pageSize int, adSlot AdSlot) (resPublisherAdPos ResPublisherAdPos, err error) {
+	params := ParamsPublisher{
+		Action:    actionPublisherAdPosGeneral,
+		StartDate: startDate,
+		EndDate:   endDate,
+		Page:      page,
+		PageSize:  pageSize,
+		AdSlot:    adSlot,
+	}
+
+	response, err := cube.fetchData(params)
+	if err != nil {
+		return
+	}
+
+	err = json.Unmarshal(response, &resPublisherAdPos)
+	if err != nil {
+		return
+	}
+
+	if resPublisherAdPos.CommonError.ErrCode != 0 {
+		err = fmt.Errorf("GetPublisherAdPosGeneral Error , errcode=%d , errmsg=%s", resPublisherAdPos.CommonError.ErrCode, resPublisherAdPos.CommonError.ErrMsg)
+		return
+	}
+
+	if resPublisherAdPos.BaseResp.Ret != 0 {
+		err = fmt.Errorf("GetPublisherAdPosGeneral Error , errcode=%d , errmsg=%s", resPublisherAdPos.BaseResp.Ret, resPublisherAdPos.BaseResp.ErrMsg)
+		return
+	}
+
+	if resPublisherAdPos.Base.Ret != 0 {
+		err = fmt.Errorf("GetPublisherAdPosGeneral Error , errcode=%d , errmsg=%s", resPublisherAdPos.Base.Ret, resPublisherAdPos.Base.ErrMsg)
+		return
+	}
+	return
+}
+
+//GetPublisherCpsGeneral 获取公众号返佣商品数据
+func (cube *DataCube) GetPublisherCpsGeneral(startDate, endDate string, page, pageSize int) (resPublisherCps ResPublisherCps, err error) {
+	params := ParamsPublisher{
+		Action:    actionPublisherCpsGeneral,
+		StartDate: startDate,
+		EndDate:   endDate,
+		Page:      page,
+		PageSize:  pageSize,
+	}
+
+	response, err := cube.fetchData(params)
+	if err != nil {
+		return
+	}
+
+	err = json.Unmarshal(response, &resPublisherCps)
+	if err != nil {
+		return
+	}
+
+	if resPublisherCps.CommonError.ErrCode != 0 {
+		err = fmt.Errorf("GetPublisherCpsGeneral Error , errcode=%d , errmsg=%s", resPublisherCps.CommonError.ErrCode, resPublisherCps.CommonError.ErrMsg)
+		return
+	}
+
+	if resPublisherCps.BaseResp.Ret != 0 {
+		err = fmt.Errorf("GetPublisherCpsGeneral Error , errcode=%d , errmsg=%s", resPublisherCps.BaseResp.Ret, resPublisherCps.BaseResp.ErrMsg)
+		return
+	}
+
+	if resPublisherCps.Base.Ret != 0 {
+		err = fmt.Errorf("GetPublisherCpsGeneral Error , errcode=%d , errmsg=%s", resPublisherCps.Base.Ret, resPublisherCps.Base.ErrMsg)
+		return
+	}
+
+	return
+}
+
+//GetPublisherSettlement 获取公众号结算收入数据及结算主体信息
+func (cube *DataCube) GetPublisherSettlement(startDate, endDate string, page, pageSize int) (resPublisherSettlement ResPublisherSettlement, err error) {
+	params := ParamsPublisher{
+		Action:    actionPublisherSettlement,
+		StartDate: startDate,
+		EndDate:   endDate,
+		Page:      page,
+		PageSize:  pageSize,
+	}
+
+	response, err := cube.fetchData(params)
+	if err != nil {
+		return
+	}
+
+	err = json.Unmarshal(response, &resPublisherSettlement)
+	if err != nil {
+		return
+	}
+
+	if resPublisherSettlement.CommonError.ErrCode != 0 {
+		err = fmt.Errorf("GetPublisherSettlement Error , errcode=%d , errmsg=%s", resPublisherSettlement.CommonError.ErrCode, resPublisherSettlement.CommonError.ErrMsg)
+		return
+	}
+
+	if resPublisherSettlement.BaseResp.Ret != 0 {
+		err = fmt.Errorf("GetPublisherSettlement Error , errcode=%d , errmsg=%s", resPublisherSettlement.BaseResp.Ret, resPublisherSettlement.BaseResp.ErrMsg)
+		return
+	}
+
+	if resPublisherSettlement.Base.Ret != 0 {
+		err = fmt.Errorf("GetPublisherSettlement Error , errcode=%d , errmsg=%s", resPublisherSettlement.Base.Ret, resPublisherSettlement.Base.ErrMsg)
+		return
+	}
+	return
+}

+ 77 - 0
officialaccount/datacube/user.go

@@ -0,0 +1,77 @@
+package datacube
+
+import (
+	"fmt"
+	"github.com/silenceper/wechat/v2/util"
+)
+
+const (
+	getUserSummary    = "https://api.weixin.qq.com/datacube/getusersummary"
+	getUserAccumulate = "https://api.weixin.qq.com/datacube/getusercumulate"
+)
+
+//ResUserSummary 获取用户增减数据响应
+type ResUserSummary struct {
+	util.CommonError
+
+	List []struct {
+		RefDate    string `json:"ref_date"`
+		UserSource int    `json:"user_source"`
+		NewUser    int    `json:"new_user"`
+		CancelUser int    `json:"cancel_user"`
+	} `json:"list"`
+}
+
+//ResUserAccumulate 获取累计用户数据响应
+type ResUserAccumulate struct {
+	util.CommonError
+
+	List []struct {
+		RefDate      string `json:"ref_date"`
+		CumulateUser int    `json:"cumulate_user"`
+	} `json:"list"`
+}
+
+//GetUserSummary 获取用户增减数据
+func (cube *DataCube) GetUserSummary(s string, e string) (resUserSummary ResUserSummary, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getUserSummary, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resUserSummary, "GetUserSummary")
+	return
+}
+
+//GetUserAccumulate 获取累计用户数据
+func (cube *DataCube) GetUserAccumulate(s string, e string) (resUserAccumulate ResUserAccumulate, err error) {
+	accessToken, err := cube.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", getUserAccumulate, accessToken)
+	reqDate := &reqDate{
+		BeginDate: s,
+		EndDate:   e,
+	}
+
+	response, err := util.PostJSON(uri, reqDate)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(response, &resUserAccumulate, "GetUserAccumulate")
+	return
+}

+ 1 - 1
officialaccount/device/qrcode.go

@@ -60,7 +60,7 @@ func (d *Device) VerifyQRCode(ticket string) (res ResVerifyQRCode, err error) {
 	req := map[string]interface{}{
 		"ticket": ticket,
 	}
-	fmt.Println(req)
+
 	var response []byte
 	if response, err = util.PostJSON(uri, req); err != nil {
 		return

+ 6 - 0
officialaccount/officialaccount.go

@@ -1,6 +1,7 @@
 package officialaccount
 
 import (
+	"github.com/silenceper/wechat/v2/officialaccount/datacube"
 	"net/http"
 
 	"github.com/silenceper/wechat/v2/credential"
@@ -101,3 +102,8 @@ func (officialAccount *OfficialAccount) GetDevice() *device.Device {
 func (officialAccount *OfficialAccount) GetBroadcast() *broadcast.Broadcast {
 	return broadcast.NewBroadcast(officialAccount.ctx)
 }
+
+//GetDataCube 数据统计
+func (officialAccount *OfficialAccount) GetDataCube() *datacube.DataCube {
+	return datacube.NewCube(officialAccount.ctx)
+}