Files
rootandClaude Opus 5 01d9f7bc86 新增广告V2模块,双端下载落地页调整
- 新增 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>
2026-09-22 16:53:23 +08:00

320 lines
8.0 KiB
Go

package advertiseSer
import (
"context"
"stat/api/proto"
"stat/common/genId"
"stat/common/log"
"stat/common/redis"
"stat/common/stdLog"
"stat/common/utils"
"stat/models/advertiseLocMod"
"stat/models/advertiseMod"
"stat/models/appInfoMod"
"strings"
"time"
"go.mongodb.org/mongo-driver/bson"
)
// 获取广告列表
func ListAds(ctx context.Context, param proto.ListAdsReq) (code stdLog.CodeType, data interface{}, err error) {
resp := proto.ListAdsRes{
Total: 0,
List: make([]proto.AdsInfo, 0),
}
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
// 获取广告位置信息
total, adsList, err := advertiseMod.FindAdvertiseList(ctx, param.AppId, param.IsActive,
param.LocId, skip, limit, param.Link, param.Title, param.DaoLiangTitle)
if err != nil {
code = stdLog.Failure
return
}
resp.Total = total
// 获取app信息
appIds := make([]int32, 0)
for _, v := range adsList {
appIds = append(appIds, v.AppId)
}
appList, err := appInfoMod.GetAppInfosByAppIds(ctx, appIds)
if err != nil {
code = stdLog.Failure
return
}
appMap := make(map[int32]appInfoMod.AppInfo)
for _, v := range appList {
appMap[v.AppId] = v
}
for _, v := range adsList {
appName := ""
if appInfo, ok := appMap[v.AppId]; ok {
appName = appInfo.Name
}
// 获取位置信息
adsLocInfo, err := advertiseLocMod.FindLocationByAppIdAndLocId(ctx, v.AppId, v.LocId)
if err != nil {
code = stdLog.Failure
return code, data, err
}
str := make([]string, 0)
if v.Tags != "" {
split := strings.Split(strings.TrimSpace(v.Tags), ",")
for _, s := range split {
str = append(str, s)
}
}
tmp := proto.AdsInfo{
Id: v.Id,
AppId: v.AppId,
AppName: appName,
Title: v.Title,
DaoLiangTitle: v.DaoLiangTitle,
Tags: str,
CoverImg: v.CoverImg,
VisibleChannels: v.VisibleChannels,
LocId: v.LocId,
LocName: adsLocInfo.Name,
ImgSizeDesc: adsLocInfo.ImgSizeDesc,
Links: make([]proto.LinkInfo, 0),
Sort: v.Sort,
IsActive: v.IsActive,
StartTime: v.StartTime,
EndTime: v.EndTime,
Remark: v.Remark,
ClickCount: v.ClickCount,
AmbientColor: v.AmbientColor,
CreatedAt: v.CreatedAt,
UpdatedAt: v.UpdatedAt,
}
for _, v := range v.Links {
tmp.Links = append(tmp.Links, proto.LinkInfo{
Link: v.Link,
Foreign: v.Foreign,
Weight: v.Weight,
})
}
resp.List = append(resp.List, tmp)
}
code = stdLog.Success
data = resp
return
}
// 新增广告
func AddAds(ctx context.Context, param proto.AddAdsReq, account string) (code stdLog.CodeType, data interface{}, err error) {
adsId := genId.GetNextAdsId()
now := time.Now()
doc := advertiseMod.Advertise{
Id: adsId,
AppId: param.AppId,
Title: param.Title,
Tags: param.Tags,
CoverImg: param.CoverImg,
LocId: param.LocId,
Sort: param.Sort,
IsActive: param.IsActive,
StartTime: param.StartTime,
EndTime: param.EndTime,
Remark: param.Remark,
AmbientColor: param.AmbientColor,
CreatedAt: now,
UpdatedAt: now,
}
for _, v := range param.Links {
doc.Links = append(doc.Links, advertiseMod.LinkInfo{
Link: v.Link,
Foreign: v.Foreign,
Weight: v.Weight,
})
}
if err = advertiseMod.AddAdvertise(ctx, &doc); err != nil {
code = stdLog.Failure
return
}
newInfo, _ := advertiseMod.FindOneAdvertise(ctx, bson.M{"_id": adsId})
if newInfo != nil {
CheckChange(ctx, nil, newInfo, account)
}
code = stdLog.Success
return
}
// 修改广告位置
func UpdateAds(ctx context.Context, param proto.UpdateAdsReq, account string) (code stdLog.CodeType, data interface{}, err error) {
now := time.Now()
doc := advertiseMod.UpdateAds{
AppId: param.AppId,
Title: param.Title,
DaoLiangTitle: param.DaoLiangTitle,
Tags: param.Tags,
CoverImg: param.CoverImg,
Location: param.Location,
JumpType: param.JumpType,
Sort: param.Sort,
IsActive: param.IsActive,
StartTime: param.StartTime,
EndTime: param.EndTime,
Remark: param.Remark,
AmbientColor: param.AmbientColor,
UpdatedAt: &now,
}
if *param.Links != nil {
links := make([]advertiseMod.LinkInfo, 0)
for _, v := range *param.Links {
links = append(links, advertiseMod.LinkInfo{
Link: v.Link,
Foreign: v.Foreign,
Weight: v.Weight,
})
}
doc.Links = &links
}
if param.VisibleChannels != nil {
channels := make([]string, 0)
if len(*param.VisibleChannels) > 0 {
ch := strings.Split(*param.VisibleChannels, ",")
channels = append(channels, ch...)
}
doc.VisibleChannels = &channels
}
if doc.Location != nil {
locId := int32(*doc.Location)
doc.LocId = &locId
}
oldInfo, err := advertiseMod.FindOneAdvertise(ctx, bson.M{"_id": *param.Id})
if err != nil {
code = stdLog.Failure
return
}
if err = advertiseMod.UpdateAdvertise(ctx, *param.Id, doc); err != nil {
code = stdLog.Failure
return
}
newInfo, err := advertiseMod.FindOneAdvertise(ctx, bson.M{"_id": *param.Id})
if err != nil {
code = stdLog.Failure
return
}
CheckChange(ctx, oldInfo, newInfo, account)
code = stdLog.Success
return
}
// 删除广告
func DelAds(ctx context.Context, param proto.DelAdsReq, account string) (code stdLog.CodeType, data interface{}, err error) {
oldInfo, err := advertiseMod.FindOneAdvertise(ctx, bson.M{"_id": *param.Id})
if err != nil {
code = stdLog.Failure
return
}
if err = advertiseMod.DelAdvertise(ctx, *param.Id); err != nil {
code = stdLog.Failure
return
}
CheckChange(ctx, oldInfo, nil, account)
code = stdLog.Success
return
}
// 外部获取广告
func GetAdvertise(ctx context.Context, param proto.AdvertiseReq) (code stdLog.CodeType, resp *proto.AdvertiseRes, err error) {
resp = &proto.AdvertiseRes{AdvertiseList: make([]*proto.AdvertiseInfo, 0)}
log.Info("GetAdvertise", log.Any("param", param))
adsList, err := advertiseMod.FindAdsByAppAndLocation(ctx, param.AppId, param.LocId)
if err != nil {
code = stdLog.Failure
return
}
log.Info("GetAdvertise", log.Any("param", param), log.Any("adsList", len(adsList)))
// 遍历广告,处理系统广告
for _, v := range adsList {
adsInfo := &proto.AdvertiseInfo{
Id: v.Id,
Title: v.Title,
CoverImg: v.CoverImg,
LocId: v.LocId,
JumpType: v.JumpType,
Sort: v.Sort,
Type: 0,
Tags: v.Tags,
Remark: v.Remark,
AmbientColor: v.AmbientColor,
}
if len(v.Links) >= 1 {
adsInfo.Link = v.Links[0].Link
}
resp.AdvertiseList = append(resp.AdvertiseList, adsInfo)
}
code = stdLog.Success
err = nil
return
}
// 增加广告点击次数
// 兼容策略:先按 param.Id 精确查;如果广告已过期/删除或不属于该 AppId,回退到 (AppId, LocId) 上的最新一条广告
// LocId 未传时,回退查询只按 AppId 定位;仍找不到才返回 AdvertiseInvalid
func IncAdsClickCount(ctx context.Context, param proto.AdsClickReq) (code stdLog.CodeType, data interface{}, err error) {
// 先按 Id 查
adsInfo, findErr := advertiseMod.FindAdsById(ctx, *param.Id)
needFallback := findErr != nil || adsInfo.Id == 0 || adsInfo.AppId != *param.AppId
if needFallback {
var locId int32
if param.LocId != nil {
locId = *param.LocId
}
latest, latestErr := advertiseMod.FindLatestAdsByAppAndLoc(ctx, *param.AppId, locId)
if latestErr != nil || latest.Id == 0 {
code = stdLog.AdvertiseInvalid
err = latestErr
return
}
adsInfo = latest
}
// save to redis(用真正命中的广告 Id 统计)
now := time.Now().Local()
set := redis.GetAdvertiseClickDayHash(now)
name := redis.GetAdvertiseClickHourName(adsInfo.AppId, adsInfo.Id, now.Hour())
if _, err = set.IncInt(name, 1); err != nil {
code = stdLog.Failure
return
}
// update click+1 to db
if err = advertiseMod.IncAdsClickCount(ctx, adsInfo.Id); err != nil {
code = stdLog.Failure
return
}
code = stdLog.Success
return
}