- 新增 advitise v2: info/location/bind/click-stat/day-click-stat 模型与 对应的 service、api、router、proto 定义 - models/index.go 注册 5 个新模型的索引初始化 - 鉴权白名单: /rsdownload/ 改为 /download/,放行 advitise v2 的 get/click 接口 - statg/config: proxyUrl 增加 fileRes1 - common/genId: lid 生成逻辑补充 - 新增 91fans / huangguo 的 mobileconfig 描述文件 - 新增 deploy/nginx/cpcdownload.mfcg.me.conf 网关配置 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
286 lines
8.2 KiB
Go
286 lines
8.2 KiB
Go
package advitiseV2Ser
|
|
|
|
import (
|
|
"context"
|
|
"stat/api/proto"
|
|
"stat/models/advitiseClickStatMod"
|
|
"stat/models/advitiseDayClickStatMod"
|
|
"stat/models/advitiseInfoMod"
|
|
"stat/models/advitiseLocationMod"
|
|
"stat/models/appInfoMod"
|
|
"time"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
const statDateLayout = "2006-01-02"
|
|
|
|
func parseDateRange(startS, endS string) (start, end time.Time, ok bool) {
|
|
// 允许两种格式:yyyy-MM-dd 和 yyyy-MM-dd HH:mm:ss
|
|
tryLayouts := []string{"2006-01-02 15:04:05", "2006-01-02"}
|
|
if startS != "" {
|
|
for _, l := range tryLayouts {
|
|
if t, err := time.ParseInLocation(l, startS, time.Local); err == nil {
|
|
start = t.Truncate(24 * time.Hour)
|
|
ok = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if endS != "" {
|
|
for _, l := range tryLayouts {
|
|
if t, err := time.ParseInLocation(l, endS, time.Local); err == nil {
|
|
end = t.Truncate(24 * time.Hour)
|
|
ok = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// DayClickList 广告每日点击汇总列表(advitise_day_click_stat)
|
|
func DayClickList(ctx context.Context, req *proto.AdvitiseDayClickListReq) (*proto.AdvitiseDayClickListResp, error) {
|
|
resp := &proto.AdvitiseDayClickListResp{List: make([]*proto.AdvitiseDayClickItem, 0)}
|
|
filter := bson.M{}
|
|
if req.Id != nil {
|
|
filter[advitiseDayClickStatMod.FN.AdvitiseDayClickStat.AdvitiseId] = *req.Id
|
|
}
|
|
start, end, _ := parseDateRange(req.StatStartDate, req.StatEndDate)
|
|
dateFilter := bson.M{}
|
|
if !start.IsZero() {
|
|
dateFilter["$gte"] = start
|
|
}
|
|
if !end.IsZero() {
|
|
dateFilter["$lte"] = end
|
|
}
|
|
if len(dateFilter) > 0 {
|
|
filter[advitiseDayClickStatMod.FN.AdvitiseDayClickStat.StatDate] = dateFilter
|
|
}
|
|
|
|
total, err := advitiseDayClickStatMod.Count(ctx, filter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
opt := &options.FindOptions{}
|
|
opt.SetSort(bson.D{{Key: advitiseDayClickStatMod.FN.AdvitiseDayClickStat.StatDate, Value: -1}})
|
|
opt.SetSkip((req.PageNum - 1) * req.PageSize)
|
|
opt.SetLimit(req.PageSize)
|
|
list, err := advitiseDayClickStatMod.FindMany(ctx, filter, opt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// 批量拉广告标题
|
|
advIds := make([]int64, 0, len(list))
|
|
for _, r := range list {
|
|
advIds = append(advIds, r.AdvitiseId)
|
|
}
|
|
nameMap := loadInfoNames(ctx, advIds)
|
|
|
|
for _, r := range list {
|
|
resp.List = append(resp.List, &proto.AdvitiseDayClickItem{
|
|
AdsId: r.AdvitiseId,
|
|
AdsName: nameMap[r.AdvitiseId],
|
|
Clicks: r.Clicks,
|
|
StatDate: r.StatDate.Format(statDateLayout),
|
|
})
|
|
}
|
|
resp.Total = total
|
|
return resp, nil
|
|
}
|
|
|
|
// ClickList 广告细维度点击列表(advitise_click_stat)。既用于「每日点击详情」也用于「广告位查看统计」入口
|
|
func ClickList(ctx context.Context, req *proto.AdvitiseClickListReq) (*proto.AdvitiseClickListResp, error) {
|
|
resp := &proto.AdvitiseClickListResp{List: make([]*proto.AdvitiseClickItem, 0)}
|
|
filter := bson.M{}
|
|
if req.AdsId != nil {
|
|
filter[advitiseClickStatMod.FN.AdvitiseClickStat.AdvitiseId] = *req.AdsId
|
|
}
|
|
if req.AppId != nil {
|
|
filter[advitiseClickStatMod.FN.AdvitiseClickStat.AppId] = *req.AppId
|
|
}
|
|
if req.LocationId != nil {
|
|
filter[advitiseClickStatMod.FN.AdvitiseClickStat.LocationId] = *req.LocationId
|
|
}
|
|
start, end, _ := parseDateRange(req.StatStartDate, req.StatEndDate)
|
|
dateFilter := bson.M{}
|
|
if !start.IsZero() {
|
|
dateFilter["$gte"] = start
|
|
}
|
|
if !end.IsZero() {
|
|
dateFilter["$lte"] = end
|
|
}
|
|
if len(dateFilter) > 0 {
|
|
filter[advitiseClickStatMod.FN.AdvitiseClickStat.StatDate] = dateFilter
|
|
}
|
|
|
|
total, err := advitiseClickStatMod.Count(ctx, filter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
opt := &options.FindOptions{}
|
|
opt.SetSort(bson.D{{Key: advitiseClickStatMod.FN.AdvitiseClickStat.StatDate, Value: -1}})
|
|
opt.SetSkip((req.PageNum - 1) * req.PageSize)
|
|
opt.SetLimit(req.PageSize)
|
|
list, err := advitiseClickStatMod.FindMany(ctx, filter, opt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 批量映射名字
|
|
advIds := make([]int64, 0)
|
|
appIds := make(map[int32]bool)
|
|
locIds := make(map[int32]bool)
|
|
for _, r := range list {
|
|
advIds = append(advIds, r.AdvitiseId)
|
|
appIds[r.AppId] = true
|
|
locIds[r.LocationId] = true
|
|
}
|
|
advNameMap := loadInfoNames(ctx, advIds)
|
|
appNameMap, _ := appInfoMod.GetAllAppIdNameMap(ctx)
|
|
// 位置名字 — 用 (appId, locId) 组合查
|
|
type locKey struct {
|
|
appId int32
|
|
locId int32
|
|
}
|
|
locNameMap := make(map[locKey]string)
|
|
if len(locIds) > 0 {
|
|
locIdsSlice := make([]int32, 0, len(locIds))
|
|
for id := range locIds {
|
|
locIdsSlice = append(locIdsSlice, id)
|
|
}
|
|
appIdsSlice := make([]int32, 0, len(appIds))
|
|
for id := range appIds {
|
|
appIdsSlice = append(appIdsSlice, id)
|
|
}
|
|
locs, lerr := advitiseLocationMod.FindManyAdvitiseLocations(ctx, bson.M{
|
|
advitiseLocationMod.FN.AdvitiseLocation.AppId: bson.M{"$in": appIdsSlice},
|
|
advitiseLocationMod.FN.AdvitiseLocation.LocId: bson.M{"$in": locIdsSlice},
|
|
})
|
|
if lerr == nil {
|
|
for _, l := range locs {
|
|
locNameMap[locKey{appId: l.AppId, locId: l.LocId}] = l.Name
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, r := range list {
|
|
resp.List = append(resp.List, &proto.AdvitiseClickItem{
|
|
AppId: r.AppId,
|
|
AppName: appNameMap[r.AppId],
|
|
AdsId: r.AdvitiseId,
|
|
AdsName: advNameMap[r.AdvitiseId],
|
|
LocationId: r.LocationId,
|
|
LocationName: locNameMap[locKey{appId: r.AppId, locId: r.LocationId}],
|
|
Clicks: r.Clicks,
|
|
StatDate: r.StatDate.Format(statDateLayout),
|
|
})
|
|
}
|
|
resp.Total = total
|
|
return resp, nil
|
|
}
|
|
|
|
// ClickSummary 按时间范围汇总广告点击量,分页按 clicks desc
|
|
// aggregate pipeline: $match → $group by advitiseId → $sort clicks desc → $facet(list, total, grandTotal)
|
|
func ClickSummary(ctx context.Context, req *proto.AdvitiseClickSummaryReq) (*proto.AdvitiseClickSummaryResp, error) {
|
|
resp := &proto.AdvitiseClickSummaryResp{List: make([]*proto.AdvitiseClickSummaryItem, 0)}
|
|
|
|
match := bson.M{}
|
|
if req.Id != nil {
|
|
match[advitiseClickStatMod.FN.AdvitiseClickStat.AdvitiseId] = *req.Id
|
|
}
|
|
if req.AppId != nil {
|
|
match[advitiseClickStatMod.FN.AdvitiseClickStat.AppId] = *req.AppId
|
|
}
|
|
if req.LocationId != nil {
|
|
match[advitiseClickStatMod.FN.AdvitiseClickStat.LocationId] = *req.LocationId
|
|
}
|
|
start, end, _ := parseDateRange(req.StatStartDate, req.StatEndDate)
|
|
dateFilter := bson.M{}
|
|
if !start.IsZero() {
|
|
dateFilter["$gte"] = start
|
|
}
|
|
if !end.IsZero() {
|
|
dateFilter["$lte"] = end
|
|
}
|
|
if len(dateFilter) > 0 {
|
|
match[advitiseClickStatMod.FN.AdvitiseClickStat.StatDate] = dateFilter
|
|
}
|
|
|
|
pipeline := []bson.M{
|
|
{"$match": match},
|
|
{"$group": bson.M{
|
|
"_id": "$" + advitiseClickStatMod.FN.AdvitiseClickStat.AdvitiseId,
|
|
"totalClicks": bson.M{"$sum": "$" + advitiseClickStatMod.FN.AdvitiseClickStat.Clicks},
|
|
}},
|
|
{"$facet": bson.M{
|
|
"list": []bson.M{
|
|
{"$sort": bson.M{"totalClicks": -1}},
|
|
{"$skip": (req.PageNum - 1) * req.PageSize},
|
|
{"$limit": req.PageSize},
|
|
},
|
|
"total": []bson.M{{"$count": "count"}},
|
|
"grandTotal": []bson.M{{"$group": bson.M{"_id": nil, "sum": bson.M{"$sum": "$totalClicks"}}}},
|
|
}},
|
|
}
|
|
|
|
type facetRow struct {
|
|
List []struct {
|
|
Id int64 `bson:"_id"`
|
|
TotalClicks int64 `bson:"totalClicks"`
|
|
} `bson:"list"`
|
|
Total []struct {
|
|
Count int64 `bson:"count"`
|
|
} `bson:"total"`
|
|
GrandTotal []struct {
|
|
Sum int64 `bson:"sum"`
|
|
} `bson:"grandTotal"`
|
|
}
|
|
rows := make([]facetRow, 0, 1)
|
|
if err := advitiseClickStatMod.Aggregate(ctx, &rows, pipeline); err != nil {
|
|
return nil, err
|
|
}
|
|
var facet facetRow
|
|
if len(rows) > 0 {
|
|
facet = rows[0]
|
|
}
|
|
if len(facet.Total) > 0 {
|
|
resp.Total = facet.Total[0].Count
|
|
}
|
|
if len(facet.GrandTotal) > 0 {
|
|
resp.GrandTotal = facet.GrandTotal[0].Sum
|
|
}
|
|
|
|
advIds := make([]int64, 0, len(facet.List))
|
|
for _, r := range facet.List {
|
|
advIds = append(advIds, r.Id)
|
|
}
|
|
nameMap := loadInfoNames(ctx, advIds)
|
|
for _, r := range facet.List {
|
|
resp.List = append(resp.List, &proto.AdvitiseClickSummaryItem{
|
|
AdsId: r.Id,
|
|
AdsName: nameMap[r.Id],
|
|
TotalClicks: r.TotalClicks,
|
|
})
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
func loadInfoNames(ctx context.Context, advIds []int64) map[int64]string {
|
|
m := make(map[int64]string)
|
|
if len(advIds) == 0 {
|
|
return m
|
|
}
|
|
infos, err := advitiseInfoMod.FindManyAdvitiseInfos(ctx, bson.M{
|
|
advitiseInfoMod.FN.AdvitiseInfo.Id: bson.M{"$in": advIds},
|
|
})
|
|
if err != nil {
|
|
return m
|
|
}
|
|
for _, info := range infos {
|
|
m[info.Id] = info.Title
|
|
}
|
|
return m
|
|
}
|