# API Reference (Non-Python)

本文件面向 **Node.js / Java** 客户端，说明如何在不依赖 `hezor_common` Python 包的情况下，按相同协议直接调用 Hezor2 API。

## 概览

Hezor2 SDK 本质是对 HTTP API 的封装。非 Python 客户端可直接调用相同端点：

- `GET /health`：健康检查
- `POST /creations/webhook`：发布创作报告

Base URL 通常来自环境变量 `HEZOR2_API_BASE_URL`。

## 认证与请求头

### 必需请求头

- `Content-Type: application/json`

### 常用认证头

- `Authorization: Bearer <HEZOR2_API_KEY>`

### 可选元信息头

- `X-META-INFO: <jwt_token>`

说明：

- 当你需要和 Python SDK 一致的“元信息签名传递”时，添加 `X-META-INFO`。
- `X-META-INFO` 的 payload 结构与 `MetaInfo` 一致，字段包括：
  - `subject`
  - `subject_code`（支持单主体如 `"yidashan"`，或多级主体如 `"yidashan/market_dept"`、`"jiangsu/jintan"`）
  - `caller_id`
  - `data_coverage`
  - `creation_slug`
  - `creation_name`
- JWT 默认可设置过期时间（Python SDK 默认 3600 秒）。

## X-META-INFO 签名方法

`X-META-INFO` 的签名方法已拆分为独立文档，请查看：

- `docs/signature_method.md`

## 数据模型来源（用于对齐字段）

非 Python 客户端虽然不导入 Python 类，但应按这些模型定义组织 JSON：

- `CreationWebhookRequest`：`src/hezor_common/data_model/creations/execution/webhook.py`
- `CreationWebhookResponse`：`src/hezor_common/data_model/creations/execution/webhook.py`
- `CreationGenerateResult`：`src/hezor_common/data_model/creations/core/creation_generate_result.py`

`POST /creations/webhook` 请求体对应 `CreationWebhookRequest`：

- `action: string`
- `payload: object`（结构对应 `CreationGenerateResult`）
- `task_id: string`
- `execution_id: string`

服务端返回对应 `CreationWebhookResponse`：

- `status: string`
- `report_id: string`
- `task_id: string`
- `execution_id: string`
- `message: string`

## 接口 1：健康检查

### 请求

```http
GET /health
```

### Node.js 示例

```javascript
const baseUrl = process.env.HEZOR2_API_BASE_URL;
const apiKey = process.env.HEZOR2_API_KEY;

async function healthCheck() {
  const res = await fetch(`${baseUrl}/health`, {
    method: "GET",
    headers: {
      "Content-Type": "application/json",
      ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
    },
  });

  if (!res.ok) {
    throw new Error(`Health check failed: ${res.status}`);
  }

  const data = await res.json();
  const isHealthy = data.status === "healthy";
  return { isHealthy, data };
}
```

### Java 示例（Java 11+ HttpClient）

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Hezor2HealthCheck {
    public static void main(String[] args) throws Exception {
        String baseUrl = System.getenv("HEZOR2_API_BASE_URL");
        String apiKey = System.getenv("HEZOR2_API_KEY");

        HttpClient client = HttpClient.newHttpClient();

        HttpRequest.Builder builder = HttpRequest.newBuilder()
                .uri(URI.create(baseUrl + "/health"))
                .GET()
                .header("Content-Type", "application/json");

        if (apiKey != null && !apiKey.isBlank()) {
            builder.header("Authorization", "Bearer " + apiKey);
        }

        HttpResponse<String> response = client.send(builder.build(), HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() / 100 != 2) {
            throw new RuntimeException("Health check failed: " + response.statusCode());
        }

        String body = response.body();
        System.out.println(body);
        // 可按 JSON 解析 status 字段，判断是否等于 "healthy"
    }
}
```

## 接口 2：发布创作报告

### 请求

```http
POST /creations/webhook
Content-Type: application/json
Authorization: Bearer <api_key>
X-META-INFO: <jwt_token>   # 可选
```

### 请求体示例（结构示意）

注意：`payload` 需要完整满足 `CreationGenerateResult` 结构。下面仅示意关键层级。

```json
{
  "action": "publish_creation_report",
  "task_id": "monthly_report",
  "execution_id": "exec_2026_01_27_001",
  "payload": {
    "original_query": "生成鮨大山 2025年12月 单店盈利模型",
    "creation": {},
    "params": {},
    "chapter_results": [],
    "summary": "...",
    "title": "...",
    "data_coverage": "202512-202512",
    "full_content": "...",
    "creation_id": "rpt_xxx",
    "prefix": "...",
    "postfix": "...",
    "file_path": "..."
  }
}
```

如果你需要严格字段清单，请以源码模型为准：

- `src/hezor_common/data_model/creations/core/creation_generate_result.py`

### Node.js 示例

```javascript
const baseUrl = process.env.HEZOR2_API_BASE_URL;
const apiKey = process.env.HEZOR2_API_KEY;

async function publishCreationReport(creationResult, taskId, executionId, metaInfoJwt) {
  const body = {
    action: "publish_creation_report",
    payload: creationResult,
    task_id: taskId,
    execution_id: executionId,
  };

  const headers = {
    "Content-Type": "application/json",
    ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
    ...(metaInfoJwt ? { "X-META-INFO": metaInfoJwt } : {}),
  };

  const res = await fetch(`${baseUrl}/creations/webhook`, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
  });

  if (!res.ok) {
    const text = await res.text();
    throw new Error(`Publish failed: ${res.status} ${text}`);
  }

  return await res.json();
}
```

### Java 示例（Java 11+ HttpClient）

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Hezor2PublishCreation {
    public static void main(String[] args) throws Exception {
        String baseUrl = System.getenv("HEZOR2_API_BASE_URL");
        String apiKey = System.getenv("HEZOR2_API_KEY");
        String metaInfoJwt = System.getenv("HEZOR2_META_INFO_JWT"); // 可选

        // 实际项目建议使用 Jackson/Gson 进行序列化，这里演示最小可运行请求
        String jsonBody = """
        {
          "action": "publish_creation_report",
          "task_id": "monthly_report",
          "execution_id": "exec_2026_01_27_001",
          "payload": {
            "original_query": "demo",
            "creation": {},
            "params": {},
            "chapter_results": [],
            "summary": "demo",
            "title": "demo",
            "data_coverage": "202512-202512",
            "full_content": "demo",
            "creation_id": "rpt_demo",
            "prefix": "",
            "postfix": "",
            "file_path": ""
          }
        }
        """;

        HttpRequest.Builder builder = HttpRequest.newBuilder()
                .uri(URI.create(baseUrl + "/creations/webhook"))
                .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
                .header("Content-Type", "application/json");

        if (apiKey != null && !apiKey.isBlank()) {
            builder.header("Authorization", "Bearer " + apiKey);
        }
        if (metaInfoJwt != null && !metaInfoJwt.isBlank()) {
            builder.header("X-META-INFO", metaInfoJwt);
        }

        HttpClient client = HttpClient.newHttpClient();
        HttpResponse<String> response = client.send(builder.build(), HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() / 100 != 2) {
            throw new RuntimeException("Publish failed: " + response.statusCode() + " " + response.body());
        }

        System.out.println(response.body());
    }
}
```

## 与 Python SDK 行为对齐建议

- URL 与路径对齐：
  - `GET {baseUrl}/health`
  - `POST {baseUrl}/creations/webhook`
- 鉴权对齐：
  - 使用 `Authorization: Bearer <api_key>`
  - 需要元信息签名时再加 `X-META-INFO`
- 重试策略：
  - 对 `5xx` 和网络抖动做指数退避重试
- 超时设置：
  - 建议与 Python SDK 一致，从 `120s` 起步
- 请求体校验：
  - 以 `CreationGenerateResult` 源码字段为准，避免缺字段导致 4xx
