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 }