2025-01-08 15:52:20 +08:00
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"net/http"
|
2025-01-09 15:17:36 +08:00
|
|
|
|
"net/url"
|
2025-01-08 15:52:20 +08:00
|
|
|
|
|
|
|
|
|
credential "github.com/bytedance/douyin-openapi-credential-go/client"
|
|
|
|
|
openApiSdkClient "github.com/bytedance/douyin-openapi-sdk-go/client"
|
|
|
|
|
)
|
|
|
|
|
|
2025-01-09 15:26:37 +08:00
|
|
|
|
// 定义抖音 API 响应的结构体
|
2025-01-09 15:17:36 +08:00
|
|
|
|
type APIResponse struct {
|
|
|
|
|
BaseResp struct {
|
|
|
|
|
StatusCode int `json:"StatusCode"`
|
|
|
|
|
StatusMessage string `json:"StatusMessage"`
|
|
|
|
|
} `json:"BaseResp"`
|
|
|
|
|
Data struct {
|
|
|
|
|
Records []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"`
|
|
|
|
|
} `json:"records"`
|
|
|
|
|
Total int `json:"total"`
|
|
|
|
|
} `json:"data"`
|
|
|
|
|
ErrMsg string `json:"err_msg"`
|
|
|
|
|
ErrNo int `json:"err_no"`
|
|
|
|
|
LogID string `json:"log_id"`
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-08 15:52:20 +08:00
|
|
|
|
// 获取 access_token 的函数
|
|
|
|
|
func GetAccessToken() (string, error) {
|
|
|
|
|
// 初始化 SDK 客户端,设置 app_id 和 secret
|
|
|
|
|
opt := new(credential.Config).
|
|
|
|
|
SetClientKey("tt8b32fd8f14071db707"). // 替换为你的 app_id
|
|
|
|
|
SetClientSecret("44018e80b1bde34395a52de67ce1e0c37c572d80") // 替换为你的 secret
|
|
|
|
|
|
|
|
|
|
// 创建 SDK 客户端
|
|
|
|
|
sdkClient, err := openApiSdkClient.NewClient(opt)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", fmt.Errorf("SDK 初始化失败: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 构造获取 token 的请求参数
|
|
|
|
|
sdkRequest := &openApiSdkClient.AppsV2TokenRequest{}
|
|
|
|
|
sdkRequest.SetAppid("tt8b32fd8f14071db707") // 设置应用 ID
|
|
|
|
|
sdkRequest.SetGrantType("client_credential") // 设置授权类型(client_credentials 模式)
|
|
|
|
|
sdkRequest.SetSecret("44018e80b1bde34395a52de67ce1e0c37c572d80") // 设置密钥
|
|
|
|
|
|
|
|
|
|
// 调用 SDK 获取 access_token
|
|
|
|
|
sdkResponse, err := sdkClient.AppsV2Token(sdkRequest)
|
|
|
|
|
if err != nil {
|
2025-01-09 15:26:37 +08:00
|
|
|
|
return "", fmt.Errorf("SDK 调用失败: %v", err)
|
2025-01-08 15:52:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 返回 access_token
|
|
|
|
|
return *sdkResponse.Data.AccessToken, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
// 定义 HTTP 路由和处理函数
|
|
|
|
|
http.HandleFunc("/get-ecpm", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
// 检查 HTTP 请求方法是否为 GET
|
|
|
|
|
if r.Method != http.MethodGet {
|
|
|
|
|
http.Error(w, "无效的请求方法", http.StatusMethodNotAllowed)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 从请求中获取参数
|
2025-01-09 15:26:37 +08:00
|
|
|
|
openID := r.URL.Query().Get("open_id") // 获取 open_id 参数
|
|
|
|
|
mpID := r.URL.Query().Get("mp_id") // 获取 mp_id 参数
|
|
|
|
|
dateHour := r.URL.Query().Get("date_hour") // 获取 date_hour 参数
|
|
|
|
|
pageNo := r.URL.Query().Get("pageNo") // 获取 pageNo 参数
|
|
|
|
|
pageSize := r.URL.Query().Get("pageSize") // 获取 pageSize 参数
|
2025-01-08 15:52:20 +08:00
|
|
|
|
|
2025-01-09 15:26:37 +08:00
|
|
|
|
// 检查是否缺少必要参数
|
2025-01-09 15:17:36 +08:00
|
|
|
|
if mpID == "" || dateHour == "" {
|
2025-01-14 10:33:27 +08:00
|
|
|
|
errorMessage := "缺少必要的参数 mp_id 或 date_hour"
|
|
|
|
|
fmt.Println(errorMessage)
|
|
|
|
|
http.Error(w, errorMessage, http.StatusBadRequest)
|
2025-01-08 15:52:20 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
2025-01-09 15:26:37 +08:00
|
|
|
|
|
|
|
|
|
// 获取动态的 access_token
|
2025-01-08 15:52:20 +08:00
|
|
|
|
accessToken, err := GetAccessToken()
|
|
|
|
|
if err != nil {
|
2025-01-14 10:33:27 +08:00
|
|
|
|
errorMessage := fmt.Sprintf("获取 access_token 失败: %v", err)
|
|
|
|
|
fmt.Println(errorMessage)
|
|
|
|
|
http.Error(w, errorMessage, http.StatusInternalServerError)
|
2025-01-08 15:52:20 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
2025-01-09 15:26:37 +08:00
|
|
|
|
|
|
|
|
|
// 构造抖音 API 请求的 URL
|
|
|
|
|
apiURL := "https://minigame.zijieapi.com/mgplatform/api/apps/data/get_ecpm"
|
2025-01-09 15:17:36 +08:00
|
|
|
|
params := url.Values{}
|
|
|
|
|
params.Add("open_id", openID)
|
|
|
|
|
params.Add("mp_id", mpID)
|
|
|
|
|
params.Add("date_hour", dateHour)
|
|
|
|
|
params.Add("access_token", accessToken)
|
|
|
|
|
params.Add("page_no", pageNo)
|
|
|
|
|
params.Add("page_size", pageSize)
|
2025-01-09 15:26:37 +08:00
|
|
|
|
|
2025-01-09 15:17:36 +08:00
|
|
|
|
fullURL := fmt.Sprintf("%s?%s", apiURL, params.Encode())
|
2025-01-08 15:52:20 +08:00
|
|
|
|
|
2025-01-09 15:26:37 +08:00
|
|
|
|
// 调用抖音 API
|
2025-01-09 15:17:36 +08:00
|
|
|
|
resp, err := http.Get(fullURL)
|
2025-01-08 15:52:20 +08:00
|
|
|
|
if err != nil {
|
2025-01-14 10:33:27 +08:00
|
|
|
|
errorMessage := "调用抖音 API 失败"
|
|
|
|
|
fmt.Println(errorMessage, err)
|
|
|
|
|
http.Error(w, errorMessage, http.StatusInternalServerError)
|
2025-01-08 15:52:20 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
2025-01-09 15:26:37 +08:00
|
|
|
|
// 读取抖音 API 响应数据
|
2025-01-08 15:52:20 +08:00
|
|
|
|
responseBody, err := ioutil.ReadAll(resp.Body)
|
|
|
|
|
if err != nil {
|
2025-01-14 10:33:27 +08:00
|
|
|
|
errorMessage := "读取抖音 API 响应失败"
|
|
|
|
|
fmt.Println(errorMessage, err)
|
|
|
|
|
http.Error(w, errorMessage, http.StatusInternalServerError)
|
2025-01-08 15:52:20 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
2025-01-09 15:26:37 +08:00
|
|
|
|
|
|
|
|
|
// 解析抖音 API 响应数据
|
2025-01-09 15:17:36 +08:00
|
|
|
|
var apiResponse APIResponse
|
|
|
|
|
err = json.Unmarshal(responseBody, &apiResponse)
|
|
|
|
|
if err != nil {
|
2025-01-14 10:33:27 +08:00
|
|
|
|
errorMessage := "解析 API 响应数据失败"
|
|
|
|
|
fmt.Println(errorMessage, err)
|
|
|
|
|
http.Error(w, errorMessage, http.StatusInternalServerError)
|
2025-01-09 15:17:36 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
2025-01-09 15:26:37 +08:00
|
|
|
|
|
|
|
|
|
// 检查 API 是否返回错误
|
2025-01-09 15:17:36 +08:00
|
|
|
|
if apiResponse.ErrNo != 0 {
|
2025-01-14 10:33:27 +08:00
|
|
|
|
errorMessage := fmt.Sprintf("抖音 API 返回错误: %s (错误码: %d)", apiResponse.ErrMsg, apiResponse.ErrNo)
|
|
|
|
|
fmt.Println(errorMessage)
|
|
|
|
|
http.Error(w, errorMessage, http.StatusInternalServerError)
|
2025-01-09 15:17:36 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
2025-01-09 15:26:37 +08:00
|
|
|
|
|
|
|
|
|
// 计算 ECPM
|
2025-01-09 15:17:36 +08:00
|
|
|
|
totalCost := 0
|
|
|
|
|
totalRecords := len(apiResponse.Data.Records)
|
|
|
|
|
|
|
|
|
|
for _, record := range apiResponse.Data.Records {
|
|
|
|
|
totalCost += record.Cost
|
|
|
|
|
}
|
2025-01-08 15:52:20 +08:00
|
|
|
|
|
2025-01-09 15:26:37 +08:00
|
|
|
|
// 如果没有记录,则返回错误
|
2025-01-09 15:17:36 +08:00
|
|
|
|
if totalRecords == 0 {
|
2025-01-14 10:33:27 +08:00
|
|
|
|
errorMessage := "未找到记录,无法计算 ECPM"
|
|
|
|
|
fmt.Println(errorMessage)
|
|
|
|
|
http.Error(w, errorMessage, http.StatusOK)
|
2025-01-09 15:17:36 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ecpm := float64(totalCost) / 100000 * 1000 / float64(totalRecords)
|
|
|
|
|
|
2025-01-14 10:33:27 +08:00
|
|
|
|
// 根据 ECPM 值判断并返回 "true" 或 "false"
|
|
|
|
|
var result string
|
|
|
|
|
if ecpm > 300 && totalRecords > 2 {
|
|
|
|
|
result = "true"
|
|
|
|
|
} else {
|
|
|
|
|
result = "false"
|
2025-01-09 15:17:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-01-14 10:33:27 +08:00
|
|
|
|
// 打印日志
|
|
|
|
|
fmt.Printf("ECPM: %.2f, Result: %s\n", ecpm, result)
|
|
|
|
|
|
|
|
|
|
// 返回结果
|
2025-01-09 15:17:36 +08:00
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
2025-01-14 10:33:27 +08:00
|
|
|
|
w.Write([]byte(result))
|
2025-01-08 15:52:20 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 启动 HTTP 服务器
|
|
|
|
|
fmt.Println("服务器已启动:http://localhost:8080")
|
|
|
|
|
err := http.ListenAndServe(":8080", nil) // 监听 8080 端口
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println("服务器启动失败:", err)
|
|
|
|
|
}
|
|
|
|
|
}
|