init: 初始化 web_hgdjb_server 项目
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,298 @@
|
||||
package adcenter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"videoweb/pub/common/crypt"
|
||||
"videoweb/pub/common/godef"
|
||||
"videoweb/pub/common/httputil"
|
||||
"videoweb/pub/common/log"
|
||||
"videoweb/pub/redis"
|
||||
)
|
||||
|
||||
/*
|
||||
测试:
|
||||
部门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 AdCacheInfo 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
|
||||
DeptCode string `json:"deptCode"` // 部门 code
|
||||
MerchantCode string `json:"merchantCode"` // 商户 code
|
||||
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"` // 广告限制数量
|
||||
}
|
||||
|
||||
type Extra struct {
|
||||
ImgType int64 `json:"imgType"` // 图片展示类型 0:应用图标 1.小图标 2.大图标
|
||||
DownloadCount int64 `json:"downloadCount"`
|
||||
}
|
||||
|
||||
func (ad AdDetailInfo) GetExtraData() (res Extra) {
|
||||
_ = json.Unmarshal([]byte(ad.AdExtData), &res)
|
||||
return
|
||||
}
|
||||
|
||||
func (ad AdDetailInfo) GetRealLink() string {
|
||||
return strings.ReplaceAll(ad.AdvertiseUrl, "inner://yinseinner/", "yinseinner://")
|
||||
}
|
||||
|
||||
func (ad AdDetailInfo) GetCoverLsj() string {
|
||||
if ad.AdvertiseIcon == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return path.Join("laosiji", 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
|
||||
}
|
||||
|
||||
// JtAdvertiseThreeServer 集团广告中心
|
||||
func JtAdvertiseThreeServer(apiDomain, merchantCode, appCode, aesKey string) ([]AdSlot, error) {
|
||||
var foreverCacheKey = fmt.Sprintf("jtAdForever-%s-%s", merchantCode, appCode)
|
||||
redisKey := fmt.Sprintf("jtAd-%s-%s", merchantCode, appCode)
|
||||
str, err := redis.Get(redisKey)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("JtAdvertiseThreeServer 缓存获取广告列表信息异常:%v", err))
|
||||
}
|
||||
var resp []AdSlot
|
||||
if str != nil {
|
||||
if err = json.Unmarshal([]byte(*str), &resp); err == nil {
|
||||
return resp, nil
|
||||
}
|
||||
log.Error(fmt.Sprintf("JtAdvertiseThreeServer 解析缓存数据异常:%v", err))
|
||||
}
|
||||
req := JtAdvertiseReq{
|
||||
MerchantCode: merchantCode,
|
||||
AppCode: appCode,
|
||||
AdStatus: 1,
|
||||
}
|
||||
|
||||
bodyStr, _ := json.Marshal(req)
|
||||
headers := map[string]string{"Content-Type": "application/json"}
|
||||
serverResp, err := fetchJtAdvertiseWithFallback(apiDomain, headers, bodyStr)
|
||||
if err != nil {
|
||||
return getFromLastTime(foreverCacheKey, err)
|
||||
}
|
||||
|
||||
adsData, err := crypt.AdDecrypt(serverResp.Data, aesKey)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("JtAdvertiseThreeServer 解密广告数据异常:%v", err))
|
||||
return nil, errors.New("response data err")
|
||||
}
|
||||
err = json.Unmarshal([]byte(adsData), &resp)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("JtAdvertiseThreeServer 解析 JSON 失败:%v", err))
|
||||
return nil, errors.New("data format error")
|
||||
}
|
||||
|
||||
// 对广告进行升序排序
|
||||
for i := range resp {
|
||||
sort.Slice(resp[i].AdDetailInfoList, func(j, k int) bool {
|
||||
return resp[i].AdDetailInfoList[j].Sort < resp[i].AdDetailInfoList[k].Sort
|
||||
})
|
||||
}
|
||||
|
||||
// 将最后一次请求成功的数据更新到二级缓存
|
||||
godef.Go(func() {
|
||||
setAdsCache(foreverCacheKey, resp)
|
||||
})
|
||||
// 写入缓存
|
||||
godef.Go(func() {
|
||||
data, err := json.Marshal(&resp)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("JtAdvertiseThreeServer json序列化缓存数据异常:%v", err))
|
||||
return
|
||||
}
|
||||
if err = redis.Set(redisKey, data, time.Minute); err != nil {
|
||||
log.Error(fmt.Sprintf("JtAdvertiseThreeServer 保存缓存数据异常:%v", err))
|
||||
}
|
||||
})
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
var jtAdvertiseFallbackDomains = []string{
|
||||
"https://apiad01.cc",
|
||||
"https://apiad02.cc",
|
||||
"https://apiad03.cc",
|
||||
"https://awsapiad01.cc",
|
||||
"https://d3o2peztwy6jno.cloudfront.net",
|
||||
}
|
||||
|
||||
func jtAdvertiseRequestDomains(apiDomain string) []string {
|
||||
seen := make(map[string]struct{}, 1+len(jtAdvertiseFallbackDomains))
|
||||
domains := make([]string, 0, 1+len(jtAdvertiseFallbackDomains))
|
||||
add := func(domain string) {
|
||||
domain = strings.TrimRight(strings.TrimSpace(domain), "/")
|
||||
if domain == "" {
|
||||
return
|
||||
}
|
||||
if _, ok := seen[domain]; ok {
|
||||
return
|
||||
}
|
||||
seen[domain] = struct{}{}
|
||||
domains = append(domains, domain)
|
||||
}
|
||||
add(apiDomain)
|
||||
for _, domain := range jtAdvertiseFallbackDomains {
|
||||
add(domain)
|
||||
}
|
||||
return domains
|
||||
}
|
||||
|
||||
func fetchJtAdvertiseWithFallback(apiDomain string, headers map[string]string, body []byte) (*JtAdvertiseRes, error) {
|
||||
var lastErr error
|
||||
for _, domain := range jtAdvertiseRequestDomains(apiDomain) {
|
||||
resp, err := fetchJtAdvertise(domain, headers, body)
|
||||
if err == nil {
|
||||
return resp, nil
|
||||
}
|
||||
lastErr = err
|
||||
log.Error("JtAdvertiseThreeServer 请求异常,尝试下一个域名", log.Any("domain", domain), log.E(err))
|
||||
}
|
||||
if lastErr == nil {
|
||||
lastErr = errors.New("adcenter api domain empty")
|
||||
}
|
||||
return nil, lastErr
|
||||
}
|
||||
|
||||
func fetchJtAdvertise(apiDomain string, headers map[string]string, body []byte) (*JtAdvertiseRes, error) {
|
||||
url := apiDomain + "/openapi/getAdvertiseList"
|
||||
var serverResp *JtAdvertiseRes
|
||||
code, err := httputil.Client().POSTJsonWithJResp(&serverResp, url, headers, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if code != http.StatusOK {
|
||||
return nil, fmt.Errorf("response status %d", code)
|
||||
}
|
||||
if serverResp == nil {
|
||||
return nil, errors.New("empty response")
|
||||
}
|
||||
if serverResp.Code != 0 {
|
||||
return nil, fmt.Errorf("response code %d: %s", serverResp.Code, serverResp.Msg)
|
||||
}
|
||||
return serverResp, nil
|
||||
}
|
||||
|
||||
// getFromLastTime 返回最后一次请求的广告数据
|
||||
func getFromLastTime(foreverCacheKey string, e error) ([]AdSlot, error) {
|
||||
str, err := redis.Get(foreverCacheKey)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("getFromLastTime 缓存获取广告列表信息异常:%v", err))
|
||||
return nil, e
|
||||
}
|
||||
|
||||
var resp []AdSlot
|
||||
if str != nil {
|
||||
if err = json.Unmarshal([]byte(*str), &resp); err == nil {
|
||||
return resp, nil
|
||||
}
|
||||
log.Error(fmt.Sprintf("getFromLastTime 解析缓存数据异常:%v", err))
|
||||
}
|
||||
|
||||
return nil, e
|
||||
}
|
||||
|
||||
// setAdsCache 更新最后一次请求的广告数据
|
||||
func setAdsCache(foreverCacheKey string, resp []AdSlot) {
|
||||
data, err := json.Marshal(&resp)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("setAdsCache json序列化缓存数据异常:%v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if err := redis.Set(foreverCacheKey, data, time.Hour*72); err != nil {
|
||||
log.Error(fmt.Sprintf("setAdsCache 保存缓存数据异常:%v", err))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user