# Java 示例

## 依赖

使用 OkHttp 和 Jackson（Maven）：

```xml
<dependencies>
  <dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.12.0</version>
  </dependency>
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.16.0</version>
  </dependency>
</dependencies>
```

## 配置

```java
import okhttp3.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

public class EaseBGClient {
    private static final String API_KEY = System.getenv().getOrDefault("EASEBG_API_KEY", "your_api_key_here");
    private static final String API_BASE = System.getenv().getOrDefault("EASEBG_API_BASE", "https://api.easebg.com/api/v1");
    private static final OkHttpClient client = new OkHttpClient();
    private static final ObjectMapper mapper = new ObjectMapper();
    private static final MediaType JSON_TYPE = MediaType.parse("application/json");

    private static Request.Builder authRequest(String url) {
        return new Request.Builder()
                .url(url)
                .header("X-API-Key", API_KEY)
                .header("Content-Type", "application/json");
    }
}
```

## 1. 创建抠图任务

```java
public static JsonNode createTask(String imageUrl, String modelVersion, String callbackUrl) throws Exception {
    Map<String, Object> payload = new HashMap<>();
    payload.put("imageUrl", imageUrl);
    if (modelVersion != null) payload.put("modelVersion", modelVersion);
    if (callbackUrl != null) payload.put("callbackUrl", callbackUrl);

    String json = mapper.writeValueAsString(payload);
    RequestBody body = RequestBody.create(json, JSON_TYPE);

    Request request = authRequest(API_BASE + "/open/developer/tasks")
            .post(body)
            .build();

    try (Response response = client.newCall(request).execute()) {
        JsonNode root = mapper.readTree(response.body().string());
        if (!response.isSuccessful()) {
            String errMsg = root.path("error").path("message").asText("Unknown error");
            throw new RuntimeException("API Error " + response.code() + ": " + errMsg);
        }
        return root.path("data");
    }
}

// 使用示例
JsonNode task = createTask(
    "https://example.com/photo.jpg",
    "easebg-2.0",
    "https://your-app.com/webhooks/easebg"
);
System.out.println("Task created: " + task.path("id").asText());
```

## 2. 查询任务状态

```java
public static JsonNode getTask(String taskId) throws Exception {
    Request request = authRequest(API_BASE + "/open/developer/tasks/" + taskId)
            .get()
            .build();

    try (Response response = client.newCall(request).execute()) {
        JsonNode root = mapper.readTree(response.body().string());
        if (!response.isSuccessful()) {
            throw new RuntimeException("Failed to get task: " + response.code());
        }
        return root.path("data");
    }
}

// 轮询等待
public static JsonNode waitForTask(String taskId, int intervalMs, int maxAttempts) throws Exception {
    for (int i = 0; i < maxAttempts; i++) {
        JsonNode task = getTask(taskId);
        String status = task.path("status").asText();

        if ("succeeded".equals(status)) return task;
        if ("failed".equals(status)) {
            throw new RuntimeException("Task failed: " + task.path("errorMessage").asText());
        }

        Thread.sleep(intervalMs);
    }
    throw new RuntimeException("Task timed out");
}
```

## 3. 获取任务结果

```java
public static void downloadResult(String taskId, String outputPath) throws Exception {
    JsonNode task = getTask(taskId);

    if (!"succeeded".equals(task.path("status").asText()) || task.path("resultUrl").isNull()) {
        throw new RuntimeException("Task not completed or no result");
    }

    String resultUrl = task.path("resultUrl").asText();
    Request request = new Request.Builder().url(resultUrl).build();

    try (Response response = client.newCall(request).execute()) {
        Files.write(Paths.get(outputPath), response.body().bytes());
        System.out.println("Result saved to " + outputPath);
    }
}
```

## 4. 取消任务

```java
public static void cancelTask(String taskId) throws Exception {
    Request request = authRequest(API_BASE + "/open/developer/tasks/" + taskId)
            .delete()
            .build();

    try (Response response = client.newCall(request).execute()) {
        switch (response.code()) {
            case 204:
                System.out.println("Task canceled successfully");
                break;
            case 409:
                throw new RuntimeException("Task cannot be canceled in current state");
            default:
                throw new RuntimeException("Cancel failed: " + response.code());
        }
    }
}
```

## 5. 创建批量任务

```java
public static JsonNode createBatchTask(List<String> imageUrls, String callbackUrl) throws Exception {
    Map<String, Object> payload = new HashMap<>();
    payload.put("imageUrls", imageUrls);
    if (callbackUrl != null) payload.put("callbackUrl", callbackUrl);

    String json = mapper.writeValueAsString(payload);
    RequestBody body = RequestBody.create(json, JSON_TYPE);

    Request request = authRequest(API_BASE + "/open/developer/batch-tasks")
            .post(body)
            .build();

    try (Response response = client.newCall(request).execute()) {
        JsonNode root = mapper.readTree(response.body().string());
        if (!response.isSuccessful()) {
            throw new RuntimeException("Batch task failed: " + response.code());
        }
        return root;
    }
}
```

## 6. Webhook 签名验证

```java
public static boolean verifyWebhookSignature(String payload, String signature, String secret) throws Exception {
    Mac mac = Mac.getInstance("HmacSHA256");
    SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
    mac.init(keySpec);

    byte[] computed = mac.doFinal(payload.getBytes("UTF-8"));
    String computedHex = bytesToHex(computed);

    return computedHex.equals(signature);
}

private static String bytesToHex(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    for (byte b : bytes) {
        sb.append(String.format("%02x", b));
    }
    return sb.toString();
}

// Spring Boot Controller 示例
/*
@RestController
@RequestMapping("/webhooks/easebg")
public class WebhookController {

    @Value("${easebg.webhook.secret}")
    private String webhookSecret;

    @PostMapping
    public ResponseEntity<Map<String, Boolean>> handleWebhook(@RequestBody String payload,
            @RequestHeader("X-EaseBG-Signature") String signature) throws Exception {

        if (!EaseBGClient.verifyWebhookSignature(payload, signature, webhookSecret)) {
            return ResponseEntity.status(401).body(Map.of("error", false));
        }

        JsonNode event = new ObjectMapper().readTree(payload);
        System.out.println("Received: " + event.path("event").asText());

        return ResponseEntity.ok(Map.of("received", true));
    }
}
*/
```

## 7. 错误处理

```java
public static class EaseBGApiException extends Exception {
    private final int statusCode;
    private final String code;

    public EaseBGApiException(int statusCode, String code, String message) {
        super(String.format("[%s] %s", code, message));
        this.statusCode = statusCode;
        this.code = code;
    }

    public String getCode() { return code; }
    public int getStatusCode() { return statusCode; }
}

public static JsonNode apiRequest(String method, String path, String jsonBody) throws Exception {
    RequestBody body = jsonBody != null ? RequestBody.create(jsonBody, JSON_TYPE) : null;
    Request request = authRequest(API_BASE + path).method(method, body).build();

    try (Response response = client.newCall(request).execute()) {
        String respBody = response.body().string();
        JsonNode root = mapper.readTree(respBody);

        if (!response.isSuccessful()) {
            JsonNode error = root.path("error");
            throw new EaseBGApiException(
                response.code(),
                error.path("code").asText("UNKNOWN"),
                error.path("message").asText("Request failed")
            );
        }
        return root;
    }
}

// 使用示例
try {
    String json = mapper.writeValueAsString(Map.of("imageUrl", "https://example.com/photo.jpg"));
    JsonNode result = apiRequest("POST", "/open/developer/tasks", json);
} catch (EaseBGApiException e) {
    switch (e.getCode()) {
        case "INSUFFICIENT_CREDITS":
            System.err.println("积分不足，请充值");
            break;
        case "RATE_LIMIT_EXCEEDED":
            System.err.println("请求频率超限");
            break;
        default:
            System.err.println("API 错误: " + e.getMessage());
    }
}
```

## 8. 分页查询

```java
public static JsonNode listTasks(int page, int pageSize, String status) throws Exception {
    HttpUrl.Builder urlBuilder = HttpUrl.parse(API_BASE + "/open/developer/tasks").newBuilder()
            .addQueryParameter("page", String.valueOf(page))
            .addQueryParameter("pageSize", String.valueOf(pageSize));

    if (status != null && !status.isEmpty()) {
        urlBuilder.addQueryParameter("status", status);
    }

    Request request = authRequest(urlBuilder.build().toString()).get().build();

    try (Response response = client.newCall(request).execute()) {
        return mapper.readTree(response.body().string());
    }
}

// 自动翻页
public static List<JsonNode> getAllTasks(String status) throws Exception {
    List<JsonNode> allTasks = new ArrayList<>();
    int page = 1;
    int totalPages = 1;

    while (page <= totalPages) {
        JsonNode result = listTasks(page, 20, status);
        result.path("data").forEach(allTasks::add);
        totalPages = result.path("meta").path("totalPages").asInt(1);
        page++;
    }

    return allTasks;
}
```
