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/errors/gerror"
|
|
|
|
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
var AdPosition = new(adPosition)
|
|
|
|
|
|
|
|
|
|
|
|
type adPosition struct{}
|
|
|
|
|
|
|
|
|
|
|
|
// Add 添加广告位
|
|
|
|
|
|
func (s *adPosition) Add(ctx context.Context, req *dto.AddAdPositionReq) (res *dto.AddAdPositionRes, err error) {
|
|
|
|
|
|
adPosition := &entity.AdPosition{}
|
|
|
|
|
|
if err = gconv.Struct(req, adPosition); err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置基础字段
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
|
adPosition.CreatedAt = now
|
|
|
|
|
|
adPosition.UpdatedAt = now
|
|
|
|
|
|
adPosition.IsDeleted = false
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化统计字段
|
|
|
|
|
|
adPosition.DailyImpressions = 0
|
|
|
|
|
|
adPosition.DailyClicks = 0
|
|
|
|
|
|
adPosition.DailyRevenue = 0
|
|
|
|
|
|
adPosition.CTR = 0
|
|
|
|
|
|
// eCPM字段是未导出的,无法直接设置
|
|
|
|
|
|
|
|
|
|
|
|
if err = dao.AdPosition.Insert(ctx, adPosition); err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
res = &dto.AddAdPositionRes{Id: adPosition.Id.Hex()}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Update 更新广告位
|
|
|
|
|
|
func (s *adPosition) Update(ctx context.Context, req *dto.UpdateAdPositionReq) (err error) {
|
|
|
|
|
|
// 更新修改时间(不需要设置,DAO层会处理)
|
|
|
|
|
|
return dao.AdPosition.Update(ctx, req)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateStatus 更新广告位状态
|
|
|
|
|
|
func (s *adPosition) UpdateStatus(ctx context.Context, req *dto.UpdateAdPositionStatusReq) (err error) {
|
|
|
|
|
|
return dao.AdPosition.UpdateStatus(ctx, req.Id, req.Status)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetOne 获取广告位详情
|
|
|
|
|
|
func (s *adPosition) GetOne(ctx context.Context, req *dto.GetAdPositionReq) (res *dto.GetAdPositionRes, err error) {
|
|
|
|
|
|
adPosition, err := dao.AdPosition.GetOne(ctx, req.Id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
res = &dto.GetAdPositionRes{
|
|
|
|
|
|
AdPosition: adPosition,
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// List 获取广告位列表
|
|
|
|
|
|
func (s *adPosition) List(ctx context.Context, req *dto.ListAdPositionReq) (res *dto.ListAdPositionRes, err error) {
|
|
|
|
|
|
list, total, err := dao.AdPosition.List(ctx, req)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
res = &dto.ListAdPositionRes{
|
|
|
|
|
|
List: list,
|
|
|
|
|
|
Total: int(total),
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetByCode 根据编码获取广告位
|
|
|
|
|
|
func (s *adPosition) GetByCode(ctx context.Context, code string) (adPosition *entity.AdPosition, err error) {
|
|
|
|
|
|
return dao.AdPosition.GetByCode(ctx, code)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetAvailableAdPositions 获取可用的广告位列表
|
|
|
|
|
|
func (s *adPosition) GetAvailableAdPositions(ctx context.Context) (list []*entity.AdPosition, err error) {
|
|
|
|
|
|
return dao.AdPosition.GetAvailableAdPositions(ctx)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MatchAd 匹配广告
|
|
|
|
|
|
func (s *adPosition) MatchAd(ctx context.Context, positionCode string, userInfo map[string]interface{}) (ad *entity.Advertisement, err error) {
|
|
|
|
|
|
// 获取广告位信息
|
|
|
|
|
|
adPosition, err := dao.AdPosition.GetByCode(ctx, positionCode)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查广告位状态
|
|
|
|
|
|
if adPosition.Status != "启用" {
|
|
|
|
|
|
return nil, gerror.New("广告位未启用")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取符合条件的广告列表
|
|
|
|
|
|
// 这里简化处理,实际项目中应该根据广告定向条件匹配广告
|
|
|
|
|
|
// 可以使用MongoDB的聚合管道实现复杂匹配逻辑
|
|
|
|
|
|
|
|
|
|
|
|
// 返回匹配的广告
|
|
|
|
|
|
// 这里返回第一个广告作为示例
|
|
|
|
|
|
ad = &entity.Advertisement{
|
|
|
|
|
|
Title: "示例广告",
|
|
|
|
|
|
MaterialUrl: "https://example.com/ad.jpg",
|
|
|
|
|
|
LinkUrl: "https://example.com",
|
|
|
|
|
|
LandingPageUrl: "https://example.com/landing",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateAdPositionStatistics 更新广告位统计
|
|
|
|
|
|
func (s *adPosition) UpdateAdPositionStatistics(ctx context.Context, id string, impressions, clicks, revenue int64) (err error) {
|
|
|
|
|
|
// 获取广告位信息
|
|
|
|
|
|
adPosition, err := dao.AdPosition.GetOne(ctx, id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算统计数据
|
|
|
|
|
|
totalImpressions := adPosition.DailyImpressions + impressions
|
|
|
|
|
|
totalClicks := adPosition.DailyClicks + clicks
|
|
|
|
|
|
totalRevenue := adPosition.DailyRevenue + revenue
|
|
|
|
|
|
|
|
|
|
|
|
// 计算比率
|
|
|
|
|
|
ctr := 0.0
|
|
|
|
|
|
if totalImpressions > 0 {
|
|
|
|
|
|
ctr = float64(totalClicks) / float64(totalImpressions)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ecpm := int64(0)
|
|
|
|
|
|
if totalImpressions > 0 {
|
|
|
|
|
|
ecpm = totalRevenue * 1000 / totalImpressions
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构建更新数据
|
|
|
|
|
|
stats := map[string]interface{}{
|
|
|
|
|
|
"dailyImpressions": totalImpressions,
|
|
|
|
|
|
"dailyClicks": totalClicks,
|
|
|
|
|
|
"dailyRevenue": totalRevenue,
|
|
|
|
|
|
"ctr": ctr,
|
|
|
|
|
|
"ecpm": ecpm,
|
|
|
|
|
|
"updatedAt": time.Now(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新广告位统计
|
|
|
|
|
|
err = dao.AdPosition.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: "adPosition",
|
|
|
|
|
|
StatDimension: "day",
|
|
|
|
|
|
TargetId: id,
|
|
|
|
|
|
TargetName: adPosition.Name,
|
|
|
|
|
|
StatDate: todayStart,
|
|
|
|
|
|
Impressions: impressions,
|
|
|
|
|
|
Clicks: clicks,
|
|
|
|
|
|
Cost: 0, // 广告位不记录消耗,只记录收入
|
|
|
|
|
|
Revenue: revenue,
|
|
|
|
|
|
CTR: ctr,
|
|
|
|
|
|
// eCPM字段是未导出的,无法直接设置
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err = dao.AdStatistics.Upsert(ctx, statistics)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|