- 新增 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>
215 lines
6.5 KiB
Go
215 lines
6.5 KiB
Go
package advitiseV2Ser
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"stat/api/proto"
|
||
"stat/common/log"
|
||
"stat/models/advitiseBindMod"
|
||
"stat/models/advitiseClickStatMod"
|
||
"stat/models/advitiseDayClickStatMod"
|
||
"stat/models/advitiseInfoMod"
|
||
"stat/models/advitiseLocationMod"
|
||
"time"
|
||
|
||
"go.mongodb.org/mongo-driver/bson"
|
||
"go.mongodb.org/mongo-driver/mongo/options"
|
||
)
|
||
|
||
// idScale 把 (bindId, locId) 编码进单个 int64 id 的乘数
|
||
// 约束:locId < idScale。int64 上限允许 bindId 最大约 9.2e13,远超实际 LID 生成量
|
||
const idScale = int64(100000)
|
||
|
||
// GetAdvitises 外部拉广告:响应结构与老 GetAdvertise (proto.AdvertiseRes) 一致
|
||
// - 每条广告绑定一个 (bindId, locId) 组合
|
||
// - CoverImg 依据 location.AdvitiseSize 选 info 里对应尺寸组的第一张
|
||
// - 返回的 id = bindId*idScale + locId,client 点击时原样回传,后端解码定位 (bind, locId)
|
||
// - BindId 也单独返回,给愿意用二维接口的 client 用
|
||
func GetAdvitises(ctx context.Context, req *proto.GetAdvitiseV2Req) (*proto.AdvertiseRes, error) {
|
||
resp := &proto.AdvertiseRes{AdvertiseList: make([]*proto.AdvertiseInfo, 0)}
|
||
|
||
// 1) 拉 bind
|
||
filter := bson.M{advitiseBindMod.FN.AdvitiseBind.AppId: req.AppId}
|
||
if req.LocationId != nil {
|
||
filter[advitiseBindMod.FN.AdvitiseBind.LocationId] = *req.LocationId
|
||
}
|
||
opt := &options.FindOptions{}
|
||
opt.SetSort(bson.D{
|
||
{Key: advitiseBindMod.FN.AdvitiseBind.Sort, Value: -1},
|
||
{Key: advitiseBindMod.FN.AdvitiseBind.CreatedAt, Value: -1},
|
||
})
|
||
binds, err := advitiseBindMod.FindManyAdvitiseBinds(ctx, filter, opt)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if len(binds) == 0 {
|
||
return resp, nil
|
||
}
|
||
|
||
// 2) 批量拉当前生效 info
|
||
advitiseIds := uniqInt64(func() []int64 {
|
||
out := make([]int64, 0, len(binds))
|
||
for _, b := range binds {
|
||
out = append(out, b.AdvitiseId)
|
||
}
|
||
return out
|
||
}())
|
||
now := time.Now()
|
||
infos, err := advitiseInfoMod.FindManyAdvitiseInfos(ctx, bson.M{
|
||
advitiseInfoMod.FN.AdvitiseInfo.Id: bson.M{"$in": advitiseIds},
|
||
advitiseInfoMod.FN.AdvitiseInfo.Status: true,
|
||
advitiseInfoMod.FN.AdvitiseInfo.StartTime: bson.M{"$lte": now},
|
||
advitiseInfoMod.FN.AdvitiseInfo.EndTime: bson.M{"$gte": now},
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
infoMap := make(map[int64]*advitiseInfoMod.AdvitiseInfo, len(infos))
|
||
for _, info := range infos {
|
||
infoMap[info.Id] = info
|
||
}
|
||
|
||
// 3) 批量拉 location(用于 CoverImg 尺寸匹配)
|
||
locIdsAll := uniqInt32(func() []int32 {
|
||
out := make([]int32, 0)
|
||
for _, b := range binds {
|
||
out = append(out, b.LocationId...)
|
||
}
|
||
return out
|
||
}())
|
||
locMap := make(map[int32]*advitiseLocationMod.AdvitiseLocation)
|
||
if len(locIdsAll) > 0 {
|
||
locs, lerr := advitiseLocationMod.FindManyAdvitiseLocations(ctx, bson.M{
|
||
advitiseLocationMod.FN.AdvitiseLocation.AppId: req.AppId,
|
||
advitiseLocationMod.FN.AdvitiseLocation.LocId: bson.M{"$in": locIdsAll},
|
||
})
|
||
if lerr == nil {
|
||
for _, l := range locs {
|
||
locMap[l.LocId] = l
|
||
}
|
||
}
|
||
}
|
||
|
||
// 4) 展开 bind × location 为 item
|
||
for _, b := range binds {
|
||
info, ok := infoMap[b.AdvitiseId]
|
||
if !ok {
|
||
continue
|
||
}
|
||
for _, locId := range b.LocationId {
|
||
if req.LocationId != nil && locId != *req.LocationId {
|
||
continue
|
||
}
|
||
var size advitiseLocationMod.AdvitiseSizeType
|
||
if loc, ok := locMap[locId]; ok && loc != nil {
|
||
size = loc.AdvitiseSize
|
||
}
|
||
resp.AdvertiseList = append(resp.AdvertiseList, &proto.AdvertiseInfo{
|
||
Id: b.Id*idScale + int64(locId),
|
||
Title: info.Title,
|
||
CoverImg: pickCoverImg(info, size),
|
||
LocId: locId,
|
||
JumpType: 0, // V2 素材未维护跳转类型,默认外部浏览器
|
||
Link: info.Link,
|
||
Sort: b.Sort,
|
||
Type: 0,
|
||
Tags: "",
|
||
Remark: info.Remark,
|
||
AmbientColor: "",
|
||
BindId: b.Id,
|
||
})
|
||
}
|
||
}
|
||
return resp, nil
|
||
}
|
||
|
||
// pickCoverImg 按位置尺寸选一张封面;取不到就用任一非空数组的第一张兜底
|
||
func pickCoverImg(info *advitiseInfoMod.AdvitiseInfo, size advitiseLocationMod.AdvitiseSizeType) string {
|
||
var arr []string
|
||
switch size {
|
||
case advitiseLocationMod.AdvitiseSizeFullScreen:
|
||
arr = info.FullScreenImgs
|
||
case advitiseLocationMod.AdvitiseSizeBanner720300:
|
||
arr = info.Banner720300Imgs
|
||
case advitiseLocationMod.AdvitiseSizeBanner720150:
|
||
arr = info.Banner720150Imgs
|
||
case advitiseLocationMod.AdvitiseSizeBanner750700:
|
||
arr = info.Banner750700Imgs
|
||
case advitiseLocationMod.AdvitiseSizeBanner800450:
|
||
arr = info.Banner800450Imgs
|
||
case advitiseLocationMod.AdvitiseSizePopUps600800:
|
||
arr = info.PopUps600800Imgs
|
||
case advitiseLocationMod.AdvitiseSizePopUps512512:
|
||
arr = info.PopUps512512Imgs
|
||
case advitiseLocationMod.AdvitiseSizeSmallCube:
|
||
arr = info.SmallCubeImgs
|
||
case advitiseLocationMod.AdvitiseSizeVerticalCube:
|
||
arr = info.VerticalCubeImgs
|
||
}
|
||
if len(arr) > 0 {
|
||
return arr[0]
|
||
}
|
||
// 兜底:任一非空数组的第一张
|
||
for _, cand := range [][]string{
|
||
info.FullScreenImgs, info.Banner720300Imgs, info.Banner720150Imgs,
|
||
info.Banner750700Imgs, info.Banner800450Imgs,
|
||
info.PopUps600800Imgs, info.PopUps512512Imgs,
|
||
info.SmallCubeImgs, info.VerticalCubeImgs,
|
||
} {
|
||
if len(cand) > 0 {
|
||
return cand[0]
|
||
}
|
||
}
|
||
return ""
|
||
}
|
||
|
||
func uniqInt64(in []int64) []int64 {
|
||
seen := make(map[int64]struct{}, len(in))
|
||
out := make([]int64, 0, len(in))
|
||
for _, v := range in {
|
||
if _, ok := seen[v]; ok {
|
||
continue
|
||
}
|
||
seen[v] = struct{}{}
|
||
out = append(out, v)
|
||
}
|
||
return out
|
||
}
|
||
|
||
func uniqInt32(in []int32) []int32 {
|
||
seen := make(map[int32]struct{}, len(in))
|
||
out := make([]int32, 0, len(in))
|
||
for _, v := range in {
|
||
if _, ok := seen[v]; ok {
|
||
continue
|
||
}
|
||
seen[v] = struct{}{}
|
||
out = append(out, v)
|
||
}
|
||
return out
|
||
}
|
||
|
||
// ClickAdvitise 点击回传:media 逻辑
|
||
// 1. advitise_click_stat upsert +1 (appId, advitiseId, locationId, day)
|
||
// 2. advitise_day_click_stat upsert +1 (advitiseId, day)
|
||
func ClickAdvitise(ctx context.Context, req *proto.ClickAdvitiseV2Req) error {
|
||
bindId := req.Id / idScale
|
||
location := int32(req.Id % idScale)
|
||
bind, err := advitiseBindMod.FindOneAdvitiseBind(ctx, bson.M{advitiseBindMod.FN.AdvitiseBind.Id: bindId})
|
||
if err != nil || bind == nil {
|
||
return errors.New("bind not found")
|
||
}
|
||
|
||
day := time.Now().Truncate(24 * time.Hour)
|
||
|
||
if err := advitiseClickStatMod.InsertOrUpdate(ctx, bind.AppId, bind.AdvitiseId, location, day); err != nil {
|
||
log.Warn("v2 click: fine stat upsert failed", log.E(err))
|
||
return err
|
||
}
|
||
if err := advitiseDayClickStatMod.InsertOrUpdate(ctx, bind.AdvitiseId, day); err != nil {
|
||
// 汇总表失败不阻断细维度已成功的记录
|
||
log.Warn("v2 click: day rollup upsert failed", log.E(err))
|
||
}
|
||
return nil
|
||
}
|