306 lines
11 KiB
Go
306 lines
11 KiB
Go
package googleanalytics
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
"videoweb/pub/common/httputil"
|
|
"videoweb/pub/common/log"
|
|
"videoweb/pub/common/srvdef"
|
|
"videoweb/pub/common/utils"
|
|
"videoweb/pub/models/analyticsdailystat"
|
|
"videoweb/pub/models/analyticsstat"
|
|
"videoweb/web/webg"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
analyticsdata "google.golang.org/api/analyticsdata/v1beta"
|
|
"google.golang.org/api/option"
|
|
)
|
|
|
|
// HourlyData 用于存储和排序每小时的数据
|
|
type HourlyData struct {
|
|
Hour int
|
|
Users int64
|
|
}
|
|
type ChannelAnalyzeReq struct {
|
|
Channel string `from:"channel" json:"channel" binding:"required"` // 渠道
|
|
Begin time.Time `json:"begin" form:"begin"` // 开始时间
|
|
End time.Time `json:"end" form:"end"` // 结束时间
|
|
AppId *int32 `from:"appId" json:"appId" ` // 应用id
|
|
}
|
|
|
|
type AnalyzeItem struct {
|
|
Channel string `json:"channel" form:"channel"` // 渠道
|
|
AppId int32 `json:"appId" form:"appId"` // 应用ID
|
|
AppName string `json:"appName" form:"appName"` // 应用名称
|
|
Begin time.Time `json:"begin" form:"begin"` // 开始时间
|
|
End time.Time `json:"end" form:"end"` // 结束时间
|
|
RegisterCount int64 `json:"registerCount" form:"registerCount"` // 总注册IP数
|
|
RegisterIpCount int64 `json:"registerIpCount" form:"registerIpCount"` // 总注册IP数
|
|
VisitCount int64 `json:"visitCount" form:"visitCount"` // 总访问数
|
|
AdvertiseClick int64 `json:"advertiseClick" form:"advertiseClick"` // 广告点击数
|
|
RechargeCount int64 `json:"rechargeCount" form:"rechargeCount"` // 充值次数
|
|
RechargeAmount int64 `json:"rechargeAmount" form:"rechargeAmount"` // 充值金额
|
|
RechargeUsers int64 `json:"rechargeUsers" form:"rechargeUsers"` // 充值用户数
|
|
YesTodayRegisterCount int64 `json:"yesTodayRegisterCount" form:"yesTodayRegisterCount"` // 昨日注册IP数
|
|
YesTodayVisitCount int64 `json:"yesTodayVisitCount" form:"yesTodayVisitCount"` // 昨日访问数
|
|
YesTodayRechargeAmount int64 `json:"yesTodayRechargeAmount" form:"yesTodayRechargeAmount"` // 昨日充值
|
|
YesTodayRetainRate int64 `json:"yesTodayRetainRate" form:"yesTodayRetainRate"` // 昨日次流
|
|
}
|
|
type ChannelAnalyzeResp struct {
|
|
List []*AnalyzeItem `json:"list" form:"list"`
|
|
}
|
|
type AnalyzeResp struct {
|
|
Code int `json:"code"`
|
|
Msg string `json:"msg"`
|
|
Data ChannelAnalyzeResp `json:"data"`
|
|
}
|
|
|
|
func AnalyticsData(appId int64, appName string, propertyId string) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
// 1. 认证授权 - 使用服务账号的 JSON 密钥文件
|
|
credentialsFile := "composed-anvil-470512-r8-fd674ac0d7dc.json"
|
|
b, err := os.ReadFile(credentialsFile)
|
|
if err != nil {
|
|
log.Error("AnalyticsData 读取凭证文件失败", log.E(err))
|
|
return
|
|
}
|
|
|
|
curStatTime := utils.RecentMinute(now.Add(time.Minute), 30) // 按20分钟对齐
|
|
|
|
data := analyticsstat.AnalyticsInfo{
|
|
AppId: appId,
|
|
SumDate: curStatTime,
|
|
AppName: appName,
|
|
DaySumDate: utils.BeginOfTime(curStatTime),
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
// 2. 创建 API 客户端
|
|
client, err := analyticsdata.NewService(ctx, option.WithCredentialsJSON(b))
|
|
if err != nil {
|
|
log.Error("AnalyticsData 无法创建 Analytics Data 客户端", log.E(err))
|
|
return
|
|
}
|
|
if client == nil {
|
|
log.Error("AnalyticsData client 为空")
|
|
return
|
|
}
|
|
|
|
req := &analyticsdata.RunRealtimeReportRequest{
|
|
// 虽然实时报告默认就是最近30分钟,但你可以通过 dateRanges 进行限制(如果需要的话)
|
|
// DateRanges: []*analyticsdata.DateRange{
|
|
// {StartDate: "30 minutes ago", EndDate: "now"}, // 某些客户端库可能支持相对时间
|
|
// },
|
|
Dimensions: []*analyticsdata.Dimension{
|
|
// 添加你关心的维度,例如国家或地区
|
|
{Name: "country"},
|
|
// 实时报告支持的维度有限,请参考官方文档:
|
|
// https://developers.google.com/analytics/devguides/reporting/data/v1/realtime-basics?hl=zh-cn
|
|
},
|
|
Metrics: []*analyticsdata.Metric{
|
|
{Name: "activeUsers"}, // 获取活跃用户数
|
|
},
|
|
}
|
|
|
|
// 4. 调用 RealTime API
|
|
resp, err := client.Properties.RunRealtimeReport(propertyId, req).Do()
|
|
if err != nil {
|
|
log.Error("AnalyticsData RunRealtimeReport 请求报告出错", log.E(err))
|
|
return
|
|
}
|
|
if resp == nil {
|
|
log.Error("AnalyticsData RunRealtimeReport resp 为空")
|
|
return
|
|
}
|
|
|
|
for _, row := range resp.Rows {
|
|
if row == nil || len(row.MetricValues) == 0 || row.MetricValues[0] == nil {
|
|
continue
|
|
}
|
|
activeUsers, _ := strconv.ParseInt(row.MetricValues[0].Value, 10, 64)
|
|
data.ActiveUsers += activeUsers
|
|
}
|
|
|
|
yestodayReq := &analyticsdata.RunReportRequest{
|
|
DateRanges: []*analyticsdata.DateRange{
|
|
{StartDate: "yesterday", EndDate: "yesterday"}, // 查询昨天
|
|
},
|
|
Dimensions: []*analyticsdata.Dimension{
|
|
{Name: "date"}, // 核心:添加日期维度,按天分组
|
|
},
|
|
Metrics: []*analyticsdata.Metric{
|
|
{Name: "screenPageViews"},
|
|
{Name: "totalUsers"},
|
|
{Name: "activeUsers"}, // 获取活跃用户数
|
|
{Name: "sessions"}, // 会话数
|
|
{Name: "newUsers"}, // 新增数量
|
|
},
|
|
}
|
|
|
|
yestodayResp, err := client.Properties.RunReport(propertyId, yestodayReq).Do()
|
|
if err != nil {
|
|
log.Error("AnalyticsData RunReport 请求报告出错", log.E(err))
|
|
return
|
|
}
|
|
if yestodayResp == nil {
|
|
log.Error("AnalyticsData RunReport yestodayResp 为空")
|
|
return
|
|
}
|
|
for _, row := range yestodayResp.Rows {
|
|
if row == nil || len(row.MetricValues) < 5 {
|
|
continue
|
|
}
|
|
data.YestodayPV, _ = strconv.ParseInt(row.MetricValues[0].Value, 10, 64)
|
|
data.YestodayUV, _ = strconv.ParseInt(row.MetricValues[1].Value, 10, 64)
|
|
data.YestodayActiveUsers, _ = strconv.ParseInt(row.MetricValues[2].Value, 10, 64)
|
|
data.YestodaySessions, _ = strconv.ParseInt(row.MetricValues[3].Value, 10, 64)
|
|
data.YestodayNewUsers, _ = strconv.ParseInt(row.MetricValues[4].Value, 10, 64)
|
|
}
|
|
|
|
analyticsstat.InsertOneAnalyticsInfo(ctx, &data)
|
|
}
|
|
|
|
func AnalyticsDailyData(appId int64, appName string, channel string, propertyId string) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
// 1. 认证授权 - 使用服务账号的 JSON 密钥文件
|
|
credentialsFile := "composed-anvil-470512-r8-fd674ac0d7dc.json"
|
|
b, err := os.ReadFile(credentialsFile)
|
|
if err != nil {
|
|
log.Error("AnalyticsDailyData 读取凭证文件失败", log.E(err))
|
|
return
|
|
}
|
|
|
|
// 2. 创建 API 客户端
|
|
client, err := analyticsdata.NewService(ctx, option.WithCredentialsJSON(b))
|
|
if err != nil {
|
|
log.Error("AnalyticsDailyData 无法创建 Analytics Data 客户端", log.E(err))
|
|
return
|
|
}
|
|
if client == nil {
|
|
log.Error("AnalyticsDailyData client 为空")
|
|
return
|
|
}
|
|
|
|
data := analyticsdailystat.AnalyticsDailyInfo{
|
|
AppId: appId,
|
|
SumDate: utils.BeginOfTime(now),
|
|
AppName: appName,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
yestodayReq := &analyticsdata.RunReportRequest{
|
|
DateRanges: []*analyticsdata.DateRange{
|
|
{StartDate: "7daysAgo", EndDate: "today"}, // 查询昨天
|
|
},
|
|
Dimensions: []*analyticsdata.Dimension{
|
|
{Name: "date"}, // 核心:添加日期维度,按天分组
|
|
},
|
|
Metrics: []*analyticsdata.Metric{
|
|
{Name: "screenPageViews"},
|
|
{Name: "totalUsers"},
|
|
{Name: "activeUsers"}, // 获取活跃用户数
|
|
{Name: "sessions"}, // 会话数
|
|
{Name: "newUsers"}, // 新增数量
|
|
},
|
|
}
|
|
|
|
yestodayResp, err := client.Properties.RunReport(propertyId, yestodayReq).Do()
|
|
if err != nil {
|
|
log.Error("AnalyticsDailyData RunReport 请求报告出错", log.E(err))
|
|
return
|
|
}
|
|
if yestodayResp == nil {
|
|
log.Error("AnalyticsDailyData RunReport yestodayResp 为空")
|
|
return
|
|
}
|
|
for _, row := range yestodayResp.Rows {
|
|
if row == nil || len(row.DimensionValues) == 0 || row.DimensionValues[0] == nil || len(row.MetricValues) < 5 {
|
|
continue
|
|
}
|
|
// data.SumDate
|
|
layout := "20060102" // Go 的特定格式:2006=年,01=月,02=日
|
|
|
|
// 解析字符串为时间对象
|
|
dayTime, err := time.Parse(layout, row.DimensionValues[0].Value)
|
|
if err != nil {
|
|
log.Error("AnalyticsDailyData time.Parse 失败", log.E(err))
|
|
continue
|
|
}
|
|
data.SumDate = dayTime.Add(-8 * time.Hour)
|
|
data.YestodayPV, _ = strconv.ParseInt(row.MetricValues[0].Value, 10, 64)
|
|
data.YestodayUV, _ = strconv.ParseInt(row.MetricValues[1].Value, 10, 64)
|
|
data.YestodayActiveUsers, _ = strconv.ParseInt(row.MetricValues[2].Value, 10, 64)
|
|
data.YestodaySessions, _ = strconv.ParseInt(row.MetricValues[3].Value, 10, 64)
|
|
data.YestodayNewUsers, _ = strconv.ParseInt(row.MetricValues[4].Value, 10, 64)
|
|
|
|
LastFilter := bson.M{
|
|
analyticsdailystat.FN.AnalyticsDailyInfo.SumDate: data.SumDate,
|
|
analyticsdailystat.FN.AnalyticsDailyInfo.AppId: appId,
|
|
}
|
|
|
|
_, serr := analyticsdailystat.FindOneAnalyticsDailyInfo(ctx, LastFilter)
|
|
if serr != nil && serr.Code == srvdef.ErrNoData {
|
|
analyticsdailystat.InsertOneAnalyticsDailyInfo(ctx, &data)
|
|
} else {
|
|
LastUpdate := bson.M{
|
|
"$set": bson.M{
|
|
analyticsdailystat.FN.AnalyticsDailyInfo.YestodayPV: data.YestodayPV,
|
|
analyticsdailystat.FN.AnalyticsDailyInfo.YestodayUV: data.YestodayUV,
|
|
analyticsdailystat.FN.AnalyticsDailyInfo.YestodayActiveUsers: data.YestodayActiveUsers,
|
|
analyticsdailystat.FN.AnalyticsDailyInfo.YestodaySessions: data.YestodaySessions,
|
|
analyticsdailystat.FN.AnalyticsDailyInfo.YestodayNewUsers: data.YestodayNewUsers,
|
|
analyticsdailystat.FN.AnalyticsDailyInfo.UpdatedAt: now,
|
|
},
|
|
}
|
|
analyticsdailystat.UpdateOneAnalyticsDailyInfo(ctx, LastFilter, LastUpdate)
|
|
}
|
|
}
|
|
|
|
analyzeList := ChannelAnalyzeResp{}
|
|
req := ChannelAnalyzeReq{
|
|
Channel: channel,
|
|
Begin: utils.BeginOfTime(now.AddDate(0, 0, -1)),
|
|
End: utils.EndOfTime(now.AddDate(0, 0, -1)),
|
|
}
|
|
|
|
param, _ := utils.ToJsonM(req)
|
|
innerStatUrl := webg.Conf.NewInnerStatUrl + "/api/stat/dailyStat/channel/analyze"
|
|
resp := AnalyzeResp{}
|
|
code, err := httputil.Client().POSTJsonWithJResp(&resp, innerStatUrl, nil, param)
|
|
if err != nil {
|
|
log.Error("ChannelAnalyze httputil innerStatUrl failed!", log.E(err))
|
|
}
|
|
if code != 200 {
|
|
log.Error("ChannelAnalyze httputil innerStatUrl code1!=200", log.Any("code", code))
|
|
}
|
|
log.Info("ChannelAnalyze", log.Any("req", req), log.Any("innerStatUrl", innerStatUrl), log.Any("analyzeList", resp.Data))
|
|
analyzeList = resp.Data
|
|
|
|
for _, channelAnalyze := range analyzeList.List {
|
|
data.LeadNewCount += float64(channelAnalyze.RegisterCount)
|
|
data.LeadAmount += float64(channelAnalyze.RechargeAmount)
|
|
}
|
|
LastFilter := bson.M{
|
|
analyticsdailystat.FN.AnalyticsDailyInfo.SumDate: utils.BeginOfTime(now),
|
|
analyticsdailystat.FN.AnalyticsDailyInfo.AppId: appId,
|
|
}
|
|
_, serr := analyticsdailystat.FindOneAnalyticsDailyInfo(ctx, LastFilter)
|
|
if serr != nil && serr.Code == srvdef.ErrNoData {
|
|
analyticsdailystat.InsertOneAnalyticsDailyInfo(ctx, &data)
|
|
} else {
|
|
LastUpdate := bson.M{
|
|
"$set": bson.M{
|
|
analyticsdailystat.FN.AnalyticsDailyInfo.LeadNewCount: data.LeadNewCount,
|
|
analyticsdailystat.FN.AnalyticsDailyInfo.LeadAmount: data.LeadAmount,
|
|
},
|
|
}
|
|
|
|
analyticsdailystat.UpdateOneAnalyticsDailyInfo(ctx, LastFilter, LastUpdate)
|
|
}
|
|
}
|