# CITEO POA 生成接口文档

## 接口地址

**POST** `/fr_epr_reg/api/epr/generate-citeo-poa`

服务器地址：`http://automation.usaeu.com:8888/`

完整地址：`http://automation.usaeu.com:8888/fr_epr_reg/api/epr/generate-citeo-poa`

## 功能说明

接收 **EPRRegInfo 表主键 ID**，由服务端查询源库（`sqlsrv_source`）获取公司数据，
基于 `storage/Citeo_Template.docx` 模板生成 CITEO 授权书（POA）DOCX，转换为 PDF 后
上传到腾讯云 COS，返回 PDF 访问地址。

与 LEKO POA 的区别：LEKO POA 由 `epr:process` 轮询源库生成；CITEO POA 通过本接口
传入 EPRRegInfo ID 即时生成。

## 访问控制

接口受 `internal.network` 中间件保护，仅允许内网（172.16.x.x 子网）请求访问，非内网请求返回 401。

> **注意**：IP 限制默认开启（生产环境，由 `INTERNAL_NETWORK_ENFORCE` 控制，默认 `true`）。本地开发机不在 172.16 子网时，可在 `.env` 设置 `INTERNAL_NETWORK_ENFORCE=false` 临时关闭。

## 请求

### Content-Type

`application/json`

### 请求参数

| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| Id | string | 是 | EPRRegInfo 表主键 ID（对应 `EPRRegInfo.Id`），最长 36 字符（UUID） |

接口仅接收 `Id` 一个参数。公司数据（英文名、国家、注册号、VAT 税号、城市、地址、法人姓名拼音等）全部由服务端根据该 ID 查询源库获取，不在请求体中传递。

### 请求示例

```json
{
    "Id": "8F3B2A1C-4D5E-6F70-8190-1234567890AB"
}
```

## 响应

### 成功响应（200）

```json
{
    "code": 200,
    "msg": "CITEO POA generated successfully",
    "data": {
        "pdf_url": "https://vat-1259285998.cos.ap-guangzhou.myqcloud.com/citeo_poa/Care & Bloom Limited/6a4ca7840d26c/POA-Care & Bloom Limited.pdf"
    }
}
```

| 字段 | 类型 | 说明 |
|------|------|------|
| code | int | 状态码，200 表示成功 |
| msg | string | 响应消息 |
| data.pdf_url | string | 生成的 POA PDF 文件腾讯云 COS 访问地址（文件名已解码为可读形式，`&`、空格、非 ASCII 均以原始字符出现；文件名经 `FileNameSanitizer` 净化，不含 `\ / : * ? " ' < > \| # %`） |

### 参数校验失败（400）

```json
{
    "code": 400,
    "msg": "Id is required",
    "data": null
}
```

### 业务错误（400）

源库记录不存在：

```json
{
    "code": 400,
    "msg": "Source record not found for EprRegInfoId: 8F3B2A1C-4D5E-6F70-8190-1234567890AB",
    "data": null
}
```

必填字段为空（含按国家区分的营业执照号）：

```json
{
    "code": 400,
    "msg": "必填数据为空: VAT税号(VATNumber)",
    "data": null
}
```

模板缺失 / 生成失败：

```json
{
    "code": 400,
    "msg": "CITEO POA template not found: E:/ou/meiou-app/fr_epr/fr_epr_reg/storage/Citeo_Template.docx",
    "data": null
}
```

### 非内网访问（401）

```json
{
    "code": 401,
    "msg": "Unauthorized: internal network only",
    "data": null
}
```

## 生成细节

### 必填字段校验 (`CiteoPoaService::validateRecord()`)

通用必填：NameEng, CountryTwoCode, CityEngName, RegAddressEng, LegalPersonFullNamePinYin。按国家区分的营业执照号：EU（除法国外）需 VATNumber；其他国家/法国需 RegNumber。记录不存在或必填字段为空返回 400。

### 模板占位符

模板：`storage/Citeo_Template.docx`（由 `.doc` 转换而来，«...» MERGEFIELD 已改为 `{{...}}` 文本占位符）。7 个占位符，9 处使用：

| 占位符 | 使用次数 | 来源 |
|--------|----------|------|
| `{{NameEng}}` | 1 | 源库 NameEng |
| `{{BusinessLicenseNo}}` | 1 | EU（除法国）取 VATNumber，否则取 RegNumber |
| `{{CityEngName}}` | 2 | 源库 CityEngName（stripCitySuffix 处理） |
| `{{RegAddressEng}}` | 1 | 源库 RegAddressEng |
| `{{LegalPersonFullNamePinYin}}` | 1 | 源库 LegalPersonFullNamePinYin（纯文本） |
| `{{CurrentDay}}` | 2 | `date('Y.m.d')` |
| `{{LegalPersonSignature}}` | 1 | 法人签名 PNG（内联图片，与 LEKO POA 一致） |

### 生成流程

查询源库（`SourceRepository::fetchByEprRegInfoId()`） -> 校验 -> `WordTemplateProcessor` 生成 DOCX -> `PdfConverter::convertDocxToPdf($path, false)`（`firstPageOnly=false`，签名在第 2 页） -> `OssUploader` 上传 COS `citeo_poa/` 目录 -> 返回 pdf_url。

LEKO（`DocxGenerator`）与 CITEO（`CiteoPoaService`）各自显式传签名参数，互不影响。

## cURL 调用示例

```bash
curl -X POST \
  http://automation.usaeu.com:8888/fr_epr_reg/api/epr/generate-citeo-poa \
  -H 'Content-Type: application/json' \
  -d '{
    "Id": "8F3B2A1C-4D5E-6F70-8190-1234567890AB"
  }'
```
