# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Important Rules

**⚠️ Code Modification Policy: DO NOT modify any code or files without explicit user approval first.** Always present your analysis and proposed changes, then wait for confirmation before using Edit/Write/Bash(git) to make changes. This applies to all files in the repository — no exceptions.

**🔍 Before writing any new diagnostic/test script for a bug:** first check the `scripts/` directory for an existing similar one (e.g. `diagnose_credit_report_authority.php`, `test_business_license_ocr.php`). Reuse or extend an existing script rather than creating a duplicate.

## Project Overview

PHP 文档生成系统：为中国出海企业生成海牙认证所需的多语种（中/西/英）商业文档，合并为整本 PDF。Word 模板用 `{{placeholder}}` 占位符，Ghostscript 合并 PDF。

## Core Architecture (`src/`)

| Class | Purpose |
|-------|---------|
| DocumentGenerator / VATDocumentGenerator | PDF 生成主编排 |
| VATAsyncProcessor / AsyncTaskManager / VATDataService | 异步队列 / 任务表 / VAT 数据 + AR 校验 |
| WordTemplateProcessor / LibreOfficeConverter | 模板处理 / Word→PDF |
| PdfMerger / PdfNormalizer / ImageToPdfConverter | PDF 合并 / 规范化 / 图片转 PDF |
| BaiduOCR / HKCompanyOCR / OCRDataProcessor | OCR 服务 |
| VolcanoTranslator / DeepLTranslator / TranslatorWithFallback | 翻译（带回退链） |
| DatabaseHelper / Database / EnvLoader | SQL Server 访问 / 环境变量 |
| CosUploader / WeChatWorkNotifier / Logger | COS 上传 / 企微通知 / UTF-8 日志 |

其余类（PortraitExtractor、QrCode* 等）按名自明。

### Document Generation Flow

按 ApplicationId 读数据 → 按法定顺序生成：海牙授权书(ES/EN) → 收信授权书(ES/EN) → 信用报告(原件+ES翻译) → 营业执照(原件+ES翻译) → 身份证(原件+ES翻译) → 合并整本 PDF → 更新状态 → 清理临时文件。

### Authorization Authority (AR field)

`AR`（必填 M/O，取自 `GeneralTemplateEPR.AR`）决定 5 份授权文档与 030 文件的模板：**M** = mokj 原版；**O** = Onesea 版（`*_Onesea` 后缀，占位符相同只切路径）。涉及 Hague_Power_of_Attorney_{ES,EN}、Letter_Recipient_Authorization_{ES,EN}、APODERAMIENTO_Template。`EPRHaiyaProcessingTasks.AR` 四条路径（source/API × 301/352）都写入；030 流程 AR 强制必填（共用 `validateArField`）；RPA 经 `python/rpa_030_get.py` 读并归一化大写选模板。检查：`scripts/test_ar_onesea_templates.php`。

### Passport Handling (LegalPersonIDCardType = "Passport")

不做 OCR、不生成身份证翻译；护照图/PDF 合并到营业执照翻译之后；海牙地址用 `LegalPersonIDCardAddressEng`、性别用 `LegalPersonGender`（数据库值）。最终顺序：授权书(西/英) → 收信授权书(西/英) → 信用报告(原件+翻译) → 营业执照(原件+翻译) → 护照。

### Hong Kong Company Handling

API 流程（API 字段契约与 es_haiya 一致，`_source='api'`）仅以**注册国 `Country`** 判定（含 香港/HK/HONG KONG，`CompanyCountry` 不参与判定不兜底）；source 流程以 `CompanyCountry` 判定。香港公司：跳过营业执照、信用报告的下载/OCR/翻译且不合并；仍生成海牙授权文件；**SpecsName=1/2（包装法/包装法+VAT）时查册文件必填**（`findCompanyParticularsFile` 下载 → `is_company_particulars_pdf` 检测，需含 'Company Particulars'/'公司资料' 关键词，缺失报错置 7），原件+西语翻译件合并。国家字段（LegalPersonCountry/Country/CompanyCountry）只传二字码，中文/英文名/区域代码由 `src/CountryDict.php` 从 `cache/countries.json` 解析（对齐 es_haiya，不在 API 流程查 source 库国家表）。

## Queue Processor (`task/queue_processor.php`)

```bash
php task/queue_processor.php [--daemon] [--interval=N] [--max-tasks=N]
```

每轮顺序：**030（PushType=352）→ API 海牙（DataSource=API, 301）→ source 301**：

- **030**：只校验字段（不查文件），复制 LegalSignedFile/AR/BusinessMobile/CompanyName 到任务表，直接 TaskStatus=2 落库交 RPA（PHP 不设 PushTaxBureauStatus=6，RPA 设 6）；防重复只挡 `pdf030_fid IS NULL`；RPA 判死（pdf_result=3）→ TaskStatus=3 + 业务单置 7 + 企微通知
- **API 301**：从 `TaskData.generator_data` 重建生成；成功上传 SaaS 共享桶 usaeu-1259285998（`tencent_cos.api_flow_bucket`，与 source 桶 vat 区分）且 COS 键为统一相对路径 `{tencent_cos.api_flow_oss_prefix}{当前年}/es_epr_haiya/{业务流水号}/{文件名}`（2026-08-31 契约 + 2026-09-02 唯一性子目录：业务流水号 = bizParam.BusinessSerialNumber（受理必填）、兜底 BusinessId——文件名不含唯一因子，不加子目录会覆盖同 key；海牙/APODERAMIENTO/030 同子目录，文件命名不变），写 `ResultData`（file1=盖章要求完整 URL（外部静态资源），file2=海牙, file3=APODERAMIENTO, file4=030（RPA 回填），后三者及 cos_key/cos_url 均为 OSS 相对路径，不带域名）+ TaskStatus=2；失败 RetryCount<MaxRetries 回 pending，耗尽 FAILED + 企微通知（最终失败回调 `AsyncResultNotifier`：data=null 错误原因放 msg）
- **source 301**：查 `EPRRegInfo`（Country=ES, PushTaxBureauStatus=5, PushType=301）自动建任务 → 生成 → 成功设 6 / 失败设 7

任务状态：0=PENDING 1=PROCESSING 2=COMPLETED 3=FAILED 4=CANCELLED

> 完整流程见 `docs/QUEUE_PROCESSOR_README.md`。公共 API 端点（`public/es_hague_api.php` 301 / `public/es_030_api.php` 030，异步受理，Bearer `EPR_API_TOKEN`，AR 必填，不查 source 库；受理阶段香港文件/信用报告**真实下载校验**——COS 签名 URL 访问，见 `VATDataService::buildFileArrayFromParam`/`downloadApiFileToTemp`）见 `UNIFIED_API_DESIGN.md` §13。

## Common Commands

```bash
php task/queue_processor.php --daemon    # 生产守护进程
tail -f logs/queue_processor_*.log       # UTF-8 日志
```

## System Dependencies

PHP 7.4+（sqlsrv, pdo_sqlsrv, zip, gd）· LibreOffice（Word→PDF）· ImageMagick（PDF→图，营业执照 OCR）· Ghostscript（PDF 合并）· SQL Server · Python 3

## Configuration

- `.env`：数据库（dev/prod + RPA）、API keys、`HAIYA_SIGNATURE_REQUIREMENT_URL`（盖章要求静态 PDF；更新需三处同步+重启 queue_processor，见 `docs/QUICK_REFERENCE.md`）、公共 API（`EPR_API_TOKEN`/`SOURCE_OSS_BASE_URL`/`ALLOWED_DOWNLOAD_HOSTS`/`EPR_API_TEST_LOCAL_FILES`/`ES_HAGUE_RESULT_CALLBACK_URL` 默认 test-cloud 回调、显式留空禁用）、COS 桶分流（`API_FLOW_COS_BUCKET` 默认 usaeu-1259285998 / `API_FLOW_OSS_PREFIX` 默认 `common-prod/generatefile/`）、`APP_ENV`
- `config/config.php`：多环境数据库、文件路径、LibreOffice 路径；模板在 `template/`（`{{placeholder}}` 格式）

## Database Tables

| Table | Purpose |
|-------|---------|
| EPRRegInfo | VAT 注册信息（PushTaxBureauStatus 5→6/7，PushType 301/352） |
| EPRHaiyaProcessingTasks | 任务表（rpa 库；DataSource、AR、pdf_result、pdf030_fid、ResultData） |
| EPRBusinessRecord / DocumentApplications | 业务记录 / 申请记录 |
| CreditReportInfo / BusinessLicenseInfo / IdCardInfo | 文档数据 |

## Python Scripts (`python/`)

`rpa_030_get.py` / `rpa_030_save.py` — RPA 030 取任务/存文件（API 流程：SaaS 桶分流 + `es_epr_haiya/{业务流水号}` 相对路径（常量 `API_FLOW_OSS_PREFIX`/`API_FLOW_OSS_MODULE_DIR`，文件顶部，RPA 进程不读 .env）；ResultData.file4 回填（相对路径）、死锁限时重试、异步结果通知（data.files 文件数组，url=OSS 相对路径））；`image_to_pdf.py` — 图片→A4 PDF

## Troubleshooting

| Issue | Solution |
|-------|----------|
| OCR 失败 | 检查腾讯云/百度凭证；营业执照 PDF 需 ImageMagick |
| PDF 合并失败 | 安装 Ghostscript |
| 信用报告解析失败 | `php scripts/diagnose_credit_report_finding.php [订单号]` → `diagnose_credit_report_authority.php` |
| 香港公司报缺少查册文件 | `php scripts/diagnose_missing_particulars.php [订单号]`（只读复现全链路）；下载/检测/解析瞬时失败均伪装成此错误，重推可救（2026-09-07 三环节已加失败日志） |
| 生成文件报 40001 死锁 | vat_db 只读，应用层重试；`php scripts/diagnose_vat_deadlock.php [订单号]` |
| 生产行为与仓库不一致 | 生产部署在 `E:\phpProject\PRO\es\es_haiya_epr`（本地开发仓库为 `E:\ou\meiou-app\es_haiya_epr`）；先确认生产代码已同步（2026-09-08 生产已同步仓库最新代码含 aac1372；API 接口流程未测试，生产仅 source 流程在用） |

> 其他问题：README.md「常见问题」+ docs/CONTRIBUTING.md「Common Issues」

## Documentation (`docs/`)

文档导航见 `docs/DOCUMENTATION_INDEX.md`（含更新历史）。常用：`QUICK_REFERENCE.md`（命令速查 + 静态文件更新流程）、`QUEUE_PROCESSOR_README.md`（队列完整指南）、`ENV.md`（环境变量）、`PDF_ID_CARD_SUPPORT.md`（身份证/护照 PDF）。接口设计：`UNIFIED_API_DESIGN.md`（根目录，不入库；主副本在 app_withdrawn）。
