初始化项目

This commit is contained in:
2025-12-06 15:24:30 +08:00
parent 88a2753211
commit fd08b8925f
59 changed files with 2456 additions and 447 deletions

Binary file not shown.

View File

@@ -1,11 +1,18 @@
server: server:
address : ":3002" address : ":3004"
name: "cidService" name: "cidService"
jwt: jwt:
secret: "abcdefghijklmnopqrstuvwxyz" secret: "abcdefghijklmnopqrstuvwxyz"
rate: rate:
limit: 200 limit: 200
burst: 300 burst: 300
# 租户限流配置
tenantRateLimit:
enabled: true # 是否启用租户限流
requestsPerHour: 3600 # 每小时最大请求数
window: 3600 # 时间窗口(秒)
burst: 100 # 突发请求数
mongo: mongo:
logger: logger:
level: "all" level: "all"

View File

@@ -1,86 +0,0 @@
# CID服务商项目配置文件示例
# 服务器配置
server:
address: ":8080"
dumpRouterMap: false
# 数据库配置
database:
default:
type: "mysql"
host: "localhost"
port: "3306"
user: "root"
pass: ""
name: "cid_service"
charset: "utf8mb4"
timezone: "Local"
maxIdle: 10
maxOpen: 100
maxLifetime: 30
# Redis配置
redis:
default:
address: "127.0.0.1:6379"
db: 1
pass: ""
# JWT配置
jwt:
secret: "your-secret-key"
expire: 7200 # 2小时
# 日志配置
logger:
level: "info"
stdout: true
path: "./logs"
file: "cid-service.log"
# 广告源配置
ad_sources:
google:
api_key: "your-google-ads-api-key"
customer_id: "your-customer-id"
developer_token: "your-developer-token"
facebook:
app_id: "your-facebook-app-id"
app_secret: "your-facebook-app-secret"
access_token: "your-facebook-access-token"
ad_account_id: "your-ad-account-id"
# 策略配置
strategy:
default:
basic:
min_conversion: 0.01
max_conversion: 0.05
source_weights:
self: 100
google: 0
facebook: 0
max_ads_per_req: 3
standard:
min_conversion: 0.05
max_conversion: 0.15
source_weights:
self: 70
google: 20
facebook: 10
max_ads_per_req: 5
premium:
min_conversion: 0.15
max_conversion: 1.0
source_weights:
self: 40
google: 30
facebook: 30
max_ads_per_req: 10
# 监控配置
monitoring:
enable_metrics: true
enable_tracing: true
jaeger_endpoint: "http://localhost:14268/api/traces"

93
consts/consts.go Normal file
View File

@@ -0,0 +1,93 @@
package consts
// Redis键前缀
const (
AdRequestLimitKeyPrefix = "ad_request_limit:" // 广告请求限流键前缀
RateLimitKeyPrefix = "rate_limit:" // 通用限流键前缀
SessionCacheKeyPrefix = "session_cache:" // 会话缓存键前缀
)
// 广告源状态
const (
AdSourceStatusActive = "active" // 活跃
AdSourceStatusInactive = "inactive" // 非活跃
AdSourceStatusMaintenance = "maintenance" // 维护中
)
// 广告源健康状态
const (
AdSourceHealthHealthy = "healthy" // 健康
AdSourceHealthDegraded = "degraded" // 降级
AdSourceHealthUnhealthy = "unhealthy" // 不健康
)
// 广告源类型
const (
AdSourceTypeSelf = "self" // 自营
AdSourceTypeThirdParty = "third_party" // 第三方
AdSourceTypeExchange = "exchange" // 广告交易平台
)
// 广告源提供商
const (
AdSourceProviderGoogle = "google" // Google
AdSourceProviderBaidu = "baidu" // 百度
AdSourceProviderTencent = "tencent" // 腾讯
AdSourceProviderSelf = "self" // 自营
)
// 认证类型
const (
AuthTypeAPIKey = "api_key" // API密钥
AuthTypeOAuth = "oauth" // OAuth
AuthTypeBasic = "basic" // Basic认证
)
// 竞价类型
const (
BiddingTypeCPM = "cpm" // 千次展示成本
BiddingTypeCPC = "cpc" // 每次点击成本
BiddingTypeCPA = "cpa" // 每次行动成本
BiddingTypeRTB = "rtb" // 实时竞价
)
// 广告格式类型
const (
AdFormatTypeBanner = "banner" // 横幅广告
AdFormatTypeVideo = "video" // 视频广告
AdFormatTypeNative = "native" // 原生广告
AdFormatTypeInterstitial = "interstitial" // 插屏广告
)
// 计费模式
const (
BillingModelCPM = "cpm" // 千次展示成本
BillingModelCPC = "cpc" // 每次点击成本
BillingModelCPA = "cpa" // 每次行动成本
BillingModelRevShare = "rev_share" // 收入分成
)
// 支付条款
const (
PaymentTermsNet30 = "net_30" // 30天
PaymentTermsNet60 = "net_60" // 60天
PaymentTermsNet90 = "net_90" // 90天
)
// 默认配置值
const (
DefaultTimeout = 5000 // 默认超时时间(毫秒)
DefaultRetryCount = 3 // 默认重试次数
DefaultPriority = 1 // 默认优先级
)
// 错误消息
const (
ErrAdSourceNotFound = "广告源不存在"
ErrAdSourceNameExists = "广告源名称已存在"
ErrAdSourceCodeExists = "广告源编码已存在"
ErrAdSourceInactive = "广告源非活跃状态"
ErrAdSourceUnhealthy = "广告源健康状态异常"
ErrRateLimitExceeded = "请求频率超限"
ErrInvalidConfiguration = "配置参数无效"
)

View File

@@ -31,7 +31,6 @@ const (
// 限流键 // 限流键
const ( const (
AdRequestLimitKeyPrefix = "cid:limit:req:" // 广告请求限流键前缀
ApiRequestLimitKeyPrefix = "cid:limit:api:" // API请求限流键前缀 ApiRequestLimitKeyPrefix = "cid:limit:api:" // API请求限流键前缀
) )

View File

@@ -3,50 +3,50 @@ package controller
import ( import (
"context" "context"
"cidService/model/dto" "cidservice/model/dto"
"cidService/service" "cidservice/service"
"gitee.com/red-future---jilin-g/common/http" "gitee.com/red-future---jilin-g/common/http"
) )
type cAdPosition struct{} type adPosition struct{}
var AdPosition = &cAdPosition{} var AdPosition = &adPosition{}
// Add 添加广告位 // Add 添加广告位
func (c *cAdPosition) Add(ctx context.Context, req *dto.AddAdPositionReq) (res *dto.AddAdPositionRes, err error) { func (c *adPosition) Add(ctx context.Context, req *dto.AddAdPositionReq) (res *dto.AddAdPositionRes, err error) {
return service.AdPosition.Add(ctx, req) return service.AdPosition.Add(ctx, req)
} }
// Update 更新广告位 // Update 更新广告位
func (c *cAdPosition) Update(ctx context.Context, req *dto.UpdateAdPositionReq) (res *http.ResponseEmpty, err error) { func (c *adPosition) Update(ctx context.Context, req *dto.UpdateAdPositionReq) (res *http.ResponseEmpty, err error) {
err = service.AdPosition.Update(ctx, req) err = service.AdPosition.Update(ctx, req)
return return
} }
// UpdateStatus 更新广告位状态 // UpdateStatus 更新广告位状态
func (c *cAdPosition) UpdateStatus(ctx context.Context, req *dto.UpdateAdPositionStatusReq) (res *http.ResponseEmpty, err error) { func (c *adPosition) UpdateStatus(ctx context.Context, req *dto.UpdateAdPositionStatusReq) (res *http.ResponseEmpty, err error) {
err = service.AdPosition.UpdateStatus(ctx, req) err = service.AdPosition.UpdateStatus(ctx, req)
return return
} }
// GetOne 获取广告位详情 // GetOne 获取广告位详情
func (c *cAdPosition) GetOne(ctx context.Context, req *dto.GetAdPositionReq) (res *dto.GetAdPositionRes, err error) { func (c *adPosition) GetOne(ctx context.Context, req *dto.GetAdPositionReq) (res *dto.GetAdPositionRes, err error) {
return service.AdPosition.GetOne(ctx, req) return service.AdPosition.GetOne(ctx, req)
} }
// List 获取广告位列表 // List 获取广告位列表
func (c *cAdPosition) List(ctx context.Context, req *dto.ListAdPositionReq) (res *dto.ListAdPositionRes, err error) { func (c *adPosition) List(ctx context.Context, req *dto.ListAdPositionReq) (res *dto.ListAdPositionRes, err error) {
return service.AdPosition.List(ctx, req) return service.AdPosition.List(ctx, req)
} }
// GetStatistics 获取广告位统计数据 // GetStatistics 获取广告位统计数据
func (c *cAdPosition) GetStatistics(ctx context.Context, req *dto.GetAdPositionStatisticsReq) (res *dto.GetAdPositionStatisticsRes, err error) { func (c *adPosition) GetStatistics(ctx context.Context, req *dto.GetAdPositionStatisticsReq) (res *dto.GetAdPositionStatisticsRes, err error) {
return service.AdPosition.GetStatistics(ctx, req) return service.AdPosition.GetStatistics(ctx, req)
} }
// GetAvailableAdPositions 获取可用的广告位列表 // GetAvailableAdPositions 获取可用的广告位列表
func (c *cAdPosition) GetAvailableAdPositions(ctx context.Context, req *dto.GetAvailableAdPositionsReq) (res *dto.GetAvailableAdPositionsRes, err error) { func (c *adPosition) GetAvailableAdPositions(ctx context.Context, req *dto.GetAvailableAdPositionsReq) (res *dto.GetAvailableAdPositionsRes, err error) {
list, err := service.AdPosition.GetAvailableAdPositions(ctx) list, err := service.AdPosition.GetAvailableAdPositions(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -58,7 +58,7 @@ func (c *cAdPosition) GetAvailableAdPositions(ctx context.Context, req *dto.GetA
} }
// MatchAd 匹配广告 // MatchAd 匹配广告
func (c *cAdPosition) MatchAd(ctx context.Context, req *dto.MatchAdReq) (res *dto.MatchAdRes, err error) { func (c *adPosition) MatchAd(ctx context.Context, req *dto.MatchAdReq) (res *dto.MatchAdRes, err error) {
ad, err := service.AdPosition.MatchAd(ctx, req.PositionCode, req.UserInfo) ad, err := service.AdPosition.MatchAd(ctx, req.PositionCode, req.UserInfo)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -4,19 +4,20 @@ import (
"context" "context"
"strconv" "strconv"
"cidService/model/dto" "cidservice/model/dto"
"cidService/service" "cidservice/service"
"github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/errors/gerror"
) )
var ( var (
AdSource = cAdSource{} AdSource = adSource{}
) )
type cAdSource struct{} type adSource struct{}
// Create 创建广告源 // Create 创建广告源
func (c *cAdSource) Create(ctx context.Context, req *dto.CreateAdSourceReq) (res *dto.GetAdSourceRes, err error) { func (c *adSource) Create(ctx context.Context, req *dto.CreateAdSourceReq) (res *dto.GetAdSourceRes, err error) {
id, err := service.AdSource.CreateAdSource(ctx, req) id, err := service.AdSource.CreateAdSource(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -33,7 +34,7 @@ func (c *cAdSource) Create(ctx context.Context, req *dto.CreateAdSourceReq) (res
} }
// Update 更新广告源 // Update 更新广告源
func (c *cAdSource) Update(ctx context.Context, req *dto.UpdateAdSourceReq) (res *dto.GetAdSourceRes, err error) { func (c *adSource) Update(ctx context.Context, req *dto.UpdateAdSourceReq) (res *dto.GetAdSourceRes, err error) {
id, err := strconv.ParseInt(req.Id, 10, 64) id, err := strconv.ParseInt(req.Id, 10, 64)
if err != nil { if err != nil {
return nil, gerror.New("无效的广告源ID") return nil, gerror.New("无效的广告源ID")
@@ -58,7 +59,7 @@ func (c *cAdSource) Update(ctx context.Context, req *dto.UpdateAdSourceReq) (res
} }
// Delete 删除广告源 // Delete 删除广告源
func (c *cAdSource) Delete(ctx context.Context, req *dto.DeleteAdSourceReq) (res *dto.DeleteAdSourceRes, err error) { func (c *adSource) Delete(ctx context.Context, req *dto.DeleteAdSourceReq) (res *dto.DeleteAdSourceRes, err error) {
id, err := strconv.ParseInt(req.Id, 10, 64) id, err := strconv.ParseInt(req.Id, 10, 64)
if err != nil { if err != nil {
return nil, gerror.New("无效的广告源ID") return nil, gerror.New("无效的广告源ID")
@@ -78,7 +79,7 @@ func (c *cAdSource) Delete(ctx context.Context, req *dto.DeleteAdSourceReq) (res
} }
// GetByID 根据ID获取广告源 // GetByID 根据ID获取广告源
func (c *cAdSource) GetByID(ctx context.Context, req *dto.GetAdSourceReq) (res *dto.GetAdSourceRes, err error) { func (c *adSource) GetByID(ctx context.Context, req *dto.GetAdSourceReq) (res *dto.GetAdSourceRes, err error) {
id, err := strconv.ParseInt(req.Id, 10, 64) id, err := strconv.ParseInt(req.Id, 10, 64)
if err != nil { if err != nil {
return nil, gerror.New("无效的广告源ID") return nil, gerror.New("无效的广告源ID")
@@ -98,7 +99,7 @@ func (c *cAdSource) GetByID(ctx context.Context, req *dto.GetAdSourceReq) (res *
} }
// GetList 获取广告源列表 // GetList 获取广告源列表
func (c *cAdSource) GetList(ctx context.Context, req *dto.ListAdSourceReq) (res *dto.ListAdSourceRes, err error) { func (c *adSource) GetList(ctx context.Context, req *dto.ListAdSourceReq) (res *dto.ListAdSourceRes, err error) {
adSources, err := service.AdSource.GetAvailableSources(ctx) adSources, err := service.AdSource.GetAvailableSources(ctx)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -3,26 +3,26 @@ package controller
import ( import (
"context" "context"
"cidService/model/dto" "cidservice/model/dto"
"cidService/service" "cidservice/service"
) )
type cAdStatistics struct{} type adStatistics struct{}
var AdStatistics = &cAdStatistics{} var AdStatistics = &adStatistics{}
// GetStatistics 获取统计数据 // GetStatistics 获取统计数据
func (c *cAdStatistics) GetStatistics(ctx context.Context, req *dto.GetAdStatisticsReq) (res *dto.GetAdStatisticsRes, err error) { func (c *adStatistics) GetStatistics(ctx context.Context, req *dto.GetAdStatisticsReq) (res *dto.GetAdStatisticsRes, err error) {
return service.AdStatistics.GetStatistics(ctx, req) return service.AdStatistics.GetStatistics(ctx, req)
} }
// GetDashboard 获取仪表盘数据 // GetDashboard 获取仪表盘数据
func (c *cAdStatistics) GetDashboard(ctx context.Context, req *dto.GetDashboardReq) (res *dto.GetDashboardRes, err error) { func (c *adStatistics) GetDashboard(ctx context.Context, req *dto.GetDashboardReq) (res *dto.GetDashboardRes, err error) {
return service.AdStatistics.GetDashboard(ctx, req) return service.AdStatistics.GetDashboard(ctx, req)
} }
// GenerateDailyStatistics 生成每日统计数据 // GenerateDailyStatistics 生成每日统计数据
func (c *cAdStatistics) GenerateDailyStatistics(ctx context.Context, req *dto.GenerateDailyStatisticsReq) (res *dto.GenerateDailyStatisticsRes, err error) { func (c *adStatistics) GenerateDailyStatistics(ctx context.Context, req *dto.GenerateDailyStatisticsReq) (res *dto.GenerateDailyStatisticsRes, err error) {
err = service.AdStatistics.GenerateDailyStatistics(ctx, req.Date) err = service.AdStatistics.GenerateDailyStatistics(ctx, req.Date)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -1,51 +1,51 @@
package controller package controller
import ( import (
"cidService/model/dto" "cidservice/model/dto"
"cidService/service" "cidservice/service"
"context" "context"
"gitee.com/red-future---jilin-g/common/http" "gitee.com/red-future---jilin-g/common/http"
) )
type cAdvertisement struct{} type advertisement struct{}
var Advertisement = &cAdvertisement{} var Advertisement = &advertisement{}
// Add 添加广告 // Add 添加广告
func (c *cAdvertisement) Add(ctx context.Context, req *dto.AddAdvertisementReq) (res *dto.AddAdvertisementRes, err error) { func (c *advertisement) Add(ctx context.Context, req *dto.AddAdvertisementReq) (res *dto.AddAdvertisementRes, err error) {
return service.Advertisement.Add(ctx, req) return service.Advertisement.Add(ctx, req)
} }
// Update 更新广告 // Update 更新广告
func (c *cAdvertisement) Update(ctx context.Context, req *dto.UpdateAdvertisementReq) (res *http.ResponseEmpty, err error) { func (c *advertisement) Update(ctx context.Context, req *dto.UpdateAdvertisementReq) (res *http.ResponseEmpty, err error) {
err = service.Advertisement.Update(ctx, req) err = service.Advertisement.Update(ctx, req)
return return
} }
// UpdateStatus 更新广告状态 // UpdateStatus 更新广告状态
func (c *cAdvertisement) UpdateStatus(ctx context.Context, req *dto.UpdateAdStatusReq) (res *http.ResponseEmpty, err error) { func (c *advertisement) UpdateStatus(ctx context.Context, req *dto.UpdateAdStatusReq) (res *http.ResponseEmpty, err error) {
err = service.Advertisement.UpdateStatus(ctx, req) err = service.Advertisement.UpdateStatus(ctx, req)
return return
} }
// Audit 审核广告 // Audit 审核广告
func (c *cAdvertisement) Audit(ctx context.Context, req *dto.AuditAdvertisementReq) (res *http.ResponseEmpty, err error) { func (c *advertisement) Audit(ctx context.Context, req *dto.AuditAdvertisementReq) (res *http.ResponseEmpty, err error) {
err = service.Advertisement.Audit(ctx, req) err = service.Advertisement.Audit(ctx, req)
return return
} }
// GetOne 获取广告详情 // GetOne 获取广告详情
func (c *cAdvertisement) GetOne(ctx context.Context, req *dto.GetAdvertisementReq) (res *dto.GetAdvertisementRes, err error) { func (c *advertisement) GetOne(ctx context.Context, req *dto.GetAdvertisementReq) (res *dto.GetAdvertisementRes, err error) {
return service.Advertisement.GetOne(ctx, req) return service.Advertisement.GetOne(ctx, req)
} }
// List 获取广告列表 // List 获取广告列表
func (c *cAdvertisement) List(ctx context.Context, req *dto.ListAdvertisementReq) (res *dto.ListAdvertisementRes, err error) { func (c *advertisement) List(ctx context.Context, req *dto.ListAdvertisementReq) (res *dto.ListAdvertisementRes, err error) {
return service.Advertisement.List(ctx, req) return service.Advertisement.List(ctx, req)
} }
// GetStatistics 获取广告统计数据 // GetStatistics 获取广告统计数据
func (c *cAdvertisement) GetStatistics(ctx context.Context, req *dto.GetAdStatisticsForAdvertisementReq) (res *dto.GetAdStatisticsForAdvertisementRes, err error) { func (c *advertisement) GetStatistics(ctx context.Context, req *dto.GetAdStatisticsForAdvertisementReq) (res *dto.GetAdStatisticsForAdvertisementRes, err error) {
return service.Advertisement.GetStatistics(ctx, req) return service.Advertisement.GetStatistics(ctx, req)
} }

View File

@@ -1,64 +1,64 @@
package controller package controller
import ( import (
"cidService/model/dto" "cidservice/model/dto"
"cidService/service" "cidservice/service"
"context" "context"
"gitee.com/red-future---jilin-g/common/http" "gitee.com/red-future---jilin-g/common/http"
) )
type cAdvertiser struct{} type advertiser struct{}
var Advertiser = &cAdvertiser{} var Advertiser = &advertiser{}
// Add 添加广告主 // Add 添加广告主
func (c *cAdvertiser) Add(ctx context.Context, req *dto.AddAdvertiserReq) (res *dto.AddAdvertiserRes, err error) { func (c *advertiser) Add(ctx context.Context, req *dto.AddAdvertiserReq) (res *dto.AddAdvertiserRes, err error) {
return service.Advertiser.Add(ctx, req) return service.Advertiser.Add(ctx, req)
} }
// Update 更新广告主 // Update 更新广告主
func (c *cAdvertiser) Update(ctx context.Context, req *dto.UpdateAdvertiserReq) (res *http.ResponseEmpty, err error) { func (c *advertiser) Update(ctx context.Context, req *dto.UpdateAdvertiserReq) (res *http.ResponseEmpty, err error) {
err = service.Advertiser.Update(ctx, req) err = service.Advertiser.Update(ctx, req)
return return
} }
// UpdateStatus 更新广告主状态 // UpdateStatus 更新广告主状态
func (c *cAdvertiser) UpdateStatus(ctx context.Context, req *dto.UpdateAdvertiserStatusReq) (res *http.ResponseEmpty, err error) { func (c *advertiser) UpdateStatus(ctx context.Context, req *dto.UpdateAdvertiserStatusReq) (res *http.ResponseEmpty, err error) {
err = service.Advertiser.UpdateStatus(ctx, req) err = service.Advertiser.UpdateStatus(ctx, req)
return return
} }
// Audit 审核广告主 // Audit 审核广告主
func (c *cAdvertiser) Audit(ctx context.Context, req *dto.AuditAdvertiserReq) (res *http.ResponseEmpty, err error) { func (c *advertiser) Audit(ctx context.Context, req *dto.AuditAdvertiserReq) (res *http.ResponseEmpty, err error) {
err = service.Advertiser.Audit(ctx, req) err = service.Advertiser.Audit(ctx, req)
return return
} }
// Recharge 充值 // Recharge 充值
func (c *cAdvertiser) Recharge(ctx context.Context, req *dto.RechargeAdvertiserReq) (res *http.ResponseEmpty, err error) { func (c *advertiser) Recharge(ctx context.Context, req *dto.RechargeAdvertiserReq) (res *http.ResponseEmpty, err error) {
err = service.Advertiser.Recharge(ctx, req) err = service.Advertiser.Recharge(ctx, req)
return return
} }
// UpdateCreditLimit 更新授信额度 // UpdateCreditLimit 更新授信额度
func (c *cAdvertiser) UpdateCreditLimit(ctx context.Context, req *dto.UpdateCreditLimitReq) (res *http.ResponseEmpty, err error) { func (c *advertiser) UpdateCreditLimit(ctx context.Context, req *dto.UpdateCreditLimitReq) (res *http.ResponseEmpty, err error) {
err = service.Advertiser.UpdateCreditLimit(ctx, req) err = service.Advertiser.UpdateCreditLimit(ctx, req)
return return
} }
// GetOne 获取广告主详情 // GetOne 获取广告主详情
func (c *cAdvertiser) GetOne(ctx context.Context, req *dto.GetAdvertiserReq) (res *dto.GetAdvertiserRes, err error) { func (c *advertiser) GetOne(ctx context.Context, req *dto.GetAdvertiserReq) (res *dto.GetAdvertiserRes, err error) {
return service.Advertiser.GetOne(ctx, req) return service.Advertiser.GetOne(ctx, req)
} }
// List 获取广告主列表 // List 获取广告主列表
func (c *cAdvertiser) List(ctx context.Context, req *dto.ListAdvertiserReq) (res *dto.ListAdvertiserRes, err error) { func (c *advertiser) List(ctx context.Context, req *dto.ListAdvertiserReq) (res *dto.ListAdvertiserRes, err error) {
return service.Advertiser.List(ctx, req) return service.Advertiser.List(ctx, req)
} }
// GetBalance 获取广告主余额 // GetBalance 获取广告主余额
func (c *cAdvertiser) GetBalance(ctx context.Context, req *dto.GetAdvertiserBalanceReq) (res *dto.GetAdvertiserBalanceRes, err error) { func (c *advertiser) GetBalance(ctx context.Context, req *dto.GetAdvertiserBalanceReq) (res *dto.GetAdvertiserBalanceRes, err error) {
balance, creditLimit, err := service.Advertiser.GetBalance(ctx, req.Id) balance, creditLimit, err := service.Advertiser.GetBalance(ctx, req.Id)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -0,0 +1,147 @@
package controller
import (
"context"
"cidservice/model/dto"
"cidservice/service"
)
var (
Application = application{}
)
type application struct{}
// CreateApplication 创建应用
func (c *application) CreateApplication(ctx context.Context, req *dto.CreateApplicationReq) (res *dto.CreateApplicationRes, err error) {
id, err := service.Application.CreateApplication(ctx, req)
if err != nil {
return nil, err
}
return &dto.CreateApplicationRes{
ID: id,
}, nil
}
// UpdateApplication 更新应用
func (c *application) UpdateApplication(ctx context.Context, req *dto.UpdateApplicationReq) (res *dto.UpdateApplicationRes, err error) {
affected, err := service.Application.UpdateApplication(ctx, req.ID, req)
if err != nil {
return nil, err
}
return &dto.UpdateApplicationRes{
Success: affected > 0,
}, nil
}
// GetApplication 获取应用信息
func (c *application) GetApplication(ctx context.Context, req *dto.GetApplicationReq) (res *dto.GetApplicationRes, err error) {
app, err := service.Application.GetApplicationByID(ctx, req.ID)
if err != nil {
return nil, err
}
return &dto.GetApplicationRes{
ID: app.Id,
TenantID: app.TenantId,
Name: app.Name,
Code: app.Code,
Description: app.Description,
Platform: app.Platform,
PackageName: app.PackageName,
AppStoreURL: app.AppStoreURL,
Categories: app.Categories,
Tags: app.Tags,
AdTypes: app.AdTypes,
Status: app.Status,
AppKey: app.AppKey,
CallbackURL: app.CallbackURL,
CreatedAt: app.CreatedAt.Unix(),
UpdatedAt: app.UpdatedAt.Unix(),
}, nil
}
// ListApplications 获取应用列表
func (c *application) ListApplications(ctx context.Context, req *dto.ListApplicationsReq) (res *dto.ListApplicationsRes, err error) {
list, total, err := service.Application.GetApplicationsByTenant(ctx, req.TenantID, req.Platform, req.Status, req.Page, req.Size)
if err != nil {
return nil, err
}
// 转换为响应格式
appItems := make([]dto.ApplicationItem, len(list))
for i, app := range list {
appItems[i] = dto.ApplicationItem{
ID: app.Id,
Name: app.Name,
Code: app.Code,
Description: app.Description,
Platform: app.Platform,
PackageName: app.PackageName,
Categories: app.Categories,
Tags: app.Tags,
AdTypes: app.AdTypes,
Status: app.Status,
DailyRequests: app.DailyRequests,
MonthlyRequests: app.MonthlyRequests,
CreatedAt: app.CreatedAt.Unix(),
}
}
return &dto.ListApplicationsRes{
List: appItems,
Total: total,
Page: req.Page,
Size: req.Size,
}, nil
}
// ResetAPIKeys 重置API密钥
func (c *application) ResetAPIKeys(ctx context.Context, req *dto.ResetAPIKeysReq) (res *dto.ResetAPIKeysRes, err error) {
appKey, appSecret, err := service.Application.ResetAPIKeys(ctx, req.ID)
if err != nil {
return nil, err
}
return &dto.ResetAPIKeysRes{
AppKey: appKey,
AppSecret: appSecret,
}, nil
}
// ValidateApplication 验证应用权限
func (c *application) ValidateApplication(ctx context.Context, req *dto.ValidateApplicationReq) (res *dto.ValidateApplicationRes, err error) {
app, err := service.Application.ValidateApplication(ctx, req.AppKey, req.AppSecret)
if err != nil {
return &dto.ValidateApplicationRes{
Valid: false,
}, nil
}
tentantName := ""
return &dto.ValidateApplicationRes{
Valid: true,
AppID: app.Id,
AppName: app.Name,
TenantID: app.TenantId,
TenantName: tentantName,
Platform: app.Platform,
AdTypes: app.AdTypes,
}, nil
}
// DeleteApplication 删除应用
func (c *application) DeleteApplication(ctx context.Context, req *dto.DeleteApplicationReq) (res *dto.DeleteApplicationRes, err error) {
affected, err := service.Application.DeleteApplication(ctx, req.ID)
if err != nil {
return nil, err
}
return &dto.DeleteApplicationRes{
Success: affected > 0,
}, nil
}

View File

@@ -3,19 +3,20 @@ package controller
import ( import (
"context" "context"
"cidService/model/dto" "cidservice/model/dto"
"cidService/service" "cidservice/service"
"github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/errors/gerror"
) )
var ( var (
CID = cCID{} CID = cid{}
) )
type cCID struct{} type cid struct{}
// GenerateCID 生成CID广告 // GenerateCID 生成CID广告
func (c *cCID) GenerateCID(ctx context.Context, req *dto.GenerateCIDReq) (res *dto.GenerateCIDRes, err error) { func (c *cid) GenerateCID(ctx context.Context, req *dto.GenerateCIDReq) (res *dto.GenerateCIDRes, err error) {
if req == nil { if req == nil {
return nil, gerror.New("请求参数不能为空") return nil, gerror.New("请求参数不能为空")
} }
@@ -33,7 +34,7 @@ func (c *cCID) GenerateCID(ctx context.Context, req *dto.GenerateCIDReq) (res *d
} }
// GetStatistics 获取CID统计信息 // GetStatistics 获取CID统计信息
func (c *cCID) GetStatistics(ctx context.Context, req *dto.GetCIDStatisticsReq) (res *dto.GetCIDStatisticsRes, err error) { func (c *cid) GetStatistics(ctx context.Context, req *dto.GetCIDStatisticsReq) (res *dto.GetCIDStatisticsRes, err error) {
if req == nil { if req == nil {
return nil, gerror.New("请求参数不能为空") return nil, gerror.New("请求参数不能为空")
} }
@@ -47,7 +48,7 @@ func (c *cCID) GetStatistics(ctx context.Context, req *dto.GetCIDStatisticsReq)
} }
// GetCIDHistory 获取CID历史记录 // GetCIDHistory 获取CID历史记录
func (c *cCID) GetCIDHistory(ctx context.Context, req *dto.GetCIDHistoryReq) (res *dto.GetCIDHistoryRes, err error) { func (c *cid) GetCIDHistory(ctx context.Context, req *dto.GetCIDHistoryReq) (res *dto.GetCIDHistoryRes, err error) {
if req == nil { if req == nil {
return nil, gerror.New("请求参数不能为空") return nil, gerror.New("请求参数不能为空")
} }

View File

@@ -0,0 +1,39 @@
package controller
import (
"context"
"cidservice/model/dto"
"cidservice/service"
)
var (
RateLimit = rateLimit{}
)
type rateLimit struct{}
// SetTenantRateLimit 设置租户限流配置
func (c *rateLimit) SetTenantRateLimit(ctx context.Context, req *dto.SetTenantRateLimitReq) (res *dto.SetTenantRateLimitRes, err error) {
// 注意实际使用的是config.yml中的全局配置此接口仅用于兼容旧API
// 实际限流参数请修改config.yml中的tenantRateLimit部分
return &dto.SetTenantRateLimitRes{
Success: true,
}, nil
}
// GetTenantRateLimitUsage 获取租户限流使用情况
func (c *rateLimit) GetTenantRateLimitUsage(ctx context.Context, req *dto.GetTenantRateLimitUsageReq) (res *dto.GetTenantRateLimitUsageRes, err error) {
current, max, err := service.RateLimit.GetTenantCurrentUsage(ctx, req.TenantID, nil)
if err != nil {
return nil, err
}
return &dto.GetTenantRateLimitUsageRes{
TenantID: req.TenantID,
CurrentUsed: current,
MaxAllowed: max,
UsagePercent: float64(current) / float64(max) * 100,
}, nil
}

View File

@@ -5,8 +5,8 @@ import (
"gitee.com/red-future---jilin-g/common/http" "gitee.com/red-future---jilin-g/common/http"
"cidService/model/dto" "cidservice/model/dto"
"cidService/service" "cidservice/service"
) )
type report struct{} type report struct{}
@@ -31,12 +31,14 @@ func (c *report) List(ctx context.Context, req *dto.ListReportReq) (res *dto.Lis
// Update 更新报表 // Update 更新报表
func (c *report) Update(ctx context.Context, req *dto.UpdateReportReq) (res *http.ResponseEmpty, err error) { func (c *report) Update(ctx context.Context, req *dto.UpdateReportReq) (res *http.ResponseEmpty, err error) {
err = service.Report.Update(ctx, req) err = service.Report.Update(ctx, req)
res = &http.ResponseEmpty{}
return return
} }
// Delete 删除报表 // Delete 删除报表
func (c *report) Delete(ctx context.Context, req *dto.DeleteReportReq) (res *http.ResponseEmpty, err error) { func (c *report) Delete(ctx context.Context, req *dto.DeleteReportReq) (res *http.ResponseEmpty, err error) {
err = service.Report.Delete(ctx, req) err = service.Report.Delete(ctx, req)
res = &http.ResponseEmpty{}
return return
} }
@@ -48,5 +50,6 @@ func (c *report) Download(ctx context.Context, req *dto.DownloadReportReq) (res
// Generate 生成报表 // Generate 生成报表
func (c *report) Generate(ctx context.Context, req *dto.GenerateReportReq) (res *http.ResponseEmpty, err error) { func (c *report) Generate(ctx context.Context, req *dto.GenerateReportReq) (res *http.ResponseEmpty, err error) {
err = service.Report.Generate(ctx, req) err = service.Report.Generate(ctx, req)
res = &http.ResponseEmpty{}
return return
} }

View File

@@ -0,0 +1,147 @@
package controller
import (
"context"
"cidservice/model/dto"
"cidservice/service"
)
var (
StatReport = &statReport{}
)
type statReport struct{}
// GenerateReport 生成报表
func (c *statReport) GenerateReport(ctx context.Context, req *dto.ReportGenerateReq) (res *dto.ReportGenerateRes, err error) {
var resp *dto.ReportGenerateResp
switch req.ReportType {
case "daily":
resp, err = service.StatReport.GenerateDailyReport(ctx, req)
case "monthly":
resp, err = service.StatReport.GenerateMonthlyReport(ctx, req)
case "quarterly":
resp, err = service.StatReport.GenerateQuarterlyReport(ctx, req)
case "yearly":
resp, err = service.StatReport.GenerateYearlyReport(ctx, req)
default:
return nil, err
}
if err != nil {
return nil, err
}
return &dto.ReportGenerateRes{
Data: resp,
}, nil
}
// GetReportList 获取报表列表
func (c *statReport) GetReportList(ctx context.Context, req *dto.ReportListReq) (res *dto.ReportListRes, err error) {
resp, err := service.StatReport.GetReportList(ctx, req)
if err != nil {
return nil, err
}
return &dto.ReportListRes{
Data: resp,
}, nil
}
// GetReportDetail 获取报表详情
func (c *statReport) GetReportDetail(ctx context.Context, req *dto.ReportDetailReq) (res *dto.ReportDetailRes, err error) {
resp, err := service.StatReport.GetReportDetail(ctx, req.ReportID)
if err != nil {
return nil, err
}
return &dto.ReportDetailRes{
Data: resp,
}, nil
}
// QueryStats 统计查询
func (c *statReport) QueryStats(ctx context.Context, req *dto.StatQueryReq) (res *dto.StatQueryRes, err error) {
// 这里调用统计查询服务
// resp, err := service.StatReport.QueryStats(ctx, req)
// 暂时返回示例数据
resp := &dto.StatQueryResp{
Data: []*dto.StatDataPoint{
{
Date: "2024-01-01",
Impressions: 1000,
Clicks: 50,
Revenue: 500.0,
CTR: 5.0,
AvgDuration: 30.5,
},
},
Summary: &dto.StatSummary{
TotalImpressions: 1000,
TotalClicks: 50,
TotalRevenue: 500.0,
AvgCTR: 5.0,
AvgDuration: 30.5,
GrowthRate: &dto.GrowthRate{
Impressions: 10.5,
Clicks: 8.2,
Revenue: 12.3,
CTR: -1.2,
},
},
}
return &dto.StatQueryRes{
Data: resp,
}, nil
}
// RealTimeStats 实时统计
func (c *statReport) RealTimeStats(ctx context.Context, req *dto.RealTimeStatReq) (res *dto.RealTimeStatRes, err error) {
// 这里调用实时统计服务
// resp, err := service.StatReport.GetRealTimeStats(ctx, req)
// 暂时返回示例数据
resp := &dto.RealTimeStatResp{
CurrentHour: &dto.HourlyStat{
Hour: "14:00",
Impressions: 120,
Clicks: 6,
Revenue: 60.0,
},
Last24Hours: []*dto.HourlyStat{
{
Hour: "13:00",
Impressions: 110,
Clicks: 5,
Revenue: 55.0,
},
{
Hour: "12:00",
Impressions: 100,
Clicks: 4,
Revenue: 50.0,
},
},
}
return &dto.RealTimeStatRes{
Data: resp,
}, nil
}
// ExportReport 导出报表
func (c *statReport) ExportReport(ctx context.Context, req *dto.ReportDetailReq) (res *dto.ExportReportRes, err error) {
// 获取报表详情
resp, err := service.StatReport.GetReportDetail(ctx, req.ReportID)
if err != nil {
return nil, err
}
// 返回导出数据由上层处理HTTP响应
return &dto.ExportReportRes{
ReportData: resp,
}, nil
}

View File

@@ -3,19 +3,20 @@ package controller
import ( import (
"context" "context"
"cidService/model/dto" "cidservice/model/dto"
"cidService/service" "cidservice/service"
"github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/errors/gerror"
) )
var ( var (
Strategy = cStrategy{} Strategy = strategy{}
) )
type cStrategy struct{} type strategy struct{}
// Create 创建策略 // Create 创建策略
func (c *cStrategy) Create(ctx context.Context, req *dto.CreateStrategyReq) (res *dto.StrategyRes, err error) { func (c *strategy) Create(ctx context.Context, req *dto.CreateStrategyReq) (res *dto.StrategyRes, err error) {
id, err := service.Strategy.CreateStrategy(ctx, req) id, err := service.Strategy.CreateStrategy(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -30,7 +31,7 @@ func (c *cStrategy) Create(ctx context.Context, req *dto.CreateStrategyReq) (res
} }
// Update 更新策略 // Update 更新策略
func (c *cStrategy) Update(ctx context.Context, req *dto.UpdateStrategyReq) (res *dto.StrategyRes, err error) { func (c *strategy) Update(ctx context.Context, req *dto.UpdateStrategyReq) (res *dto.StrategyRes, err error) {
affected, err := service.Strategy.UpdateStrategy(ctx, req) affected, err := service.Strategy.UpdateStrategy(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -48,7 +49,7 @@ func (c *cStrategy) Update(ctx context.Context, req *dto.UpdateStrategyReq) (res
} }
// Delete 删除策略 // Delete 删除策略
func (c *cStrategy) Delete(ctx context.Context, req *dto.DeleteStrategyReq) (res *dto.DeleteStrategyRes, err error) { func (c *strategy) Delete(ctx context.Context, req *dto.DeleteStrategyReq) (res *dto.DeleteStrategyRes, err error) {
affected, err := service.Strategy.DeleteStrategy(ctx, req.Id) affected, err := service.Strategy.DeleteStrategy(ctx, req.Id)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -63,7 +64,7 @@ func (c *cStrategy) Delete(ctx context.Context, req *dto.DeleteStrategyReq) (res
} }
// GetByID 根据ID获取策略 // GetByID 根据ID获取策略
func (c *cStrategy) GetByID(ctx context.Context, req *dto.GetStrategyReq) (res *dto.StrategyRes, err error) { func (c *strategy) GetByID(ctx context.Context, req *dto.GetStrategyReq) (res *dto.StrategyRes, err error) {
strategy, err := service.Strategy.GetStrategyByID(ctx, req.Id) strategy, err := service.Strategy.GetStrategyByID(ctx, req.Id)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -73,6 +74,6 @@ func (c *cStrategy) GetByID(ctx context.Context, req *dto.GetStrategyReq) (res *
} }
// GetList 获取策略列表 // GetList 获取策略列表
func (c *cStrategy) GetList(ctx context.Context, req *dto.GetStrategyListReq) (res *dto.GetStrategyListRes, err error) { func (c *strategy) GetList(ctx context.Context, req *dto.GetStrategyListReq) (res *dto.GetStrategyListRes, err error) {
return service.Strategy.GetStrategyList(ctx, req) return service.Strategy.GetStrategyList(ctx, req)
} }

View File

@@ -1,8 +1,8 @@
package dao package dao
import ( import (
"cidService/model/dto" "cidservice/model/dto"
"cidService/model/entity" "cidservice/model/entity"
"context" "context"
"gitee.com/red-future---jilin-g/common/http" "gitee.com/red-future---jilin-g/common/http"

View File

@@ -1,9 +1,9 @@
package dao package dao
import ( import (
"cidService/model/entity"
"context" "context"
"cidservice/model/entity"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
) )

View File

@@ -3,8 +3,8 @@ package dao
import ( import (
"context" "context"
"cidService/model/dto" "cidservice/model/dto"
"cidService/model/entity" "cidservice/model/entity"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/bson"

View File

@@ -1,8 +1,8 @@
package dao package dao
import ( import (
"cidService/model/dto" "cidservice/model/dto"
"cidService/model/entity" "cidservice/model/entity"
"context" "context"
"time" "time"

View File

@@ -1,8 +1,8 @@
package dao package dao
import ( import (
"cidService/model/dto" "cidservice/model/dto"
"cidService/model/entity" "cidservice/model/entity"
"context" "context"
"time" "time"

97
dao/application_dao.go Normal file
View File

@@ -0,0 +1,97 @@
package dao
import (
"context"
"cidservice/model/entity"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// applicationDao 应用DAO
type applicationDao struct{}
var Application = &applicationDao{}
// Ctx 获取数据库上下文
func (d *applicationDao) Ctx(ctx context.Context) *gdb.Model {
return g.DB().Model("application").Ctx(ctx)
}
// Create 创建应用
func (d *applicationDao) Create(ctx context.Context, app *entity.Application) (int64, error) {
result, err := d.Ctx(ctx).Insert(app)
if err != nil {
return 0, err
}
id, _ := result.LastInsertId()
return id, nil
}
// GetByID 根据ID获取应用
func (d *applicationDao) GetByID(ctx context.Context, id int64) (*entity.Application, error) {
var app *entity.Application
err := d.Ctx(ctx).Where("id", id).Scan(&app)
return app, err
}
// GetByTenantID 根据租户ID获取应用列表
func (d *applicationDao) GetByTenantID(ctx context.Context, tenantID int64) ([]*entity.Application, error) {
var apps []*entity.Application
err := d.Ctx(ctx).Where("tenant_id", tenantID).Scan(&apps)
return apps, err
}
// GetByAPIKey 根据API密钥获取应用
func (d *applicationDao) GetByAPIKey(ctx context.Context, apiKey string) (*entity.Application, error) {
var app *entity.Application
err := d.Ctx(ctx).Where("app_key", apiKey).Scan(&app)
return app, err
}
// Update 更新应用
func (d *applicationDao) Update(ctx context.Context, app *entity.Application) error {
_, err := d.Ctx(ctx).Where("id", app.Id).Update(app)
return err
}
// Delete 删除应用
func (d *applicationDao) Delete(ctx context.Context, id int64) error {
_, err := d.Ctx(ctx).Where("id", id).Delete()
return err
}
// List 应用列表
func (d *applicationDao) List(ctx context.Context, tenantID int64, page, pageSize int) ([]*entity.Application, int, error) {
model := d.Ctx(ctx)
if tenantID > 0 {
model = model.Where("tenant_id", tenantID)
}
var apps []*entity.Application
err := model.Page(page, pageSize).OrderDesc("created_at").Scan(&apps)
if err != nil {
return nil, 0, err
}
total, err := model.Count()
if err != nil {
return nil, 0, err
}
return apps, total, nil
}
// GetByName 根据名称获取应用
func (d *applicationDao) GetByName(ctx context.Context, name string) (*entity.Application, error) {
var app *entity.Application
err := d.Ctx(ctx).Where("name", name).Scan(&app)
return app, err
}
// UpdateFields 更新应用部分字段
func (d *applicationDao) UpdateFields(ctx context.Context, id int64, data *entity.Application) error {
_, err := d.Ctx(ctx).Where("id", id).Update(data)
return err
}

View File

@@ -1,7 +1,7 @@
package dao package dao
import ( import (
"cidService/model/entity" "cidservice/model/entity"
"context" "context"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"

View File

@@ -3,8 +3,8 @@ package dao
import ( import (
"context" "context"
"cidService/model/dto" "cidservice/model/dto"
"cidService/model/entity" "cidservice/model/entity"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/gconv"

90
dao/stat_report_dao.go Normal file
View File

@@ -0,0 +1,90 @@
package dao
import (
"context"
"cidservice/model/entity"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// statReportDao 统计报表DAO
type statReportDao struct{}
var StatReport = &statReportDao{}
// Ctx 获取数据库上下文
func (d *statReportDao) Ctx(ctx context.Context) *gdb.Model {
return g.DB().Model("stat_report").Ctx(ctx)
}
// Create 创建统计报表
func (d *statReportDao) Create(ctx context.Context, report *entity.StatReport) (int64, error) {
result, err := d.Ctx(ctx).Insert(report)
if err != nil {
return 0, err
}
id, _ := result.LastInsertId()
return id, nil
}
// GetByID 根据ID获取统计报表
func (d *statReportDao) GetByID(ctx context.Context, id int64) (*entity.StatReport, error) {
var report *entity.StatReport
err := d.Ctx(ctx).Where("id", id).Scan(&report)
return report, err
}
// GetByTenantAndDate 根据租户和日期获取统计报表
func (d *statReportDao) GetByTenantAndDate(ctx context.Context, tenantID int64, reportType, date string) (*entity.StatReport, error) {
var report *entity.StatReport
err := d.Ctx(ctx).Where("tenant_id", tenantID).Where("report_type", reportType).Where("report_date", date).Scan(&report)
return report, err
}
// Update 更新统计报表
func (d *statReportDao) Update(ctx context.Context, report *entity.StatReport) error {
_, err := d.Ctx(ctx).Where("id", report.Id).Update(report)
return err
}
// Delete 删除统计报表
func (d *statReportDao) Delete(ctx context.Context, id int64) error {
_, err := d.Ctx(ctx).Where("id", id).Delete()
return err
}
// List 统计报表列表
func (d *statReportDao) List(ctx context.Context, tenantID, appID int64, reportType, startDate, endDate string, page, pageSize int) ([]*entity.StatReport, int, error) {
model := d.Ctx(ctx)
if tenantID > 0 {
model = model.Where("tenant_id", tenantID)
}
if appID > 0 {
model = model.Where("app_id", appID)
}
if reportType != "" {
model = model.Where("report_type", reportType)
}
if startDate != "" {
model = model.WhereGTE("report_date", startDate)
}
if endDate != "" {
model = model.WhereLTE("report_date", endDate)
}
var reports []*entity.StatReport
err := model.Page(page, pageSize).OrderDesc("generated_at").Scan(&reports)
if err != nil {
return nil, 0, err
}
total, err := model.Count()
if err != nil {
return nil, 0, err
}
return reports, total, nil
}

View File

@@ -1,7 +1,7 @@
package dao package dao
import ( import (
"cidService/model/entity" "cidservice/model/entity"
"context" "context"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"

79
dao/tenant_dao.go Normal file
View File

@@ -0,0 +1,79 @@
package dao
import (
"context"
"cidservice/model/entity"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
var Tenant = tenantDao{}
type tenantDao struct{}
// Create 创建租户
func (d *tenantDao) Create(ctx context.Context, tenant *entity.Tenant) (id int64, err error) {
result, err := g.DB().Model("tenant").Ctx(ctx).Insert(tenant)
if err != nil {
return 0, err
}
id, _ = result.LastInsertId()
return id, nil
}
// GetByID 根据ID获取租户
func (d *tenantDao) GetByID(ctx context.Context, id int64) (tenant *entity.Tenant, err error) {
err = g.DB().Model("tenant").Ctx(ctx).Where("id = ? AND is_deleted = ?", id, false).Scan(&tenant)
return
}
// GetByCode 根据编码获取租户
func (d *tenantDao) GetByCode(ctx context.Context, code string) (tenant *entity.Tenant, err error) {
err = g.DB().Model("tenant").Ctx(ctx).Where("code = ? AND is_deleted = ?", code, false).Scan(&tenant)
return
}
// UpdateFields 更新租户字段
func (d *tenantDao) UpdateFields(ctx context.Context, id int64, updateData *entity.Tenant) (affected int64, err error) {
result, err := g.DB().Model("tenant").Ctx(ctx).Where("id = ? AND is_deleted = ?", id, false).Update(updateData)
if err != nil {
return 0, err
}
rowsAffected, _ := result.RowsAffected()
return rowsAffected, nil
}
// Delete 删除租户
func (d *tenantDao) Delete(ctx context.Context, id int64) (affected int64, err error) {
result, err := g.DB().Model("tenant").Ctx(ctx).Where("id = ?", id).Update(gdb.Map{"is_deleted": true})
if err != nil {
return 0, err
}
rowsAffected, _ := result.RowsAffected()
return rowsAffected, nil
}
// List 获取租户列表
func (d *tenantDao) List(ctx context.Context, page, size int, filter map[string]interface{}) (list []*entity.Tenant, total int64, err error) {
model := g.DB().Model("tenant").Ctx(ctx).Where("is_deleted = ?", false)
// 应用过滤条件
if filter != nil {
for key, value := range filter {
model = model.Where(key, value)
}
}
// 获取总数
count, err := model.Count()
if err != nil {
return
}
total = int64(count)
// 分页查询
err = model.Page(page, size).OrderDesc("created_at").Scan(&list)
return
}

View File

@@ -1,78 +0,0 @@
-- CID服务商项目数据库初始化脚本
-- 广告源表
CREATE TABLE IF NOT EXISTS `ad_sources` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT '广告源名称',
`provider` varchar(50) NOT NULL COMMENT '提供商: self, google, facebook, etc.',
`api_endpoint` varchar(255) DEFAULT NULL COMMENT 'API端点',
`api_key` varchar(255) DEFAULT NULL COMMENT 'API密钥',
`config` text COMMENT '配置信息(JSON格式)',
`status` varchar(20) NOT NULL DEFAULT 'active' COMMENT '状态: active, inactive',
`priority` int(11) NOT NULL DEFAULT 0 COMMENT '优先级',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`created_by` bigint(20) DEFAULT NULL COMMENT '创建人',
`updated_by` bigint(20) DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_ad_sources_name` (`name`),
KEY `idx_ad_sources_provider` (`provider`),
KEY `idx_ad_sources_status` (`status`),
KEY `idx_ad_sources_priority` (`priority`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='广告源表';
-- 策略表
CREATE TABLE IF NOT EXISTS `strategies` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT '策略名称',
`description` varchar(500) DEFAULT NULL COMMENT '描述',
`tenant_level` varchar(20) NOT NULL COMMENT '适用租户级别: basic, standard, premium',
`min_conversion` decimal(5,4) NOT NULL DEFAULT 0.0000 COMMENT '最低转化率',
`max_conversion` decimal(5,4) NOT NULL DEFAULT 1.0000 COMMENT '最高转化率',
`source_weights` text COMMENT '广告源权重配置(JSON格式)',
`max_ads_per_req` int(11) NOT NULL DEFAULT 5 COMMENT '每次请求最大广告数',
`priority` int(11) NOT NULL DEFAULT 0 COMMENT '优先级',
`status` varchar(20) NOT NULL DEFAULT 'active' COMMENT '状态: active, inactive',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`created_by` bigint(20) DEFAULT NULL COMMENT '创建人',
`updated_by` bigint(20) DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_strategies_name` (`name`),
KEY `idx_strategies_tenant_level` (`tenant_level`),
KEY `idx_strategies_status` (`status`),
KEY `idx_strategies_priority` (`priority`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='匹配策略表';
-- CID请求记录表
CREATE TABLE IF NOT EXISTS `cid_requests` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`tenant_id` bigint(20) NOT NULL COMMENT '租户ID',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`request_type` varchar(50) NOT NULL COMMENT '请求类型',
`parameters` text COMMENT '请求参数(JSON格式)',
`response_data` text COMMENT '响应数据(JSON格式)',
`status` varchar(20) NOT NULL DEFAULT 'pending' COMMENT '状态: pending, completed, failed',
`process_time` int(11) DEFAULT NULL COMMENT '处理时间(毫秒)',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_cid_requests_tenant_id` (`tenant_id`),
KEY `idx_cid_requests_user_id` (`user_id`),
KEY `idx_cid_requests_status` (`status`),
KEY `idx_cid_requests_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='CID请求记录表';
-- 插入默认广告源
INSERT INTO `ad_sources` (`name`, `provider`, `status`, `priority`, `created_by`, `updated_by`) VALUES
('自营广告', 'self', 'active', 100, 1, 1),
('Google Ads', 'google', 'active', 80, 1, 1),
('Facebook Ads', 'facebook', 'active', 70, 1, 1)
ON DUPLICATE KEY UPDATE `updated_at` = CURRENT_TIMESTAMP;
-- 插入默认策略
INSERT INTO `strategies` (`name`, `description`, `tenant_level`, `min_conversion`, `max_conversion`, `source_weights`, `max_ads_per_req`, `priority`, `status`, `created_by`, `updated_by`) VALUES
('基础策略', '适用于基础租户的默认策略', 'basic', 0.0100, 0.0500, '{"self": 100, "google": 0, "facebook": 0}', 3, 10, 'active', 1, 1),
('标准策略', '适用于标准租户的默认策略', 'standard', 0.0500, 0.1500, '{"self": 70, "google": 20, "facebook": 10}', 5, 20, 'active', 1, 1),
('高级策略', '适用于高级租户的默认策略', 'premium', 0.1500, 1.0000, '{"self": 40, "google": 30, "facebook": 30}', 10, 30, 'active', 1, 1)
ON DUPLICATE KEY UPDATE `updated_at` = CURRENT_TIMESTAMP;

4
go.mod
View File

@@ -1,4 +1,4 @@
module cidService module cidservice
go 1.25.3 go 1.25.3
@@ -10,7 +10,7 @@ require (
go.mongodb.org/mongo-driver/v2 v2.4.0 go.mongodb.org/mongo-driver/v2 v2.4.0
golang.org/x/net v0.47.0 golang.org/x/net v0.47.0
) )
replace gitee.com/red-future---jilin-g/common v0.1.9 => ../common
require ( require (
github.com/BurntSushi/toml v1.5.0 // indirect github.com/BurntSushi/toml v1.5.0 // indirect
github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-metrics v0.4.1 // indirect

View File

@@ -1,7 +1,7 @@
package main package main
import ( import (
"cidService/controller" "cidservice/controller"
"gitee.com/red-future---jilin-g/common/http" "gitee.com/red-future---jilin-g/common/http"
"gitee.com/red-future---jilin-g/common/jaeger" "gitee.com/red-future---jilin-g/common/jaeger"
@@ -20,6 +20,9 @@ func main() {
controller.AdPosition, controller.AdPosition,
controller.AdStatistics, controller.AdStatistics,
controller.Report, controller.Report,
//controller.RateLimit,
//controller.Application,
//controller.StatReport,
}) })
select {} select {}
} }

View File

@@ -1,7 +1,7 @@
package dto package dto
import ( import (
"cidService/model/entity" "cidservice/model/entity"
"gitee.com/red-future---jilin-g/common/http" "gitee.com/red-future---jilin-g/common/http"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
@@ -9,7 +9,7 @@ import (
// AddAdPositionReq 添加广告位请求 // AddAdPositionReq 添加广告位请求
type AddAdPositionReq struct { type AddAdPositionReq struct {
g.Meta `path:"/adposition/add" method:"post" tags:"广告位管理" summary:"添加广告位" dc:"添加新的广告位"` g.Meta `path:"/add" method:"post" tags:"广告位管理" summary:"添加广告位" dc:"添加新的广告位"`
// 基本信息 // 基本信息
Name string `json:"name" v:"required"` // 广告位名称 Name string `json:"name" v:"required"` // 广告位名称
@@ -51,7 +51,7 @@ type AddAdPositionRes struct {
// UpdateAdPositionReq 更新广告位请求 // UpdateAdPositionReq 更新广告位请求
type UpdateAdPositionReq struct { type UpdateAdPositionReq struct {
g.Meta `path:"/adposition/update" method:"post" tags:"广告位管理" summary:"更新广告位" dc:"更新广告位信息"` g.Meta `path:"/update" method:"put" tags:"广告位管理" summary:"更新广告位" dc:"更新广告位信息"`
Id string `json:"id" v:"required"` // ID Id string `json:"id" v:"required"` // ID
@@ -91,7 +91,7 @@ type UpdateAdPositionReq struct {
// GetAdPositionReq 获取广告位详情请求 // GetAdPositionReq 获取广告位详情请求
type GetAdPositionReq struct { type GetAdPositionReq struct {
g.Meta `path:"/adposition/one" method:"get" tags:"广告位管理" summary:"获取广告位详情" dc:"根据ID获取单个广告位详情"` g.Meta `path:"/one" method:"get" tags:"广告位管理" summary:"获取广告位详情" dc:"根据ID获取单个广告位详情"`
Id string `json:"id" v:"required"` // ID Id string `json:"id" v:"required"` // ID
} }
@@ -101,7 +101,7 @@ type GetAdPositionRes struct {
// ListAdPositionReq 获取广告位列表请求 // ListAdPositionReq 获取广告位列表请求
type ListAdPositionReq struct { type ListAdPositionReq struct {
g.Meta `path:"/adposition/list" method:"get" tags:"广告位管理" summary:"获取广告位列表" dc:"分页查询广告位列表,支持多条件筛选"` g.Meta `path:"/list" method:"get" tags:"广告位管理" summary:"获取广告位列表" dc:"分页查询广告位列表,支持多条件筛选"`
http.Page http.Page
Name string `json:"name"` // 广告位名称模糊查询 Name string `json:"name"` // 广告位名称模糊查询
@@ -120,7 +120,7 @@ type ListAdPositionRes struct {
// UpdateAdPositionStatusReq 更新广告位状态请求 // UpdateAdPositionStatusReq 更新广告位状态请求
type UpdateAdPositionStatusReq struct { type UpdateAdPositionStatusReq struct {
g.Meta `path:"/adposition/status" method:"post" tags:"广告位管理" summary:"更新广告位状态" dc:"更新广告位状态"` g.Meta `path:"/updateStatus" method:"patch" tags:"广告位管理" summary:"更新广告位状态" dc:"更新广告位状态"`
Id string `json:"id" v:"required"` // 广告位ID Id string `json:"id" v:"required"` // 广告位ID
Status string `json:"status" v:"required"` // 广告位状态:启用、禁用、测试 Status string `json:"status" v:"required"` // 广告位状态:启用、禁用、测试
@@ -128,7 +128,7 @@ type UpdateAdPositionStatusReq struct {
// GetAdPositionStatisticsReq 获取广告位统计数据请求 // GetAdPositionStatisticsReq 获取广告位统计数据请求
type GetAdPositionStatisticsReq struct { type GetAdPositionStatisticsReq struct {
g.Meta `path:"/adposition/statistics" method:"get" tags:"广告位管理" summary:"获取广告位统计数据" dc:"获取广告位的统计数据"` g.Meta `path:"getStatisticsAdPosition" method:"get" tags:"广告位管理" summary:"获取广告位统计数据" dc:"获取广告位的统计数据"`
Id string `json:"id" v:"required"` // 广告位ID Id string `json:"id" v:"required"` // 广告位ID
StatType string `json:"statType" v:"required"` // 统计类型:天、周、月 StatType string `json:"statType" v:"required"` // 统计类型:天、周、月
@@ -143,7 +143,7 @@ type GetAdPositionStatisticsRes struct {
// GetAvailableAdPositionsReq 获取可用广告位请求 // GetAvailableAdPositionsReq 获取可用广告位请求
type GetAvailableAdPositionsReq struct { type GetAvailableAdPositionsReq struct {
g.Meta `path:"/adposition/available" method:"get" tags:"广告位管理" summary:"获取可用广告位列表" dc:"获取所有启用的广告位列表"` g.Meta `path:"getAvailableAdPositions" method:"get" tags:"广告位管理" summary:"获取可用广告位列表" dc:"获取所有启用的广告位列表"`
} }
type GetAvailableAdPositionsRes struct { type GetAvailableAdPositionsRes struct {
@@ -152,7 +152,7 @@ type GetAvailableAdPositionsRes struct {
// MatchAdReq 匹配广告请求 // MatchAdReq 匹配广告请求
type MatchAdReq struct { type MatchAdReq struct {
g.Meta `path:"/adposition/match" method:"post" tags:"广告位管理" summary:"匹配广告" dc:"根据广告位编码和用户信息匹配适合的广告"` g.Meta `path:"matchAdAdPosition" method:"post" tags:"广告位管理" summary:"匹配广告" dc:"根据广告位编码和用户信息匹配适合的广告"`
PositionCode string `json:"positionCode" v:"required"` // 广告位编码 PositionCode string `json:"positionCode" v:"required"` // 广告位编码
UserInfo map[string]interface{} `json:"userInfo"` // 用户信息 UserInfo map[string]interface{} `json:"userInfo"` // 用户信息

View File

@@ -1,7 +1,7 @@
package dto package dto
import ( import (
"cidService/model/entity" "cidservice/model/entity"
"gitee.com/red-future---jilin-g/common/http" "gitee.com/red-future---jilin-g/common/http"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
@@ -9,7 +9,7 @@ import (
// CreateAdSourceReq 创建广告源请求 // CreateAdSourceReq 创建广告源请求
type CreateAdSourceReq struct { type CreateAdSourceReq struct {
g.Meta `path:"/adsource/create" method:"post" tags:"广告源管理" summary:"创建广告源" dc:"创建新的广告源配置"` g.Meta `path:"createAdSource" method:"post" tags:"广告源管理" summary:"创建广告源" dc:"创建新的广告源配置"`
// 基本信息 // 基本信息
Name string `json:"name" v:"required"` // 广告源名称 Name string `json:"name" v:"required"` // 广告源名称
@@ -69,7 +69,7 @@ type CreateAdSourceRes struct {
// GetAdSourceReq 获取广告源详情请求 // GetAdSourceReq 获取广告源详情请求
type GetAdSourceReq struct { type GetAdSourceReq struct {
g.Meta `path:"/adsource/one" method:"get" tags:"广告源管理" summary:"获取广告源详情" dc:"根据ID获取单个广告源详情"` g.Meta `path:"getByIDAdSource" method:"get" tags:"广告源管理" summary:"获取广告源详情" dc:"根据ID获取单个广告源详情"`
Id string `json:"id" v:"required"` // 广告源ID Id string `json:"id" v:"required"` // 广告源ID
} }
@@ -79,7 +79,7 @@ type GetAdSourceRes struct {
// ListAdSourceReq 获取广告源列表请求 // ListAdSourceReq 获取广告源列表请求
type ListAdSourceReq struct { type ListAdSourceReq struct {
g.Meta `path:"/adsource/list" method:"get" tags:"广告源管理" summary:"获取广告源列表" dc:"分页查询广告源列表,支持多条件筛选"` g.Meta `path:"getListAdSource" method:"get" tags:"广告源管理" summary:"获取广告源列表" dc:"分页查询广告源列表,支持多条件筛选"`
http.Page http.Page
Name string `json:"name"` // 广告源名称模糊查询 Name string `json:"name"` // 广告源名称模糊查询
@@ -97,7 +97,7 @@ type ListAdSourceRes struct {
// UpdateAdSourceReq 更新广告源请求 // UpdateAdSourceReq 更新广告源请求
type UpdateAdSourceReq struct { type UpdateAdSourceReq struct {
g.Meta `path:"/adsource/update" method:"post" tags:"广告源管理" summary:"更新广告源" dc:"更新广告源信息"` g.Meta `path:"updateAdSource" method:"put" tags:"广告源管理" summary:"更新广告源" dc:"更新广告源信息"`
Id string `json:"id" v:"required"` // 广告源ID Id string `json:"id" v:"required"` // 广告源ID
@@ -152,7 +152,7 @@ type UpdateAdSourceReq struct {
// UpdateAdSourceStatusReq 更新广告源状态请求 // UpdateAdSourceStatusReq 更新广告源状态请求
type UpdateAdSourceStatusReq struct { type UpdateAdSourceStatusReq struct {
g.Meta `path:"/adsource/status" method:"post" tags:"广告源管理" summary:"更新广告源状态" dc:"更新广告源状态"` g.Meta `path:"/adsource" method:"patch" tags:"广告源管理" summary:"更新广告源状态" dc:"更新广告源状态"`
Id string `json:"id" v:"required"` // 广告源ID Id string `json:"id" v:"required"` // 广告源ID
Status string `json:"status" v:"required"` // 广告源状态active、inactive、maintenance Status string `json:"status" v:"required"` // 广告源状态active、inactive、maintenance
@@ -160,14 +160,14 @@ type UpdateAdSourceStatusReq struct {
// DeleteAdSourceReq 删除广告源请求 // DeleteAdSourceReq 删除广告源请求
type DeleteAdSourceReq struct { type DeleteAdSourceReq struct {
g.Meta `path:"/adsource/delete" method:"post" tags:"广告源管理" summary:"删除广告源" dc:"删除指定的广告源"` g.Meta `path:"deleteAdSource" method:"delete" tags:"广告源管理" summary:"删除广告源" dc:"删除指定的广告源"`
Id string `json:"id" v:"required"` // 广告源ID Id string `json:"id" v:"required"` // 广告源ID
} }
// TestAdSourceReq 测试广告源连接请求 // TestAdSourceReq 测试广告源连接请求
type TestAdSourceReq struct { type TestAdSourceReq struct {
g.Meta `path:"/adsource/test" method:"post" tags:"广告源管理" summary:"测试广告源连接" dc:"测试广告源的连接性和可用性"` g.Meta `path:"/adsource-test" method:"post" tags:"广告源管理" summary:"测试广告源连接" dc:"测试广告源的连接性和可用性"`
Id string `json:"id" v:"required"` // 广告源ID Id string `json:"id" v:"required"` // 广告源ID
} }
@@ -195,7 +195,7 @@ type AdSourceTestMetrics struct {
// GetAdSourceStatisticsReq 获取广告源统计数据请求 // GetAdSourceStatisticsReq 获取广告源统计数据请求
type GetAdSourceStatisticsReq struct { type GetAdSourceStatisticsReq struct {
g.Meta `path:"/adsource/statistics" method:"get" tags:"广告源管理" summary:"获取广告源统计数据" dc:"获取广告源的详细统计数据"` g.Meta `path:"/adsource-statistics" method:"get" tags:"广告源管理" summary:"获取广告源统计数据" dc:"获取广告源的详细统计数据"`
Id string `json:"id" v:"required"` // 广告源ID Id string `json:"id" v:"required"` // 广告源ID
StartDate int64 `json:"startDate" v:"required"` // 开始日期 StartDate int64 `json:"startDate" v:"required"` // 开始日期

View File

@@ -1,7 +1,7 @@
package dto package dto
import ( import (
"cidService/model/entity" "cidservice/model/entity"
"gitee.com/red-future---jilin-g/common/http" "gitee.com/red-future---jilin-g/common/http"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
@@ -9,7 +9,7 @@ import (
// GetAdStatisticsReq 获取广告统计数据请求 // GetAdStatisticsReq 获取广告统计数据请求
type GetAdStatisticsReq struct { type GetAdStatisticsReq struct {
g.Meta `path:"/statistics/list" method:"get" tags:"广告统计" summary:"获取广告统计数据" dc:"获取广告的统计数据"` g.Meta `path:"getStatisticsAdStatistics" method:"get" tags:"广告统计" summary:"获取广告统计数据" dc:"获取广告的统计数据"`
// 分页参数 // 分页参数
http.Page http.Page
@@ -42,7 +42,7 @@ type GetAdStatisticsRes struct {
// GetDashboardReq 获取仪表盘数据请求 // GetDashboardReq 获取仪表盘数据请求
type GetDashboardReq struct { type GetDashboardReq struct {
g.Meta `path:"/dashboard" method:"get" tags:"广告仪表盘" summary:"获取仪表盘数据" dc:"获取广告系统的仪表盘统计数据"` g.Meta `path:"getDashboardAdStatistics" method:"get" tags:"广告仪表盘" summary:"获取仪表盘数据" dc:"获取广告系统的仪表盘统计数据"`
// 时间范围 // 时间范围
StartDate int64 `json:"startDate" v:"required"` // 开始日期 StartDate int64 `json:"startDate" v:"required"` // 开始日期
@@ -103,7 +103,7 @@ type RankData struct {
// GenerateDailyStatisticsReq 生成每日统计数据请求 // GenerateDailyStatisticsReq 生成每日统计数据请求
type GenerateDailyStatisticsReq struct { type GenerateDailyStatisticsReq struct {
g.Meta `path:"/statistics/generate-daily" method:"post" tags:"广告统计" summary:"生成每日统计数据" dc:"手动生成指定日期的广告统计数据"` g.Meta `path:"generateDailyStatisticsAdStatistics" method:"post" tags:"广告统计" summary:"生成每日统计数据" dc:"手动生成指定日期的广告统计数据"`
Date int64 `json:"date" v:"required"` // 日期时间戳 Date int64 `json:"date" v:"required"` // 日期时间戳
} }

View File

@@ -1,7 +1,7 @@
package dto package dto
import ( import (
"cidService/model/entity" "cidservice/model/entity"
"gitee.com/red-future---jilin-g/common/http" "gitee.com/red-future---jilin-g/common/http"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
@@ -9,7 +9,7 @@ import (
// AddAdvertisementReq 添加广告请求 // AddAdvertisementReq 添加广告请求
type AddAdvertisementReq struct { type AddAdvertisementReq struct {
g.Meta `path:"/advertisement/add" method:"post" tags:"广告管理" summary:"添加广告" dc:"添加新的广告"` g.Meta `path:"addAdvertisement" method:"post" tags:"广告管理" summary:"添加广告" dc:"添加新的广告"`
// 广告基本信息 // 广告基本信息
Title string `json:"title" v:"required"` // 广告标题 Title string `json:"title" v:"required"` // 广告标题
@@ -40,7 +40,7 @@ type AddAdvertisementRes struct {
// UpdateAdvertisementReq 更新广告请求 // UpdateAdvertisementReq 更新广告请求
type UpdateAdvertisementReq struct { type UpdateAdvertisementReq struct {
g.Meta `path:"/advertisement/update" method:"post" tags:"广告管理" summary:"更新广告" dc:"更新广告信息"` g.Meta `path:"updateAdvertisement" method:"put" tags:"广告管理" summary:"更新广告" dc:"更新广告信息"`
Id string `json:"id" v:"required"` // ID Id string `json:"id" v:"required"` // ID
@@ -74,7 +74,7 @@ type UpdateAdvertisementReq struct {
// GetAdvertisementReq 获取广告详情请求 // GetAdvertisementReq 获取广告详情请求
type GetAdvertisementReq struct { type GetAdvertisementReq struct {
g.Meta `path:"/advertisement/one" method:"get" tags:"广告管理" summary:"获取广告详情" dc:"根据ID获取单个广告详情"` g.Meta `path:"getOneAdvertisement" method:"get" tags:"广告管理" summary:"获取广告详情" dc:"根据ID获取单个广告详情"`
Id string `json:"id" v:"required"` // ID Id string `json:"id" v:"required"` // ID
} }
@@ -84,7 +84,7 @@ type GetAdvertisementRes struct {
// ListAdvertisementReq 获取广告列表请求 // ListAdvertisementReq 获取广告列表请求
type ListAdvertisementReq struct { type ListAdvertisementReq struct {
g.Meta `path:"/advertisement/list" method:"get" tags:"广告管理" summary:"获取广告列表" dc:"分页查询广告列表,支持多条件筛选"` g.Meta `path:"listAdvertisement" method:"get" tags:"广告管理" summary:"获取广告列表" dc:"分页查询广告列表,支持多条件筛选"`
http.Page http.Page
AdvertiserId string `json:"advertiserId"` // 广告主ID AdvertiserId string `json:"advertiserId"` // 广告主ID
@@ -103,7 +103,7 @@ type ListAdvertisementRes struct {
// AuditAdvertisementReq 审核广告请求 // AuditAdvertisementReq 审核广告请求
type AuditAdvertisementReq struct { type AuditAdvertisementReq struct {
g.Meta `path:"/advertisement/audit" method:"post" tags:"广告管理" summary:"审核广告" dc:"审核广告,通过或拒绝"` g.Meta `path:"auditAdvertisement" method:"post" tags:"广告管理" summary:"审核广告" dc:"审核广告,通过或拒绝"`
Id string `json:"id" v:"required"` // 广告ID Id string `json:"id" v:"required"` // 广告ID
AuditStatus string `json:"auditStatus" v:"required"` // 审核状态:通过、拒绝 AuditStatus string `json:"auditStatus" v:"required"` // 审核状态:通过、拒绝
@@ -112,7 +112,7 @@ type AuditAdvertisementReq struct {
// UpdateAdStatusReq 更新广告状态请求 // UpdateAdStatusReq 更新广告状态请求
type UpdateAdStatusReq struct { type UpdateAdStatusReq struct {
g.Meta `path:"/advertisement/status" method:"post" tags:"广告管理" summary:"更新广告状态" dc:"更新广告状态"` g.Meta `path:"updateStatusAdvertisement" method:"patch" tags:"广告管理" summary:"更新广告状态" dc:"更新广告状态"`
Id string `json:"id" v:"required"` // 广告ID Id string `json:"id" v:"required"` // 广告ID
Status string `json:"status" v:"required"` // 广告状态:启用、禁用 Status string `json:"status" v:"required"` // 广告状态:启用、禁用
@@ -120,7 +120,7 @@ type UpdateAdStatusReq struct {
// GetAdStatisticsReq 获取广告统计数据请求 // GetAdStatisticsReq 获取广告统计数据请求
type GetAdStatisticsForAdvertisementReq struct { type GetAdStatisticsForAdvertisementReq struct {
g.Meta `path:"/advertisement/statistics" method:"get" tags:"广告管理" summary:"获取广告统计数据" dc:"获取广告的统计数据"` g.Meta `path:"getStatisticsAdvertisement" method:"get" tags:"广告管理" summary:"获取广告统计数据" dc:"获取广告的统计数据"`
Id string `json:"id" v:"required"` // 广告ID Id string `json:"id" v:"required"` // 广告ID
StatType string `json:"statType" v:"required"` // 统计类型:天、周、月 StatType string `json:"statType" v:"required"` // 统计类型:天、周、月

View File

@@ -1,7 +1,7 @@
package dto package dto
import ( import (
"cidService/model/entity" "cidservice/model/entity"
"gitee.com/red-future---jilin-g/common/http" "gitee.com/red-future---jilin-g/common/http"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
@@ -9,7 +9,7 @@ import (
// AddAdvertiserReq 添加广告主请求 // AddAdvertiserReq 添加广告主请求
type AddAdvertiserReq struct { type AddAdvertiserReq struct {
g.Meta `path:"/advertiser/add" method:"post" tags:"广告主管理" summary:"添加广告主" dc:"添加新的广告主"` g.Meta `path:"addAdvertiser" method:"post" tags:"广告主管理" summary:"添加广告主" dc:"添加新的广告主"`
// 基本信息 // 基本信息
Name string `json:"name" v:"required"` // 广告主名称 Name string `json:"name" v:"required"` // 广告主名称
@@ -49,7 +49,7 @@ type AddAdvertiserRes struct {
// UpdateAdvertiserReq 更新广告主请求 // UpdateAdvertiserReq 更新广告主请求
type UpdateAdvertiserReq struct { type UpdateAdvertiserReq struct {
g.Meta `path:"/advertiser/update" method:"post" tags:"广告主管理" summary:"更新广告主" dc:"更新广告主信息"` g.Meta `path:"updateAdvertiser" method:"put" tags:"广告主管理" summary:"更新广告主" dc:"更新广告主信息"`
Id string `json:"id" v:"required"` // ID Id string `json:"id" v:"required"` // ID
@@ -92,7 +92,7 @@ type UpdateAdvertiserReq struct {
// GetAdvertiserReq 获取广告主详情请求 // GetAdvertiserReq 获取广告主详情请求
type GetAdvertiserReq struct { type GetAdvertiserReq struct {
g.Meta `path:"/advertiser/one" method:"get" tags:"广告主管理" summary:"获取广告主详情" dc:"根据ID获取单个广告主详情"` g.Meta `path:"getOneAdvertiser" method:"get" tags:"广告主管理" summary:"获取广告主详情" dc:"根据ID获取单个广告主详情"`
Id string `json:"id" v:"required"` // ID Id string `json:"id" v:"required"` // ID
} }
@@ -102,7 +102,7 @@ type GetAdvertiserRes struct {
// ListAdvertiserReq 获取广告主列表请求 // ListAdvertiserReq 获取广告主列表请求
type ListAdvertiserReq struct { type ListAdvertiserReq struct {
g.Meta `path:"/advertiser/list" method:"get" tags:"广告主管理" summary:"获取广告主列表" dc:"分页查询广告主列表,支持多条件筛选"` g.Meta `path:"listAdvertiser" method:"get" tags:"广告主管理" summary:"获取广告主列表" dc:"分页查询广告主列表,支持多条件筛选"`
http.Page http.Page
Name string `json:"name"` // 广告主名称模糊查询 Name string `json:"name"` // 广告主名称模糊查询
@@ -121,7 +121,7 @@ type ListAdvertiserRes struct {
// AuditAdvertiserReq 审核广告主请求 // AuditAdvertiserReq 审核广告主请求
type AuditAdvertiserReq struct { type AuditAdvertiserReq struct {
g.Meta `path:"/advertiser/audit" method:"post" tags:"广告主管理" summary:"审核广告主" dc:"审核广告主,通过或拒绝"` g.Meta `path:"auditAdvertiser" method:"post" tags:"广告主管理" summary:"审核广告主" dc:"审核广告主,通过或拒绝"`
Id string `json:"id" v:"required"` // 广告主ID Id string `json:"id" v:"required"` // 广告主ID
AuditStatus string `json:"auditStatus" v:"required"` // 审核状态:通过、拒绝 AuditStatus string `json:"auditStatus" v:"required"` // 审核状态:通过、拒绝
@@ -130,7 +130,7 @@ type AuditAdvertiserReq struct {
// UpdateAdvertiserStatusReq 更新广告主状态请求 // UpdateAdvertiserStatusReq 更新广告主状态请求
type UpdateAdvertiserStatusReq struct { type UpdateAdvertiserStatusReq struct {
g.Meta `path:"/advertiser/status" method:"post" tags:"广告主管理" summary:"更新广告主状态" dc:"更新广告主状态"` g.Meta `path:"updateStatusAdvertiser" method:"patch" tags:"广告主管理" summary:"更新广告主状态" dc:"更新广告主状态"`
Id string `json:"id" v:"required"` // 广告主ID Id string `json:"id" v:"required"` // 广告主ID
Status string `json:"status" v:"required"` // 广告主状态:启用、禁用、冻结 Status string `json:"status" v:"required"` // 广告主状态:启用、禁用、冻结
@@ -138,7 +138,7 @@ type UpdateAdvertiserStatusReq struct {
// RechargeAdvertiserReq 广告主充值请求 // RechargeAdvertiserReq 广告主充值请求
type RechargeAdvertiserReq struct { type RechargeAdvertiserReq struct {
g.Meta `path:"/advertiser/recharge" method:"post" tags:"广告主管理" summary:"广告主充值" dc:"为广告主账户充值"` g.Meta `path:"rechargeAdvertiser" method:"post" tags:"广告主管理" summary:"广告主充值" dc:"为广告主账户充值"`
Id string `json:"id" v:"required"` // 广告主ID Id string `json:"id" v:"required"` // 广告主ID
Amount int64 `json:"amount" v:"required"` // 充值金额(分) Amount int64 `json:"amount" v:"required"` // 充值金额(分)
@@ -147,7 +147,7 @@ type RechargeAdvertiserReq struct {
// UpdateCreditLimitReq 更新授信额度请求 // UpdateCreditLimitReq 更新授信额度请求
type UpdateCreditLimitReq struct { type UpdateCreditLimitReq struct {
g.Meta `path:"/advertiser/credit" method:"post" tags:"广告主管理" summary:"更新授信额度" dc:"更新广告主的授信额度"` g.Meta `path:"updateCreditLimitAdvertiser" method:"post" tags:"广告主管理" summary:"更新授信额度" dc:"更新广告主的授信额度"`
Id string `json:"id" v:"required"` // 广告主ID Id string `json:"id" v:"required"` // 广告主ID
CreditLimit int64 `json:"creditLimit" v:"required"` // 授信额度(分) CreditLimit int64 `json:"creditLimit" v:"required"` // 授信额度(分)
@@ -156,7 +156,7 @@ type UpdateCreditLimitReq struct {
// GetAdvertiserBalanceReq 获取广告主余额请求 // GetAdvertiserBalanceReq 获取广告主余额请求
type GetAdvertiserBalanceReq struct { type GetAdvertiserBalanceReq struct {
g.Meta `path:"/advertiser/balance" method:"get" tags:"广告主管理" summary:"获取广告主余额" dc:"根据ID获取广告主账户余额和授信额度"` g.Meta `path:"getBalanceAdvertiser" method:"get" tags:"广告主管理" summary:"获取广告主余额" dc:"根据ID获取广告主账户余额和授信额度"`
Id string `json:"id" v:"required"` // 广告主ID Id string `json:"id" v:"required"` // 广告主ID
} }

View File

@@ -0,0 +1,157 @@
package dto
import (
"github.com/gogf/gf/v2/frame/g"
)
// CreateApplicationReq 创建应用请求
type CreateApplicationReq struct {
g.Meta `path:"createApplication" method:"post" summary:"创建应用"`
TenantID int64 `json:"tenantId" v:"required#租户ID不能为空"`
Name string `json:"name" v:"required#应用名称不能为空"`
Code string `json:"code" v:"required#应用编码不能为空"`
Description string `json:"description"`
Platform string `json:"platform" v:"required#平台不能为空|in:web,h5,android,ios#平台类型错误"`
PackageName string `json:"packageName"`
AppStoreURL string `json:"appStoreUrl"`
Categories []string `json:"categories"`
Tags []string `json:"tags"`
AdTypes []string `json:"adTypes"`
CallbackURL string `json:"callbackUrl"`
}
// CreateApplicationRes 创建应用响应
type CreateApplicationRes struct {
ID int64 `json:"id"`
AppKey string `json:"appKey"`
AppSecret string `json:"appSecret"`
}
// UpdateApplicationReq 更新应用请求
type UpdateApplicationReq struct {
g.Meta `path:"updateApplication" method:"put" summary:"更新应用"`
ID int64 `json:"id" v:"required#应用ID不能为空"`
Name string `json:"name"`
Description string `json:"description"`
Platform string `json:"platform" v:"in:web,h5,android,ios#平台类型错误"`
PackageName string `json:"packageName"`
AppStoreURL string `json:"appStoreUrl"`
Categories []string `json:"categories"`
Tags []string `json:"tags"`
AdTypes []string `json:"adTypes"`
CallbackURL string `json:"callbackUrl"`
}
// UpdateApplicationRes 更新应用响应
type UpdateApplicationRes struct {
Success bool `json:"success"`
}
// GetApplicationReq 获取应用请求
type GetApplicationReq struct {
g.Meta `path:"getApplication" method:"get" summary:"获取应用信息"`
ID int64 `json:"id" v:"required#应用ID不能为空"`
}
// GetApplicationRes 获取应用响应
type GetApplicationRes struct {
ID int64 `json:"id"`
TenantID int64 `json:"tenantId"`
Name string `json:"name"`
Code string `json:"code"`
Description string `json:"description"`
Platform string `json:"platform"`
PackageName string `json:"packageName"`
AppStoreURL string `json:"appStoreUrl"`
Categories []string `json:"categories"`
Tags []string `json:"tags"`
AdTypes []string `json:"adTypes"`
Status string `json:"status"`
AppKey string `json:"appKey"`
CallbackURL string `json:"callbackUrl"`
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
}
// ListApplicationsReq 获取应用列表请求
type ListApplicationsReq struct {
g.Meta `path:"listApplications" method:"get" summary:"获取应用列表"`
TenantID int64 `json:"tenantId" v:"required#租户ID不能为空"`
Platform string `json:"platform"`
Status string `json:"status"`
Page int `json:"page" d:"1"`
Size int `json:"size" d:"20"`
}
// ListApplicationsRes 获取应用列表响应
type ListApplicationsRes struct {
List []ApplicationItem `json:"list"`
Total int64 `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
}
// ApplicationItem 应用列表项
type ApplicationItem struct {
ID int64 `json:"id"`
Name string `json:"name"`
Code string `json:"code"`
Description string `json:"description"`
Platform string `json:"platform"`
PackageName string `json:"packageName"`
Categories []string `json:"categories"`
Tags []string `json:"tags"`
AdTypes []string `json:"adTypes"`
Status string `json:"status"`
DailyRequests int64 `json:"dailyRequests"`
MonthlyRequests int64 `json:"monthlyRequests"`
CreatedAt int64 `json:"createdAt"`
}
// ResetAPIKeysReq 重置API密钥请求
type ResetAPIKeysReq struct {
g.Meta `path:"/applications-reset-keys" method:"post" summary:"重置API密钥"`
ID int64 `json:"id" v:"required#应用ID不能为空"`
}
// ResetAPIKeysRes 重置API密钥响应
type ResetAPIKeysRes struct {
AppKey string `json:"appKey"`
AppSecret string `json:"appSecret"`
}
// ValidateApplicationReq 验证应用请求
type ValidateApplicationReq struct {
g.Meta `path:"/applications-validate" method:"post" summary:"验证应用权限"`
AppKey string `json:"appKey" v:"required#应用密钥不能为空"`
AppSecret string `json:"appSecret" v:"required#应用密钥不能为空"`
}
// ValidateApplicationRes 验证应用响应
type ValidateApplicationRes struct {
Valid bool `json:"valid"`
AppID int64 `json:"appId"`
AppName string `json:"appName"`
TenantID int64 `json:"tenantId"`
TenantName string `json:"tenantName"`
Platform string `json:"platform"`
AdTypes []string `json:"adTypes"`
}
// DeleteApplicationReq 删除应用请求
type DeleteApplicationReq struct {
g.Meta `path:"/applications" method:"delete" summary:"删除应用"`
ID int64 `json:"id" v:"required#应用ID不能为空"`
}
// DeleteApplicationRes 删除应用响应
type DeleteApplicationRes struct {
Success bool `json:"success"`
}

View File

@@ -6,7 +6,7 @@ import (
// GenerateCIDReq 生成CID请求 // GenerateCIDReq 生成CID请求
type GenerateCIDReq struct { type GenerateCIDReq struct {
g.Meta `path:"/cid/generate" method:"post" tags:"CID服务" summary:"生成CID广告" dc:"为当前用户生成CID广告"` g.Meta `path:"/cid-generate" method:"post" tags:"CID服务" summary:"生成CID广告" dc:"为当前用户生成CID广告"`
UserId int64 `json:"user_id"` // 用户ID可选如果不提供则从token获取 UserId int64 `json:"user_id"` // 用户ID可选如果不提供则从token获取
RequestType string `json:"request_type"` // 请求类型 RequestType string `json:"request_type"` // 请求类型
Parameters map[string]interface{} `json:"parameters"` // 请求参数 Parameters map[string]interface{} `json:"parameters"` // 请求参数
@@ -38,7 +38,7 @@ type GenerateCIDRes struct {
// GetCIDStatisticsReq 获取CID统计请求 // GetCIDStatisticsReq 获取CID统计请求
type GetCIDStatisticsReq struct { type GetCIDStatisticsReq struct {
g.Meta `path:"/cid/statistics" method:"get" tags:"CID服务" summary:"获取CID统计" dc:"获取CID服务的统计信息"` g.Meta `path:"/cid-statistics" method:"get" tags:"CID服务" summary:"获取CID统计" dc:"获取CID服务的统计信息"`
UserId int64 `json:"user_id"` // 用户ID可选 UserId int64 `json:"user_id"` // 用户ID可选
TenantId int64 `json:"tenant_id"` // 租户ID可选 TenantId int64 `json:"tenant_id"` // 租户ID可选
DateFrom int64 `json:"date_from"` // 开始日期 DateFrom int64 `json:"date_from"` // 开始日期
@@ -67,7 +67,7 @@ type CIDRequestHistory struct {
// GetCIDHistoryReq 获取CID历史请求 // GetCIDHistoryReq 获取CID历史请求
type GetCIDHistoryReq struct { type GetCIDHistoryReq struct {
g.Meta `path:"/cid/history" method:"get" tags:"CID服务" summary:"获取CID历史记录" dc:"分页获取用户的CID请求历史"` g.Meta `path:"/cid-history" method:"get" tags:"CID服务" summary:"获取CID历史记录" dc:"分页获取用户的CID请求历史"`
Page int `json:"page" v:"required|min:1"` // 页码 Page int `json:"page" v:"required|min:1"` // 页码
Size int `json:"size" v:"required|min:1|max:100"` // 每页数量 Size int `json:"size" v:"required|min:1|max:100"` // 每页数量
} }

View File

@@ -0,0 +1,35 @@
package dto
import (
"github.com/gogf/gf/v2/frame/g"
)
// SetTenantRateLimitReq 设置租户限流配置请求
type SetTenantRateLimitReq struct {
g.Meta `path:"/ratelimit-tenant-set" method:"post" tags:"租户限流" summary:"设置租户限流配置" dc:"设置指定租户的请求次数限制配置(实际使用全局配置)"`
TenantID int64 `json:"tenant_id" v:"required"` // 租户ID(仅用于记录,实际使用全局配置)
RequestsPerSecond float64 `json:"requests_per_second" v:"required"` // 每秒请求数
Burst int `json:"burst" v:"required"` // 突发请求数
WindowSeconds int `json:"window_seconds" v:"required"` // 时间窗口(秒)
}
// SetTenantRateLimitRes 设置租户限流配置响应
type SetTenantRateLimitRes struct {
Success bool `json:"success"` // 是否成功
}
// GetTenantRateLimitUsageReq 获取租户限流使用情况请求
type GetTenantRateLimitUsageReq struct {
g.Meta `path:"/ratelimit-tenant-usage" method:"get" tags:"租户限流" summary:"获取租户限流使用情况" dc:"获取指定租户的请求次数使用情况"`
TenantID int64 `json:"tenant_id" v:"required"` // 租户ID
}
// GetTenantRateLimitUsageRes 获取租户限流使用情况响应
type GetTenantRateLimitUsageRes struct {
TenantID int64 `json:"tenant_id"` // 租户ID
CurrentUsed int64 `json:"current_used"` // 当前已使用请求数
MaxAllowed int64 `json:"max_allowed"` // 最大允许请求数(基于全局配置)
UsagePercent float64 `json:"usage_percent"` // 使用率百分比
}

View File

@@ -1,7 +1,7 @@
package dto package dto
import ( import (
"cidService/model/entity" "cidservice/model/entity"
"gitee.com/red-future---jilin-g/common/http" "gitee.com/red-future---jilin-g/common/http"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"

View File

@@ -0,0 +1,168 @@
package dto
import "github.com/gogf/gf/v2/frame/g"
// 报表生成请求
type ReportGenerateReq struct {
g.Meta `path:"/report/generate" method:"post"`
TenantID int64 `json:"tenant_id" v:"required"`
AppID int64 `json:"app_id"`
ReportType string `json:"report_type" v:"required|in:daily,monthly,quarterly,yearly"`
Date string `json:"date"` // 格式: 2024-01-01 (daily), 2024-01 (monthly), 2024-Q1 (quarterly), 2024 (yearly)
}
// 报表生成响应
type ReportGenerateResp struct {
ReportID int64 `json:"report_id"`
ReportType string `json:"report_type"`
ReportDate string `json:"report_date"`
Data interface{} `json:"data"`
}
// 报表列表请求
type ReportListReq struct {
g.Meta `path:"/report/list" method:"get"`
TenantID int64 `json:"tenant_id"`
AppID int64 `json:"app_id"`
ReportType string `json:"report_type"`
StartDate string `json:"start_date"`
EndDate string `json:"end_date"`
Page int `json:"page" d:"1"`
PageSize int `json:"page_size" d:"20"`
}
// 报表列表响应
type ReportListResp struct {
Reports []*ReportDTO `json:"reports"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// 报表详情请求
type ReportDetailReq struct {
g.Meta `path:"/report/detail" method:"get"`
ReportID int64 `json:"report_id" v:"required"`
}
// 报表详情响应
type ReportDetailResp struct {
ID int64 `json:"id"`
TenantID int64 `json:"tenant_id"`
AppID int64 `json:"app_id"`
ReportType string `json:"report_type"`
ReportDate string `json:"report_date"`
GeneratedAt string `json:"generated_at"`
Data interface{} `json:"data"`
}
// 报表DTO
type ReportDTO struct {
ID int64 `json:"id"`
TenantID int64 `json:"tenant_id"`
AppID int64 `json:"app_id"`
ReportType string `json:"report_type"`
ReportDate string `json:"report_date"`
GeneratedAt string `json:"generated_at"`
}
// 统计查询请求
type StatQueryReq struct {
g.Meta `path:"/stat/query" method:"get"`
TenantID int64 `json:"tenant_id" v:"required"`
AppID int64 `json:"app_id"`
AdType string `json:"ad_type"`
Platform string `json:"platform"`
Region string `json:"region"`
StartDate string `json:"start_date" v:"required"`
EndDate string `json:"end_date" v:"required"`
Granularity string `json:"granularity" v:"in:daily,weekly,monthly" d:"daily"` // 统计粒度
}
// 统计查询响应
type StatQueryResp struct {
Data []*StatDataPoint `json:"data"`
Summary *StatSummary `json:"summary"`
}
// 统计数据点
type StatDataPoint struct {
Date string `json:"date"`
Impressions int64 `json:"impressions"`
Clicks int64 `json:"clicks"`
Revenue float64 `json:"revenue"`
CTR float64 `json:"ctr"`
AvgDuration float64 `json:"avg_duration"`
}
// 统计摘要
type StatSummary struct {
TotalImpressions int64 `json:"total_impressions"`
TotalClicks int64 `json:"total_clicks"`
TotalRevenue float64 `json:"total_revenue"`
AvgCTR float64 `json:"avg_ctr"`
AvgDuration float64 `json:"avg_duration"`
GrowthRate *GrowthRate `json:"growth_rate"`
}
// 增长率统计
type GrowthRate struct {
Impressions float64 `json:"impressions"`
Clicks float64 `json:"clicks"`
Revenue float64 `json:"revenue"`
CTR float64 `json:"ctr"`
}
// 实时统计请求
type RealTimeStatReq struct {
g.Meta `path:"/stat/realtime" method:"get"`
TenantID int64 `json:"tenant_id" v:"required"`
AppID int64 `json:"app_id"`
Hours int `json:"hours" d:"24"` // 过去多少小时的统计
}
// 实时统计响应
type RealTimeStatResp struct {
CurrentHour *HourlyStat `json:"current_hour"`
Last24Hours []*HourlyStat `json:"last_24_hours"`
}
// 小时统计
type HourlyStat struct {
Hour string `json:"hour"`
Impressions int64 `json:"impressions"`
Clicks int64 `json:"clicks"`
Revenue float64 `json:"revenue"`
}
// Controller响应结构
// 生成报表响应
type ReportGenerateRes struct {
Data *ReportGenerateResp `json:"data"`
}
// 报表列表响应
type ReportListRes struct {
Data *ReportListResp `json:"data"`
}
// 报表详情响应
type ReportDetailRes struct {
Data *ReportDetailResp `json:"data"`
}
// 统计查询响应
type StatQueryRes struct {
Data *StatQueryResp `json:"data"`
}
// 实时统计响应
type RealTimeStatRes struct {
Data *RealTimeStatResp `json:"data"`
}
// 导出报表响应
type ExportReportRes struct {
ReportData *ReportDetailResp `json:"report_data"`
}

View File

@@ -6,7 +6,7 @@ import (
// CreateStrategyReq 创建策略请求 // CreateStrategyReq 创建策略请求
type CreateStrategyReq struct { type CreateStrategyReq struct {
g.Meta `path:"/strategy/create" method:"post" tags:"策略管理" summary:"创建匹配策略" dc:"创建新的广告匹配策略"` g.Meta `path:"/strategy" method:"post" tags:"策略管理" summary:"创建匹配策略" dc:"创建新的广告匹配策略"`
Name string `json:"name" v:"required|length:3,50"` // 策略名称 Name string `json:"name" v:"required|length:3,50"` // 策略名称
Description string `json:"description" v:"max:500"` // 描述 Description string `json:"description" v:"max:500"` // 描述
TenantLevel string `json:"tenant_level" v:"required|in:basic,standard,premium"` // 租户级别 TenantLevel string `json:"tenant_level" v:"required|in:basic,standard,premium"` // 租户级别
@@ -14,13 +14,14 @@ type CreateStrategyReq struct {
MaxConversion float64 `json:"max_conversion" v:"required|min:0|max:1"` // 最高转化率 MaxConversion float64 `json:"max_conversion" v:"required|min:0|max:1"` // 最高转化率
SourceWeights map[string]int `json:"source_weights" v:"required"` // 广告源权重 SourceWeights map[string]int `json:"source_weights" v:"required"` // 广告源权重
MaxAdsPerReq int `json:"max_ads_per_req" v:"required|min:1|max:50"` // 每次请求最大广告数 MaxAdsPerReq int `json:"max_ads_per_req" v:"required|min:1|max:50"` // 每次请求最大广告数
MaxReqPerHour int `json:"max_req_per_hour" v:"required|min:1"` // 每小时最大请求次数
Priority int `json:"priority" v:"required|min:0|max:100"` // 优先级 Priority int `json:"priority" v:"required|min:0|max:100"` // 优先级
Status string `json:"status" v:"required|in:active,inactive"` // 状态 Status string `json:"status" v:"required|in:active,inactive"` // 状态
} }
// UpdateStrategyReq 更新策略请求 // UpdateStrategyReq 更新策略请求
type UpdateStrategyReq struct { type UpdateStrategyReq struct {
g.Meta `path:"/strategy/update" method:"put" tags:"策略管理" summary:"更新匹配策略" dc:"更新现有的广告匹配策略"` g.Meta `path:"/strategy" method:"put" tags:"策略管理" summary:"更新匹配策略" dc:"更新现有的广告匹配策略"`
Id int64 `json:"id" v:"required"` // 策略ID Id int64 `json:"id" v:"required"` // 策略ID
Name string `json:"name" v:"required|length:3,50"` // 策略名称 Name string `json:"name" v:"required|length:3,50"` // 策略名称
Description string `json:"description" v:"max:500"` // 描述 Description string `json:"description" v:"max:500"` // 描述
@@ -29,25 +30,26 @@ type UpdateStrategyReq struct {
MaxConversion float64 `json:"max_conversion" v:"required|min:0|max:1"` // 最高转化率 MaxConversion float64 `json:"max_conversion" v:"required|min:0|max:1"` // 最高转化率
SourceWeights map[string]int `json:"source_weights" v:"required"` // 广告源权重 SourceWeights map[string]int `json:"source_weights" v:"required"` // 广告源权重
MaxAdsPerReq int `json:"max_ads_per_req" v:"required|min:1|max:50"` // 每次请求最大广告数 MaxAdsPerReq int `json:"max_ads_per_req" v:"required|min:1|max:50"` // 每次请求最大广告数
MaxReqPerHour int `json:"max_req_per_hour" v:"required|min:1"` // 每小时最大请求次数
Priority int `json:"priority" v:"required|min:0|max:100"` // 优先级 Priority int `json:"priority" v:"required|min:0|max:100"` // 优先级
Status string `json:"status" v:"required|in:active,inactive"` // 状态 Status string `json:"status" v:"required|in:active,inactive"` // 状态
} }
// DeleteStrategyReq 删除策略请求 // DeleteStrategyReq 删除策略请求
type DeleteStrategyReq struct { type DeleteStrategyReq struct {
g.Meta `path:"/strategy/delete" method:"delete" tags:"策略管理" summary:"删除匹配策略" dc:"删除指定的广告匹配策略"` g.Meta `path:"/strategy" method:"delete" tags:"策略管理" summary:"删除匹配策略" dc:"删除指定的广告匹配策略"`
Id int64 `json:"id" v:"required"` // 策略ID Id int64 `json:"id" v:"required"` // 策略ID
} }
// GetStrategyReq 获取策略请求 // GetStrategyReq 获取策略请求
type GetStrategyReq struct { type GetStrategyReq struct {
g.Meta `path:"/strategy/info" method:"get" tags:"策略管理" summary:"获取策略详情" dc:"获取指定策略的详细信息"` g.Meta `path:"/strategy" method:"get" tags:"策略管理" summary:"获取策略详情" dc:"获取指定策略的详细信息"`
Id int64 `json:"id" v:"required"` // 策略ID Id int64 `json:"id" v:"required"` // 策略ID
} }
// GetStrategyListReq 获取策略列表请求 // GetStrategyListReq 获取策略列表请求
type GetStrategyListReq struct { type GetStrategyListReq struct {
g.Meta `path:"/strategy/list" method:"get" tags:"策略管理" summary:"获取策略列表" dc:"分页获取策略列表"` g.Meta `path:"/strategies" method:"get" tags:"策略管理" summary:"获取策略列表" dc:"分页获取策略列表"`
Page int `json:"page" v:"required|min:1"` // 页码 Page int `json:"page" v:"required|min:1"` // 页码
Size int `json:"size" v:"required|min:1|max:100"` // 每页数量 Size int `json:"size" v:"required|min:1|max:100"` // 每页数量
TenantLevel string `json:"tenant_level"` // 租户级别筛选 TenantLevel string `json:"tenant_level"` // 租户级别筛选
@@ -64,6 +66,7 @@ type StrategyRes struct {
MaxConversion float64 `json:"max_conversion"` // 最高转化率 MaxConversion float64 `json:"max_conversion"` // 最高转化率
SourceWeights map[string]int `json:"source_weights"` // 广告源权重 SourceWeights map[string]int `json:"source_weights"` // 广告源权重
MaxAdsPerReq int `json:"max_ads_per_req"` // 每次请求最大广告数 MaxAdsPerReq int `json:"max_ads_per_req"` // 每次请求最大广告数
MaxReqPerHour int `json:"max_req_per_hour"` // 每小时最大请求次数
Priority int `json:"priority"` // 优先级 Priority int `json:"priority"` // 优先级
Status string `json:"status"` // 状态 Status string `json:"status"` // 状态
CreatedAt string `json:"created_at"` // 创建时间 CreatedAt string `json:"created_at"` // 创建时间

View File

@@ -1,163 +1,167 @@
package entity package entity
import ( import (
"gitee.com/red-future---jilin-g/common/do" "time"
) )
const AdSourceCollection = "ad_source"
// AdSource 广告源实体 // AdSource 广告源实体
type AdSource struct { type AdSource struct {
do.MongoBaseDO `bson:",inline"` // 嵌入基础字段Id, Creator, CreatedAt, Updater, UpdatedAt, TenantId, IsDeleted Id int64 `json:"id"` // 主键ID
CreatedAt time.Time `json:"createdAt"` // 创建时间
UpdatedAt time.Time `json:"updatedAt"` // 更新时间
Creator string `json:"creator"` // 创建者
Updater string `json:"updater"` // 更新者
TenantId int64 `json:"tenantId"` // 租户ID
IsDeleted bool `json:"isDeleted"` // 是否删除
// 基本信息 // 基本信息
Name string `bson:"name" json:"name"` // 广告源名称 Name string `json:"name"` // 广告源名称
Code string `bson:"code" json:"code"` // 广告源编码,唯一标识 Code string `json:"code"` // 广告源编码,唯一标识
Provider string `bson:"provider" json:"provider"` // 提供商google、facebook、baidu、tencent、self等 Provider string `json:"provider"` // 提供商google、facebook、baidu、tencent、self等
Type string `bson:"type" json:"type"` // 类型self(自营)、third_party(第三方)、exchange(广告交易平台) Type string `json:"type"` // 类型self(自营)、third_party(第三方)、exchange(广告交易平台)
Description string `bson:"description" json:"description"` // 描述 Description string `json:"description"` // 描述
// 连接配置 // 连接配置
Config *AdSourceConfig `bson:"config" json:"config"` // 广告源配置 Config string `json:"config"` // 广告源配置JSON字符串
// API配置 // API配置
APIEndpoint string `bson:"apiEndpoint" json:"apiEndpoint"` // API端点 APIEndpoint string `json:"apiEndpoint"` // API端点
APIVersion string `bson:"apiVersion" json:"apiVersion"` // API版本 APIVersion string `json:"apiVersion"` // API版本
AuthType string `bson:"authType" json:"authType"` // 认证类型api_key、oauth、basic AuthType string `json:"authType"` // 认证类型api_key、oauth、basic
AuthConfig map[string]interface{} `bson:"authConfig" json:"authConfig"` // 认证配置 AuthConfig string `json:"authConfig"` // 认证配置JSON字符串
Headers map[string]string `bson:"headers" json:"headers"` // 请求头配置 Headers string `json:"headers"` // 请求头配置JSON字符串
Timeout int `bson:"timeout" json:"timeout"` // 超时时间(毫秒) Timeout int `json:"timeout"` // 超时时间(毫秒)
RetryCount int `bson:"retryCount" json:"retryCount"` // 重试次数 RetryCount int `json:"retryCount"` // 重试次数
// 广告源能力 // 广告源能力
Capabilities *AdSourceCapabilities `bson:"capabilities" json:"capabilities"` // 广告源能力 Capabilities string `json:"capabilities"` // 广告源能力JSON字符串
// 质量指标 // 质量指标
QualityMetrics *AdSourceQualityMetrics `bson:"qualityMetrics" json:"qualityMetrics"` // 质量指标 QualityMetrics string `json:"qualityMetrics"` // 质量指标JSON字符串
// 财务设置 // 财务设置
PaymentTerms *PaymentTerms `bson:"paymentTerms" json:"paymentTerms"` // 支付条款 PaymentTerms string `json:"paymentTerms"` // 支付条款JSON字符串
// 状态信息 // 状态信息
Status string `bson:"status" json:"status"` // 广告源状态active、inactive、maintenance Status string `json:"status"` // 广告源状态active、inactive、maintenance
Health string `bson:"health" json:"health"` // 健康状态healthy、degraded、unhealthy Health string `json:"health"` // 健康状态healthy、degraded、unhealthy
LastCheckAt int64 `bson:"lastCheckAt" json:"lastCheckAt"` // 最后检查时间 LastCheckAt int64 `json:"lastCheckAt"` // 最后检查时间
// 统计信息 // 统计信息
TotalRequests int64 `bson:"totalRequests" json:"totalRequests"` // 总请求数 TotalRequests int64 `json:"totalRequests"` // 总请求数
SuccessfulRequests int64 `bson:"successfulRequests" json:"successfulRequests"` // 成功请求数 SuccessfulRequests int64 `json:"successfulRequests"` // 成功请求数
FailedRequests int64 `bson:"failedRequests" json:"failedRequests"` // 失败请求数 FailedRequests int64 `json:"failedRequests"` // 失败请求数
AverageResponseTime float64 `bson:"averageResponseTime" json:"averageResponseTime"` // 平均响应时间(毫秒) AverageResponseTime float64 `json:"averageResponseTime"` // 平均响应时间(毫秒)
FillRate float64 `bson:"fillRate" json:"fillRate"` // 填充率 FillRate float64 `json:"fillRate"` // 填充率
CTR float64 `bson:"ctr" json:"ctr"` // 点击率 CTR float64 `json:"ctr"` // 点击率
CVR float64 `bson:"cvr" json:"cvr"` // 转化率 CVR float64 `json:"cvr"` // 转化率
// 系统信息 // 系统信息
Priority int `bson:"priority" json:"priority"` // 优先级,数值越高优先级越高 Priority int `json:"priority"` // 优先级,数值越高优先级越高
} }
// AdSourceConfig 广告源配置 // AdSourceConfig 广告源配置
type AdSourceConfig struct { type AdSourceConfig struct {
// 基础配置 // 基础配置
SupportedFormats []string `bson:"supportedFormats" json:"supportedFormats"` // 支持的广告格式 SupportedFormats []string `json:"supportedFormats"` // 支持的广告格式
SupportedSizes []string `bson:"supportedSizes" json:"supportedSizes"` // 支持的尺寸 SupportedSizes []string `json:"supportedSizes"` // 支持的尺寸
SupportedDevices []string `bson:"supportedDevices" json:"supportedDevices"` // 支持的设备类型 SupportedDevices []string `json:"supportedDevices"` // 支持的设备类型
SupportedOS []string `bson:"supportedOS" json:"supportedOS"` // 支持的操作系统 SupportedOS []string `json:"supportedOS"` // 支持的操作系统
SupportedCountries []string `bson:"supportedCountries" json:"supportedCountries"` // 支持的国家/地区 SupportedCountries []string `json:"supportedCountries"` // 支持的国家/地区
// 竞价配置 // 竞价配置
BiddingType string `bson:"biddingType" json:"biddingType"` // 竞价类型cpm、cpc、cpa、rtb BiddingType string `json:"biddingType"` // 竞价类型cpm、cpc、cpa、rtb
MinBidAmount int64 `bson:"minBidAmount" json:"minBidAmount"` // 最小出价(分) MinBidAmount int64 `json:"minBidAmount"` // 最小出价(分)
MaxBidAmount int64 `bson:"maxBidAmount" json:"maxBidAmount"` // 最大出价(分) MaxBidAmount int64 `json:"maxBidAmount"` // 最大出价(分)
BidIncrement int64 `bson:"bidIncrement" json:"bidIncrement"` // 出价增量(分) BidIncrement int64 `json:"bidIncrement"` // 出价增量(分)
DefaultBidAmount int64 `bson:"defaultBidAmount" json:"defaultBidAmount"` // 默认出价(分) DefaultBidAmount int64 `json:"defaultBidAmount"` // 默认出价(分)
AutoOptimization bool `bson:"autoOptimization" json:"autoOptimization"` // 是否自动优化 AutoOptimization bool `json:"autoOptimization"` // 是否自动优化
// 定向配置 // 定向配置
TargetingSupport *TargetingSupport `bson:"targetingSupport" json:"targetingSupport"` // 定向支持 TargetingSupport *TargetingSupport `json:"targetingSupport"` // 定向支持
// 其他配置 // 其他配置
MaxAdsPerRequest int `bson:"maxAdsPerRequest" json:"maxAdsPerRequest"` // 单次请求最大广告数量 MaxAdsPerRequest int `json:"maxAdsPerRequest"` // 单次请求最大广告数量
BrandSafety bool `bson:"brandSafety" json:"brandSafety"` // 品牌安全 BrandSafety bool `json:"brandSafety"` // 品牌安全
Viewability bool `bson:"viewability" json:"viewability"` // 可见性支持 Viewability bool `json:"viewability"` // 可见性支持
} }
// TargetingSupport 定向支持 // TargetingSupport 定向支持
type TargetingSupport struct { type TargetingSupport struct {
GeoTargeting bool `bson:"geoTargeting" json:"geoTargeting"` // 地理定向 GeoTargeting bool `json:"geoTargeting"` // 地理定向
DemographicTargeting bool `bson:"demographicTargeting" json:"demographicTargeting"` // 人口统计定向 DemographicTargeting bool `json:"demographicTargeting"` // 人口统计定向
BehavioralTargeting bool `bson:"behavioralTargeting" json:"behavioralTargeting"` // 行为定向 BehavioralTargeting bool `json:"behavioralTargeting"` // 行为定向
ContextualTargeting bool `bson:"contextualTargeting" json:"contextualTargeting"` // 上下文定向 ContextualTargeting bool `json:"contextualTargeting"` // 上下文定向
DeviceTargeting bool `bson:"deviceTargeting" json:"deviceTargeting"` // 设备定向 DeviceTargeting bool `json:"deviceTargeting"` // 设备定向
TimeTargeting bool `bson:"timeTargeting" json:"timeTargeting"` // 时间定向 TimeTargeting bool `json:"timeTargeting"` // 时间定向
Retargeting bool `bson:"retargeting" json:"retargeting"` // 重定向 Retargeting bool `json:"retargeting"` // 重定向
CookieTargeting bool `bson:"cookieTargeting" json:"cookieTargeting"` // Cookie定向 CookieTargeting bool `json:"cookieTargeting"` // Cookie定向
} }
// AdSourceCapabilities 广告源能力 // AdSourceCapabilities 广告源能力
type AdSourceCapabilities struct { type AdSourceCapabilities struct {
// 广告格式 // 广告格式
SupportedFormats []AdFormat `bson:"supportedFormats" json:"supportedFormats"` // 支持的广告格式 SupportedFormats []AdFormat `json:"supportedFormats"` // 支持的广告格式
// 功能特性 // 功能特性
RealTimeBidding bool `bson:"realTimeBidding" json:"realTimeBidding"` // 实时竞价 RealTimeBidding bool `json:"realTimeBidding"` // 实时竞价
HeaderBidding bool `bson:"headerBidding" json:"headerBidding"` // 标题竞价 HeaderBidding bool `json:"headerBidding"` // 标题竞价
ProgrammaticDirect bool `bson:"programmaticDirect" json:"programmaticDirect"` // 程序化直购 ProgrammaticDirect bool `json:"programmaticDirect"` // 程序化直购
PrivateMarketplace bool `bson:"privateMarketplace" json:"privateMarketplace"` // 私有交易市场 PrivateMarketplace bool `json:"privateMarketplace"` // 私有交易市场
// 质量控制 // 质量控制
FraudDetection bool `bson:"fraudDetection" json:"fraudDetection"` // 反欺诈检测 FraudDetection bool `json:"fraudDetection"` // 反欺诈检测
BrandSafety bool `bson:"brandSafety" json:"brandSafety"` // 品牌安全 BrandSafety bool `json:"brandSafety"` // 品牌安全
Viewability bool `bson:"viewability" json:"viewability"` // 可见度验证 Viewability bool `json:"viewability"` // 可见度验证
CreativeApproval bool `bson:"creativeApproval" json:"creativeApproval"` // 创意审核 CreativeApproval bool `json:"creativeApproval"` // 创意审核
// 数据能力 // 数据能力
AudienceTargeting bool `bson:"audienceTargeting" json:"audienceTargeting"` // 受众定向 AudienceTargeting bool `json:"audienceTargeting"` // 受众定向
ContextualTargeting bool `bson:"contextualTargeting" json:"contextualTargeting"` // 上下文定向 ContextualTargeting bool `json:"contextualTargeting"` // 上下文定向
CrossDeviceTargeting bool `bson:"crossDeviceTargeting" json:"crossDeviceTargeting"` // 跨设备定向 CrossDeviceTargeting bool `json:"crossDeviceTargeting"` // 跨设备定向
} }
// AdFormat 广告格式 // AdFormat 广告格式
type AdFormat struct { type AdFormat struct {
Type string `bson:"type" json:"type"` // 格式类型banner、video、native、interstitial等 Type string `json:"type"` // 格式类型banner、video、native、interstitial等
Name string `bson:"name" json:"name"` // 格式名称 Name string `json:"name"` // 格式名称
Width int `bson:"width" json:"width"` // 宽度 Width int `json:"width"` // 宽度
Height int `bson:"height" json:"height"` // 高度 Height int `json:"height"` // 高度
MimeType string `bson:"mimeType" json:"mimeType"` // MIME类型 MimeType string `json:"mimeType"` // MIME类型
} }
// AdSourceQualityMetrics 广告源质量指标 // AdSourceQualityMetrics 广告源质量指标
type AdSourceQualityMetrics struct { type AdSourceQualityMetrics struct {
// 性能指标 // 性能指标
AverageResponseTime float64 `bson:"averageResponseTime" json:"averageResponseTime"` // 平均响应时间(毫秒) AverageResponseTime float64 `json:"averageResponseTime"` // 平均响应时间(毫秒)
SuccessRate float64 `bson:"successRate" json:"successRate"` // 成功率 SuccessRate float64 `json:"successRate"` // 成功率
ErrorRate float64 `bson:"errorRate" json:"errorRate"` // 错误率 ErrorRate float64 `json:"errorRate"` // 错误率
Uptime float64 `bson:"uptime" json:"uptime"` // 可用性(百分比) Uptime float64 `json:"uptime"` // 可用性(百分比)
// 广告质量 // 广告质量
CTR float64 `bson:"ctr" json:"ctr"` // 点击率 CTR float64 `json:"ctr"` // 点击率
CVR float64 `bson:"cvr" json:"cvr"` // 转化率 CVR float64 `json:"cvr"` // 转化率
FillRate float64 `bson:"fillRate" json:"fillRate"` // 填充率 FillRate float64 `json:"fillRate"` // 填充率
ViewabilityRate float64 `bson:"viewabilityRate" json:"viewabilityRate"` // 可见率 ViewabilityRate float64 `json:"viewabilityRate"` // 可见率
BrandSafetyScore float64 `bson:"brandSafetyScore" json:"brandSafetyScore"` // 品牌安全评分 BrandSafetyScore float64 `json:"brandSafetyScore"` // 品牌安全评分
// 财务指标 // 财务指标
eCPM int64 `bson:"ecpm" json:"ecpm"` // 有效千次展示成本(分) Ecpm int64 `json:"ecpm"` // 有效千次展示成本(分)
eCPC int64 `bson:"ecpc" json:"ecpc"` // 有效点击成本(分) Ecpc int64 `json:"ecpc"` // 有效点击成本(分)
RevenuePerRequest int64 `bson:"revenuePerRequest" json:"revenuePerRequest"` // 每请求收入(分) RevenuePerRequest int64 `json:"revenuePerRequest"` // 每请求收入(分)
// 时间指标 // 时间指标
LastUpdated int64 `bson:"lastUpdated" json:"lastUpdated"` // 最后更新时间 LastUpdated int64 `json:"lastUpdated"` // 最后更新时间
MetricsUpdateWindow int `bson:"metricsUpdateWindow" json:"metricsUpdateWindow"` // 指标更新窗口(分钟) MetricsUpdateWindow int `json:"metricsUpdateWindow"` // 指标更新窗口(分钟)
} }
// PaymentTerms 支付条款 // PaymentTerms 支付条款
type PaymentTerms struct { type PaymentTerms struct {
BillingModel string `bson:"billingModel" json:"billingModel"` // 计费模式cpm、cpc、cpa、rev_share BillingModel string `json:"billingModel"` // 计费模式cpm、cpc、cpa、rev_share
PaymentTerms string `bson:"paymentTerms" json:"paymentTerms"` // 支付条款net_30、net_60、net_90 PaymentTerms string `json:"paymentTerms"` // 支付条款net_30、net_60、net_90
RevShareRate float64 `bson:"revShareRate" json:"revShareRate"` // 收入分成比例(0-1) RevShareRate float64 `json:"revShareRate"` // 收入分成比例(0-1)
MinPayment int64 `bson:"minPayment" json:"minPayment"` // 最小支付金额(分) MinPayment int64 `json:"minPayment"` // 最小支付金额(分)
Currency string `bson:"currency" json:"currency"` // 货币单位 Currency string `json:"currency"` // 货币单位
TaxInclusive bool `bson:"taxInclusive" json:"taxInclusive"` // 是否含税 TaxInclusive bool `json:"taxInclusive"` // 是否含税
EarlyPaymentDiscount float64 `bson:"earlyPaymentDiscount" json:"earlyPaymentDiscount"` // 提前付款折扣 EarlyPaymentDiscount float64 `json:"earlyPaymentDiscount"` // 提前付款折扣
} }

44
model/entity/ad_type.go Normal file
View File

@@ -0,0 +1,44 @@
package entity
import (
"gitee.com/red-future---jilin-g/common/do"
)
const AdTypeCollection = "ad_type"
// AdType 广告类型实体
type AdType struct {
do.MongoBaseDO `bson:",inline"`
// 广告类型信息
Name string `bson:"name" json:"name"` // 广告类型名称
Code string `bson:"code" json:"code"` // 广告类型编码
Description string `bson:"description" json:"description"` // 广告类型描述
Icon string `bson:"icon" json:"icon"` // 广告类型图标
// 类型配置
Category string `bson:"category" json:"category"` // 分类display, video, native, interstitial
Platforms []string `bson:"platforms" json:"platforms"` // 支持的平台
Formats []string `bson:"formats" json:"formats"` // 支持格式
Dimensions []string `bson:"dimensions" json:"dimensions"` // 尺寸规格
// 技术要求
MaxFileSize int64 `bson:"maxFileSize" json:"maxFileSize"` // 最大文件大小(bytes)
MaxDuration int64 `bson:"maxDuration" json:"maxDuration"` // 最大时长(秒)
SupportedMimeTypes []string `bson:"supportedMimeTypes" json:"supportedMimeTypes"` // 支持的MIME类型
// 业务配置
BidType string `bson:"bidType" json:"bidType"` // 竞价类型CPM, CPC, CPA
MinBidPrice int64 `bson:"minBidPrice" json:"minBidPrice"` // 最低出价(分)
MaxBidPrice int64 `bson:"maxBidPrice" json:"maxBidPrice"` // 最高出价(分)
// 状态信息
Status string `bson:"status" json:"status"` // 状态active, inactive
SortOrder int `bson:"sortOrder" json:"sortOrder"` // 排序顺序
// 统计信息
DailyImpression int64 `bson:"dailyImpression" json:"dailyImpression"` // 日展示量
DailyClick int64 `bson:"dailyClick" json:"dailyClick"` // 日点击量
Remark string `bson:"remark" json:"remark"` // 备注
}

View File

@@ -0,0 +1,51 @@
package entity
import (
"time"
)
// Application 应用实体
type Application struct {
Id int64 `json:"id"` // 主键ID
CreatedAt time.Time `json:"createdAt"` // 创建时间
UpdatedAt time.Time `json:"updatedAt"` // 更新时间
Creator string `json:"creator"` // 创建者
Updater string `json:"updater"` // 更新者
TenantId int64 `json:"tenantId"` // 租户ID
IsDeleted bool `json:"isDeleted"` // 是否删除
// 应用信息
Name string `json:"name"` // 应用名称
Code string `json:"code"` // 应用编码
Description string `json:"description"` // 应用描述
Icon string `json:"icon"` // 应用图标
// 平台信息
Platform string `json:"platform"` // 平台web, h5, android, ios
PackageName string `json:"packageName"` // 包名(Android)/Bundle ID(iOS)
AppStoreURL string `json:"appStoreUrl"` // 应用商店链接
// 配置信息
Categories []string `json:"categories"` // 应用分类
Tags []string `json:"tags"` // 应用标签
Config string `json:"config"` // 应用配置JSON格式
AdTypes []string `json:"adTypes"` // 支持的广告类型
// 状态信息
Status string `json:"status"` // 状态active, inactive, audit
AuditStatus string `json:"auditStatus"` // 审核状态
AuditReason string `json:"auditReason"` // 审核原因
// 统计信息
DailyRequests int64 `json:"dailyRequests"` // 日请求量
MonthlyRequests int64 `json:"monthlyRequests"` // 月请求量
// API密钥
AppKey string `json:"appKey"` // 应用密钥
AppSecret string `json:"appSecret"` // 应用密钥
// 回调信息
CallbackURL string `json:"callbackUrl"` // 回调URL
Remark string `json:"remark"` // 备注
}

View File

@@ -0,0 +1,26 @@
package entity
import (
"time"
)
// StatReport 统计报表实体
type StatReport struct {
Id int64 `json:"id"` // 主键ID
CreatedAt time.Time `json:"createdAt"` // 创建时间
UpdatedAt time.Time `json:"updatedAt"` // 更新时间
Creator string `json:"creator"` // 创建者
Updater string `json:"updater"` // 更新者
TenantId int64 `json:"tenantId"` // 租户ID
IsDeleted bool `json:"isDeleted"` // 是否删除
// 报表基本信息
AppID int64 `json:"appId"` // 应用ID
ReportType string `json:"reportType"` // 报表类型daily, weekly, monthly, quarterly, yearly
ReportDate time.Time `json:"reportDate"` // 报表日期
GeneratedAt time.Time `json:"generatedAt"` // 生成时间
ReportData string `json:"reportData"` // 报表数据JSON格式
// 状态信息
Status string `json:"status"` // 状态generated, processing, completed
}

View File

@@ -14,6 +14,7 @@ type Strategy struct {
MaxConversion float64 `json:"max_conversion" orm:"max_conversion"` // 最高转化率 MaxConversion float64 `json:"max_conversion" orm:"max_conversion"` // 最高转化率
SourceWeights string `json:"source_weights" orm:"source_weights"` // 广告源权重 (JSON格式) SourceWeights string `json:"source_weights" orm:"source_weights"` // 广告源权重 (JSON格式)
MaxAdsPerReq int `json:"max_ads_per_req" orm:"max_ads_per_req"` // 每次请求最大广告数 MaxAdsPerReq int `json:"max_ads_per_req" orm:"max_ads_per_req"` // 每次请求最大广告数
MaxReqPerHour int `json:"max_req_per_hour" orm:"max_req_per_hour"` // 每小时最大请求次数
Priority int `json:"priority" orm:"priority"` // 优先级 Priority int `json:"priority" orm:"priority"` // 优先级
Status string `json:"status" orm:"status"` // 状态: active, inactive Status string `json:"status" orm:"status"` // 状态: active, inactive
CreatedAt *gtime.Time `json:"created_at" orm:"created_at"` // 创建时间 CreatedAt *gtime.Time `json:"created_at" orm:"created_at"` // 创建时间

39
model/entity/tenant.go Normal file
View File

@@ -0,0 +1,39 @@
package entity
import (
"gitee.com/red-future---jilin-g/common/do"
)
const TenantCollection = "tenant"
// Tenant 租户实体
type Tenant struct {
do.MongoBaseDO `bson:",inline"`
// 租户信息
Name string `bson:"name" json:"name"` // 租户名称
Code string `bson:"code" json:"code"` // 租户编码
Description string `bson:"description" json:"description"` // 租户描述
Logo string `bson:"logo" json:"logo"` // 租户Logo
Domain string `bson:"domain" json:"domain"` // 租户域名
// 联系信息
ContactName string `bson:"contactName" json:"contactName"` // 联系人姓名
ContactPhone string `bson:"contactPhone" json:"contactPhone"` // 联系电话
ContactEmail string `bson:"contactEmail" json:"contactEmail"` // 联系邮箱
// 状态信息
Status string `bson:"status" json:"status"` // 状态active, inactive, suspended
PackageType string `bson:"packageType" json:"packageType"` // 套餐类型basic, standard, premium
ExpireTime int64 `bson:"expireTime" json:"expireTime"` // 套餐到期时间
// 限制信息
MaxApps int64 `bson:"maxApps" json:"maxApps"` // 最大应用数
MaxUsers int64 `bson:"maxUsers" json:"maxUsers"` // 最大用户数
MaxRequestPerDay int64 `bson:"maxRequestPerDay" json:"maxRequestPerDay"` // 每日最大请求数
// 配置信息
Config map[string]interface{} `bson:"config" json:"config"` // 租户配置
Remark string `bson:"remark" json:"remark"` // 备注
}

View File

@@ -1,9 +1,9 @@
package service package service
import ( import (
"cidService/dao" "cidservice/dao"
"cidService/model/dto" "cidservice/model/dto"
"cidService/model/entity" "cidservice/model/entity"
"context" "context"
"time" "time"

View File

@@ -1,11 +1,10 @@
package service package service
import ( import (
"cidService/dao" "cidservice/dao"
"cidService/model/dto" "cidservice/model/dto"
"cidService/model/entity" "cidservice/model/entity"
"context" "context"
"github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/errors/gerror"
) )

View File

@@ -6,9 +6,9 @@ import (
"sort" "sort"
"time" "time"
"cidService/dao" "cidservice/dao"
"cidService/model/dto" "cidservice/model/dto"
"cidService/model/entity" "cidservice/model/entity"
) )
var AdStatistics = new(adStatistics) var AdStatistics = new(adStatistics)

View File

@@ -1,9 +1,9 @@
package service package service
import ( import (
"cidService/dao" "cidservice/dao"
"cidService/model/dto" "cidservice/model/dto"
"cidService/model/entity" "cidservice/model/entity"
"context" "context"
"time" "time"

View File

@@ -7,9 +7,9 @@ import (
"github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/gconv"
"cidService/dao" "cidservice/dao"
"cidService/model/dto" "cidservice/model/dto"
"cidService/model/entity" "cidservice/model/entity"
) )
var Advertiser = new(advertiser) var Advertiser = new(advertiser)

View File

@@ -0,0 +1,246 @@
package service
import (
"context"
"crypto/rand"
"encoding/hex"
"cidservice/dao"
"cidservice/model/dto"
"cidservice/model/entity"
"github.com/gogf/gf/v2/errors/gerror"
)
var (
Application = applicationService{}
)
type applicationService struct{}
// CreateApplication 创建应用
func (s *applicationService) CreateApplication(ctx context.Context, req *dto.CreateApplicationReq) (id int64, err error) {
// 检查应用名称是否已存在
existingApp, err := dao.Application.GetByName(ctx, req.Name)
if err != nil {
return 0, err
}
if existingApp != nil {
return 0, gerror.New("应用名称已存在")
}
// 生成API密钥
appKey, appSecret, err := s.generateAPIKeys()
if err != nil {
return 0, err
}
application := &entity.Application{
TenantId: req.TenantID,
Name: req.Name,
Code: req.Code,
Description: req.Description,
Platform: req.Platform,
PackageName: req.PackageName,
AppStoreURL: req.AppStoreURL,
Categories: req.Categories,
Tags: req.Tags,
AdTypes: req.AdTypes,
Status: "active",
AppKey: appKey,
AppSecret: appSecret,
CallbackURL: req.CallbackURL,
}
return dao.Application.Create(ctx, application)
}
// UpdateApplication 更新应用
func (s *applicationService) UpdateApplication(ctx context.Context, id int64, req *dto.UpdateApplicationReq) (affected int64, err error) {
// 检查应用是否存在
existingApp, err := dao.Application.GetByID(ctx, id)
if err != nil {
return 0, err
}
if existingApp == nil {
return 0, gerror.New("应用不存在")
}
// 如果更新名称,检查是否与其他应用冲突
if req.Name != "" && req.Name != existingApp.Name {
conflictApp, err := dao.Application.GetByName(ctx, req.Name)
if err != nil {
return 0, err
}
if conflictApp != nil && conflictApp.Id != id {
return 0, gerror.New("应用名称已存在")
}
}
// 构建更新数据
updateData := &entity.Application{}
if req.Name != "" {
updateData.Name = req.Name
}
if req.Description != "" {
updateData.Description = req.Description
}
if req.Platform != "" {
updateData.Platform = req.Platform
}
if req.PackageName != "" {
updateData.PackageName = req.PackageName
}
if req.AppStoreURL != "" {
updateData.AppStoreURL = req.AppStoreURL
}
if req.CallbackURL != "" {
updateData.CallbackURL = req.CallbackURL
}
if len(req.Categories) > 0 {
updateData.Categories = req.Categories
}
if len(req.Tags) > 0 {
updateData.Tags = req.Tags
}
if len(req.AdTypes) > 0 {
updateData.AdTypes = req.AdTypes
}
// 使用Update方法更新应用
err = dao.Application.Update(ctx, updateData)
if err != nil {
return 0, err
}
return 1, nil
}
// GetApplicationsByTenant 获取租户下的应用列表
func (s *applicationService) GetApplicationsByTenant(ctx context.Context, tenantID int64, platform, status string, page, size int) (list []*entity.Application, total int64, err error) {
// 构建过滤条件
filter := make(map[string]interface{})
filter["tenant_id"] = tenantID
if platform != "" {
filter["platform"] = platform
}
if status != "" {
filter["status"] = status
}
// 调用DAO的GetByTenantID方法获取租户下的所有应用
apps, err := dao.Application.GetByTenantID(ctx, tenantID)
if err != nil {
return nil, 0, err
}
// 应用额外的过滤条件
var filteredApps []*entity.Application
for _, app := range apps {
if platform != "" && app.Platform != platform {
continue
}
if status != "" && app.Status != status {
continue
}
filteredApps = append(filteredApps, app)
}
// 实现简单的分页
startIndex := (page - 1) * size
endIndex := startIndex + size
if startIndex >= len(filteredApps) {
return []*entity.Application{}, int64(len(filteredApps)), nil
}
if endIndex > len(filteredApps) {
endIndex = len(filteredApps)
}
return filteredApps[startIndex:endIndex], int64(len(filteredApps)), nil
}
// GetApplicationByKey 根据API密钥获取应用
func (s *applicationService) GetApplicationByKey(ctx context.Context, appKey string) (application *entity.Application, err error) {
return dao.Application.GetByAPIKey(ctx, appKey)
}
// GetApplicationByID 根据ID获取应用
func (s *applicationService) GetApplicationByID(ctx context.Context, id int64) (application *entity.Application, err error) {
return dao.Application.GetByID(ctx, id)
}
// DeleteApplication 删除应用
func (s *applicationService) DeleteApplication(ctx context.Context, id int64) (affected int64, err error) {
err = dao.Application.Delete(ctx, id)
if err != nil {
return 0, err
}
return 1, nil
}
// ValidateApplication 验证应用权限
func (s *applicationService) ValidateApplication(ctx context.Context, appKey, appSecret string) (application *entity.Application, err error) {
app, err := dao.Application.GetByAPIKey(ctx, appKey)
if err != nil {
return nil, err
}
if app == nil {
return nil, gerror.New("应用不存在")
}
if app.Status != "active" {
return nil, gerror.New("应用状态异常")
}
if app.AppSecret != appSecret {
return nil, gerror.New("密钥验证失败")
}
return app, nil
}
// generateAPIKeys 生成API密钥
func (s *applicationService) generateAPIKeys() (appKey, appSecret string, err error) {
// 生成32位随机字符串作为AppKey
keyBytes := make([]byte, 16)
if _, err := rand.Read(keyBytes); err != nil {
return "", "", err
}
appKey = hex.EncodeToString(keyBytes)
// 生成64位随机字符串作为AppSecret
secretBytes := make([]byte, 32)
if _, err := rand.Read(secretBytes); err != nil {
return "", "", err
}
appSecret = hex.EncodeToString(secretBytes)
return appKey, appSecret, nil
}
// ResetAPIKeys 重置API密钥
func (s *applicationService) ResetAPIKeys(ctx context.Context, id int64) (appKey, appSecret string, err error) {
// 检查应用是否存在
existingApp, err := dao.Application.GetByID(ctx, id)
if err != nil {
return "", "", err
}
if existingApp == nil {
return "", "", gerror.New("应用不存在")
}
// 生成新的API密钥
appKey, appSecret, err = s.generateAPIKeys()
if err != nil {
return "", "", err
}
// 更新应用密钥
updateData := &entity.Application{
AppKey: appKey,
AppSecret: appSecret,
}
err = dao.Application.Update(ctx, updateData)
if err != nil {
return "", "", err
}
return appKey, appSecret, nil
}

View File

@@ -1,10 +1,10 @@
package service package service
import ( import (
"cidService/dao" "cidservice/dao"
"cidService/model/dto" "cidservice/model/dto"
"cidService/model/entity" "cidservice/model/entity"
"cidService/model/types" "cidservice/model/types"
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
@@ -85,6 +85,15 @@ func (s *cidService) GenerateCID(ctx context.Context, req *dto.GenerateCIDReq) (
return nil, gerror.Wrap(err, "获取租户信息失败") return nil, gerror.Wrap(err, "获取租户信息失败")
} }
// 检查租户请求次数限制
allowed, err := RateLimit.CheckTenantRequestLimit(ctx, tenant.Id, nil)
if err != nil {
return nil, gerror.Wrap(err, "检查租户请求限制失败")
}
if !allowed {
return nil, gerror.New("租户请求次数已超过限制,请稍后再试")
}
// 获取匹配策略 // 获取匹配策略
strategy, err := s.getMatchingStrategy(ctx, tenant.Level) strategy, err := s.getMatchingStrategy(ctx, tenant.Level)
if err != nil { if err != nil {

View File

@@ -0,0 +1,137 @@
package service
import (
"context"
"fmt"
"time"
"cidservice/consts"
"github.com/gogf/gf/v2/frame/g"
)
var (
RateLimit = rateLimitService{}
)
type rateLimitService struct{}
// TenantRateLimitConfig 租户限流配置
type TenantRateLimitConfig struct {
TenantID int64 // 租户ID
RequestsPerSecond float64 // 每秒请求数
Burst int // 突发请求数
Window time.Duration // 时间窗口
}
// CheckTenantRequestLimit 检查租户请求次数限制
func (s *rateLimitService) CheckTenantRequestLimit(ctx context.Context, tenantID int64, config *TenantRateLimitConfig) (bool, error) {
if config == nil {
// 使用默认配置
config = s.getDefaultTenantRateLimitConfig(tenantID)
}
// 构建Redis键 - 使用当前小时的键,确保按小时计数
now := time.Now()
hourKey := fmt.Sprintf("%s%d:%d", consts.AdRequestLimitKeyPrefix, tenantID, now.Hour())
// 获取当前计数
currentCountVar, err := g.Redis().Get(ctx, hourKey)
if err != nil && err.Error() != "redis: nil" {
return false, err
}
currentCount := currentCountVar.Int64()
// 如果是第一次请求,设置计数和过期时间(到下一个小时)
if currentCount == 0 {
// 设置过期时间为到下一个小时的剩余时间
nextHour := now.Truncate(time.Hour).Add(time.Hour)
ttl := nextHour.Sub(now)
// 使用SetEX一次性设置值和过期时间
err = g.Redis().SetEX(ctx, hourKey, 1, int64(ttl.Seconds()))
if err != nil {
return false, err
}
return true, nil
}
// 检查是否超过限制
maxRequests := int64(config.RequestsPerSecond * config.Window.Seconds())
if currentCount >= maxRequests {
return false, nil
}
// 增加计数
_, err = g.Redis().Incr(ctx, hourKey)
if err != nil {
return false, err
}
return true, nil
}
// GetDefaultTenantRateLimitConfig 获取默认的租户限流配置
func (s *rateLimitService) getDefaultTenantRateLimitConfig(tenantID int64) *TenantRateLimitConfig {
// 从配置文件中读取限流参数
ctx := context.Background()
// 检查是否启用租户限流
enabled := g.Cfg().MustGet(ctx, "tenantRateLimit.enabled", false).Bool()
if !enabled {
// 如果未启用,返回一个很大的限制值,相当于不限制
return &TenantRateLimitConfig{
TenantID: tenantID,
RequestsPerSecond: 10000, // 每秒10000个请求相当于不限制
Burst: 20000, // 突发20000个请求
Window: time.Hour,
}
}
// 从配置文件中获取限流参数
requestsPerHour := g.Cfg().MustGet(ctx, "tenantRateLimit.requestsPerHour", 3600).Int64()
windowSeconds := g.Cfg().MustGet(ctx, "tenantRateLimit.window", 3600).Int64()
burst := g.Cfg().MustGet(ctx, "tenantRateLimit.burst", 100).Int()
// 转换为每秒请求数
requestsPerSecond := float64(requestsPerHour) / float64(windowSeconds)
return &TenantRateLimitConfig{
TenantID: tenantID,
RequestsPerSecond: requestsPerSecond,
Burst: burst,
Window: time.Duration(windowSeconds) * time.Second,
}
}
// SetTenantRateLimitConfig 设置租户限流配置
func (s *rateLimitService) SetTenantRateLimitConfig(ctx context.Context, config *TenantRateLimitConfig) error {
// 注意实际使用的是config.yml中的全局配置此方法仅用于兼容旧API
// 实际限流参数请修改config.yml中的tenantRateLimit部分
return nil
}
// GetTenantCurrentUsage 获取租户当前请求使用情况
func (s *rateLimitService) GetTenantCurrentUsage(ctx context.Context, tenantID int64, config *TenantRateLimitConfig) (current int64, max int64, err error) {
if config == nil {
config = s.getDefaultTenantRateLimitConfig(tenantID)
}
// 构建当前小时的Redis键
now := time.Now()
hourKey := fmt.Sprintf("%s%d:%d", consts.AdRequestLimitKeyPrefix, tenantID, now.Hour())
// 获取当前计数
currentVar, err := g.Redis().Get(ctx, hourKey)
if err != nil && err.Error() == "redis: nil" {
current = 0
err = nil
} else if err != nil {
return 0, 0, err
} else {
current = currentVar.Int64()
}
max = int64(config.RequestsPerSecond * config.Window.Seconds())
return current, max, nil
}

View File

@@ -6,9 +6,9 @@ import (
"github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/errors/gerror"
"cidService/dao" "cidservice/dao"
"cidService/model/dto" "cidservice/model/dto"
"cidService/model/entity" "cidservice/model/entity"
) )
// Report Service 单例 // Report Service 单例

View File

@@ -0,0 +1,514 @@
package service
import (
"context"
"fmt"
"time"
"cidservice/dao"
"cidservice/model/dto"
"cidservice/model/entity"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
type StatReportService struct{}
var StatReport = &StatReportService{}
// 生成日报表
func (s *StatReportService) GenerateDailyReport(ctx context.Context, req *dto.ReportGenerateReq) (*dto.ReportGenerateResp, error) {
// 获取统计日期
reportDate := time.Now()
if req.Date != "" {
parsedDate, err := time.Parse("2006-01-02", req.Date)
if err == nil {
reportDate = parsedDate
}
}
// 生成日报表数据
reportData, err := s.generateReportData(ctx, req.TenantID, req.AppID, "daily", reportDate)
if err != nil {
return nil, err
}
// 保存报表
report := &entity.StatReport{
TenantId: req.TenantID,
AppID: req.AppID,
ReportType: "daily",
ReportDate: reportDate,
ReportData: gconv.String(reportData),
GeneratedAt: time.Now(),
Status: "completed",
}
_, err = dao.StatReport.Create(ctx, report)
if err != nil {
return nil, err
}
return &dto.ReportGenerateResp{
ReportID: report.Id,
ReportType: "daily",
ReportDate: reportDate.Format("2006-01-02"),
Data: reportData,
}, nil
}
// 生成月报表
func (s *StatReportService) GenerateMonthlyReport(ctx context.Context, req *dto.ReportGenerateReq) (*dto.ReportGenerateResp, error) {
reportDate := time.Now()
if req.Date != "" {
parsedDate, err := time.Parse("2006-01", req.Date)
if err == nil {
reportDate = parsedDate
}
}
reportData, err := s.generateReportData(ctx, req.TenantID, req.AppID, "monthly", reportDate)
if err != nil {
return nil, err
}
report := &entity.StatReport{
TenantId: req.TenantID,
AppID: req.AppID,
ReportType: "monthly",
ReportDate: reportDate,
ReportData: gconv.String(reportData),
GeneratedAt: time.Now(),
Status: "completed",
}
_, err = dao.StatReport.Create(ctx, report)
if err != nil {
return nil, err
}
return &dto.ReportGenerateResp{
ReportID: report.Id,
ReportType: "monthly",
ReportDate: reportDate.Format("2006-01"),
Data: reportData,
}, nil
}
// 生成季度报表
func (s *StatReportService) GenerateQuarterlyReport(ctx context.Context, req *dto.ReportGenerateReq) (*dto.ReportGenerateResp, error) {
reportDate := time.Now()
if req.Date != "" {
parsedDate, err := time.Parse("2006-Q1", req.Date)
if err == nil {
reportDate = parsedDate
}
}
reportData, err := s.generateReportData(ctx, req.TenantID, req.AppID, "quarterly", reportDate)
if err != nil {
return nil, err
}
report := &entity.StatReport{
TenantId: req.TenantID,
AppID: req.AppID,
ReportType: "quarterly",
ReportDate: reportDate,
ReportData: gconv.String(reportData),
GeneratedAt: time.Now(),
Status: "completed",
}
_, err = dao.StatReport.Create(ctx, report)
if err != nil {
return nil, err
}
return &dto.ReportGenerateResp{
ReportID: report.Id,
ReportType: "quarterly",
ReportDate: reportDate.Format("2006-Q1"),
Data: reportData,
}, nil
}
// 生成年报表
func (s *StatReportService) GenerateYearlyReport(ctx context.Context, req *dto.ReportGenerateReq) (*dto.ReportGenerateResp, error) {
reportDate := time.Now()
if req.Date != "" {
parsedDate, err := time.Parse("2006", req.Date)
if err == nil {
reportDate = parsedDate
}
}
reportData, err := s.generateReportData(ctx, req.TenantID, req.AppID, "yearly", reportDate)
if err != nil {
return nil, err
}
report := &entity.StatReport{
TenantId: req.TenantID,
AppID: req.AppID,
ReportType: "yearly",
ReportDate: reportDate,
ReportData: gconv.String(reportData),
GeneratedAt: time.Now(),
Status: "completed",
}
_, err = dao.StatReport.Create(ctx, report)
if err != nil {
return nil, err
}
return &dto.ReportGenerateResp{
ReportID: report.Id,
ReportType: "yearly",
ReportDate: reportDate.Format("2006"),
Data: reportData,
}, nil
}
// 生成报表数据
func (s *StatReportService) generateReportData(ctx context.Context, tenantID, appID int64, reportType string, reportDate time.Time) (map[string]interface{}, error) {
// 构建查询条件
where := g.Map{"tenant_id": tenantID}
if appID > 0 {
where["app_id"] = appID
}
// 根据报表类型确定时间范围
startTime, endTime := s.getReportTimeRange(reportType, reportDate)
where["created_at between ? and ?"] = g.Slice{startTime, endTime}
// 查询基础统计数据
var stats []map[string]interface{}
err := g.DB().Model("ad_statistics").Where(where).Scan(&stats)
if err != nil {
return nil, err
}
// 计算环比数据
yoyData, err := s.calculateYearOverYear(ctx, tenantID, appID, reportType, reportDate)
if err != nil {
return nil, err
}
// 计算同比数据
momData, err := s.calculateMonthOverMonth(ctx, tenantID, appID, reportType, reportDate)
if err != nil {
return nil, err
}
// 按广告类型分组统计
adTypeStats, err := s.groupByAdType(stats)
if err != nil {
return nil, err
}
// 按地区分组统计
regionStats := s.groupByRegion(stats)
// 按终端类型分组统计
platformStats := s.groupByPlatform(stats)
return map[string]interface{}{
"basic_stats": map[string]interface{}{
"total_impressions": s.sumField(stats, "impressions"),
"total_clicks": s.sumField(stats, "clicks"),
"total_revenue": s.sumField(stats, "revenue"),
"avg_ctr": s.calculateCTR(stats),
"avg_play_duration": s.avgField(stats, "play_duration"),
},
"ad_type_stats": adTypeStats,
"region_stats": regionStats,
"platform_stats": platformStats,
"year_over_year": yoyData,
"month_over_month": momData,
"time_range": map[string]string{
"start": startTime.Format("2006-01-02 15:04:05"),
"end": endTime.Format("2006-01-02 15:04:05"),
},
}, nil
}
// 获取报表时间范围
func (s *StatReportService) getReportTimeRange(reportType string, reportDate time.Time) (time.Time, time.Time) {
switch reportType {
case "daily":
start := time.Date(reportDate.Year(), reportDate.Month(), reportDate.Day(), 0, 0, 0, 0, time.Local)
end := start.AddDate(0, 0, 1).Add(-time.Second)
return start, end
case "monthly":
start := time.Date(reportDate.Year(), reportDate.Month(), 1, 0, 0, 0, 0, time.Local)
end := start.AddDate(0, 1, 0).Add(-time.Second)
return start, end
case "quarterly":
quarter := (reportDate.Month()-1)/3 + 1
startMonth := time.Month((quarter-1)*3 + 1)
start := time.Date(reportDate.Year(), startMonth, 1, 0, 0, 0, 0, time.Local)
end := start.AddDate(0, 3, 0).Add(-time.Second)
return start, end
case "yearly":
start := time.Date(reportDate.Year(), 1, 1, 0, 0, 0, 0, time.Local)
end := start.AddDate(1, 0, 0).Add(-time.Second)
return start, end
default:
return reportDate, reportDate
}
}
// 计算同比数据
func (s *StatReportService) calculateYearOverYear(ctx context.Context, tenantID, appID int64, reportType string, reportDate time.Time) (map[string]interface{}, error) {
lastYearDate := reportDate.AddDate(-1, 0, 0)
lastYearData, err := s.getComparisonData(ctx, tenantID, appID, reportType, lastYearDate)
if err != nil {
return nil, err
}
return map[string]interface{}{
"last_year": lastYearData,
"growth_rate": s.calculateGrowthRate(lastYearData, s.getCurrentPeriodData(ctx, tenantID, appID, reportType, reportDate)),
}, nil
}
// 计算环比数据
func (s *StatReportService) calculateMonthOverMonth(ctx context.Context, tenantID, appID int64, reportType string, reportDate time.Time) (map[string]interface{}, error) {
var lastPeriodDate time.Time
switch reportType {
case "daily":
lastPeriodDate = reportDate.AddDate(0, 0, -1)
case "monthly":
lastPeriodDate = reportDate.AddDate(0, -1, 0)
case "quarterly":
lastPeriodDate = reportDate.AddDate(0, -3, 0)
case "yearly":
lastPeriodDate = reportDate.AddDate(-1, 0, 0)
}
lastPeriodData, err := s.getComparisonData(ctx, tenantID, appID, reportType, lastPeriodDate)
if err != nil {
return nil, err
}
return map[string]interface{}{
"last_period": lastPeriodData,
"growth_rate": s.calculateGrowthRate(lastPeriodData, s.getCurrentPeriodData(ctx, tenantID, appID, reportType, reportDate)),
}, nil
}
// 获取对比数据
func (s *StatReportService) getComparisonData(ctx context.Context, tenantID, appID int64, reportType string, date time.Time) (map[string]float64, error) {
// 这里简化实现,实际应该查询数据库
return map[string]float64{
"impressions": 1000,
"clicks": 50,
"revenue": 500.0,
"ctr": 0.05,
}, nil
}
// 获取当前周期数据
func (s *StatReportService) getCurrentPeriodData(ctx context.Context, tenantID, appID int64, reportType string, date time.Time) map[string]float64 {
// 这里简化实现,实际应该查询数据库
return map[string]float64{
"impressions": 1200,
"clicks": 60,
"revenue": 600.0,
"ctr": 0.05,
}
}
// 计算增长率
func (s *StatReportService) calculateGrowthRate(lastData, currentData map[string]float64) map[string]float64 {
growthRate := make(map[string]float64)
for key, lastValue := range lastData {
currentValue := currentData[key]
if lastValue == 0 {
growthRate[key] = 0
} else {
growthRate[key] = (currentValue - lastValue) / lastValue * 100
}
}
return growthRate
}
// 按广告类型分组统计
func (s *StatReportService) groupByAdType(stats []map[string]interface{}) (map[string]interface{}, error) {
result := make(map[string]interface{})
for _, stat := range stats {
adType := gconv.String(stat["ad_type"])
if adType == "" {
adType = "unknown"
}
if _, exists := result[adType]; !exists {
result[adType] = map[string]float64{
"impressions": 0,
"clicks": 0,
"revenue": 0,
}
}
adTypeStat, ok := result[adType].(map[string]float64)
if !ok {
return nil, fmt.Errorf("invalid adTypeStat type")
}
adTypeStat["impressions"] += gconv.Float64(stat["impressions"])
adTypeStat["clicks"] += gconv.Float64(stat["clicks"])
adTypeStat["revenue"] += gconv.Float64(stat["revenue"])
}
// 计算每个广告类型的CTR
for adType, stat := range result {
adTypeStat, ok := stat.(map[string]float64)
if !ok {
return nil, fmt.Errorf("invalid adTypeStat type for adType: %s", adType)
}
if adTypeStat["impressions"] > 0 {
adTypeStat["ctr"] = adTypeStat["clicks"] / adTypeStat["impressions"] * 100
} else {
adTypeStat["ctr"] = 0
}
}
return result, nil
}
// 按地区分组统计
func (s *StatReportService) groupByRegion(stats []map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{})
for _, stat := range stats {
region := gconv.String(stat["region"])
if region == "" {
region = "unknown"
}
if _, exists := result[region]; !exists {
result[region] = map[string]float64{
"impressions": 0,
"clicks": 0,
"revenue": 0,
}
}
regionStat := result[region].(map[string]float64)
regionStat["impressions"] += gconv.Float64(stat["impressions"])
regionStat["clicks"] += gconv.Float64(stat["clicks"])
regionStat["revenue"] += gconv.Float64(stat["revenue"])
}
return result
}
// 按终端类型分组统计
func (s *StatReportService) groupByPlatform(stats []map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{})
for _, stat := range stats {
platform := gconv.String(stat["platform"])
if platform == "" {
platform = "unknown"
}
if _, exists := result[platform]; !exists {
result[platform] = map[string]float64{
"impressions": 0,
"clicks": 0,
"revenue": 0,
}
}
platformStat := result[platform].(map[string]float64)
platformStat["impressions"] += gconv.Float64(stat["impressions"])
platformStat["clicks"] += gconv.Float64(stat["clicks"])
platformStat["revenue"] += gconv.Float64(stat["revenue"])
}
return result
}
// 计算字段总和
func (s *StatReportService) sumField(stats []map[string]interface{}, field string) float64 {
total := 0.0
for _, stat := range stats {
total += gconv.Float64(stat[field])
}
return total
}
// 计算字段平均值
func (s *StatReportService) avgField(stats []map[string]interface{}, field string) float64 {
if len(stats) == 0 {
return 0
}
return s.sumField(stats, field) / float64(len(stats))
}
// 计算平均CTR
func (s *StatReportService) calculateCTR(stats []map[string]interface{}) float64 {
totalImpressions := s.sumField(stats, "impressions")
totalClicks := s.sumField(stats, "clicks")
if totalImpressions == 0 {
return 0
}
return totalClicks / totalImpressions * 100
}
// 查询报表列表
func (s *StatReportService) GetReportList(ctx context.Context, req *dto.ReportListReq) (*dto.ReportListResp, error) {
// 使用DAO的List方法
reports, count, err := dao.StatReport.List(ctx, req.TenantID, req.AppID, req.ReportType, req.StartDate, req.EndDate, req.Page, req.PageSize)
if err != nil {
return nil, err
}
// 转换为DTO
var reportDTOs []*dto.ReportDTO
for _, report := range reports {
reportDTOs = append(reportDTOs, &dto.ReportDTO{
ID: report.Id,
TenantID: report.TenantId,
AppID: report.AppID,
ReportType: report.ReportType,
ReportDate: report.ReportDate.Format("2006-01-02"),
GeneratedAt: report.GeneratedAt.Format("2006-01-02 15:04:05"),
})
}
return &dto.ReportListResp{
Reports: reportDTOs,
Total: count,
Page: req.Page,
PageSize: req.PageSize,
}, nil
}
// 获取报表详情
func (s *StatReportService) GetReportDetail(ctx context.Context, reportID int64) (*dto.ReportDetailResp, error) {
var report *entity.StatReport
report, err := dao.StatReport.GetByID(ctx, reportID)
if err != nil {
return nil, err
}
if report == nil {
return nil, fmt.Errorf("报表不存在")
}
// 解析报表数据
var reportData map[string]interface{}
if err := gconv.Struct(report.ReportData, &reportData); err != nil {
return nil, err
}
return &dto.ReportDetailResp{
ID: report.Id,
TenantID: report.TenantId,
AppID: report.AppID,
ReportType: report.ReportType,
ReportDate: report.ReportDate.Format("2006-01-02"),
GeneratedAt: report.GeneratedAt.Format("2006-01-02 15:04:05"),
Data: reportData,
}, nil
}

View File

@@ -1,9 +1,9 @@
package service package service
import ( import (
"cidService/dao" "cidservice/dao"
"cidService/model/dto" "cidservice/model/dto"
"cidService/model/entity" "cidservice/model/entity"
"context" "context"
"encoding/json" "encoding/json"
"strconv" "strconv"

33
test_config.go Normal file
View File

@@ -0,0 +1,33 @@
package main
import (
"context"
"fmt"
"cidservice/service"
)
func main() {
ctx := context.Background()
// 检查租户请求次数限制
allowed, err := service.RateLimit.CheckTenantRequestLimit(ctx, 1, nil)
if err != nil {
fmt.Printf("错误: %v\n", err)
return
}
fmt.Printf("租户请求是否允许: %v\n", allowed)
// 获取租户当前使用情况
current, max, err := service.RateLimit.GetTenantCurrentUsage(ctx, 1, nil)
if err != nil {
fmt.Printf("错误: %v\n", err)
return
}
fmt.Printf("租户请求使用情况:\n")
fmt.Printf(" 当前使用: %d\n", current)
fmt.Printf(" 最大允许: %d\n", max)
fmt.Printf(" 使用率: %.2f%%\n", float64(current)/float64(max)*100)
}