河南科技学院三月听我说简友广场

全量、增量更新从Facebook获取的数据的定时任务

2023-01-03  本文已影响0人  胡小毛

说明:

广告系列 : 广告组关系 1 : n

广告组 : 广告关系 1 : n

业务逻辑

全量、增量更新从Facebook获取的数据的定时任务

代码实现

schedule-console-调度服务

internal-cron

cron-cron.go(启动定时任务)

package cron

import (
    "context"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/logic"
    "gitlab.ftsview.com/fotoable-go/gkafka"
    "gitlab.ftsview.com/fotoable-go/glog"

    "github.com/robfig/cron/v3"
)

var cronIns Cron

type Cron struct {
    cron *cron.Cron

    ctx context.Context
}

func InitCron() {
    // nyc, _ := time.LoadLocation("Asia/Shanghai")
    // c := cron.New(cron.WithLocation(nyc))
    c := cron.New()
    c.Start()
    cronIns.cron = c
    cronIns.ctx = context.Background()

    cronIns.StartUpdateFBAdData()
}

func (c Cron) StartCron(timer string, f func()) {
    _, err := c.cron.AddFunc(timer, f)
    if err != nil {
        glog.Errorf(c.ctx, "start cron error %w", err)
    }
}

func (c Cron) StartUpdateFBAdData() {
    // 测试时间5s一更新:@every 5s
    // 01 00 * * *
    c.StartCron("01 00 * * *", func() {
        result, _ := logic.SingletonFbCampaginLogic().UpdateFBAds(cronIns.ctx, nil)
        glog.Infof(cronIns.ctx, "本次更新广告系列数:%d, 广告组数: %d, 广告数: %d", result.Campagins, result.AdSets, result.Ads)
        //TODO 通知BI本天更新完成
        gkafka.ProducerString(context.Background(), "adv_asset_notice", "ok")
    })

    //c.StartCron("30 17 * * *", func() {
    //  if config.GlobConfig.Syncbi {
    //      logic.SingletonFBAdsetLogic().CromSyncBi(cronIns.ctx)
    //  }
    //})
}

internal-logic

logic-fb_campagic.go(广告系列logic)

package logic

import (
    "context"
    "fmt"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/constants"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/handler/bean"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/service"
    "gitlab.ftsview.com/fotoable-go/glog"
    "sync"
    "sync/atomic"
)

var (
    _fbCampaginOnce  sync.Once
    _fbCampaginLogic *FbCampaginLogic
)

type FbCampaginLogic struct {
    fbCampaginService *service.FbCampaginService    // 广告系列service
    ramblerService    *service.RamblerService       // 获取账号service
    fbAdSetService    *service.FbAdSetService       // 广告组service
    fbAdService       *service.FbAdService          // 广告service
}

func SingletonFbCampaginLogic() *FbCampaginLogic {
    _fbCampaginOnce.Do(func() {
        _fbCampaginLogic = &FbCampaginLogic{
            fbCampaginService: service.SingletonFbCampaginService(),
            ramblerService:    service.SingletonRamblerService(),
            fbAdSetService:    service.SingletonFbAdSetService(),
            fbAdService:       service.SingletonFbAdService(),
        }
    })
    return _fbCampaginLogic
}

// UpdateFBAds 增量更新facebook 所有广告数据
func (f *FbCampaginLogic) UpdateFBAds(ctx context.Context,
    fields *bean.CampaignListRequest) (bean.UpdateResult, error) {
    var (
        accountIDs []string
        refresh    int

        campaignCounts int64
        adSetCounts    int64
        adCounts       int64
    )

    if fields != nil {
        accountIDs = fields.AccountID
        refresh = fields.Refresh
    }
    // 获取当前开通自动化投放的账号
    accountList := f.ramblerService.GetFBCruiserAccount(ctx, accountIDs...)
    fmt.Println("accountList:==", accountList)
    for _, it := range accountList {
        // 获取广告系列最新的更新时间 暂定全量更新,
        // 全量更新 ,暂不Redis 设置时间戳,因为 facebook  update_time,预算更改,不会更新update_time字段问题,所以全量拉取
        campaignAt := f.fbCampaginService.GetMaxUpdateTime(ctx, "2543780585639422")
        // 获取广告组最新的更新时间
        adSetAt := f.fbAdSetService.GetMaxUpdateTime(ctx, "2543780585639422")
        // 获取广告最新的更新时间
        adAt := f.fbAdService.GetMaxUpdateTime(ctx, "2543780585639422")

        // 账户的新增广告系列
        // 根据前端传值 refresh == 0:刷新所有; refresh ==1: 只刷新广告组和广告; refresh ==2: 系列
        if refresh == 0 || refresh == 2 {
            campaigns := f.fbCampaginService.GetCampaignsByAccount(ctx, it.AccountId, constants.EmptyString,
                constants.RetryInit, nil, campaignAt)
            glog.Infof(ctx, "account: %s,Insert Campaigns success: %d条,maxTime: %d ", it.AccountId, len(campaigns), campaignAt)
            atomic.AddInt64(&campaignCounts, int64(len(campaigns)))
            f.fbCampaginService.InsertCampaignsByAccount(ctx, campaigns, it.AccountId, constants.RealBoolean)
        }
        if refresh == 0 || refresh == 1 {
            // 账户的新增广告组
            adSets := f.fbAdSetService.GetFBAdSetList(ctx, it.AccountId, constants.EmptyString, constants.RetryInit,
                nil, adSetAt) // > 1669015007 有6条数据
            glog.Infof(ctx, "account: %s,Insert AdSets success: %d条,maxTime: %d ", it.AccountId, len(adSets), adSetAt)
            atomic.AddInt64(&adSetCounts, int64(len(adSets)))
            f.fbAdSetService.InsertAdSetByAccount(ctx, adSets, it.AccountId, constants.RealBoolean)

            // 账户的新增广告
            ads := f.fbAdService.GetFBAdList(ctx, it.AccountId, constants.EmptyString, constants.RetryInit,
                nil, adAt) // > 1669015007 有6条数据
            glog.Infof(ctx, "account: %s,Insert Ads success: %d条,maxTime: %d ", it.AccountId, len(ads), adAt)
            atomic.AddInt64(&adCounts, int64(len(ads)))
            f.fbAdService.InsertAdByAccount(ctx, ads, it.AccountId, constants.RealBoolean)
        }
    }
    result := bean.UpdateResult{
        Campagins: campaignCounts,
        AdSets:    adSetCounts,
        Ads:       adCounts,
    }
    return result, nil
}

// InsertCampaigns 拉取并入库广告系列数据
//func (f *FbcampaginLogic) InsertCampaigns(ctx context.Context) (string, error) {
//  accountList := f.ramblerService.GetFBCruiserAccount(ctx)
//  for _, it := range accountList {
//      timestamp := f.fbcampaginService.GetMaxUpdateTime(ctx, it.AccountId)
//  }
//
//  return "", nil
//}

internal-rpc

rpc - application_console.go

package rpc

import (
    "context"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/handler/bean"
    "gitlab.ftsview.com/fotoable-go/gerrors"
)
import (
    . "gitlab.ftsview.com/micro/application"
)

func FilterFBAccountID(ctx context.Context, accountIDs ...string) ([]bean.AccountID, error) {
    //filter := map[string]interface{}{"is_used_cruiser": true}
    //if len(accountIDs) > 0 {
    //  filter["account_id"] = accountIDs
    //}
    data, err := ApplicationAdapter.FilterFBAccountID(ctx, &FilterFBAccountIDReq{
        Id:              accountIDs,
        IsEnableCruiser: true,
    })
    if err != nil {
        return nil, gerrors.Wrap(err, "rpc FilterFBAccountID err")
    }
    var Ids []bean.AccountID
    for _, it := range data.Id {
        id := bean.AccountID{AccountId: it}
        Ids = append(Ids, id)
    }
    return Ids, nil
}

rpc - client.go

package rpc

import (
    "fmt"
    "strings"

    "gitlab.ftsview.com/aircraft/schedule-console/internal/constants"

    "gitlab.ftsview.com/micro/application"
)

func MustInitRpc(endpoint []string) {
    addr := fmt.Sprintf("etcd://%s", strings.Join(endpoint, constants.Comma))
    application.ApplicationAdapterInit(addr)
}

internal-service

service-rambler.go()

package service

import (
    "context"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/handler/bean"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/rpc"
    "sync"
)

var (
    _ramblerOnce    sync.Once
    _ramblerService *RamblerService
)

type RamblerService struct {
}

func SingletonRamblerService() *RamblerService {
    _ramblerOnce.Do(func() {
        _ramblerService = &RamblerService{}
    })
    return _ramblerService
}

func (l *RamblerService) GetFBCruiserAccount(ctx context.Context, accountIDs ...string) []bean.AccountID {
    //filter := map[string]interface{}{"is_used_cruiser": true}
    //if len(accountIDs) > 0 {
    //  filter["account_id"] = accountIDs
    //}
    res, err := rpc.FilterFBAccountID(ctx, accountIDs...)
    if err != nil {
        return nil
    }
    return res
}

service-fb_campagin.go

package service

import (
    "context"
    "fmt"
    "github.com/spf13/cast"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/config"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/constants"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/handler/bean"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/store/model"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/store/query"
    "gitlab.ftsview.com/fotoable-go/glog"
    "gitlab.ftsview.com/fotoable-go/gmysql"
    "gitlab.ftsview.com/fotoable-go/gredis"
    "gitlab.ftsview.com/fotoable-go/gutil"
    "gopkg.in/resty.v1"
    "strconv"
    "sync"
    "time"
)

var (
    _fbCampaginOnce    sync.Once
    _fbCampaginService *FbCampaginService
)

type FbCampaginService struct {
}

func SingletonFbCampaginService() *FbCampaginService {
    _fbCampaginOnce.Do(func() {
        _fbCampaginService = &FbCampaginService{}
    })
    return _fbCampaginService
}

type FBCursor struct {
    Before string `json:"before"`
    After  string `json:"after"`
}
type FBPaging struct {
    Cursors FBCursor `json:"cursors"`
    Next    string   `json:"next"`
}

// FBCampaignResult FaceBook获取的原始Campagin
type FBCampaignResult struct {
    Data   []bean.FBCampagin `json:"data"`
    Paging FBPaging          `json:"paging"`
}

// FbCampaignResult 处理后返回的Campagin
type FbCampaignResult struct {
    Data   []bean.FbCampagin `json:"data"`
    Paging FBPaging          `json:"paging"`
}

func (f *FbCampaginService) GetMaxUpdateTime(ctx context.Context, accountId string) int64 {
    maxTime, err := gredis.Redis(constants.RedisName).HGet(ctx, constants.CampaignMaxUpdateTime, accountId)
    if err != nil {
        glog.Error(ctx, "GetMaxUpdateTime error: %s  ", err.Error())
        return 0
    }
    return cast.ToInt64(maxTime)
}

//GetCampaignsByAccount 获取广告系列筛选项
func (f *FbCampaginService) GetCampaignsByAccount(ctx context.Context, accountId, url string, retry int,
    accCps []bean.FbCampagin, timestamp int64) []bean.FbCampagin {

    if url == constants.EmptyString {
        filter := []bean.FBFilterParams{{
            Field:    "campaign.delivery_status",
            Operator: "IN",
            Value:    []string{"active", "deleted", "archived", "inactive", "off", "pending"}}}
        url = fmt.Sprintf(`%sact_%s/campaigns/?fields=%s&access_token=%s`,
            config.GlobConfig.FbApi.BaseUrl, accountId, constants.FBCampaignFields, config.GlobConfig.FbApi.AccessToken)
        if timestamp != constants.EmptyInt {
            filter = append(filter, bean.FBFilterParams{
                Field:    "updated_time",
                Operator: "GREATER_THAN",
                Value:    strconv.FormatInt(timestamp, 10)})
        }
        url = fmt.Sprintf(`%s&filtering=%s`, url, gutil.Object2JSON(filter))
    }
    resp, err := resty.New().R().Get(url)
    // 请求重试机制 重试3次
    if err != nil {
        if retry >= constants.RetryNum {
            glog.Error(ctx, "GetCampaignsByAccount error: ", err)
        } else {
            retry++
            f.GetCampaignsByAccount(ctx, accountId, constants.EmptyString, retry, accCps, timestamp)
        }
    }
    glog.Info(ctx, "GetCampaignsByAccount: success ", accountId)
    var result FbCampaignResult
    var faceBookCampagin FBCampaignResult
    if err := gutil.JSON2ObjectE(resp.Body(), &faceBookCampagin); err != nil {
        glog.Errorf(ctx, "GetCampaignsByAccount: json to object error.accountID: %s, error: %s",
            accountId, err.Error())
    }

    result.Paging = faceBookCampagin.Paging
    for _, datum := range faceBookCampagin.Data {
        var data bean.FbCampagin
        // 更新时间
        if datum.UpdatedTime == constants.EmptyString {
            data.UpdatedAt = 0
        } else {
            updatedAt, err := time.Parse(constants.DateLayout, datum.UpdatedTime)
            if err != nil {
                return nil
            }
            data.UpdatedAt = updatedAt.Unix()
        }
        // 创建时间
        if datum.CreatedTime == constants.EmptyString {
            data.CreatedAt = 0
        } else {
            createAt, err := time.Parse(constants.DateLayout, datum.CreatedTime)
            if err != nil {
                return nil
            }
            data.UpdatedAt = createAt.Unix()
        }
        // data
        faceBookCampaginData, err := gutil.Object2JSONE(&datum)
        if err != nil {
            return nil
        }
        data.Data = faceBookCampaginData

        // 主要字段
        data.CampaignID = datum.CampaignID
        data.AccountID = datum.AccountID
        //data.UpdatedAt = updatedAt.Unix()
        data.Name = datum.Name
        //data.CreatedAt = createdAt.Unix()
        data.BidStrategy = datum.BidStrategy
        data.DailyBudget = datum.DailyBudget
        data.EffectiveStatus = datum.EffectiveStatus
        data.LifetimeBudget = datum.LifetimeBudget
        data.SmartPromotionType = datum.SmartPromotionType
        result.Data = append(result.Data, data)
    }
    accCps = append(accCps, result.Data...)
    if result.Paging.Next != constants.EmptyString {
        accCps = f.GetCampaignsByAccount(ctx, accountId, result.Paging.Next, 0, accCps, timestamp)
    }
    return accCps
}

// InsertCampaignsByAccount 广告 系列入库
func (l *FbCampaginService) InsertCampaignsByAccount(ctx context.Context, accCps []bean.FbCampagin, accountID string, types bool) {

    db := gmysql.DB(ctx, config.GlobConfig.Mysql.DBName)
    campaginTable := query.Use(db).FbCampaign

    //FB返回的结果集是按照降序排列的,获取本次操作的最大更新时间
    if len(accCps) == 0 {
        return
    }
    if types { // 增量更新 || 全部新增
        for _, i2 := range accCps {
            var accCampagin model.FbCampaign
            accCampagin.CampaignID = i2.CampaignID
            accCampagin.AccountID = i2.AccountID
            accCampagin.UpdatedAt = i2.UpdatedAt
            accCampagin.Name = i2.Name
            accCampagin.CreatedAt = i2.CreatedAt
            accCampagin.BidStrategy = i2.BidStrategy
            accCampagin.DailyBudget = i2.DailyBudget
            accCampagin.EffectiveStatus = i2.EffectiveStatus
            accCampagin.LifetimeBudget = i2.LifetimeBudget
            accCampagin.SmartPromotionType = i2.SmartPromotionType
            accCampagin.Data = i2.Data
            err := campaginTable.WithContext(ctx).Save(&accCampagin)
            if err != nil {
                glog.Error(ctx, "增量更新 || 全部新增 campaigns error: %s", err.Error())
                return
            }
        }
    }
    //全量更新 ,暂不Redis 设置时间戳,因为   facebopl  update_time,预算更改,不会更新update_time字段问题,所以全量拉取
    //maxTime, err := time.Parse(constants.DateTimeLayout, accCps[0].UpdatedAt)
    //if err != nil {
    //  gutil.DingTalkAlarm(constants.ServiceName, fmt.Sprintf("MaxTime error, %s", accCps[0].UpdatedAt))
    //  glog.Error(ctx, "MaxTime error: %s", err.Error())
    //  return
    //}
    if err := gredis.Redis(constants.RedisName).HSet(
        ctx, constants.CampaignMaxUpdateTime, accountID, cast.ToString(accCps[0].UpdatedAt)); err != nil {
        glog.Error(ctx, "set max time to redis error: %s", err.Error())
        return
    }
}

service-fb_ad_set.go

package service

import (
    "context"
    "fmt"
    "github.com/spf13/cast"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/config"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/constants"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/handler/bean"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/store/model"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/store/query"
    "gitlab.ftsview.com/fotoable-go/glog"
    "gitlab.ftsview.com/fotoable-go/gmysql"
    "gitlab.ftsview.com/fotoable-go/gredis"
    "gitlab.ftsview.com/fotoable-go/gutil"
    "gopkg.in/resty.v1"
    "strconv"
    "sync"
    "time"
)

var (
    _fbAdSetOnce    sync.Once
    _fbAdSetService *FbAdSetService
)

type FbAdSetService struct {
}

func SingletonFbAdSetService() *FbAdSetService {
    _fbAdSetOnce.Do(func() {
        _fbAdSetService = &FbAdSetService{}
    })
    return _fbAdSetService
}

// FBAdsetResult FaceBook获取的原始广告组Adset
type FBAdsetResult struct {
    Data   []bean.FBAdSet `json:"data"`
    Paging FBPaging       `json:"paging"`
}

// FbAdsetResult 处理后返回的广告组Adset
type FbAdsetResult struct {
    Data   []bean.FbAdset `json:"data"`
    Paging FBPaging       `json:"paging"`
}

//GetMaxUpdateTime 获取广告组最新的数据更新时间
func (f *FbAdSetService) GetMaxUpdateTime(ctx context.Context, accountId string) int64 {
    maxTime, err := gredis.Redis(constants.RedisName).HGet(ctx, constants.AdSetMaxUpdateTime, accountId)
    if err != nil {
        glog.Error(ctx, "GetMaxUpdateTime error: %s  ", err.Error())
        return 0
    }
    return cast.ToInt64(maxTime)
}

//GetFBAdSetList 获取fb后台的广告组列表
func (f *FbAdSetService) GetFBAdSetList(ctx context.Context, accountId, url string, retry int, accCps []bean.FbAdset,
    timestamp int64) []bean.FbAdset {

    if url == constants.EmptyString {
        filter := []bean.FBFilterParams{{
            Field:    "adset.delivery_status",
            Operator: "IN",
            Value:    []string{"active", "deleted", "archived", "inactive", "off", "pending"}}}
        url = fmt.Sprintf(`%sact_%s/adsets/?fields=%s&access_token=%s`, config.GlobConfig.FbApi.BaseUrl,
            accountId, constants.FBAdSetFields, config.GlobConfig.FbApi.AccessToken)
        if timestamp != constants.EmptyInt {
            filter = append(filter, bean.FBFilterParams{
                Field:    "updated_time",
                Operator: "GREATER_THAN",
                Value:    strconv.FormatInt(timestamp, 10)})
        }
        url = fmt.Sprintf(`%s&filtering=%s`, url, gutil.Object2JSON(filter))
    }
    resp, err := resty.New().R().Get(url)
    // 请求重试机制 重试3次
    if err != nil {
        if retry >= constants.RetryNum {
            glog.Error(ctx, "GetAdSetsByAccount error: ", err)
        } else {
            retry++
            f.GetFBAdSetList(ctx, accountId, constants.EmptyString, retry, accCps, timestamp)
        }
    }
    glog.Info(ctx, "GetAdSetsByAccount: success ", accountId)
    var (
        result        FbAdsetResult // 处理后返回的广告组Adset
        facebookAdSet FBAdsetResult // FaceBook获取的原始广告组Adset
    )
    if err := gutil.JSON2ObjectE(resp.Body(), &facebookAdSet); err != nil {
        glog.Errorf(ctx, "GetCampaignsByAccount: json to object error.accountID: %s, error: %s",
            accountId, err.Error())
    }

    result.Paging = facebookAdSet.Paging
    for _, datum := range facebookAdSet.Data {
        adSet := bean.FbAdset{
            AdsetID:         datum.AdsetID,
            CampaignID:      datum.CampaignID,
            AccountID:       datum.AccountID,
            Name:            datum.Name,
            EffectiveStatus: datum.EffectiveStatus,
            BidStrategy:     datum.BidStrategy,
            DailyBudget:     datum.DailyBudget,
            LifetimeBudget:  datum.LifetimeBudget,
            //AttributionSpec:  datum.AttributionSpec,
            OptimizationGoal: datum.OptimizationGoal,
            //CreatedAt: 0,
            //UpdatedAt: 0
            //Data:             "",
        }
        // 更新时间
        if datum.UpdatedTime == constants.EmptyString {
            adSet.UpdatedAt = 0
        } else {
            updatedAt, err := time.Parse(constants.DateLayout, datum.UpdatedTime)
            if err != nil {
                return nil
            }
            adSet.UpdatedAt = updatedAt.Unix()
        }
        // 创建时间
        if datum.CreatedTime == constants.EmptyString {
            adSet.CreatedAt = 0
        } else {
            createAt, err := time.Parse(constants.DateLayout, datum.CreatedTime)
            if err != nil {
                return nil
            }
            adSet.UpdatedAt = createAt.Unix()
        }
        // AttributionSpec
        attributionSpec, err := gutil.Object2JSONE(&datum.AttributionSpec)
        if err != nil {
            return nil
        }
        adSet.AttributionSpec = attributionSpec
        // Data
        adSetData, err := gutil.Object2JSONE(&datum)
        if err != nil {
            return nil
        }
        adSet.Data = adSetData
        result.Data = append(result.Data, adSet)
    }
    accCps = append(accCps, result.Data...)
    if result.Paging.Next != constants.EmptyString {
        accCps = f.GetFBAdSetList(ctx, accountId, result.Paging.Next, constants.RetryInit, accCps, timestamp)
    }
    return accCps
}

// InsertAdSetByAccount 广告组信息入库
func (f *FbAdSetService) InsertAdSetByAccount(ctx context.Context, accCps []bean.FbAdset, accountID string, types bool) {

    db := gmysql.DB(ctx, config.GlobConfig.Mysql.DBName)
    adSetTable := query.Use(db).FbAdSet

    //FB返回的结果集是按照降序排列的,获取本次操作的最大更新时间
    if len(accCps) == 0 {
        return
    }
    if types { // 增量更新 || 全部新增
        for _, cp := range accCps {
            accAdSet := model.FbAdSet{
                AdsetID:          cp.AdsetID,
                CampaignID:       cp.CampaignID,
                AccountID:        cp.AccountID,
                Name:             cp.Name,
                EffectiveStatus:  cp.EffectiveStatus,
                BidStrategy:      cp.BidStrategy,
                DailyBudget:      cp.DailyBudget,
                LifetimeBudget:   cp.LifetimeBudget,
                AttributionSpec:  cp.AttributionSpec,
                OptimizationGoal: cp.OptimizationGoal,
                CreatedAt:        cp.CreatedAt,
                UpdatedAt:        cp.UpdatedAt,
                Data:             cp.Data,
            }
            err := adSetTable.WithContext(ctx).Save(&accAdSet)
            if err != nil {
                glog.Error(ctx, "增量更新 || 全部新增 adSet error: %s", err.Error())
                return
            }
        }
    }
    // 全量更新 ,暂不Redis 设置时间戳,因为   facebopl  update_time,预算更改,不会更新update_time字段问题,所以全量拉取
    //maxTime, err := time.Parse(constants.DateTimeLayout, accCps[0].UpdatedAt)
    //if err != nil {
    //  gutil.DingTalkAlarm(constants.ServiceName, fmt.Sprintf("adset MaxTime error, %s adsetID: %s",
    //      accCps[0].UpdatedAt,
    //      accCps[0].AdsetID))
    //  glog.Error(ctx, "adset MaxTime error: %s", err.Error())
    //  return
    //}
    if err := gredis.Redis(constants.RedisName).HSet(
        ctx, constants.AdSetMaxUpdateTime, accountID, cast.ToString(accCps[0].UpdatedAt)); err != nil {
        glog.Error(ctx, "adset set max time to redis error: %s", err.Error())
        return
    }
}

service-fb_ad.go

package service

import (
    "context"
    "fmt"
    "github.com/spf13/cast"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/config"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/constants"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/handler/bean"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/store/model"
    "gitlab.ftsview.com/aircraft/schedule-console/internal/store/query"
    "gitlab.ftsview.com/fotoable-go/glog"
    "gitlab.ftsview.com/fotoable-go/gmysql"
    "gitlab.ftsview.com/fotoable-go/gredis"
    "gitlab.ftsview.com/fotoable-go/gutil"
    "gopkg.in/resty.v1"
    "strconv"
    "sync"
    "time"
)

var (
    _fbAdOnce    sync.Once
    _fbAdService *FbAdService
)

type FbAdService struct {
}

func SingletonFbAdService() *FbAdService {
    _fbAdOnce.Do(func() {
        _fbAdService = &FbAdService{}
    })
    return _fbAdService
}

// FBAdResult FaceBook获取的原始广告Ad
type FBAdResult struct {
    Data   []bean.FBAd `json:"data"`
    Paging FBPaging    `json:"paging"`
}

// FbAdResult 处理后返回的广告Ad
type FbAdResult struct {
    Data   []bean.FbAd `json:"data"`
    Paging FBPaging    `json:"paging"`
}

//GetMaxUpdateTime 获取广告新的数据更新时间
func (f *FbAdService) GetMaxUpdateTime(ctx context.Context, accountId string) int64 {
    maxTime, err := gredis.Redis(constants.RedisName).HGet(ctx, constants.AdMaxUpdateTime, accountId)
    if err != nil {
        glog.Error(ctx, "GetMaxUpdateTime error: %s  ", err.Error())
        return 0
    }
    return cast.ToInt64(maxTime)
}

//GetFBAdList 获取fb的广告数据
func (f *FbAdService) GetFBAdList(ctx context.Context, accountId, url string, retry int, accCps []bean.FbAd,
    timestamp int64) []bean.FbAd {

    if url == constants.EmptyString {
        filter := []bean.FBFilterParams{{
            Field:    "ad.delivery_status",
            Operator: "IN",
            Value:    []string{"active", "deleted", "archived", "inactive", "off", "pending"}}}
        url = fmt.Sprintf(`%sact_%s/ads/?fields=%s&access_token=%s`, config.GlobConfig.FbApi.BaseUrl,
            accountId, constants.FBAdFields, config.GlobConfig.FbApi.AccessToken)
        // 增量拉取数据
        if timestamp != constants.EmptyInt {
            filter = append(filter, bean.FBFilterParams{
                Field:    "updated_time",
                Operator: "GREATER_THAN",
                Value:    strconv.FormatInt(timestamp, 10)})
        }
        url = fmt.Sprintf(`%s&filtering=%s`, url, gutil.Object2JSON(filter))
    }
    resp, err := resty.New().R().Get(url)
    glog.Info(ctx, url)
    // 请求重试机制 重试3次
    if err != nil {
        if retry >= constants.RetryNum {
            glog.Error(ctx, "GetAdsByAccount error: ", err)
        } else {
            retry++
            f.GetFBAdList(ctx, accountId, constants.EmptyString, retry, accCps, timestamp)
        }
    }
    glog.Info(ctx, "GetAdsByAccount: success ", accountId)
    var (
        result     FbAdResult // 处理后返回的广告Ad
        facebookAd FBAdResult // FaceBook获取的原始广告Ad
    )

    if err = gutil.JSON2ObjectE(resp.Body(), &facebookAd); err != nil {
        glog.Errorf(ctx, "GetAdsByAccount: json to object error.accountID: %s, error: %s",
            accountId, err.Error())
    }
    result.Paging = facebookAd.Paging
    for _, datum := range facebookAd.Data {
        ad := bean.FbAd{
            AdID:            datum.AdID,
            AdsetID:         datum.AdsetID,
            CampaignID:      datum.CampaignID,
            CreativeID:      datum.Creative["id"],
            Name:            datum.Name,
            EffectiveStatus: datum.EffectiveStatus,
            //CreatedAt:       0,
            //UpdatedAt:       0,
            //Data:            "",
        }
        // 更新时间
        if datum.UpdatedTime == constants.EmptyString {
            ad.UpdatedAt = 0
        } else {
            updatedAt, err := time.Parse(constants.DateLayout, datum.UpdatedTime)
            if err != nil {
                return nil
            }
            ad.UpdatedAt = updatedAt.Unix()
        }
        // 创建时间
        if datum.CreatedTime == constants.EmptyString {
            ad.CreatedAt = 0
        } else {
            createAt, err := time.Parse(constants.DateLayout, datum.CreatedTime)
            if err != nil {
                return nil
            }
            ad.UpdatedAt = createAt.Unix()
        }
        // Data
        adData, err := gutil.Object2JSONE(&datum)
        if err != nil {
            return nil
        }
        ad.Data = adData
        result.Data = append(result.Data, ad)
    }
    accCps = append(accCps, result.Data...)
    if result.Paging.Next != constants.EmptyString {
        accCps = f.GetFBAdList(ctx, accountId, result.Paging.Next, constants.RetryInit, accCps, timestamp)
    }
    return accCps
}

//InsertAdByAccount 入库广告数据
func (f *FbAdService) InsertAdByAccount(ctx context.Context, accCps []bean.FbAd, accountID string, types bool) {

    db := gmysql.DB(ctx, config.GlobConfig.Mysql.DBName)
    adTable := query.Use(db).FbAd

    //FB返回的结果集是按照降序排列的,获取本次操作的最大更新时间
    if len(accCps) == 0 {
        return
    }
    if types { // 增量更新
        for _, cp := range accCps {
            accAd := model.FbAd{
                AdID:            cp.AdID,
                AdsetID:         cp.AdsetID,
                CampaignID:      cp.CampaignID,
                CreativeID:      cp.CreativeID,
                Name:            cp.Name,
                EffectiveStatus: cp.EffectiveStatus,
                CreatedAt:       cp.CreatedAt,
                UpdatedAt:       cp.UpdatedAt,
                Data:            cp.Data,
            }
            err := adTable.WithContext(ctx).Save(&accAd)
            if err != nil {
                glog.Error(ctx, "增量更新 || 全部新增 ad error: %s", err.Error())
                return
            }
        }
    }
    //默认情况FB返回的为降序
    //maxTime, err := time.Parse(constants.DateTimeLayout, accCps[0].UpdatedAt)
    //if err != nil {
    //  gutil.DingTalkAlarm(constants.ServiceName, fmt.Sprintf("MaxTime error, %s", accCps[0].UpdatedAt))
    //  glog.Error(ctx, "MaxTime error: %s", err.Error())
    //  return
    //}

    //插入BI库
    //f.SyncMaterial(ctx, accCps, accountID)

    glog.Infof(ctx, "accountID max update_time. %s", cast.ToString(accCps[0].UpdatedAt))
    if err := gredis.Redis(constants.RedisName).HSet(
        ctx, constants.AdMaxUpdateTime, accountID, cast.ToString(accCps[0].UpdatedAt)); err != nil {
        glog.Error(ctx, "ad set max time to redis error: %s", err.Error())
        return
    }
}

internal - handler

bean - common.go

package bean

type BaseRequest struct {
    CompanyID int         `json:"company_id"`
    AppID     interface{} `json:"app_id"`
    GameID    interface{} `json:"game_id"`
}

type BasePageRequest struct {
    Page int64 `json:"page"`
    Size int64 `json:"size"`
}

type AccountID struct {
    AccountId string `json:"id"`
}

bean - fb_ad.go

package bean

// FbAd mapped from table <fb_ad>
type FbAd struct {
    AdID            string `gorm:"column:ad_id;primaryKey" json:"ad_id"`                     // 广告ID
    AdsetID         string `gorm:"column:adset_id;not null" json:"adset_id"`                 // 广告组ID
    CampaignID      string `gorm:"column:campaign_id;not null" json:"campaign_id"`           // 广告系列ID
    CreativeID      string `gorm:"column:creative_id;not null" json:"creative_id"`           // 广告创意ID
    Name            string `gorm:"column:name;not null" json:"name"`                         // 广告名称
    EffectiveStatus string `gorm:"column:effective_status;not null" json:"effective_status"` // 广告状态
    CreatedAt       int64  `gorm:"column:created_at;autoUpdateTime" json:"created_at"`       // 创建时间戳
    UpdatedAt       int64  `gorm:"column:updated_at;autoCreateTime" json:"updated_at"`       // 更新时间戳
    Data            string `gorm:"column:data" json:"data"`
}

// FBAd FaceBook原始广告数据
type (
    FBAd struct {
        // ID                       primitive.ObjectID `bson:"_id" json:"-"`
        CompanyID            int                      `bson:"company_id" json:"company_id,omitempty"`
        AdID                 string                   `bson:"ad_id" json:"id,omitempty"`
        AccountID            string                   `bson:"account_id" json:"account_id,omitempty"`
        AdReviewFeedback     map[string]interface{}   `bson:"ad_review_feedback" json:"ad_review_feedback,omitempty"` // 审阅后的审阅反馈
        Adlabels             []Adlabels               `bson:"adlabels" json:"adlabels,omitempty"`                     // 与此活动关联的广告标签
        Adset                map[string]interface{}   `bson:"adset" json:"adset,omitempty"`                           // 包含此广告的广告组
        AdsetID              string                   `bson:"adset_id,omitempty" json:"adset_id,omitempty"`           // 包含此广告的光广告组ID
        BidAmount            int32                    `bson:"bid_amount,omitempty" json:"bid_amount,omitempty"`       // 广告的出价金额
        Campaign             map[string]interface{}   `bson:"campaign" json:"campaign,omitempty"`                     //广告的广告系列
        CampaignID           string                   `bson:"campaign_id" json:"campaign_id,omitempty"`
        ConfiguredStatus     string                   `bson:"configured_status" json:"configured_status,omitempty"`           //  广告的配置状态
        ConversionDomain     string                   `bson:"conversion_domain" json:"conversion_domain,omitempty"`           //  发生转换的域
        Creative             map[string]string        `bson:"creative" json:"creative,omitempty"`                             //  广告创意
        EffectiveStatus      string                   `bson:"effective_status" json:"effective_status,omitempty"`             // ad的有效状态
        IssuesInfo           []map[string]interface{} `bson:"issues_info,omitempty" json:"issues_info,omitempty"`             // 广告的问题使其无法交付
        LastUpdatedByAppId   string                   `bson:"last_updated_by_app_id" json:"last_updated_by_app_id,omitempty"` // 指示用于广告最新更新的应用程序。
        Name                 string                   `bson:"name" json:"name,omitempty"`                                     //  广告名称
        PreviewShareableLink string                   `bson:"preview_shareable_link" json:"preview_shareable_link,omitempty"` // 允许用户在不同位置预览广告的链接
        Recommendations      []map[string]interface{} `bson:"recommendations" json:"recommendations,omitempty"`               //  广告的建议
        SourceAd             map[string]interface{}   `bson:"source_ad" json:"source_ad,omitempty"`                           //  复制此广告的源广告
        SourceAdId           string                   `bson:"source_ad_id" json:"source_ad_id,omitempty"`                     //  复制此广告的源广告id
        Status               string                   `bson:"status,omitempty" json:"status,omitempty"`                       // 状态
        TrackingSpecs        []map[string]interface{} `bson:"tracking_specs" json:"tracking_specs,omitempty"`                 //  跟踪规范
        CreatedTime          string                   `bson:"created_time" json:"created_time,omitempty"`
        UpdatedTime          string                   `bson:"updated_time" json:"updated_time,omitempty"`
    }
)

bean - fb_ad_set.go

package bean

// AdSetListRequest 广告组请求参数
type AdSetListRequest struct {
    BaseRequest
    BasePageRequest
    AccountID  []string `json:"account_ids"`
    CampaignID []string `json:"campaign_ids"`
    Status     []string `json:"status"`
    AdSetID    string   `json:"adset_id"`
    KeyWord    string   `json:"keyword"`
}

// FbAdset mapped from table <fb_adset>
type FbAdset struct {
    AdsetID          string `gorm:"column:adset_id;primaryKey" json:"adset_id"`                 // 广告组ID
    CampaignID       string `gorm:"column:campaign_id;not null" json:"campaign_id"`             // 广告系列ID
    AccountID        string `gorm:"column:account_id;not null" json:"account_id"`               // 账号ID
    Name             string `gorm:"column:name;not null" json:"name"`                           // 广告组名称
    EffectiveStatus  string `gorm:"column:effective_status;not null" json:"effective_status"`   // 广告组状态
    BidStrategy      string `gorm:"column:bid_strategy;not null" json:"bid_strategy"`           // 广告组策略
    DailyBudget      string `gorm:"column:daily_budget;not null" json:"daily_budget"`           // 日预算
    LifetimeBudget   string `gorm:"column:lifetime_budget;not null" json:"lifetime_budget"`     // 总预算
    AttributionSpec  string `gorm:"column:attribution_spec" json:"attribution_spec"`            // 归因设置
    OptimizationGoal string `gorm:"column:optimization_goal;not null" json:"optimization_goal"` // 优化目标
    CreatedAt        int64  `gorm:"column:created_at;autoUpdateTime" json:"created_at"`         // 创建时间戳
    UpdatedAt        int64  `gorm:"column:updated_at;autoCreateTime" json:"updated_at"`         // 更新时间戳
    Data             string `gorm:"column:data" json:"data"`
}

// FBAdSet FaceBook原始广告组数据
type (
    FBAdSet struct {
        // ID                       primitive.ObjectID `bson:"_id" json:"-"`
        CompanyID                    int                      `bson:"company_id" json:"company_id,omitempty"`
        CampaignID                   string                   `bson:"campaign_id" json:"campaign_id,omitempty"`
        AdsetID                      string                   `bson:"adset_id" json:"id,omitempty"`
        AccountID                    string                   `bson:"account_id" json:"account_id,omitempty"`
        AdsetSchedule                []map[string]interface{} `bson:"adset_schedule" json:"adset_schedule,omitempty"`             // 广告集计划,表示一天的交货时间表
        AssetFeedId                  string                   `bson:"asset_feed_id" json:"asset_feed_id,omitempty"`               // 包含内容以创建广告的资产源的ID
        Adlabels                     []Adlabels               `bson:"adlabels" json:"adlabels,omitempty"`                         // 与此活动关联的广告标签
        AttributionSpec              []SpecEvents             `bson:"attribution_spec" json:"attribution_spec,omitempty"`         // 转化时间窗
        BidAdjustments               map[string]interface{}   `bson:"bid_adjustments,omitempty" json:"bid_adjustments,omitempty"` // 投标调整类型到值的映射
        BidAmount                    int32                    `bson:"bid_amount,omitempty" json:"bid_amount,omitempty"`           // 广告组投放上线
        BidConstraints               map[string]interface{}   `bson:"bid_constraints,omitempty" json:"bid_constraints,omitempty"` //广告组投标限制条件
        BidInfo                      map[string]interface{}   `bson:"bid_info,omitempty" json:"bid_info,omitempty"`               //  投标目标与投标价值的关系图
        BidStrategy                  string                   `bson:"bid_strategy" json:"bid_strategy,omitempty"`                 //  竞价策略
        BillingEvent                 string                   `bson:"billing_event" json:"billing_event,omitempty"`               //  计费事件
        BudgetRemaining              string                   `bson:"budget_remaining" json:"budget_remaining,omitempty"`         // 广告组剩余预算
        Campaign                     map[string]interface{}   `bson:"campaign" json:"campaign,omitempty"`                         // 广告系列
        ConfiguredStatus             string                   `bson:"configured_status" json:"configured_status,omitempty"`       // 广告组级别的状态
        ContextualBundlingSpec       map[string]interface{}   `bson:"contextual_bundling_spec,omitempty" json:"contextual_bundling_spec,omitempty"`
        CreativeSequence             []interface{}            `bson:"creative_sequence" json:"creative_sequence,omitempty"`           // 向用户显示adgroup序列的顺序
        DailyBudget                  string                   `bson:"daily_budget" json:"daily_budget,omitempty"`                     //  竞选活动的每日预算
        DailyMinSpendTarget          string                   `bson:"daily_min_spend_target" json:"daily_min_spend_target,omitempty"` //  每日最低支出目标
        DailySpendCap                string                   `bson:"daily_spend_cap" json:"daily_spend_cap,omitempty"`               //  每日支出上限
        DestinationType              string                   `bson:"destination_type" json:"destination_type,omitempty"`             //  广告组的投放目标类型(平台)
        EffectiveStatus              string                   `bson:"effective_status" json:"effective_status,omitempty"`
        EndTime                      string                   `bson:"end_time" json:"end_time,omitempty"`                                             // 结束时间
        FrequencyControlSpecs        []map[string]interface{} `bson:"frequency_control_specs" json:"frequency_control_specs,omitempty"`               //  频率控制规格数组
        InstagramActorId             string                   `bson:"instagram_actor_id" json:"instagram_actor_id,omitempty"`                         // Instagram帐户id
        IsDynamicCreative            bool                     `bson:"is_dynamic_creative" json:"is_dynamic_creative,omitempty"`                       // 是否为动态创意广告组
        IssuesInfo                   []map[string]interface{} `bson:"issues_info,omitempty" json:"issues_info,omitempty"`                             // 错误支付信息
        LearningStageInfo            map[string]interface{}   `bson:"learning_stage_info" json:"learning_stage_info,omitempty"`                       // 有关排名或发布系统是否仍在学习此广告集的信息
        LifetimeBudget               string                   `bson:"lifetime_budget" json:"lifetime_budget,omitempty"`                               //  终身预算
        LifetimeImps                 int32                    `bson:"lifetime_imps" json:"lifetime_imps,omitempty"`                                   //  终生印象 仅适用于具有buying_type=FIXED_CPM
        LifetimeSpendCap             string                   `bson:"lifetime_spend_cap" json:"lifetime_spend_cap,omitempty"`                         // 终身开支上限
        LifetimeMinSpendTarget       string                   `bson:"lifetime_min_spend_target" json:"lifetime_min_spend_target,omitempty"`           // 终身最低花费目标
        MultiOptimizationGoalWeight  string                   `bson:"multi_optimization_goal_weight" json:"multi_optimization_goal_weight,omitempty"` // 多目标优化
        Name                         string                   `bson:"name" json:"name,omitempty"`                                                     //  广告组名称
        OptimizationGoal             string                   `bson:"optimization_goal" json:"optimization_goal,omitempty"`                           // 优化方式
        OptimizationSubEvent         string                   `bson:"optimization_sub_event" json:"optimization_sub_event,omitempty"`                 // 优化子事件
        PacingType                   []string                 `bson:"pacing_type,omitempty" json:"pacing_type,omitempty"`                             // 定义活动的步调类
        PromotedObject               map[string]interface{}   `bson:"promoted_object,omitempty" json:"promoted_object,omitempty"`                     // 宣传的对象
        Recommendations              []map[string]interface{} `bson:"recommendations,omitempty" json:"recommendations,omitempty"`                     // 活动的建议
        RecurringBudgetSemantics     bool                     `bson:"recurring_budget_semantics" json:"recurring_budget_semantics,omitempty"`         // 如果这个字段是true,您的日常开支可能会超过您的日常预算,
        ReviewFeedback               string                   `bson:"review_feedback" json:"review_feedback,omitempty"`                               //  动态创意广告评论
        RfPredictionId               string                   `bson:"rf_prediction_id,omitempty" json:"rf_prediction_id,omitempty"`                   // 范围和频率预测ID
        SourceAdset                  map[string]interface{}   `bson:"source_adset" json:"source_adset,omitempty"`                                     //  从中复制此活动的源市场活动id
        SourceAdsetId                string                   `bson:"source_adset_id,omitempty" json:"source_adset_id,omitempty"`                     // 特别广告类别
        StartTime                    string                   `bson:"start_time" json:"start_time,omitempty"`
        Status                       string                   `bson:"status,omitempty" json:"status,omitempty"`                                                     // 状态
        Targeting                    map[string]interface{}   `bson:"targeting" json:"targeting,omitempty"`                                                         // 受众定向
        TargetingOptimizationTypes   []map[string]interface{} `bson:"targeting_optimization_types,omitempty" json:"targeting_optimization_types,omitempty"`         // 将放松的选项作为优化的信号
        TimeBasedAdRotationIdBlocks  []map[string]interface{} `bson:"time_based_ad_rotation_id_blocks,omitempty" json:"time_based_ad_rotation_id_blocks,omitempty"` // 广告组ID的列表
        TimeBasedAdRotationIntervals []map[string]interface{} `bson:"time_based_ad_rotation_intervals,omitempty" json:"time_based_ad_rotation_intervals,omitempty"` // 特定广告创意在活动期间显示的日期范围
        UseNewAppClick               bool                     `bson:"use_new_app_click,omitempty" json:"use_new_app_click,omitempty"`                               // 顶线ID
        CreatedTime                  string                   `bson:"created_time" json:"created_time,omitempty"`
        UpdatedTime                  string                   `bson:"updated_time" json:"updated_time,omitempty"`
    }
    SpecEvents struct {
        EventType  string `json:"event_type" bson:"event_type"`
        WindowDays int    `json:"window_days" bson:"window_days"`
    }
)

bean - fb_campagin.go

package bean

import gginutil "gitlab.ftsview.com/fotoable-go/ggin-util"

// CampaignListRequest 广告系列列表请求参数
type CampaignListRequest struct {
    BaseRequest
    BasePageRequest
    Header             gginutil.HeaderToB `json:"-"`
    AccountID          []string           `json:"account_ids"`
    CampaignID         string             `json:"campaign_id"`
    Objective          string             `json:"objective"`
    SmartPromotionType string             `json:"smart_promotion_type"`
    Status             []string           `json:"status"`
    KeyWord            string             `json:"keyword"`
    Refresh            int                `json:"refresh"`
}

type UpdateResult struct {
    Campagins int64 `json:"campagins"`
    AdSets    int64 `json:"adSets"`
    Ads       int64 `json:"ads"`
}

type FbCampagin struct {
    CampaignID         string `gorm:"column:campaign_id;primaryKey" json:"campaign_id"`                 // 系列ID
    AccountID          string `gorm:"column:account_id;not null" json:"account_id"`                     // 账号ID
    Name               string `gorm:"column:name;not null" json:"name"`                                 // 系列名称
    EffectiveStatus    string `gorm:"column:effective_status;not null" json:"effective_status"`         // 状态
    SmartPromotionType string `gorm:"column:smart_promotion_type;not null" json:"smart_promotion_type"` // 广告系列类型
    BidStrategy        string `gorm:"column:bid_strategy;not null" json:"bid_strategy"`                 // 广告系列策略
    DailyBudget        string `gorm:"column:daily_budget;not null" json:"daily_budget"`                 // 日预算
    LifetimeBudget     string `gorm:"column:lifetime_budget;not null" json:"lifetime_budget"`           // 总预算
    CreatedAt          int64  `gorm:"column:created_at;autoUpdateTime" json:"created_at"`               // 创建时间戳
    UpdatedAt          int64  `gorm:"column:updated_at;autoCreateTime" json:"updated_at"`               // 更新时间戳
    Data               string `gorm:"column:data" json:"data"`
}

type FBFilterParams struct {
    Field    string      `json:"field"`
    Operator string      `json:"operator"`
    Value    interface{} `json:"value"`
}

// FaceBook原始Campagin数据
type (
    FBCampagin struct {
        // ID                       primitive.ObjectID `bson:"_id" json:"-"`
        CompanyID                int                      `bson:"company_id" json:"company_id,omitempty"`
        CampaignID               string                   `bson:"campaign_id" json:"id,omitempty"`
        AccountID                string                   `bson:"account_id" json:"account_id,omitempty"`
        AdStrategyGroupId        string                   `bson:"ad_strategy_group_id" json:"ad_strategy_group_id,omitempty"`     // 广告策略组ID
        AdStrategyId             string                   `bson:"ad_strategy_id" json:"ad_strategy_id,omitempty"`                 // 广告策略ID
        Adlabels                 []Adlabels               `bson:"adlabels" json:"adlabels,omitempty"`                             // 与此活动关联的广告标签
        BidStrategy              string                   `bson:"bid_strategy" json:"bid_strategy,omitempty"`                     // 竞价策略
        BoostedObjectId          string                   `bson:"boosted_object_id,omitempty" json:"boosted_object_id,omitempty"` // 此活动关联的增强对象
        BrandLiftStudies         []map[string]interface{} `bson:"brand_lift_studies,omitempty" json:"brand_lift_studies,omitempty"`
        BudgetRebalanceFlag      bool                     `bson:"budget_rebalance_flag" json:"budget_rebalance_flag"`
        BudgetRemaining          string                   `bson:"budget_remaining" json:"budget_remaining,omitempty"`                       //剩余预算
        BuyingType               string                   `bson:"buying_type" json:"buying_type,omitempty"`                                 //  购买类型
        CanCreateBrandLiftStudy  bool                     `bson:"can_create_brand_lift_study" json:"can_create_brand_lift_study,omitempty"` //  是否提升研究
        CanUseSpendCap           bool                     `bson:"can_use_spend_cap" json:"can_use_spend_cap,omitempty"`                     //  竞选活动能否设定开支上限
        ConfiguredStatus         string                   `bson:"configured_status" json:"configured_status"`
        CreatedTime              string                   `bson:"created_time" json:"created_time,omitempty"`
        DailyBudget              string                   `bson:"daily_budget" json:"daily_budget,omitempty"` //  竞选活动的每日预算
        EffectiveStatus          string                   `bson:"effective_status" json:"effective_status,omitempty"`
        IsSkadnetworkAttribution bool                     `bson:"is_skadnetwork_attribution" json:"is_skadnetwork_attribution,omitempty"` //  设置为时true表示活动将包括SKAdNetwork,iOS 14。
        IssuesInfo               []map[string]interface{} `bson:"issues_info,omitempty" json:"issues_info,omitempty"`                     // 错误支付信息
        LastBudgetTogglingTime   string                   `bson:"last_budget_toggling_time" json:"last_budget_toggling_time,omitempty"`   // 上次预算切换时间
        LifetimeBudget           string                   `bson:"lifetime_budget" json:"lifetime_budget,omitempty"`                       //  竞选的终身预算
        Name                     string                   `bson:"name" json:"name,omitempty"`                                             //  广告系列名称
        Objective                string                   `bson:"objective" json:"objective,omitempty"`                                   //  活动目标
        PacingType               []string                 `bson:"pacing_type,omitempty" json:"pacing_type,omitempty"`                     // 定义活动的步调类
        PromotedObject           map[string]interface{}   `bson:"promoted_object,omitempty" json:"promoted_object,omitempty"`             // 宣传的对象
        Recommendations          []map[string]interface{} `bson:"recommendations,omitempty" json:"recommendations,omitempty"`             // 活动的建议
        SmartPromotionType       string                   `bson:"smart_promotion_type,omitempty" json:"smart_promotion_type,omitempty"`   // 智能促销类型
        SourceCampaign           map[string]interface{}   `bson:"source_campaign,omitempty" json:"source_campaign,omitempty"`
        SourceCampaignId         string                   `bson:"source_campaign_id" json:"source_campaign_id,omitempty"`                             //  从中复制此活动的源市场活动id
        SpecialAdCategories      []string                 `bson:"special_ad_categories,omitempty" json:"special_ad_categories,omitempty"`             // 特别广告类别
        SpecialAdCategory        string                   `bson:"special_ad_category,omitempty" json:"special_ad_category,omitempty"`                 // 特别广告类别
        SpecialAdCategoryCountry []string                 `bson:"special_ad_category_country,omitempty" json:"special_ad_category_country,omitempty"` // 特殊广告类别的国家/地区字段。
        SpendCap                 string                   `bson:"spend_cap,omitempty" json:"spend_cap,omitempty"`                                     // 开支上限
        StartTime                string                   `bson:"start_time" json:"start_time,omitempty"`
        Status                   string                   `bson:"status,omitempty" json:"status,omitempty"` // 状态
        StopTime                 string                   `bson:"stop_time" json:"stop_time,omitempty"`
        ToplineId                string                   `bson:"topline_id,omitempty" json:"topline_id,omitempty"` // 顶线ID
        UpdatedTime              string                   `bson:"updated_time" json:"updated_time,omitempty"`
    }
    Adlabels struct {
        ID          string `bson:"id" json:"id"`
        Name        string `bson:"name" json:"name"`
        CreatedTime string `bson:"created_time" json:"created_time"`
        UpdatedTime string `bson:"updated_time" json:"updated_time"`
    }
)

redis

redis
上一篇下一篇

猜你喜欢

热点阅读