297 lines
10 KiB
Go
297 lines
10 KiB
Go
package adser
|
|
|
|
import (
|
|
"91porn-server/app/proto"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"91porn-server/app/appg"
|
|
"91porn-server/app/service/activityclient"
|
|
"91porn-server/common"
|
|
"91porn-server/common/httputil"
|
|
"91porn-server/common/log"
|
|
"91porn-server/models/v/usermod"
|
|
"91porn-server/models/v/walletmod"
|
|
|
|
"github.com/robfig/cron/v3"
|
|
)
|
|
|
|
/*
|
|
测试:
|
|
部门CODE:1991129769626804224
|
|
商户CODE:1991129808640122880
|
|
应用CODE:jh_appid01
|
|
客户CODE:1991129948159451136
|
|
*/
|
|
|
|
type JtAdvertiseReq struct {
|
|
MerchantCode string `json:"merchantCode"` // 商户 code,必填
|
|
AppCode string `json:"appCode"` // 应用 code,必填
|
|
AdStatus int `json:"adStatus"` // 广告状态 1-上架,0-下架,2-未开始,3-已过期,4-即将到期 默认查询全部
|
|
//AdvertiseLocationCode string `json:"advertiseLocationCode,omitempty"` // 广告位唯一标识,选填
|
|
//AdvertiseCode string `json:"advertiseCode,omitempty"` // 广告唯一标识,选填
|
|
//PageNo int `json:"pageNo,omitempty"` // 页码,全量查询不需要传,选填
|
|
//PageSize int `json:"pageSize,omitempty"` // 每页条数,全量查询不需要传,选填
|
|
}
|
|
|
|
type JtAdvertiseRes struct {
|
|
Code int `json:"code"` // 返回状态码
|
|
Msg string `json:"msg"` // 返回消息
|
|
Data string `json:"data"` // 广告信息 - 加密
|
|
}
|
|
|
|
// AdSlot 广告位信息
|
|
type AdSlot struct {
|
|
DeptCode string `json:"deptCode"` // 部门 code
|
|
MerchantCode string `json:"merchantCode"` // 商户 code
|
|
AppCode string `json:"appCode"` // 应用 code
|
|
LocationType int `json:"locationType"` // 广告位类型:0 → 开屏广告 1 → 弹窗广告 2 → 图标广告 3 → 九宫格广告 4 → 信息流广告 5 → 瀑布流广告 6 → 详情广告 7 → 片头广告
|
|
AdvertiseLocationCode string `json:"advertiseLocationCode"` // 广告位标识
|
|
AdvertiseLocationName string `json:"advertiseLocationName"` // 广告位名称
|
|
DisplayMode int `json:"displayMode"` // 展示模式:0 → 多个轮播, 1 → 单体, 2 → 三分屏, 3 → 四分屏, 4 → 小图标, 5 → 大图标
|
|
AdvertiseHeight int `json:"advertiseHeight"` // 高
|
|
AdvertiseWidth int `json:"advertiseWidth"` // 宽
|
|
MaterialType int `json:"materialType"` // 素材类型(视频/图片):1 → 视频, 2 → 图片, 3 → 视频、图片
|
|
AdLimit int `json:"adLimit"` // 广告限制数量
|
|
AdDetailInfoList []AdDetailInfo `json:"adDetailInfoList"` // 广告详情信息列表
|
|
}
|
|
|
|
// AdDetailInfo 广告详情信息
|
|
type AdDetailInfo struct {
|
|
AdvertiseCode string `json:"advertiseCode"` // 广告业务code
|
|
CustomerCode string `json:"customerCode"` // 客户code
|
|
AdvertiseName string `json:"advertiseName"` // 广告名称
|
|
AdvertiseUrl string `json:"advertiseUrl"` // 广告链接
|
|
AdvertiseIcon string `json:"advertiseIcon"` // 广告图片地址
|
|
AdvertiseType int `json:"advertiseType"` // 广告类型:1 → 播放器, 2 → 药台, 3 → 炮台, 4 → 黄游, 5 → 直播, 6 → BC
|
|
AdMode string `json:"adMode"` // 广告模式
|
|
AppCode string `json:"appCode"` // 应用 code
|
|
LocationType int `json:"locationType"` // 广告位类型:0 → 开屏广告 1 → 弹窗广告 2 → 图标广告 3 → 九宫格广告 4 → 信息流广告 5 → 瀑布流广告 6 → 详情广告 7 → 片头广告
|
|
AdvertiseDesc string `json:"advertiseDesc"` // 广告描述
|
|
PageType string `json:"pageType"` // 内部路由页面类型:如 rechange_page → 会员充值页, gold_rechange_page → 金币充值页, invite_page → 邀请分享页
|
|
StartTimeStamp int64 `json:"startTimeStamp"` // 广告开始时间 时间戳
|
|
EndTimeStamp int64 `json:"endTimeStamp"` // 广告结束时间 时间戳
|
|
Sort int `json:"sort"` // 排序
|
|
AdExtData string `json:"adExtData"` // 扩展字段1 = CoverImgSize
|
|
}
|
|
|
|
type Extra struct {
|
|
CoverImgSize string `json:"coverImgSize"`
|
|
WatchTime int `json:"watchTime"`
|
|
DownloadCount int64 `json:"downloadCount"`
|
|
Type string `json:"type"` // 应用类型 app:APP、web:网页
|
|
Location string `json:"location"` // 位置 1:顶部 2:底部
|
|
DisplayType string `json:"displayType"` // 展示类型 heng:横版 shu:竖版
|
|
}
|
|
|
|
func (ad AdDetailInfo) GetExtraData() (res Extra) {
|
|
_ = json.Unmarshal([]byte(ad.AdExtData), &res)
|
|
return
|
|
}
|
|
|
|
func (ad AdDetailInfo) GetRealLink(user *usermod.User, w *walletmod.Wallet) string {
|
|
ad.AdvertiseUrl = activityclient.ReplaceActivityDomain(ad.AdvertiseUrl, user, w)
|
|
return strings.ReplaceAll(ad.AdvertiseUrl, "inner://yinseinner/", "yinseinner://")
|
|
}
|
|
|
|
func (ad AdDetailInfo) GetCoverLsj() string {
|
|
if ad.AdvertiseIcon == "" {
|
|
return ""
|
|
}
|
|
|
|
return strings.TrimLeft(ad.AdvertiseIcon, "/")
|
|
}
|
|
|
|
func (ad AdDetailInfo) GetAdvertiseType() int {
|
|
// 内部顺序:1 应用 2 游戏 3 炮台 4 棋牌 5 直播
|
|
// 外部顺序:1:应用 2:药台、3:炮台、4:游戏、5:直播、6:棋牌
|
|
if ad.AdvertiseType == 4 {
|
|
return 2
|
|
}
|
|
if ad.AdvertiseType == 6 {
|
|
return 4
|
|
}
|
|
|
|
return ad.AdvertiseType
|
|
}
|
|
|
|
// advertiseAppId 广告中心应用ID
|
|
const advertiseAppId int32 = 2
|
|
|
|
type advertiseClickReq struct {
|
|
Id int64 `json:"id"` // 广告id
|
|
AppId int32 `json:"appId"` // 应用id
|
|
}
|
|
|
|
type advertiseClickResp struct {
|
|
Code int64 `json:"code"` // 错误码
|
|
}
|
|
|
|
// AdvertiseClickCenter 上报广告点击到广告中心
|
|
func AdvertiseClickCenter(advertiseId int64) error {
|
|
req := advertiseClickReq{
|
|
Id: advertiseId,
|
|
AppId: advertiseAppId,
|
|
}
|
|
resp := advertiseClickResp{}
|
|
url := appg.Conf.URL.VersionUrl + "/api/stat/advitise/v2/click"
|
|
bodyStr, _ := json.Marshal(req)
|
|
code, err := httputil.DefaultClientPostJsonWithResp(&resp, url, nil, bodyStr)
|
|
if err != nil {
|
|
log.Error("AdvertiseClickCenter POSTWithJResp", log.Any("url", url), log.Any("req", req), log.E(err))
|
|
return err
|
|
}
|
|
if code != http.StatusOK {
|
|
log.Error("AdvertiseClickCenter response status", log.Any("code", code), log.Any("req", req))
|
|
return fmt.Errorf("response status err")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AsyncAdvertiseClick 异步上报广告点击,广告id非数字时忽略
|
|
func AsyncAdvertiseClick(id string) {
|
|
advertiseId, err := strconv.ParseInt(strings.TrimSpace(id), 10, 64)
|
|
if err != nil {
|
|
log.Warn("AsyncAdvertiseClick invalid advertise id", log.Any("id", id))
|
|
return
|
|
}
|
|
common.Go(func() {
|
|
_ = AdvertiseClickCenter(advertiseId)
|
|
})
|
|
}
|
|
|
|
var (
|
|
advertiseCacheOnce sync.Once
|
|
advertiseCacheMu sync.RWMutex
|
|
advertiseCache []AdSlot
|
|
advertiseCacheOK bool
|
|
)
|
|
|
|
// JtAdvertiseThreeServer 获取广告位列表(内存缓存,每两分钟从广告中心刷新一次)
|
|
func JtAdvertiseThreeServer() ([]AdSlot, error) {
|
|
advertiseCacheOnce.Do(startAdvertiseCache)
|
|
|
|
advertiseCacheMu.RLock()
|
|
defer advertiseCacheMu.RUnlock()
|
|
if !advertiseCacheOK {
|
|
return nil, errors.New("advertise cache not ready")
|
|
}
|
|
res := make([]AdSlot, len(advertiseCache))
|
|
copy(res, advertiseCache)
|
|
return res, nil
|
|
}
|
|
|
|
// startAdvertiseCache 首次加载广告并启动定时刷新
|
|
func startAdvertiseCache() {
|
|
updataAdvertiseInfo()
|
|
c := cron.New(cron.WithSeconds())
|
|
_, _ = c.AddFunc("0 */2 * * * ?", updataAdvertiseInfo) // 两分钟更新一次
|
|
c.Start()
|
|
}
|
|
|
|
// updataAdvertiseInfo 从广告中心拉取广告并更新缓存,列表为空时重试一次
|
|
func updataAdvertiseInfo() {
|
|
advertiseResp, err := advertiseCenterServer()
|
|
if err != nil {
|
|
log.Error("advertiseCenterServer", log.E(err))
|
|
return
|
|
}
|
|
|
|
log.Error("advertiseCenterServer", log.Any("advertiseResp", advertiseResp))
|
|
|
|
if len(advertiseResp.AdvertiseList) == 0 {
|
|
advertiseResp, err = advertiseCenterServer()
|
|
if err != nil {
|
|
log.Error("advertiseCenterServer", log.E(err))
|
|
return
|
|
}
|
|
}
|
|
|
|
list := advertiseResp.AdvertiseList
|
|
sort.SliceStable(list, func(i, j int) bool {
|
|
if list[i].LocId != list[j].LocId {
|
|
return list[i].LocId < list[j].LocId
|
|
}
|
|
return list[i].Sort < list[j].Sort
|
|
})
|
|
// 每条广告单独作为一个广告位返回,广告位名称取广告标题
|
|
slots := make([]AdSlot, 0, len(list))
|
|
for _, value := range list {
|
|
slots = append(slots, AdSlot{
|
|
AdvertiseLocationCode: strconv.FormatInt(int64(value.LocId), 10),
|
|
AdvertiseLocationName: value.Title,
|
|
AdDetailInfoList: []AdDetailInfo{{
|
|
AdvertiseCode: strconv.FormatInt(value.Id, 10),
|
|
AdvertiseName: value.Title,
|
|
AdvertiseUrl: value.Link,
|
|
AdvertiseIcon: value.CoverImg,
|
|
AdvertiseDesc: value.Remark,
|
|
Sort: int(value.Sort),
|
|
}},
|
|
})
|
|
}
|
|
|
|
advertiseCacheMu.Lock()
|
|
advertiseCache = slots
|
|
advertiseCacheOK = true
|
|
advertiseCacheMu.Unlock()
|
|
}
|
|
|
|
// advertiseCenterServer 请求广告中心广告列表
|
|
func advertiseCenterServer() (resp AdvertiseRes, err error) {
|
|
req := AdvertiseReq{
|
|
AppId: advertiseAppId,
|
|
}
|
|
url := appg.Conf.URL.VersionUrl + "/api/stat/advitise/v2/get"
|
|
bodyStr, _ := json.Marshal(req)
|
|
code, err := httputil.DefaultClientPostJsonWithResp(&resp, url, nil, bodyStr)
|
|
if err != nil {
|
|
log.Error("advertiseCenterServer POSTWithJResp ", log.Any("url", url), log.E(err))
|
|
return
|
|
}
|
|
|
|
log.Info("*************\n\n\n", log.Any("", resp))
|
|
if code != http.StatusOK {
|
|
log.Error("advertiseCenterServer response status ", log.Any("code", code))
|
|
err = fmt.Errorf("response status err")
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func GetAdvertiseLocation(adverId string) (res proto.AdsInfo) {
|
|
list, err := JtAdvertiseThreeServer()
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, adverLocation := range list {
|
|
for _, ad := range adverLocation.AdDetailInfoList {
|
|
if ad.AdvertiseCode == adverId {
|
|
pos, _ := strconv.Atoi(adverLocation.AdvertiseLocationCode)
|
|
extra := ad.GetExtraData()
|
|
res = proto.AdsInfo{
|
|
ID: ad.AdvertiseCode,
|
|
Title: ad.AdvertiseName,
|
|
Description: ad.AdvertiseDesc,
|
|
Cover: ad.GetCoverLsj(),
|
|
Href: ad.GetRealLink(nil, nil),
|
|
Position: pos,
|
|
PositionName: adverLocation.AdvertiseLocationName,
|
|
SortCode: ad.Sort,
|
|
CoverImgSize: extra.CoverImgSize,
|
|
WatchTime: extra.WatchTime,
|
|
}
|
|
return res
|
|
}
|
|
}
|
|
|
|
}
|
|
return
|
|
}
|