# C# 示例

## 配置

```csharp
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.IO;

public class EaseBGClient : IDisposable
{
    private static readonly string ApiKey = Environment.GetEnvironmentVariable("EASEBG_API_KEY") ?? "your_api_key_here";
    private static readonly string ApiBase = Environment.GetEnvironmentVariable("EASEBG_API_BASE") ?? "https://api.easebg.com/api/v1";
    private readonly HttpClient _httpClient;

    public EaseBGClient()
    {
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("X-API-Key", ApiKey);
        _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    }

    public void Dispose()
    {
        _httpClient?.Dispose();
    }
}
```

## 1. 创建抠图任务

```csharp
public async Task<JsonNode> CreateTaskAsync(string imageUrl, string modelVersion = "easebg-2.0", string callbackUrl = null)
{
    var payload = new { imageUrl, modelVersion, callbackUrl };
    var json = JsonSerializer.Serialize(payload);
    var content = new StringContent(json, Encoding.UTF8, "application/json");

    var response = await _httpClient.PostAsync($"{ApiBase}/open/developer/tasks", content);
    var responseBody = await response.Content.ReadAsStringAsync();
    var root = JsonNode.Parse(responseBody);

    if (!response.IsSuccessStatusCode)
    {
        var errorMsg = root?["error"]?["message"]?.GetValue<string>() ?? "Unknown error";
        throw new EaseBGApiException((int)response.StatusCode, root?["error"]?["code"]?.GetValue<string>() ?? "UNKNOWN", errorMsg);
    }

    return root?["data"];
}

// 使用示例
using var client = new EaseBGClient();
try
{
    var task = await client.CreateTaskAsync("https://example.com/photo.jpg", callbackUrl: "https://your-app.com/webhooks/easebg");
    Console.WriteLine($"Task created: {task?["id"]?.GetValue<string>()}");
}
catch (EaseBGApiException ex)
{
    Console.WriteLine($"Error: {ex.Message}");
}
```

## 2. 查询任务状态

```csharp
public async Task<JsonNode> GetTaskAsync(string taskId)
{
    var response = await _httpClient.GetAsync($"{ApiBase}/open/developer/tasks/{taskId}");
    var responseBody = await response.Content.ReadAsStringAsync();
    var root = JsonNode.Parse(responseBody);

    if (!response.IsSuccessStatusCode)
    {
        throw new EaseBGApiException((int)response.StatusCode, "UNKNOWN", "Failed to get task");
    }

    return root?["data"];
}

public async Task<JsonNode> WaitForTaskAsync(string taskId, int intervalMs = 2000, int maxAttempts = 60)
{
    for (int i = 0; i < maxAttempts; i++)
    {
        var task = await GetTaskAsync(taskId);
        var status = task?["status"]?.GetValue<string>();

        if (status == "succeeded") return task;
        if (status == "failed")
            throw new Exception($"Task failed: {task?["errorMessage"]?.GetValue<string>()}");

        await Task.Delay(intervalMs);
    }
    throw new TimeoutException("Task timed out");
}
```

## 3. 获取任务结果

```csharp
public async Task DownloadResultAsync(string taskId, string outputPath)
{
    var task = await GetTaskAsync(taskId);
    var status = task?["status"]?.GetValue<string>();
    var resultUrl = task?["resultUrl"]?.GetValue<string>();

    if (status != "succeeded" || string.IsNullOrEmpty(resultUrl))
        throw new Exception("Task not completed or no result");

    var imageBytes = await _httpClient.GetByteArrayAsync(resultUrl);
    await File.WriteAllBytesAsync(outputPath, imageBytes);

    Console.WriteLine($"Result saved to {outputPath}");
}
```

## 4. 取消任务

```csharp
public async Task CancelTaskAsync(string taskId)
{
    var response = await _httpClient.DeleteAsync($"{ApiBase}/open/developer/tasks/{taskId}");

    switch ((int)response.StatusCode)
    {
        case 204:
            Console.WriteLine("Task canceled successfully");
            break;
        case 409:
            throw new Exception("Task cannot be canceled in current state");
        default:
            throw new Exception($"Cancel failed: HTTP {(int)response.StatusCode}");
    }
}
```

## 5. 创建批量任务

```csharp
public async Task<JsonNode> CreateBatchTaskAsync(string[] imageUrls, string modelVersion = "easebg-2.0", string callbackUrl = null)
{
    var payload = new { imageUrls, modelVersion, callbackUrl };
    var json = JsonSerializer.Serialize(payload);
    var content = new StringContent(json, Encoding.UTF8, "application/json");

    var response = await _httpClient.PostAsync($"{ApiBase}/open/developer/batch-tasks", content);
    var responseBody = await response.Content.ReadAsStringAsync();

    if (!response.IsSuccessStatusCode)
        throw new Exception($"Batch task failed: HTTP {(int)response.StatusCode}");

    return JsonNode.Parse(responseBody);
}
```

## 6. Webhook 签名验证

```csharp
public static bool VerifyWebhookSignature(string payload, string signature, string secret)
{
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
    var computedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
    var computedHex = BitConverter.ToString(computedHash).Replace("-", "").ToLowerInvariant();

    // 固定时间比较
    if (computedHex.Length != signature.Length)
        return false;

    int result = 0;
    for (int i = 0; i < computedHex.Length; i++)
    {
        result |= computedHex[i] ^ signature[i];
    }
    return result == 0;
}

// ASP.NET Core Controller 示例
/*
[ApiController]
[Route("webhooks/easebg")]
public class WebhookController : ControllerBase
{
    private readonly IConfiguration _config;

    public WebhookController(IConfiguration config) => _config = config;

    [HttpPost]
    public async Task<IActionResult> HandleWebhook()
    {
        var payload = await new StreamReader(Request.Body).ReadToEndAsync();
        var signature = Request.Headers["X-EaseBG-Signature"].ToString();
        var secret = _config["EASEBG_WEBHOOK_SECRET"];

        if (!EaseBGClient.VerifyWebhookSignature(payload, signature, secret))
            return Unauthorized(new { error = "Invalid signature" });

        var event_ = JsonNode.Parse(payload);
        Console.WriteLine($"Received: {event_?["event"]} - {event_?["taskId"]}");

        return Ok(new { received = true });
    }
}
*/
```

## 7. 错误处理

```csharp
public class EaseBGApiException : Exception
{
    public int StatusCode { get; }
    public string Code { get; }

    public EaseBGApiException(int statusCode, string code, string message)
        : base($"[{code}] {message}")
    {
        StatusCode = statusCode;
        Code = code;
    }
}

// 使用示例
try
{
    using var client = new EaseBGClient();
    var task = await client.CreateTaskAsync("https://example.com/photo.jpg");
}
catch (EaseBGApiException ex)
{
    switch (ex.Code)
    {
        case "INSUFFICIENT_CREDITS":
            Console.WriteLine("积分不足，请充值");
            break;
        case "RATE_LIMIT_EXCEEDED":
            Console.WriteLine("请求频率超限，请稍后重试");
            break;
        case "INVALID_API_KEY":
            Console.WriteLine("API Key 无效");
            break;
        default:
            Console.WriteLine($"API 错误: {ex.Message}");
            break;
    }
}
catch (HttpRequestException ex)
{
    Console.WriteLine($"网络错误: {ex.Message}");
}
```

## 8. 分页查询

```csharp
public async Task<JsonNode> ListTasksAsync(int page = 1, int pageSize = 20, string status = null)
{
    var queryParams = new List<string> { $"page={page}", $"pageSize={pageSize}" };
    if (!string.IsNullOrEmpty(status))
        queryParams.Add($"status={status}");

    var query = string.Join("&", queryParams);
    var response = await _httpClient.GetAsync($"{ApiBase}/open/developer/tasks?{query}");
    var responseBody = await response.Content.ReadAsStringAsync();

    return JsonNode.Parse(responseBody);
}

public async Task<List<JsonNode>> GetAllTasksAsync(string status = null)
{
    var allTasks = new List<JsonNode>();
    int page = 1;
    int totalPages = 1;

    while (page <= totalPages)
    {
        var result = await ListTasksAsync(page, 20, status);
        var data = result?["data"]?.AsArray();
        if (data != null)
            allTasks.AddRange(data);

        totalPages = result?["meta"]?["totalPages"]?.GetValue<int>() ?? 1;
        page++;
    }

    return allTasks;
}
```
