pet-ai/server/plugin/wechat-integration/service/mini_statistics_service.go

407 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"fmt"
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/response"
"github.com/silenceper/wechat/v2/miniprogram"
)
type MiniStatisticsService struct{}
// getMiniProgram 获取微信小程序实例
func (m *MiniStatisticsService) getMiniProgram() (*miniprogram.MiniProgram, error) {
// 使用微信小程序服务获取实例
return ServiceGroupApp.MiniService.GetWechatMiniProgram()
}
// GetMiniStatistics 获取小程序基础统计数据
func (m *MiniStatisticsService) GetMiniStatistics() (*response.MiniStatisticsResponse, error) {
// 获取微信小程序实例
mini, err := m.getMiniProgram()
if err != nil {
return nil, err
}
// 获取最近7天的数据概况
endDate := time.Now().AddDate(0, 0, -1).Format("20060102")
beginDate := time.Now().AddDate(0, 0, -2).Format("20060102")
// 调用微信API获取数据概况
analysisService := mini.GetAnalysis()
summary, err := analysisService.GetAnalysisDailySummary(beginDate, endDate)
if err != nil {
global.GVA_LOG.Error("获取小程序数据概况失败: " + err.Error())
return nil, fmt.Errorf("获取小程序统计数据失败: %v", err)
}
// 计算统计数据
var totalUsers, totalSessions, totalPV, totalUV int64
var todaySessions int64
for _, item := range summary.List {
totalSessions += int64(item.VisitTotal)
if item.RefDate == endDate {
todaySessions = int64(item.VisitTotal)
}
}
// 获取访问趋势数据来补充统计
visitTrend, err := analysisService.GetAnalysisDailyVisitTrend(beginDate, endDate)
if err == nil {
for _, item := range visitTrend.List {
totalPV += int64(item.VisitPV)
totalUV += int64(item.VisitUV)
}
}
// 估算总用户数(基于最近的活跃用户数)
if len(visitTrend.List) > 0 {
totalUsers = int64(visitTrend.List[len(visitTrend.List)-1].VisitUV) * 10 // 估算倍数
}
// 计算平均停留时间和跳出率
var avgStayTime float64 = 180 // 默认3分钟
var bounceRate float64 = 0.35 // 默认35%
if len(visitTrend.List) > 0 {
var totalStayTime float64
for _, item := range visitTrend.List {
totalStayTime += item.StayTimeUV
}
avgStayTime = totalStayTime / float64(len(visitTrend.List))
// 简单的跳出率计算访问深度小于2的比例
var totalDepth float64
for _, item := range visitTrend.List {
totalDepth += item.VisitDepth
}
avgDepth := totalDepth / float64(len(visitTrend.List))
if avgDepth > 0 {
bounceRate = 1.0 / avgDepth
if bounceRate > 1.0 {
bounceRate = 0.8 // 最大80%
}
}
}
statistics := &response.MiniStatisticsResponse{
TotalUsers: int(totalUsers),
ActiveUsers: int(totalUV),
TotalSessions: int(totalSessions),
TodaySessions: int(todaySessions),
TotalPV: int(totalPV),
TotalUV: int(totalUV),
AvgStayTime: int(avgStayTime),
BounceRate: bounceRate,
}
global.GVA_LOG.Info("成功获取小程序基础统计数据")
return statistics, nil
}
// GetVisitTrendData 获取访问趋势数据
func (m *MiniStatisticsService) GetVisitTrendData(startDate, endDate string) ([]response.VisitTrendData, error) {
// 获取微信小程序实例
mini, err := m.getMiniProgram()
if err != nil {
return nil, err
}
// 格式化日期参数
if startDate == "" || endDate == "" {
endDate = time.Now().Format("20060102")
startDate = time.Now().AddDate(0, 0, -6).Format("20060102")
} else {
// 转换日期格式 YYYY-MM-DD -> YYYYMMDD
if len(startDate) == 10 {
startDate = startDate[:4] + startDate[5:7] + startDate[8:10]
}
if len(endDate) == 10 {
endDate = endDate[:4] + endDate[5:7] + endDate[8:10]
}
}
// 调用微信API获取访问趋势数据
analysisService := mini.GetAnalysis()
visitTrend, err := analysisService.GetAnalysisDailyVisitTrend(startDate, endDate)
if err != nil {
global.GVA_LOG.Error("获取小程序访问趋势数据失败: " + err.Error())
return nil, fmt.Errorf("获取访问趋势数据失败: %v", err)
}
// 转换数据格式
var trendData []response.VisitTrendData
for _, item := range visitTrend.List {
data := response.VisitTrendData{
Date: item.RefDate[:4] + "-" + item.RefDate[4:6] + "-" + item.RefDate[6:8], // YYYYMMDD -> YYYY-MM-DD
SessionCnt: item.SessionCnt,
VisitPV: item.VisitPV,
VisitUV: item.VisitUV,
VisitUVNew: item.VisitUVNew,
StayTimeUV: int(item.StayTimeUV),
StayTimeSession: int(item.StayTimeSession),
}
trendData = append(trendData, data)
}
global.GVA_LOG.Info(fmt.Sprintf("成功获取访问趋势数据,共 %d 条记录", len(trendData)))
return trendData, nil
}
// GetSourceData 获取用户来源分布数据
func (m *MiniStatisticsService) GetSourceData() ([]response.SourceData, error) {
// 获取微信小程序实例
mini, err := m.getMiniProgram()
if err != nil {
return nil, err
}
// 获取最近7天的访问分布数据
endDate := time.Now().AddDate(0, 0, -1).Format("20060102")
beginDate := time.Now().AddDate(0, 0, -7).Format("20060102")
// 调用微信API获取访问分布数据
analysisService := mini.GetAnalysis()
visitDistribution, err := analysisService.GetAnalysisVisitDistribution(beginDate, endDate)
if err != nil {
global.GVA_LOG.Error("获取小程序访问分布数据失败: " + err.Error())
return nil, fmt.Errorf("获取用户来源分布数据失败: %v", err)
}
// 转换数据格式
var sourceData []response.SourceData
var totalVisitUV int
// 先计算总访问量用于计算百分比
for _, index := range visitDistribution.List {
if index.Index == "access_source_session_cnt" { // 访问来源会话数
for _, item := range index.ItemList {
totalVisitUV += item.AccessSourceVisitUV
}
}
}
// 访问来源名称映射
sourceNames := map[int]string{
1: "小程序历史列表",
2: "搜索",
3: "会话分享",
4: "二维码",
5: "公众号主页或名称",
6: "聊天主界面下拉",
7: "系统桌面",
8: "小程序主页",
9: "附近的小程序",
10: "其他",
}
// 处理访问来源数据
for _, index := range visitDistribution.List {
if index.Index == "access_source_session_cnt" { // 访问来源会话数
for _, item := range index.ItemList {
name, exists := sourceNames[item.Key]
if !exists {
name = fmt.Sprintf("来源%d", item.Key)
}
percentage := 0.0
if totalVisitUV > 0 {
percentage = float64(item.AccessSourceVisitUV) / float64(totalVisitUV) * 100
}
data := response.SourceData{
RefType: fmt.Sprintf("%d", item.Key),
Name: name,
SessionCnt: item.Value,
VisitPV: item.Value, // 使用会话数作为PV
VisitUV: item.AccessSourceVisitUV,
Percentage: percentage,
}
sourceData = append(sourceData, data)
}
break
}
}
global.GVA_LOG.Info(fmt.Sprintf("成功获取用户来源分布数据,共 %d 条记录", len(sourceData)))
return sourceData, nil
}
// GetPageData 获取页面访问数据
func (m *MiniStatisticsService) GetPageData() ([]response.PageData, error) {
// 获取微信小程序实例
mini, err := m.getMiniProgram()
if err != nil {
return nil, err
}
// 获取最近7天的页面访问数据
endDate := time.Now().AddDate(0, 0, -1).Format("20060102")
beginDate := time.Now().AddDate(0, 0, -7).Format("20060102")
// 调用微信API获取页面访问数据
analysisService := mini.GetAnalysis()
visitPage, err := analysisService.GetAnalysisVisitPage(beginDate, endDate)
if err != nil {
global.GVA_LOG.Error("获取小程序页面访问数据失败: " + err.Error())
return nil, fmt.Errorf("获取页面访问数据失败: %v", err)
}
// 转换数据格式
var pageData []response.PageData
for _, item := range visitPage.List {
// 从页面路径提取页面名称
pageName := item.PagePath
if pageName == "" {
pageName = "未知页面"
}
data := response.PageData{
PagePath: item.PagePath,
PageName: pageName,
PagePV: item.PageVisitPV,
PageUV: item.PageVisitUV,
StayTimePV: int(item.PageStaytimePV),
EntryPagePV: item.EntrypagePV,
ExitPagePV: item.ExitpagePV,
SharePV: item.PageSharePV,
ShareUV: item.PageShareUV,
}
pageData = append(pageData, data)
}
global.GVA_LOG.Info(fmt.Sprintf("成功获取页面访问数据,共 %d 条记录", len(pageData)))
return pageData, nil
}
// GetRetentionData 获取用户留存数据
func (m *MiniStatisticsService) GetRetentionData() ([]response.RetentionData, error) {
// 获取微信小程序实例
mini, err := m.getMiniProgram()
if err != nil {
return nil, err
}
// 获取最近7天的留存数据
beginDate := time.Now().AddDate(0, 0, -1).Format("20060102")
// 调用微信API获取日留存数据
analysisService := mini.GetAnalysis()
dailyRetain, err := analysisService.GetAnalysisDailyRetain(beginDate, beginDate)
if err != nil {
global.GVA_LOG.Error("获取小程序日留存数据失败: " + err.Error())
return nil, fmt.Errorf("获取用户留存数据失败: %v", err)
}
// 转换数据格式
var retentionData []response.RetentionData
// 处理新增用户留存
if len(dailyRetain.VisitUVNew) > 0 {
data := response.RetentionData{
RefDate: dailyRetain.RefDate[:4] + "-" + dailyRetain.RefDate[4:6] + "-" + dailyRetain.RefDate[6:8],
VisitUVNew: dailyRetain.VisitUVNew[0].Value, // 当天新增用户数
VisitUV: 0, // 活跃用户数从其他接口获取
Day1: 0.0,
Day7: 0.0,
Day30: 0.0,
}
// 计算留存率
if len(dailyRetain.VisitUVNew) > 1 {
data.Day1 = float64(dailyRetain.VisitUVNew[1].Value) / float64(dailyRetain.VisitUVNew[0].Value)
}
if len(dailyRetain.VisitUVNew) > 7 {
data.Day7 = float64(dailyRetain.VisitUVNew[7].Value) / float64(dailyRetain.VisitUVNew[0].Value)
}
if len(dailyRetain.VisitUVNew) > 30 {
data.Day30 = float64(dailyRetain.VisitUVNew[30].Value) / float64(dailyRetain.VisitUVNew[0].Value)
}
// 获取活跃用户数
if len(dailyRetain.VisitUV) > 0 {
data.VisitUV = dailyRetain.VisitUV[0].Value
}
retentionData = append(retentionData, data)
}
global.GVA_LOG.Info(fmt.Sprintf("成功获取用户留存数据,共 %d 条记录", len(retentionData)))
return retentionData, nil
}
// GetDeviceData 获取设备分布数据
func (m *MiniStatisticsService) GetDeviceData() ([]response.DeviceData, error) {
// 获取微信小程序实例
mini, err := m.getMiniProgram()
if err != nil {
return nil, err
}
// 获取最近7天的用户画像数据
endDate := time.Now().AddDate(0, 0, -1).Format("20060102")
beginDate := time.Now().AddDate(0, 0, -7).Format("20060102")
// 调用微信API获取用户画像数据
analysisService := mini.GetAnalysis()
userPortrait, err := analysisService.GetAnalysisUserPortrait(beginDate, endDate)
if err != nil {
global.GVA_LOG.Error("获取小程序用户画像数据失败: " + err.Error())
return nil, fmt.Errorf("获取设备分布数据失败: %v", err)
}
// 转换设备数据格式
var deviceData []response.DeviceData
var totalVisitUV int
// 计算总访问量
for _, device := range userPortrait.VisitUV.Devices {
totalVisitUV += device.Value
}
// 转换设备数据
for _, device := range userPortrait.VisitUV.Devices {
percentage := 0.0
if totalVisitUV > 0 {
percentage = float64(device.Value) / float64(totalVisitUV) * 100
}
data := response.DeviceData{
DeviceType: device.Name,
SessionCnt: device.Value, // 使用访问量作为会话数
VisitUV: device.Value,
Percentage: percentage,
}
deviceData = append(deviceData, data)
}
// 如果没有设备数据,也处理平台数据
if len(deviceData) == 0 {
totalVisitUV = 0
for _, platform := range userPortrait.VisitUV.Platforms {
totalVisitUV += platform.Value
}
for _, platform := range userPortrait.VisitUV.Platforms {
percentage := 0.0
if totalVisitUV > 0 {
percentage = float64(platform.Value) / float64(totalVisitUV) * 100
}
data := response.DeviceData{
DeviceType: platform.Name,
SessionCnt: platform.Value,
VisitUV: platform.Value,
Percentage: percentage,
}
deviceData = append(deviceData, data)
}
}
global.GVA_LOG.Info(fmt.Sprintf("成功获取设备分布数据,共 %d 条记录", len(deviceData)))
return deviceData, nil
}