# Go 示例

## 配置

```go
package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"time"
)

const (
	apiKey  = "your_api_key_here" // 替换为你的 API Key
	apiBase = "https://api.easebg.com/api/v1"
)

func getAPIKey() string {
	if key := os.Getenv("EASEBG_API_KEY"); key != "" {
		return key
	}
	return apiKey
}

func getAPIBase() string {
	if base := os.Getenv("EASEBG_API_BASE"); base != "" {
		return base
	}
	return apiBase
}
```

## 1. 创建抠图任务

```go
type TaskCreateRequest struct {
	ImageURL     string                 `json:"imageUrl"`
	ModelVersion string                 `json:"modelVersion,omitempty"`
	CallbackURL  string                 `json:"callbackUrl,omitempty"`
	Metadata     map[string]interface{} `json:"metadata,omitempty"`
}

type APIResponse struct {
	RequestID string          `json:"request_id"`
	Data      json.RawMessage `json:"data"`
	Error     *APIError       `json:"error,omitempty"`
}

type APIError struct {
	Code    string                 `json:"code"`
	Message string                 `json:"message"`
	Details map[string]interface{} `json:"details,omitempty"`
}

func createTask(req TaskCreateRequest) (*APIResponse, error) {
	body, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("marshal error: %w", err)
	}

	httpReq, err := http.NewRequest("POST", getAPIBase()+"/open/developer/tasks", bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	httpReq.Header.Set("X-API-Key", getAPIKey())
	httpReq.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("request error: %w", err)
	}
	defer resp.Body.Close()

	respBody, _ := io.ReadAll(resp.Body)

	var apiResp APIResponse
	json.Unmarshal(respBody, &apiResp)

	if resp.StatusCode != 201 {
		if apiResp.Error != nil {
			return nil, fmt.Errorf("API error [%s]: %s", apiResp.Error.Code, apiResp.Error.Message)
		}
		return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, respBody)
	}

	return &apiResp, nil
}

// 使用示例
func main() {
	result, err := createTask(TaskCreateRequest{
		ImageURL:     "https://example.com/photo.jpg",
		ModelVersion: "easebg-2.0",
		CallbackURL:  "https://your-app.com/webhooks/easebg",
	})
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Printf("Task created: %s\n", result.RequestID)
}
```

## 2. 查询任务状态

```go
type TaskData struct {
	ID           string     `json:"id"`
	Status       string     `json:"status"`
	ModelVersion string     `json:"modelVersion"`
	SourceURL    string     `json:"sourceUrl"`
	ResultURL    *string    `json:"resultUrl"`
	DurationMs   *int64     `json:"durationMs"`
	ErrorMessage *string    `json:"errorMessage"`
	CreatedAt    time.Time  `json:"createdAt"`
	CompletedAt  *time.Time `json:"completedAt"`
}

func getTask(taskID string) (*TaskData, error) {
	httpReq, _ := http.NewRequest("GET", getAPIBase()+"/open/developer/tasks/"+taskID, nil)
	httpReq.Header.Set("X-API-Key", getAPIKey())

	resp, err := http.DefaultClient.Do(httpReq)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("failed to get task: status %d", resp.StatusCode)
	}

	var apiResp APIResponse
	json.NewDecoder(resp.Body).Decode(&apiResp)

	var task TaskData
	json.Unmarshal(apiResp.Data, &task)
	return &task, nil
}

// 轮询等待任务完成
func waitForTask(taskID string, interval time.Duration, maxAttempts int) (*TaskData, error) {
	for i := 0; i < maxAttempts; i++ {
		task, err := getTask(taskID)
		if err != nil {
			return nil, err
		}

		switch task.Status {
		case "succeeded":
			return task, nil
		case "failed":
			return nil, fmt.Errorf("task failed: %s", derefString(task.ErrorMessage))
		}

		time.Sleep(interval)
	}
	return nil, fmt.Errorf("task timed out")
}

func derefString(s *string) string {
	if s == nil {
		return ""
	}
	return *s
}
```

## 3. 获取任务结果

```go
func downloadResult(taskID string, outputPath string) error {
	task, err := getTask(taskID)
	if err != nil {
		return err
	}

	if task.Status != "succeeded" || task.ResultURL == nil {
		return fmt.Errorf("task not completed or no result")
	}

	resp, err := http.Get(*task.ResultURL)
	if err != nil {
		return fmt.Errorf("download error: %w", err)
	}
	defer resp.Body.Close()

	out, err := os.Create(outputPath)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, resp.Body)
	return err
}
```

## 4. 取消任务

```go
func cancelTask(taskID string) error {
	httpReq, _ := http.NewRequest("DELETE", getAPIBase()+"/open/developer/tasks/"+taskID, nil)
	httpReq.Header.Set("X-API-Key", getAPIKey())

	resp, err := http.DefaultClient.Do(httpReq)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	switch resp.StatusCode {
	case 204:
		fmt.Println("Task canceled successfully")
		return nil
	case 409:
		return fmt.Errorf("task cannot be canceled in current state")
	default:
		return fmt.Errorf("cancel failed: status %d", resp.StatusCode)
	}
}
```

## 5. 创建批量任务

```go
type BatchTaskRequest struct {
	ImageURLs    []string `json:"imageUrls"`
	ModelVersion string   `json:"modelVersion,omitempty"`
	CallbackURL  string   `json:"callbackUrl,omitempty"`
}

func createBatchTask(req BatchTaskRequest) (*APIResponse, error) {
	body, _ := json.Marshal(req)

	httpReq, _ := http.NewRequest("POST", getAPIBase()+"/open/developer/batch-tasks", bytes.NewReader(body))
	httpReq.Header.Set("X-API-Key", getAPIKey())
	httpReq.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(httpReq)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var apiResp APIResponse
	json.NewDecoder(resp.Body).Decode(&apiResp)

	if resp.StatusCode != 201 {
		return nil, fmt.Errorf("batch task failed: status %d", resp.StatusCode)
	}

	return &apiResp, nil
}
```

## 6. Webhook 签名验证

```go
func verifyWebhookSignature(payload []byte, signature string, secret string) bool {
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write(payload)
	computed := hex.EncodeToString(mac.Sum(nil))
	return hmac.Equal([]byte(computed), []byte(signature))
}

// HTTP Handler 示例（使用 net/http）
func webhookHandler(w http.ResponseWriter, r *http.Request) {
	payload, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Bad request", http.StatusBadRequest)
		return
	}

	signature := r.Header.Get("X-EaseBG-Signature")
	secret := os.Getenv("EASEBG_WEBHOOK_SECRET")

	if !verifyWebhookSignature(payload, signature, secret) {
		http.Error(w, "Invalid signature", http.StatusUnauthorized)
		return
	}

	var event struct {
		Event        string `json:"event"`
		TaskID       string `json:"taskId"`
		ResultURL    string `json:"resultUrl,omitempty"`
		ErrorMessage string `json:"errorMessage,omitempty"`
	}
	json.Unmarshal(payload, &event)

	fmt.Printf("Received webhook: %s - %s\n", event.Event, event.TaskID)

	switch event.Event {
	case "task.succeeded":
		fmt.Printf("Result: %s\n", event.ResultURL)
	case "task.failed":
		fmt.Printf("Error: %s\n", event.ErrorMessage)
	}

	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]bool{"received": true})
}
```

## 7. 错误处理

```go
func apiRequest(method, path string, body io.Reader) (*http.Response, error) {
	httpReq, err := http.NewRequest(method, getAPIBase()+path, body)
	if err != nil {
		return nil, err
	}
	httpReq.Header.Set("X-API-Key", getAPIKey())
	httpReq.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("network error: %w", err)
	}

	if resp.StatusCode >= 400 {
		respBody, _ := io.ReadAll(resp.Body)
		var apiResp APIResponse
		json.Unmarshal(respBody, &apiResp)

		if apiResp.Error != nil {
			switch apiResp.Error.Code {
			case "INSUFFICIENT_CREDITS":
				return nil, fmt.Errorf("积分不足，请充值")
			case "RATE_LIMIT_EXCEEDED":
				return nil, fmt.Errorf("请求频率超限，请稍后重试")
			case "INVALID_API_KEY":
				return nil, fmt.Errorf("API Key 无效")
			default:
				return nil, fmt.Errorf("API error [%s]: %s", apiResp.Error.Code, apiResp.Error.Message)
			}
		}
		return nil, fmt.Errorf("unexpected status %d", resp.StatusCode)
	}

	return resp, nil
}
```

## 8. 分页查询

```go
type ListResponse struct {
	RequestID string     `json:"request_id"`
	Data      []TaskData `json:"data"`
	Meta      Pagination `json:"meta"`
}

type Pagination struct {
	Page       int `json:"page"`
	PageSize   int `json:"pageSize"`
	Total      int `json:"total"`
	TotalPages int `json:"totalPages"`
}

func listTasks(page, pageSize int, status string) (*ListResponse, error) {
	params := url.Values{}
	params.Set("page", fmt.Sprintf("%d", page))
	params.Set("pageSize", fmt.Sprintf("%d", pageSize))
	if status != "" {
		params.Set("status", status)
	}

	httpReq, _ := http.NewRequest("GET", getAPIBase()+"/open/developer/tasks?"+params.Encode(), nil)
	httpReq.Header.Set("X-API-Key", getAPIKey())

	resp, err := http.DefaultClient.Do(httpReq)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var result ListResponse
	json.NewDecoder(resp.Body).Decode(&result)
	return &result, nil
}

// 自动翻页
func getAllTasks(status string) ([]TaskData, error) {
	var allTasks []TaskData
	page := 1
	totalPages := 1

	for page <= totalPages {
		result, err := listTasks(page, 20, status)
		if err != nil {
			return nil, err
		}
		allTasks = append(allTasks, result.Data...)
		totalPages = result.Meta.TotalPages
		page++
	}

	return allTasks, nil
}
```
