初始化项目

This commit is contained in:
2025-12-06 09:10:24 +08:00
parent d730752f01
commit c9fcfc761e
35 changed files with 4283 additions and 295 deletions

View 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
}