# Refashion POA 生成接口文档

## 接口地址

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

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

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

## 功能说明

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

与 CITEO POA 一样为按需生成接口（非轮询）。与 CITEO POA 的区别：

- 仅查询 **SupplierName='ECO TLC'** 的纺织法记录（防止包装法 LEKO/CITEO 记录被误处理）；
- 模板含法人电话 / 邮箱占位符，故 `LegalPersonPhone` / `LegalPersonEmail` 为必填；
- **不生成法人签字图片**——签字单元格留空，由业务后续手签 / 盖章（公章暂不处理）。

## 访问控制

接口受 `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），须为 ECO TLC 纺织法记录 |

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

### 请求示例

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

## 响应

### 成功响应（200）

```json
{
    "code": 200,
    "msg": "Refashion POA generated successfully",
    "data": {
        "pdf_url": "https://vat-1259285998.cos.ap-guangzhou.myqcloud.com/refashion_poa/LOREAL S.A./6a4ca7840d26c/POA-LOREAL S.A..pdf"
    }
}
```

| 字段 | 类型 | 说明 |
|------|------|------|
| code | int | 状态码，200 表示成功 |
| msg | string | 响应消息 |
| data.pdf_url | string | 生成的 POA PDF 文件腾讯云 COS 访问地址 |

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

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

### 业务错误（400）

源库记录不存在或非 ECO TLC 纺织法记录：

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

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

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

模板缺失 / 生成失败：

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

### 非内网访问（401）

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

## 生成细节

### ECO TLC 记录校验

`SourceRepository::fetchEcoTlcRecordByEprRegInfoId()` 在 `fetchByEprRegInfoId()` 的 SQL 基础上
额外 `LEFT JOIN SupplierInformation AS si ON si.Id = vb.OfficialFeeRecycleMerchant`，并限定
`si.SupplierName = 'ECO TLC'`。记录不存在或非 ECO TLC 记录统一返回 null（不区分两种情况，
避免泄露记录是否存在），由服务层抛出 400。

该查询额外 `LEFT JOIN Base_Customer AS cust ON vb.CustomerId = cust.ID`
与 `LEFT JOIN Base_User AS customerManager ON cust.SaleUserID = customerManager.F_UserId`，
返回 `CustomerManagerName`（`COALESCE(F_RealName, '')`）与 `CustomerManagerEmail`（`F_Email`），
原用于生成后通知客户经理（该通知当前已停用，字段保留未用）。
仅此 Refashion 专用查询含这两个 JOIN；CITEO/LEKO 查询不受影响。

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

通用必填：NameEng, CountryTwoCode, CityEngName, RegAddressEng, LegalPersonFullNamePinYin,
**LegalPersonPhone, LegalPersonEmail**（后两项为 Refashion 相比 CITEO 额外必填项）。
按国家区分的营业执照号：EU（除法国外）需 VATNumber；其他国家/法国需 RegNumber。
记录不存在 / 非 ECO TLC 或必填字段为空返回 400。

### 模板占位符

模板：`storage/Refashion_Template.docx`。8 个占位符，13 处使用，全部为纯文本替换（无图片）：

| 占位符 | 使用次数 | 来源 |
|--------|----------|------|
| `{{NameEng}}` | 3 | 源库 NameEng |
| `{{BusinessLicenseNo}}` | 1 | EU（除法国）取 VATNumber，否则取 RegNumber |
| `{{CityEngName}}` | 2 | 源库 CityEngName（stripCitySuffix 处理） |
| `{{RegAddressEng}}` | 2 | 源库 RegAddressEng |
| `{{LegalPersonFullNamePinYin}}` | 1 | 源库 LegalPersonFullNamePinYin（纯文本） |
| `{{CurrentDay}}` | 2 | `date('Y.m.d')` |
| `{{LegalPersonPhone}}` | 1 | 源库 LegalPersonPhone（Word 拆分到多 run，由 normalizeSplitPlaceholders 合并后直接替换） |
| `{{LegalPersonEmail}}` | 1 | 源库 LegalPersonEmail（Word 拆分到多 run，由 normalizeSplitPlaceholders 合并后直接替换） |

> 签名（Signature）与盖章（Stamp）单元格在模板中为空，**不生成签字图片**，由业务后续手签 / 盖章。
> 模板中 Date 行的浮动图片（Sea&Mew logo, image1.png）保持不变。

### 生成流程

查询源库（`SourceRepository::fetchEcoTlcRecordByEprRegInfoId()`） -> 校验 ->
`WordTemplateProcessor` 生成 DOCX（纯文本占位符，无图片替换） ->
`PdfConverter::convertDocxToPdf($path, false)`（`firstPageOnly=false`，Refashion POA 为多页文档） ->
`OssUploader` 上传 COS `refashion_poa/` 目录 ->
返回 `{pdf_url}`。

### 文件名

`POA-{FileNameSanitizer::sanitize(NameEng)}.pdf`，公司名经 `FileNameSanitizer` 净化
（移除 `\ / : * ? " ' < > | # %`，`&` 保留，控制字符转空格）。
COS 路径：`refashion_poa/{净化后NameEng}/{uniqid}/POA-{净化后NameEng}.pdf`，
以 `{uniqid}` 隔离同名公司多次生成，避免覆盖。
返回的 `pdf_url` 中文件名已解码为可读形式（`&`、空格、非 ASCII 以原始字符出现），
调用方**不要**再对 URL 做 urldecode（否则 `+` 会被误转为空格）。

## 生成后通知（客户经理提醒）

> **已停用**：生成后通知客户经理签字盖章（邮件 / 企业微信，best-effort）当前已禁用。
> `RefashionPoaService::generate()` 中 `RefashionPoaNotifier::notify()` 调用已注释，接口仅返回生成的 PDF（`{pdf_url}`）。
> `RefashionPoaNotifier` 类及 `REFASHION_SMTP_*` 配置保留（`REFASHION_SMTP_*` 仍被 `send-refashion-mail` API 使用）；
> 恢复通知时取消 `RefashionPoaService` 内相关注释即可。

## cURL 调用示例

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