初始化项目
This commit is contained in:
367
service/ad_statistics_service.go
Normal file
367
service/ad_statistics_service.go
Normal file
@@ -0,0 +1,367 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"cidService/dao"
|
||||
"cidService/model/dto"
|
||||
"cidService/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
|
||||
}
|
||||
Reference in New Issue
Block a user