全量、增量更新從Facebook獲取的數據的定時任務 代碼實現 schedule-console-調度服務

說明:

廣告系列 : 廣告組關係 1 : n

廣告組 : 廣告關係 1 : n

業務邏輯

全量、增量更新從Facebook獲取的數據的定時任務

  • UpdateFBAds 增量更新facebook 所有廣告數據

    • 1、獲取當前開通自動化投放的賬號(調用application服務獲取Facebook賬戶ID)
    • 2、獲取時間(根據賬戶ID 從redis獲取)
      • 根據賬戶ID 獲取廣告系列最新的更新時間 暫定全量更新,
      • 根據賬戶ID 獲取廣告組最新的更新時間
      • 根據賬戶ID 獲取廣告最新的更新時間
    • 3、全量、增量更新(根據前端傳值 refresh == 0:刷新所有; refresh ==1: 只刷新廣告組和廣告; refresh ==2: 系列)
      • 第一次時間爲0,全量更新;後續只需要過濾出比上次最新更新時間大的數據,增量更新
      • 賬戶的新增廣告系列
        • 請求Facebook獲取數據,若出現錯誤,請求重試機制,重試3次
        • 處理Facebook返回的數據,取出自己需要用的,剩餘的全部存到json對象裏
        • 入庫(mysql、redis)
          • FB返回的結果集是按照時間降序排列的,獲取本次操作的最大更新時間(第0個):accCps[0].UpdatedAt
          • 入庫,gorm的save方法。(save:可更新或新增)
          • 將最新時間set到redis
          • 如果出現錯誤,釘釘報警,發送到我們內部釘釘羣,我們便可以及時查看錯誤
          • message = fmt.Sprintf("[業務異常][服務:%s][主機名:%s] %s", serverName, hostname, message)
      • 賬戶的新增廣告組
        • 與系列同理
      • 賬戶的新增廣告
        • 與系列同理
    • 4、更新完成之後,kafka 通知BI本天更新完成
    • gkafka.ProducerString(context.Background(), "adv_asset_notice", "ok")

代碼實現

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章