2025-12-06 09:10:24 +08:00
|
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2025-12-06 15:24:30 +08:00
|
|
|
|
"cidservice/dao"
|
|
|
|
|
|
"cidservice/model/dto"
|
|
|
|
|
|
"cidservice/model/entity"
|
2025-12-06 09:10:24 +08:00
|
|
|
|
"context"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
var Advertisement = new(advertisement)
|
|
|
|
|
|
|
|
|
|
|
|
type advertisement struct{}
|
|
|
|
|
|
|
|
|
|
|
|
// Add 添加广告
|
|
|
|
|
|
func (s *advertisement) Add(ctx context.Context, req *dto.AddAdvertisementReq) (res *dto.AddAdvertisementRes, err error) {
|
|
|
|
|
|
advertisement := &entity.Advertisement{}
|
|
|
|
|
|
if err = gconv.Struct(req, advertisement); err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置基础字段
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
|
advertisement.CreatedAt = now
|
|
|
|
|
|
advertisement.UpdatedAt = now
|
|
|
|
|
|
advertisement.IsDeleted = false
|
|
|
|
|
|
|
|
|
|
|
|
// 设置初始状态
|
|
|
|
|
|
advertisement.Status = "待审核"
|
|
|
|
|
|
advertisement.AuditStatus = "待审核"
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化统计字段
|
|
|
|
|
|
advertisement.Impressions = 0
|
|
|
|
|
|
advertisement.Clicks = 0
|
|
|
|
|
|
advertisement.Conversions = 0
|
|
|
|
|
|
advertisement.Cost = 0
|
|
|
|
|
|
advertisement.CTR = 0
|
|
|
|
|
|
advertisement.CVR = 0
|
|
|
|
|
|
advertisement.CPM = 0
|
|
|
|
|
|
advertisement.CPC = 0
|
|
|
|
|
|
|
|
|
|
|
|
if err = dao.Advertisement.Insert(ctx, advertisement); err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
res = &dto.AddAdvertisementRes{Id: advertisement.Id.Hex()}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Update 更新广告
|
|
|
|
|
|
func (s *advertisement) Update(ctx context.Context, req *dto.UpdateAdvertisementReq) (err error) {
|
|
|
|
|
|
// 更新修改时间(不需要设置,DAO层会处理)
|
|
|
|
|
|
return dao.Advertisement.Update(ctx, req)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateStatus 更新广告状态
|
|
|
|
|
|
func (s *advertisement) UpdateStatus(ctx context.Context, req *dto.UpdateAdStatusReq) (err error) {
|
|
|
|
|
|
return dao.Advertisement.UpdateStatus(ctx, req.Id, req.Status)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Audit 审核广告
|
|
|
|
|
|
func (s *advertisement) Audit(ctx context.Context, req *dto.AuditAdvertisementReq) (err error) {
|
|
|
|
|
|
return dao.Advertisement.Audit(ctx, req.Id, req.AuditStatus, req.AuditReason)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetOne 获取广告详情
|
|
|
|
|
|
func (s *advertisement) GetOne(ctx context.Context, req *dto.GetAdvertisementReq) (res *dto.GetAdvertisementRes, err error) {
|
|
|
|
|
|
advertisement, err := dao.Advertisement.GetOne(ctx, req.Id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
res = &dto.GetAdvertisementRes{
|
|
|
|
|
|
Advertisement: advertisement,
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// List 获取广告列表
|
|
|
|
|
|
func (s *advertisement) List(ctx context.Context, req *dto.ListAdvertisementReq) (res *dto.ListAdvertisementRes, err error) {
|
|
|
|
|
|
list, total, err := dao.Advertisement.List(ctx, req)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
res = &dto.ListAdvertisementRes{
|
|
|
|
|
|
List: list,
|
|
|
|
|
|
Total: int(total),
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateAdStatistics 更新广告统计
|
|
|
|
|
|
func (s *advertisement) UpdateAdStatistics(ctx context.Context, id string, impressions, clicks, conversions int64, cost int64) (err error) {
|
|
|
|
|
|
// 获取广告信息
|
|
|
|
|
|
ad, err := dao.Advertisement.GetOne(ctx, id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算统计数据
|
|
|
|
|
|
totalImpressions := ad.Impressions + impressions
|
|
|
|
|
|
totalClicks := ad.Clicks + clicks
|
|
|
|
|
|
totalConversions := ad.Conversions + conversions
|
|
|
|
|
|
totalCost := ad.Cost + cost
|
|
|
|
|
|
|
|
|
|
|
|
// 计算比率
|
|
|
|
|
|
ctr := 0.0
|
|
|
|
|
|
if totalImpressions > 0 {
|
|
|
|
|
|
ctr = float64(totalClicks) / float64(totalImpressions)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cvr := 0.0
|
|
|
|
|
|
if totalClicks > 0 {
|
|
|
|
|
|
cvr = float64(totalConversions) / float64(totalClicks)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cpm := int64(0)
|
|
|
|
|
|
if totalImpressions > 0 {
|
|
|
|
|
|
cpm = totalCost * 1000 / totalImpressions
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cpc := int64(0)
|
|
|
|
|
|
if totalClicks > 0 {
|
|
|
|
|
|
cpc = totalCost / totalClicks
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构建更新数据
|
|
|
|
|
|
stats := map[string]interface{}{
|
|
|
|
|
|
"impressions": totalImpressions,
|
|
|
|
|
|
"clicks": totalClicks,
|
|
|
|
|
|
"conversions": totalConversions,
|
|
|
|
|
|
"cost": totalCost,
|
|
|
|
|
|
"ctr": ctr,
|
|
|
|
|
|
"cvr": cvr,
|
|
|
|
|
|
"cpm": cpm,
|
|
|
|
|
|
"cpc": cpc,
|
|
|
|
|
|
"updatedAt": time.Now(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新广告统计
|
|
|
|
|
|
err = dao.Advertisement.UpdateStatistics(ctx, id, stats)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 插入统计记录
|
|
|
|
|
|
today := time.Now()
|
|
|
|
|
|
todayStart := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, today.Location()).Unix()
|
|
|
|
|
|
|
|
|
|
|
|
statistics := &entity.AdStatistics{
|
|
|
|
|
|
StatType: "advertisement",
|
|
|
|
|
|
StatDimension: "day",
|
|
|
|
|
|
TargetId: id,
|
|
|
|
|
|
TargetName: ad.Title,
|
|
|
|
|
|
StatDate: todayStart,
|
|
|
|
|
|
Impressions: impressions,
|
|
|
|
|
|
Clicks: clicks,
|
|
|
|
|
|
Conversions: conversions,
|
|
|
|
|
|
Cost: cost,
|
|
|
|
|
|
CTR: ctr,
|
|
|
|
|
|
CVR: cvr,
|
|
|
|
|
|
CPM: cpm,
|
|
|
|
|
|
CPC: cpc,
|
|
|
|
|
|
// CreatedAt、UpdatedAt、IsDeleted字段是嵌入字段,无需单独设置
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err = dao.AdStatistics.Upsert(ctx, statistics)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|