package main import ( "crypto/aes" "crypto/cipher" "encoding/base64" "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" ) 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 { fmt.Println("sdk call err:", err) return "", nil } fmt.Println("sdk数据", sdkResponse.Data) // 返回 access_token return *sdkResponse.Data.AccessToken, nil } func DecryptAES128CBC(encryptedData, sessionKey, iv string) ([]byte, error) { // Base64 解码 key 和 iv aesKey, err := base64.StdEncoding.DecodeString(sessionKey) if err != nil { return nil, fmt.Errorf("sessionKey Base64 解码失败: %v", err) } initialVector, err := base64.StdEncoding.DecodeString(iv) if err != nil { return nil, fmt.Errorf("iv Base64 解码失败: %v", err) } // 检查 key 和 iv 长度是否为 16 字节 if len(aesKey) != 16 { return nil, fmt.Errorf("AES key 长度必须为 16 字节") } if len(initialVector) != 16 { return nil, fmt.Errorf("IV 长度必须为 16 字节") } // Base64 解码密文 ciphertext, err := base64.StdEncoding.DecodeString(encryptedData) if err != nil { return nil, fmt.Errorf("encryptedData Base64 解码失败: %v", err) } // 创建 AES 块 block, err := aes.NewCipher(aesKey) if err != nil { return nil, fmt.Errorf("创建 AES Cipher 失败: %v", err) } // 检查密文长度是否为块大小的倍数 if len(ciphertext)%aes.BlockSize != 0 { return nil, fmt.Errorf("密文长度必须是 AES 块大小的倍数") } // 创建 CBC 解密器 mode := cipher.NewCBCDecrypter(block, initialVector) // 解密数据 decrypted := make([]byte, len(ciphertext)) mode.CryptBlocks(decrypted, ciphertext) // 去除 PKCS#7 填充 decrypted, err = PKCS7Unpad(decrypted) if err != nil { return nil, fmt.Errorf("去除 PKCS#7 填充失败: %v", err) } return decrypted, nil } // PKCS#7 填充去除函数 func PKCS7Unpad(data []byte) ([]byte, error) { length := len(data) if length == 0 { return nil, fmt.Errorf("解密后数据长度为 0") } padding := int(data[length-1]) if padding > length || padding == 0 { return nil, fmt.Errorf("无效的填充数据") } return data[:length-padding], 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") // 获取 date_hour pageSize := r.URL.Query().Get("pageSize") // 获取 date_hour // 检查是否有缺少参数 if mpID == "" || dateHour == "" { http.Error(w, "缺少必要的参数", http.StatusBadRequest) return } // 动态获取 access_token accessToken, err := GetAccessToken() apiURL := "https://minigame.zijieapi.com/mgplatform/api/apps/data/get_ecpm" if err != nil { http.Error(w, fmt.Sprintf("获取 access_token 失败: %v", err), http.StatusInternalServerError) return } fmt.Println("动态token为", accessToken) 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) // 构造请求体 // requestBody := map[string]string{ // "open_id": openID, // "mp_id": mpID, // "date_hour": dateHour, // "access_token": accessToken, // "page_no": pageNo, // "page_size": pageSize, // } fullURL := fmt.Sprintf("%s?%s", apiURL, params.Encode()) // requestBodyJSON, err := json.Marshal(requestBody) // 转为 JSON 格式 // if err != nil { // http.Error(w, "请求体编码失败", http.StatusInternalServerError) // return // } resp, err := http.Get(fullURL) if err != nil { http.Error(w, "调用抖音 API 失败", http.StatusInternalServerError) return } defer resp.Body.Close() // 读取抖音 API 的响应 responseBody, err := ioutil.ReadAll(resp.Body) if err != nil { http.Error(w, "读取抖音 API 响应失败", http.StatusInternalServerError) return } var apiResponse APIResponse err = json.Unmarshal(responseBody, &apiResponse) if err != nil { fmt.Printf("Error while parsing response JSON: %v\n", err) return } if apiResponse.ErrNo != 0 { fmt.Printf("API Error: %s (ErrNo: %d)\n", apiResponse.ErrMsg, apiResponse.ErrNo) return } totalCost := 0 totalRecords := len(apiResponse.Data.Records) for _, record := range apiResponse.Data.Records { totalCost += record.Cost } if totalRecords == 0 { fmt.Println("No records found. Unable to calculate ECPM.") return } ecpm := float64(totalCost) / 100000 * 1000 / float64(totalRecords) fmt.Println("返回值为", responseBody) // 根据key返回相应的值(这里可以替换为实际的业务逻辑) value := fmt.Sprintf("Value for key '%s'", ecpm) // 将抖音 API 的响应返回给客户端 w.Header().Set("Content-Type", "application/json") w.WriteHeader(resp.StatusCode) w.Write([]byte(value)) }) http.HandleFunc("/Login", func(w http.ResponseWriter, r *http.Request) { // 检查请求方法是否为GET if r.Method != http.MethodGet { http.Error(w, "Invalid request method", http.StatusMethodNotAllowed) return } // 从请求参数中获取"code"和"Anonymous" code := r.URL.Query().Get("code") Anonymous := r.URL.Query().Get("Anonymous") fmt.Println("code:", code) fmt.Println("Anonymous:", Anonymous) // 参数校验 if code == "" { http.Error(w, "Missing 'code' or 'Anonymous' parameter", http.StatusBadRequest) return } // 初始化 SDK 客户端 opt := new(credential.Config). SetClientKey("tt8b32fd8f14071db707"). SetClientSecret("44018e80b1bde34395a52de67ce1e0c37c572d80") sdkClient, err := openApiSdkClient.NewClient(opt) if err != nil { fmt.Println("sdk init err:", err) http.Error(w, "SDK 初始化失败", http.StatusInternalServerError) return } // 构建请求参数 sdkRequest := &openApiSdkClient.V2Jscode2sessionRequest{} sdkRequest.SetAnonymousCode(Anonymous) sdkRequest.SetAppid("tt8b32fd8f14071db707") sdkRequest.SetCode(code) sdkRequest.SetSecret("44018e80b1bde34395a52de67ce1e0c37c572d80") // 调用 SDK sdkResponse, err := sdkClient.V2Jscode2session(sdkRequest) if err != nil { fmt.Println("sdk call err:", err) http.Error(w, "SDK 调用失败", http.StatusInternalServerError) return } // 打印登录数据 fmt.Println("登录数据:", sdkResponse) // 返回响应 value := fmt.Sprintf("Session Data: %v", sdkResponse.Data) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(value)) }) // 启动 HTTP 服务器 fmt.Println("服务器已启动:http://localhost:8080") err := http.ListenAndServe(":8080", nil) // 监听 8080 端口 if err != nil { fmt.Println("服务器启动失败:", err) } }