# 02-API 接口

所有路由以 `/api` 为前缀（根路由 `/` 除外）。交互式文档见启动后的 `/docs`（Swagger UI）。

## 统一响应结构

成功（HTTP 200）：

```json
{ "code": 0, "message": "success", "data": { } }
```

失败：

| 场景 | HTTP 状态 | code |
| --- | --- | --- |
| 业务异常（`AppError`） | 由异常指定（默认 400） | 业务码（默认 1，常见 404） |
| 请求参数校验失败 | 422 | 422 |
| 未捕获异常 | 500 | 500 |

## 接口清单

### GET `/`

服务信息。

```json
{ "code": 0, "message": "success", "data": { "app": "后端服务框架", "version": "0.1.0", "docs": "/docs" } }
```

### GET `/api/health`

健康检查 + 双库连接状态（对源库/目标库各执行 `SELECT 1`）。

响应 `data`：

```json
{
  "app": "后端服务框架",
  "version": "0.1.0",
  "database": {
    "source": { "ok": true, "dialect": "mssql", "url": "mssql+pyodbc://***@host/vat_db" },
    "target": { "ok": true, "dialect": "mssql", "url": "mssql+pyodbc://***@host/rpa" }
  }
}
```

任一库连接失败返回 HTTP 503。

### POST `/api/template/render`

按企业/订单取信息、渲染单个模板，可选转 PDF、上传 COS。

请求体（`RenderRequest`）：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `template` | string | 是 | 模板文件名（相对 `templates/`），如 `at/奥地利包装法授权-template.docx` |
| `enterprise_id` | int | 否 | 企业信息 ID（源库 `Base_Customer_Company.CompanyID`） |
| `order_id` | int | 否 | 订单 ID（源库 `order_info.id`）；未传企业时自动用订单关联企业 |
| `values` | object | 否 | 额外占位符覆盖值 |
| `output_name` | string | 否 | 输出文件名（不含路径） |
| `to_pdf` | bool | 否 | 生成后是否转 PDF（docx/xlsx 生效），默认 `true` |

响应 `data` 关键字段：`enterprise_id`、`order_id`、`order_no`、`template`、`output_name`、`pdf_name`、`pdf_path`、`download_url`、`pdf_download_url`、`output_url`（COS）、`pdf_url`（COS）。

示例：

```json
{
  "template": "at/奥地利WEEE授权 - template.docx",
  "enterprise_id": 1,
  "order_id": 1,
  "values": {},
  "to_pdf": true
}
```

说明：docx 模板名含 `WEEE` 且传入订单 `categories` 时，自动按订单勾选 ☐/☒ 分类。该接口**不写** `render_record`（结果记录表仅由 `init_db` 灌入、由 `/api/render/records` 查询）。

### GET `/api/template/download/{filename}`

下载生成的文件（`filename` 为 `output_name`，路径经安全校验）。文件不存在返回 404。

### GET `/api/template/list`

列出 `templates/` 下所有模板，按国家目录解析。响应 `data` 为数组：

```json
[
  { "name": "at/奥地利WEEE授权 - template.docx", "size": 34567, "country_code": "AT", "country_name": "奥地利" },
  { "name": "nl/1.注册表-application-word.docx", "size": 22134, "country_code": "NL", "country_name": "荷兰" }
]
```

### GET `/api/render/records`

分页查询文件替换结果记录（目标库 `render_record`）。

查询参数：

| 参数 | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `enterprise_id` | int | — | 按企业 ID 过滤 |
| `status` | string | — | 按状态过滤（`success` / `partial`） |
| `page` | int | 1 | 页码（≥1） |
| `size` | int | 20 | 每页条数（1–100） |

响应 `data`：

```json
{
  "total": 1,
  "page": 1,
  "size": 20,
  "items": [
    {
      "id": 1, "enterprise_id": 1, "order_id": 1,
      "template": "at/奥地利WEEE授权 - template.docx",
      "output_name": "xx.docx", "output_path": "...", "pdf_path": "...",
      "replacements": 12, "unresolved": [], "status": "success",
      "message": null, "created_at": "2026-08-31T00:00:00"
    }
  ]
}
```

### GET `/api/enterprise/{code}/info`

企业详情（公司 / 法人 / 附件文件）。`code` 为企业代码（源库 `br.Code`）。企业不存在或无附件返回 404。

响应 `data`：

```json
{
  "id": 1,
  "name_cn": "…", "name_eng": "…", "country": "NL",
  "reg_number": "…", "reg_address": "…", "reg_address_eng": "…",
  "company_address_line1_en": "…", "company_address_line2_en": "…",
  "reg_address_city": "…", "city_eng_name": "…",
  "establishment_date": "2024-01-01", "signature_date": "2024-01-02",
  "legal_person_name": "…", "legal_person_id_number": "…",
  "legal_person_id_start_date": "…", "legal_person_id_end_date": "…",
  "legal_person_full_name_pinyin": "…", "legal_person_birth_date": "…",
  "files": [
    { "file_name": "营业执照.png", "file_size": 123, "file_type": "png", "file_path": "https://…" }
  ]
}
```

### POST `/api/enterprise/{code}`

按国家生成企业全部文档：查找模板 → 下载/转图附件 → OCR 补充 → 逐个渲染 → 合并 DOCX → 上传 COS → 回写状态与附件。无请求体。

响应 `data`：

```json
{
  "status": "success",
  "oss_url": "https://…cos…/…",
  "merged": "…\\outputs\\generate\\CompanyName\\CompanyName.docx",
  "steps": [
    { "step": "确定国家", "status": "success", "country": "NL" },
    { "step": "查找模板", "status": "success", "count": 4 },
    { "step": "渲染模板", "status": "success", "count": 4 },
    { "step": "合并DOCX", "status": "success", "path": "…" },
    { "step": "上传OSS", "status": "success", "url": "…" }
  ]
}
```

失败时 `status` 为 `"failed"`，并附带 `error` 与 `steps`（记录到失败步骤）。回写失败仅记日志，不阻断主流程。

### GET `/api/countries`

国家代码映射（`core/countries.py` 的 `COUNTRY_MAP`）。

```json
{ "code": 0, "message": "success", "data": [
  { "code": "AT", "name": "奥地利" },
  { "code": "NL", "name": "荷兰" },
  { "code": "FR", "name": "法国" },
  { "code": "GB", "name": "英国" },
  { "code": "DE", "name": "德国" }
] }
```

### POST `/api/convert/{target}`

文件格式转换。`target` 取值：`pdf`、`docx`、`doc`（大小写不敏感、可带 `.`）。

请求为 `multipart/form-data` 文件上传（字段 `file`）。响应直接返回转换后的文件（`Content-Disposition` 含目标文件名）。不支持的目标格式返回业务异常。

前置：docx/doc ↔ pdf 依赖本机 Microsoft Office + pywin32；pdf → docx/doc 用 pdf2docx。
