187 lines
4.6 KiB
Go
Raw Normal View History

2025-02-05 18:45:49 +08:00
package logic
import (
"context"
2025-02-07 15:17:01 +08:00
"encoding/json"
"errors"
"fmt"
2025-02-14 17:21:32 +08:00
"gitea.youtukeji.com.cn/youtu/youtu_grpc/app/auth_service/auth"
2025-02-14 17:30:53 +08:00
"gitea.youtukeji.com.cn/youtu/youtu_grpc/app/ecpm_service/ecpm"
"gitea.youtukeji.com.cn/youtu/youtu_grpc/app/ecpm_service/internal/svc"
2025-02-07 15:17:01 +08:00
"io"
"net/http"
"strconv"
"time"
2025-02-05 18:45:49 +08:00
"github.com/zeromicro/go-zero/core/logx"
)
type GetEcpmLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetEcpmLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetEcpmLogic {
return &GetEcpmLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
2025-02-14 10:30:15 +08:00
func (l *GetEcpmLogic) GetEcpm(in *ecpm.GetEcpmRequest) (response *ecpm.GetEcpmResponse, err error) {
2025-02-07 15:17:01 +08:00
//获取抖音accessToken
2025-02-14 10:30:15 +08:00
res, err := l.svcCtx.AuthServiceClient.GetAccessToken(l.ctx, &auth.GetAccessTokenRequest{
2025-02-07 15:17:01 +08:00
AppId: in.AppId,
})
if err != nil {
return nil, err
}
//获取抖音ECPM数据
list, err := GetEcpmData(res.AccessToken, in.AppId, in.OpenId, time.Now().Format(time.DateOnly))
if err != nil {
return nil, err
}
//计算ECPM具体值
2025-02-14 10:39:52 +08:00
ecpmVal := CalcEcpm(list)
2025-02-07 15:17:01 +08:00
//获取后端配置的ECPM值
config, ok := l.svcCtx.EcpmConfig.Get(in.AppId)
if !ok {
return nil, errors.New("未找到小程序配置")
}
//判断是否满足条件
2025-02-17 11:29:48 +08:00
if ecpmVal > config.ECPM && len(list) > config.IPU {
2025-02-07 15:17:01 +08:00
response.Ok = true
}
return
}
2025-02-14 10:43:28 +08:00
// CalcEcpm 计算ECPM值
2025-02-07 15:17:01 +08:00
func CalcEcpm(res []Record) (ecpm float64) {
// 计算 ECPM
totalRecords := len(res)
// 如果没有记录,则返回 0
if totalRecords == 0 {
return
}
totalCost := 0
for _, record := range res {
totalCost += record.Cost
}
// 总 cost / 100000 * 1000 / 总记录数
ecpm = float64(totalCost) / 100000 * 1000 / float64(totalRecords)
return
}
// GetEcpmData 获取ECPM数据
// appId: 小程序id
// openId: 抖音openId
// dateHour: 日期
func GetEcpmData(accessToken, appId, openId, dateHour string) (list []Record, err error) {
list, err = GetEcpm(GetEcpmParams{
AppId: appId,
OpenId: openId,
AccessToken: accessToken,
DateHour: dateHour,
PageSize: 500,
PageNo: 1,
})
return
}
const getEcpm = "https://minigame.zijieapi.com/mgplatform/api/apps/data/get_ecpm" // 获取ECPM
// GetEcpm 获取ECPM
// https://bytedance.larkoffice.com/docx/Vg4yd0RDSovZINxJDyIc6THhnod
// 根据分页大小循环获取,聚合总数返回
func GetEcpm(params GetEcpmParams) (list []Record, err error) {
fullURL := fmt.Sprintf("%s?open_id=%s&mp_id=%s&access_token=%s&date_hour=%s&page_size=500&page_no=", getEcpm, params.OpenId, params.AppId, params.AccessToken, params.DateHour)
for {
fullURL += strconv.Itoa(params.PageNo)
var responseBody []byte
func() {
// 调用抖音 API
var resp *http.Response
resp, err = http.Get(fullURL)
if err != nil {
err = errors.New("调用抖音 API 失败")
return
}
defer resp.Body.Close()
// 读取抖音 API 响应数据
responseBody, err = io.ReadAll(resp.Body)
if err != nil {
err = errors.New("读取抖音 API 响应失败")
return
}
}()
// 解析抖音 API 响应数据
var apiResponse GetEcpmResponseData
err = json.Unmarshal(responseBody, &apiResponse)
if err != nil {
err = errors.New("解析 API 响应数据失败")
return
}
// 检查 API 是否返回错误
if apiResponse.ErrNo != 0 {
err = fmt.Errorf("抖音 API 返回错误: %s (错误码: %d)", apiResponse.ErrMsg, apiResponse.ErrNo)
return
}
list = append(list, apiResponse.Data.Records...)
// 当页数据小于总页数时,无更多数据,返回
if len(apiResponse.Data.Records) <= params.PageSize {
return
}
params.PageNo++
}
}
// GetEcpmParams 获取ECPM参数
type GetEcpmParams struct {
AppId string `json:"app_id" form:"app_id"`
OpenId string `json:"open_id" form:"open_id"`
AccessToken string `json:"access_token" form:"access_token"`
DateHour string `json:"date_hour" form:"date_hour"`
PageSize int `json:"page_size" form:"page_size"`
PageNo int `json:"page_no" form:"page_no"`
}
type GetEcpmResponseData struct {
BaseResp struct {
StatusCode int `json:"StatusCode"`
StatusMessage string `json:"StatusMessage"`
} `json:"BaseResp"`
Data struct {
Records []Record `json:"records"`
Total int `json:"total"`
} `json:"data"`
ErrMsg string `json:"err_msg"`
ErrNo int `json:"err_no"`
LogID string `json:"log_id"`
}
2025-02-05 18:45:49 +08:00
2025-02-07 15:17:01 +08:00
type Record struct {
Aid string `json:"aid"`
Cost int `json:"cost"`
Did string `json:"did"`
EventName string `json:"event_name"`
EventTime string `json:"event_time"`
OpenID string `json:"open_id"`
ID int `json:"id"`
2025-02-05 18:45:49 +08:00
}