This commit is contained in:
xiabin 2025-01-14 18:35:29 +08:00
parent 9ab243733d
commit 75d43bc3b7
3 changed files with 123 additions and 12 deletions

View File

@ -12,9 +12,6 @@ import (
// 正式地址
const accessTokenURL = "https://developer.toutiao.com/api/apps/v2/token"
// 沙盒地址
const sandBoxTokenURL = "https://open-sandbox.douyin.com/api/apps/v2/token"
// AccessToken 管理AccessToken 的基础接口
type AccessToken interface {
GetCacheKey() string // 获取缓存的key
@ -37,7 +34,7 @@ type DefaultAccessToken struct {
}
// NewDefaultAccessToken 实例化默认的token管理类
func NewDefaultAccessToken(appId, appSecret string, cache cache.Cache, IsSandbox bool) AccessToken {
func NewDefaultAccessToken(appId, appSecret string, cache cache.Cache, IsSandbox bool) *DefaultAccessToken {
if cache == nil {
panic(any("cache is need"))
}
@ -80,11 +77,7 @@ func (dd *DefaultAccessToken) GetAccessToken() (string, error) {
}
// 开始调用接口获取token
api := accessTokenURL
if dd.SandBox {
api = sandBoxTokenURL
}
reqAccessToken, err := GetTokenFromServer(api, dd.AppId, dd.AppSecret)
reqAccessToken, err := GetTokenFromServer(accessTokenURL, dd.AppId, dd.AppSecret)
if err != nil {
return "", err
}

View File

@ -2,14 +2,19 @@ package douyin_openapi
import (
"encoding/json"
"errors"
"fmt"
accessToken "gitea.youtukeji.com.cn/xiabin/douyin-openapi/access-token"
"gitea.youtukeji.com.cn/xiabin/douyin-openapi/cache"
"gitea.youtukeji.com.cn/xiabin/douyin-openapi/util"
"io"
"net/http"
"strconv"
)
const (
code2Session = "/api/apps/v2/jscode2session" // 小程序登录地址
getEcpm = "https://minigame.zijieapi.com/mgplatform/api/apps/data/get_ecpm" // 获取ECPM
)
// DouYinOpenApiConfig 实例化配置
@ -104,3 +109,88 @@ func (d *DouYinOpenApi) Code2Session(code, anonymousCode string) (code2SessionRe
}
return
}
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"`
}
// 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"`
}
// GetEcpmResponseData 获取ECPM响应数据
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"`
}
// GetEcpm 获取ECPM
// https://bytedance.larkoffice.com/docx/Vg4yd0RDSovZINxJDyIc6THhnod
// 根据分页大小循环获取,聚合总数返回
func (d *DouYinOpenApi) 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++
}
}

View File

@ -4,12 +4,13 @@ import (
accessToken "gitea.youtukeji.com.cn/xiabin/douyin-openapi/access-token"
"gitea.youtukeji.com.cn/xiabin/douyin-openapi/cache"
"testing"
"time"
)
// 声明测试常量
const (
AppId = ""
AppSecret = ""
AppId = "tt8b32fd8f14071db707"
AppSecret = "44018e80b1bde34395a52de67ce1e0c37c572d80"
Token = ""
Salt = ""
)
@ -60,3 +61,30 @@ func TestDouYinOpenApi_Code2Session(t *testing.T) {
}
t.Logf("got a value %+v", session)
}
func TestDouYinOpenApi_GetEcpm(t *testing.T) {
// 获取动态的 access_token
token := accessToken.NewDefaultAccessToken(AppId, AppSecret, Cache, true)
at, err := token.GetAccessToken()
if err != nil {
t.Errorf("got a error: %s", err.Error())
return
}
list, err := OpenApi.GetEcpm(GetEcpmParams{
AppId: AppId,
OpenId: "_000aG3MQTzVW-yCUH-ndwv90JIyvdTSD0gf",
AccessToken: at,
DateHour: time.Now().Format(time.DateOnly),
PageSize: 500,
PageNo: 1,
})
if err != nil {
t.Errorf("got a error %s", err.Error())
return
}
t.Logf("got a value %+v", list)
}