package service import ( "context" "fmt" "sort" "time" "cid/dao" "cid/model/dto" "cid/model/entity" ) var AdStatistics = new(adStatistics) type adStatistics struct{} // List 获取统计数据列表 func (s *adStatistics) List(ctx context.Context, req *dto.GetAdStatisticsReq) (res *dto.GetAdStatisticsRes, err error) { list, total, err := dao.AdStatistics.List(ctx, req) if err != nil { return nil, err } res = &dto.GetAdStatisticsRes{ Statistics: list, Total: int(total), } return } // GetStatistics 获取统计数据 func (s *adStatistics) GetStatistics(ctx context.Context, req *dto.GetAdStatisticsReq) (res *dto.GetAdStatisticsRes, err error) { list, total, err := dao.AdStatistics.GetStatistics(ctx, req) if err != nil { return nil, err } res = &dto.GetAdStatisticsRes{ Statistics: list, Total: int(total), } return } // GetDashboard 获取仪表盘数据 func (s *adStatistics) GetDashboard(ctx context.Context, req *dto.GetDashboardReq) (res *dto.GetDashboardRes, err error) { // 构建统计查询请求 statReq := &dto.GetAdStatisticsReq{ StartDate: req.StartDate, EndDate: req.EndDate, StatDimension: req.Dimension, } // 获取所有统计数据 stats, _, err := dao.AdStatistics.GetStatistics(ctx, statReq) if err != nil { return nil, err } // 计算总览数据 overview := s.calculateOverview(stats) // 计算趋势数据 trends := s.calculateTrends(stats, req.Dimension) // 计算排行数据 topAdvertisers := s.calculateTopAdvertisers(stats) topAds := s.calculateTopAds(stats) topPositions := s.calculateTopPositions(stats) res = &dto.GetDashboardRes{ Overview: overview, Trends: trends, TopAdvertisers: topAdvertisers, TopAds: topAds, TopPositions: topPositions, } return } // GenerateDailyStatistics 生成每日统计数据 func (s *adStatistics) GenerateDailyStatistics(ctx context.Context, date int64) (err error) { // 转换日期 t := time.Unix(date, 0) startOfDay := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) endOfDay := startOfDay.Add(24 * time.Hour) // 生成广告主统计数据 err = s.generateAdvertiserStatistics(ctx, startOfDay.Unix(), endOfDay.Unix()) if err != nil { return fmt.Errorf("生成广告主统计数据失败: %v", err) } // 生成广告统计数据 err = s.generateAdvertisementStatistics(ctx, startOfDay.Unix(), endOfDay.Unix()) if err != nil { return fmt.Errorf("生成广告统计数据失败: %v", err) } // 生成广告位统计数据 err = s.generateAdPositionStatistics(ctx, startOfDay.Unix(), endOfDay.Unix()) if err != nil { return fmt.Errorf("生成广告位统计数据失败: %v", err) } return } // generateAdvertiserStatistics 生成广告主统计数据 func (s *adStatistics) generateAdvertiserStatistics(ctx context.Context, startDate int64, _ int64) (err error) { // 这里简化处理,实际项目中应该从日志表或实时数据中聚合 advertiserStats := &entity.AdStatistics{ StatType: "advertiser", StatDimension: "day", TargetId: "example_advertiser_id", TargetName: "示例广告主", StatDate: startDate, Impressions: 10000, Clicks: 500, Conversions: 50, Cost: 50000, // 500元,单位分 CTR: 0.05, // 5% CVR: 0.1, // 10% CPM: 5000, // 50元/千次展示 CPC: 100, // 1元/点击 } err = dao.AdStatistics.Upsert(ctx, advertiserStats) return } // generateAdvertisementStatistics 生成广告统计数据 func (s *adStatistics) generateAdvertisementStatistics(ctx context.Context, startDate int64, _ int64) (err error) { // 这里简化处理,实际项目中应该从日志表或实时数据中聚合 adStats := &entity.AdStatistics{ StatType: "advertisement", StatDimension: "day", TargetId: "example_ad_id", TargetName: "示例广告", StatDate: startDate, Impressions: 1000, Clicks: 50, Conversions: 5, Cost: 5000, // 50元,单位分 CTR: 0.05, // 5% CVR: 0.1, // 10% CPM: 5000, // 50元/千次展示 CPC: 100, // 1元/点击 } err = dao.AdStatistics.Upsert(ctx, adStats) return } // generateAdPositionStatistics 生成广告位统计数据 func (s *adStatistics) generateAdPositionStatistics(ctx context.Context, startDate int64, _ int64) (err error) { // 这里简化处理,实际项目中应该从日志表或实时数据中聚合 positionStats := &entity.AdStatistics{ StatType: "adPosition", StatDimension: "day", TargetId: "example_position_id", TargetName: "示例广告位", StatDate: startDate, Impressions: 5000, Clicks: 250, Revenue: 6000, // 60元,单位分 CTR: 0.05, // 5% } err = dao.AdStatistics.Upsert(ctx, positionStats) return } // calculateOverview 计算总览数据 func (s *adStatistics) calculateOverview(stats []*entity.AdStatistics) dto.OverviewData { var totalImpressions, totalClicks, totalCost, totalRevenue int64 var totalCTR, totalCVR float64 var count int // 统计不同类型的数据 advertiserCount := make(map[string]bool) adCount := make(map[string]bool) positionCount := make(map[string]bool) for _, stat := range stats { totalImpressions += stat.Impressions totalClicks += stat.Clicks totalCost += stat.Cost totalRevenue += stat.Revenue totalCTR += stat.CTR totalCVR += stat.CVR count++ // 统计不同实体的数量 if stat.StatType == "advertiser" { advertiserCount[stat.TargetId] = true } else if stat.StatType == "advertisement" { adCount[stat.TargetId] = true } else if stat.StatType == "adPosition" { positionCount[stat.TargetId] = true } } // 计算平均值 averageCTR := 0.0 averageCVR := 0.0 if count > 0 { averageCTR = totalCTR / float64(count) averageCVR = totalCVR / float64(count) } return dto.OverviewData{ TotalAdvertisers: int64(len(advertiserCount)), TotalAds: int64(len(adCount)), TotalPositions: int64(len(positionCount)), TotalImpressions: totalImpressions, TotalClicks: totalClicks, TotalCost: totalCost, TotalRevenue: totalRevenue, AverageCTR: averageCTR, AverageCVR: averageCVR, } } // calculateTrends 计算趋势数据 func (s *adStatistics) calculateTrends(stats []*entity.AdStatistics, dimension string) []dto.TrendData { trends := make([]dto.TrendData, 0) // 按日期分组统计数据 dateMap := make(map[int64]*dto.TrendData) for _, stat := range stats { if _, exists := dateMap[stat.StatDate]; !exists { dateMap[stat.StatDate] = &dto.TrendData{ Date: stat.StatDate, Impressions: 0, Clicks: 0, Cost: 0, Revenue: 0, } } trend := dateMap[stat.StatDate] trend.Impressions += stat.Impressions trend.Clicks += stat.Clicks trend.Cost += stat.Cost trend.Revenue += stat.Revenue } // 转换为切片并排序 for _, trend := range dateMap { trends = append(trends, *trend) } // 按日期排序 sort.Slice(trends, func(i, j int) bool { return trends[i].Date < trends[j].Date }) return trends } // calculateTopAdvertisers 计算广告主排行 func (s *adStatistics) calculateTopAdvertisers(stats []*entity.AdStatistics) []dto.RankData { advertiserMap := make(map[string]*dto.RankData) for _, stat := range stats { if stat.StatType == "advertiser" { if _, exists := advertiserMap[stat.TargetId]; !exists { advertiserMap[stat.TargetId] = &dto.RankData{ Id: stat.TargetId, Name: stat.TargetName, Impressions: 0, Clicks: 0, Cost: 0, Revenue: 0, } } rank := advertiserMap[stat.TargetId] rank.Impressions += stat.Impressions rank.Clicks += stat.Clicks rank.Cost += stat.Cost rank.Revenue += stat.Revenue } } return s.sortRankData(advertiserMap) } // calculateTopAds 计算广告排行 func (s *adStatistics) calculateTopAds(stats []*entity.AdStatistics) []dto.RankData { adMap := make(map[string]*dto.RankData) for _, stat := range stats { if stat.StatType == "advertisement" { if _, exists := adMap[stat.TargetId]; !exists { adMap[stat.TargetId] = &dto.RankData{ Id: stat.TargetId, Name: stat.TargetName, Impressions: 0, Clicks: 0, Cost: 0, Revenue: 0, } } rank := adMap[stat.TargetId] rank.Impressions += stat.Impressions rank.Clicks += stat.Clicks rank.Cost += stat.Cost rank.Revenue += stat.Revenue } } return s.sortRankData(adMap) } // calculateTopPositions 计算广告位排行 func (s *adStatistics) calculateTopPositions(stats []*entity.AdStatistics) []dto.RankData { positionMap := make(map[string]*dto.RankData) for _, stat := range stats { if stat.StatType == "adPosition" { if _, exists := positionMap[stat.TargetId]; !exists { positionMap[stat.TargetId] = &dto.RankData{ Id: stat.TargetId, Name: stat.TargetName, Impressions: 0, Clicks: 0, Cost: 0, Revenue: 0, } } rank := positionMap[stat.TargetId] rank.Impressions += stat.Impressions rank.Clicks += stat.Clicks rank.Cost += stat.Cost rank.Revenue += stat.Revenue } } return s.sortRankData(positionMap) } // sortRankData 对排行数据进行排序 func (s *adStatistics) sortRankData(dataMap map[string]*dto.RankData) []dto.RankData { rankList := make([]dto.RankData, 0, len(dataMap)) for _, rank := range dataMap { rankList = append(rankList, *rank) } // 按收入降序排序 sort.Slice(rankList, func(i, j int) bool { return rankList[i].Revenue > rankList[j].Revenue }) // 只返回前10名 if len(rankList) > 10 { rankList = rankList[:10] } return rankList }