190 lines
5.6 KiB
Go
190 lines
5.6 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"io/ioutil"
|
||
"net/http"
|
||
"net/url"
|
||
|
||
credential "github.com/bytedance/douyin-openapi-credential-go/client"
|
||
openApiSdkClient "github.com/bytedance/douyin-openapi-sdk-go/client"
|
||
)
|
||
|
||
// 定义抖音 API 响应的结构体
|
||
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"`
|
||
}
|
||
|
||
// 获取 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 {
|
||
return "", fmt.Errorf("SDK 调用失败: %v", err)
|
||
}
|
||
|
||
// 返回 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
|
||
}
|
||
|
||
// 从请求中获取参数
|
||
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 参数
|
||
|
||
// 检查是否缺少必要参数
|
||
if mpID == "" || dateHour == "" {
|
||
errorMessage := "缺少必要的参数 mp_id 或 date_hour"
|
||
fmt.Println(errorMessage)
|
||
http.Error(w, errorMessage, http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
// 获取动态的 access_token
|
||
accessToken, err := GetAccessToken()
|
||
if err != nil {
|
||
errorMessage := fmt.Sprintf("获取 access_token 失败: %v", err)
|
||
fmt.Println(errorMessage)
|
||
http.Error(w, errorMessage, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// 构造抖音 API 请求的 URL
|
||
apiURL := "https://minigame.zijieapi.com/mgplatform/api/apps/data/get_ecpm"
|
||
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)
|
||
|
||
fullURL := fmt.Sprintf("%s?%s", apiURL, params.Encode())
|
||
|
||
// 调用抖音 API
|
||
resp, err := http.Get(fullURL)
|
||
if err != nil {
|
||
errorMessage := "调用抖音 API 失败"
|
||
fmt.Println(errorMessage, err)
|
||
http.Error(w, errorMessage, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
// 读取抖音 API 响应数据
|
||
responseBody, err := ioutil.ReadAll(resp.Body)
|
||
if err != nil {
|
||
errorMessage := "读取抖音 API 响应失败"
|
||
fmt.Println(errorMessage, err)
|
||
http.Error(w, errorMessage, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// 解析抖音 API 响应数据
|
||
var apiResponse APIResponse
|
||
err = json.Unmarshal(responseBody, &apiResponse)
|
||
if err != nil {
|
||
errorMessage := "解析 API 响应数据失败"
|
||
fmt.Println(errorMessage, err)
|
||
http.Error(w, errorMessage, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// 检查 API 是否返回错误
|
||
if apiResponse.ErrNo != 0 {
|
||
errorMessage := fmt.Sprintf("抖音 API 返回错误: %s (错误码: %d)", apiResponse.ErrMsg, apiResponse.ErrNo)
|
||
fmt.Println(errorMessage)
|
||
http.Error(w, errorMessage, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// 计算 ECPM
|
||
totalCost := 0
|
||
totalRecords := len(apiResponse.Data.Records)
|
||
|
||
for _, record := range apiResponse.Data.Records {
|
||
totalCost += record.Cost
|
||
}
|
||
|
||
// 如果没有记录,则返回错误
|
||
if totalRecords == 0 {
|
||
errorMessage := "未找到记录,无法计算 ECPM"
|
||
fmt.Println(errorMessage)
|
||
http.Error(w, errorMessage, http.StatusOK)
|
||
return
|
||
}
|
||
|
||
ecpm := float64(totalCost) / 100000 * 1000 / float64(totalRecords)
|
||
|
||
// 根据 ECPM 值判断并返回 "true" 或 "false"
|
||
var result string
|
||
if ecpm > 300 && totalRecords > 2 {
|
||
result = "true"
|
||
} else {
|
||
result = "false"
|
||
}
|
||
|
||
// 打印日志
|
||
fmt.Printf("ECPM: %.2f, Result: %s\n", ecpm, result)
|
||
|
||
// 返回结果
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.WriteHeader(http.StatusOK)
|
||
w.Write([]byte(result))
|
||
})
|
||
|
||
// 启动 HTTP 服务器
|
||
fmt.Println("服务器已启动:http://localhost:8080")
|
||
err := http.ListenAndServe(":8080", nil) // 监听 8080 端口
|
||
if err != nil {
|
||
fmt.Println("服务器启动失败:", err)
|
||
}
|
||
}
|