# VAT数据同步系统

这是一个用于从源SQL Server数据库读取VAT业务记录（也支持经 `api.php` 由外部系统直接推送），进行数据校验，并同步到目标数据库的PHP应用程序。

## 功能特性

- 持续监控源数据库中的待处理记录
- 全面的数据校验（电话号码、邮编、日期等）
- 自动数据转换和映射
- 详细的日志记录（按天轮转）
- 支持测试和生产环境配置
- 错误处理和状态更新
- 对外接收接口 `api.php`：外部系统直接推送数据（不读 source 库），校验并落库 `uk_vat_register`，注册邮箱自动创建

## 系统要求

- PHP 8.3.25+
- SQL Server PDO扩展 (pdo_sqlsrv)
- Composer

## 安装步骤

### 第一步：配置项目路径
1. 编辑 `config.bat` 文件，修改 `PROJECT_ROOT` 为实际项目路径：
```batch
set PROJECT_ROOT=D:\your-actual-project-path
```

### 第二步：数据库准备
在目标数据库中执行SQL脚本添加必要字段：
```sql
-- 执行 sql/add_country_fields.sql
-- 添加国家字段、年度GMV字段、邮箱字段和序号管理表
```

### 第三步：安装系统
1. 编辑配置文件（含凭据与本地路径，已 gitignore 不提交；新环境按现有部署实例的 `config/app.php`、`config/database.php` 结构创建并填写数据库密码）

2. 安装依赖：
```bash
composer install
```

## 使用方法

### 启动同步服务

```bash
# 测试环境
start-test.bat
# 或直接运行: php bin/sync.php env=test

# 生产环境
start-prod.bat
# 或直接运行: php bin/sync.php env=prod
```

### 命令行参数

程序支持以下命令行参数格式：
```bash
php bin/sync.php env=test        # 测试环境
php bin/sync.php env=prod        # 生产环境
```

### 程序运行机制

- 程序每5秒执行一次同步操作（无论是否有数据）
- 默认以**持续模式**运行（`config/app.php` 中 `max_cycles = 0`），无限循环，无需重启脚本
- 通过心跳文件 `heartbeat_{env}.txt` 实时记录运行状态（timestamp/cycle/status/pid），便于监控
- 连续错误达到10次时自动重新初始化服务（清理连接 → 重连数据库 → 重置错误计数）
- 通过锁文件 `sync_{env}.lock` 防止多个实例同时运行（含过期锁检测）
- 所有操作都会同时输出到控制台和日志文件
- 旧版"限次模式"（`max_cycles > 0`）运行满次数后退出并生成 `uk_vat_reg_restart` 文件（曾由 `auto-restart.bat` 自动重启，该脚本已移除，详见 [docs/CONTINUOUS_MODE.md](docs/CONTINUOUS_MODE.md)）

### 推荐运行方式

持续模式（`max_cycles = 0`）下直接前台运行即可，`Ctrl+C` 优雅停止：
```bash
start-test.bat            # 测试环境持续运行
start-prod.bat            # 生产环境持续运行
# 或直接运行: php bin/sync.php env=test / env=prod
```

旧版限次模式（`max_cycles > 0`）的自动重启脚本 `auto-restart.bat` 已移除，当前仅支持持续模式。

### 对外接收接口（api.php）

外部系统可直接 POST 业务数据（无需 source 库），系统校验并转换后写入 `uk_vat_register`，注册邮箱自动创建（重复提交复用邮箱）：

```bash
curl -X POST "http://127.0.0.1:8099/api.php" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: [api.php 顶部 API_KEY 非空时必带]" \
  --data-binary @英国VAT注册.json
```

- 当前支持 `PushType=GB_VAT_REGISTER`、`Country=GB`；运行环境由 `api.php` 顶部 `API_ENVIRONMENT` 常量配置（test/prod），请求不能切换环境
- 鉴权由 `api.php` 顶部 `API_KEY` 控制：非空时校验 `X-API-Key` 请求头（不匹配 401），留空则关闭鉴权；请求体上限 2MB
- 响应统一信封 `{code, msg, ProcessMode, data, bizParam}`；校验失败 400 一次性聚合全部错误；处理中/并发冲突 409
- 防重按 `DataSource='api'` + `source_record_id = API:{BusinessSerialNumber}` 联合：已存在且 `0/3` → UPDATE（复用邮箱）；`1` → 409；`2` → 跳过
- **⚠ 部署顺序（硬性）：必须先执行 `sql/add_data_source_and_api_fields.sql`（正式库 rpa，追加 DataSource/biz_param 列、约束与 API 唯一索引），再部署/重启 `bin/sync.php` 与 `api.php`**——新代码 SQL 已引用 DataSource/biz_param 列，旧库直接运行会报错；测试库 `rpa_test` 执行 `sql/rebuild_rpa_test_uk_vat_register.sql`（按正式表结构重建并清空测试数据）
- RPA 处理完成后，**API 行**结果经 delivery 平台统一异步结果通知接口回调调用方（`bizParam` 原样回传，地址优先级：受理 `bizParam.callback_url` > runner 传参 > 默认地址）；**source 行维持原有回调不变**；详见 [docs/API.md](docs/API.md)
- 契约与示例：`app_withdrawn` 仓库 `docs/UNIFIED_API_DESIGN.md`（§4.4.9 / §16）与 `docs/英国VAT注册.json`；本仓库说明见 [docs/API.md](docs/API.md)

### 配置说明

项目使用统一的配置文件管理：

- `config.bat` - 项目路径配置（必须先配置）
- `config/app.php` - 应用主配置
- `config/database.php` - 数据库配置

**重要**: 首次使用必须配置 `config.bat` 中的项目路径！

## 项目结构

```
├── api.php                      # 对外接收接口入口（GB_VAT_REGISTER，不读 source 库）
├── bin/
│   ├── sync.php                 # 同步守护进程入口
│   ├── update-sales-contact.php # 批量更新销售联系人
│   └── align-email-sequence.php # 邮箱序号表校准（一次性运维脚本）
├── python/
│   ├── rpa_get.py               # RPA 领取任务（状态置 1；重试上限时 source 行更新 SaaS 源库 / api 行发失败通知）
│   └── rpa_save.py              # RPA 保存结果（api 行走 delivery 统一异步结果通知 / source 行维持老回调）
├── config/
│   ├── app.php                  # 应用配置（含凭据，gitignore）
│   └── database.php             # 数据库配置（含凭据，gitignore）
├── src/
│   ├── Database/
│   │   └── Connection.php       # 数据库连接管理
│   ├── Logger/
│   │   └── Logger.php           # 日志管理
│   ├── Models/
│   │   └── VatBusinessRecord.php # 数据模型
│   ├── Services/
│   │   ├── DataSyncService.php   # 核心同步服务（source 流程）
│   │   ├── ApiDataSyncService.php# API 数据同步服务（api.php 流程）
│   │   ├── VatRecordTransform.php# 共用纯转换工具（日期/姓名/电话/提交月份/文件字段）
│   │   ├── CountryService.php    # 国家查询/转换服务（source 库缓存，每日刷新）
│   │   ├── StaticCountryService.php # 内建国家解析服务（API 流程，不读 source 库）
│   │   ├── EmailService.php      # 注册邮箱创建服务
│   │   └── FileService.php      # 文件处理服务
│   └── Validators/
│       └── DataValidator.php     # 数据校验器（validate=source / validateApiRecord=API）
├── docs/
│   └── API.md                   # 对外接收接口使用说明
├── logs/                        # 日志文件目录
├── config.bat                  # 项目路径配置文件
├── start-test.bat             # 测试环境启动脚本
├── start-prod.bat             # 生产环境启动脚本
├── test/                      # 测试文件目录
│   ├── test-basic.php         # 基本功能测试
│   ├── test-database.php      # 数据库连接测试
│   ├── test-country.php       # 国家服务测试
│   ├── test-email.php         # 邮箱服务测试
│   ├── test-fields.php        # 字段测试
│   ├── test-submission-month.php # 提交月份计算测试
│   ├── test_phone_formatting.php  # 电话格式化测试
│   ├── test_phone_validation.php  # 电话验证测试
│   ├── test_uk_fields.php     # 英国字段测试
│   ├── test-api-validator.php # API 校验器/转换工具测试（纯逻辑）
│   └── test-api.php           # API 服务集成测试（直连 rpa_test，数据自动清理）
├── sql/                       # SQL脚本目录
│   ├── add_data_source_and_api_fields.sql # 正式库 rpa 增量迁移（DataSource/biz_param 列 + API 唯一索引，幂等）
│   ├── rebuild_rpa_test_uk_vat_register.sql # 测试库 rpa_test 按正式表结构重建（清空测试数据）
│   └── add_country_fields.sql # 添加国家字段的SQL脚本
├── composer.json              # Composer配置
└── README.md                  # 项目说明
```

## 数据校验规则

系统会对以下字段进行校验：

- **公司名称**: 必填，最大64字符，只能包含英文字符
- **VAT生效日期**: 必填，有效日期且距当前日期 ±3 个月内
- **注册号**: 可选（海外税号允许为空），最大100字符，仅字母数字
- **法人姓名**: 必填拼音格式，最大255字符
- **法人生日**: 必填，年龄16-100岁
- **地址**: 地址1和地址2都必填，各最大35字符，只能包含英文字符
- **电话号码**: 必填，7-13位纯数字（自动去掉空格、+、-等符号）
- **国家代码**: 必填，支持中文名/二字码/英文名（公司国家不得为英国）
- **产品范围**: 必填，最大255字符，只能包含英文字符
- **年度GMV**: 必填，纯数字，最大值100,000
- **附件ID**: 仅 source 流程必填，最大100字符（API 流程无附件 ID，改为请求内传 3 个文件字段的 URL）

## 附件文件处理

系统会根据附件ID从Base_AnnexesFile表中查询相关文件，并按照文件名关键词进行分类：

### 文件分类规则

1. **法人证件** (upload_file_path1) - 三选一:
   - passport (护照)
   - identity card (身份证)
   - drive license (驾驶证)

2. **其他凭证** (upload_file_path2 和 upload_file_path3) - 从以下文件中任选:
   - bank statement (银行凭证)
   - credit card statement (信用卡流水)
   - electricity bill (电费账单)
   - water bill (水费账单)
   - property fee bill (物业费账单)
   - telephone bill (话费账单)
   - gas bill (燃气费)
   - broadband bill (宽带账单)
   - property ownership certificate (房产证)
   - birth certificate (户口本)
   - social security certificate (社保凭证)

**重要**: upload_file_path1、upload_file_path2、upload_file_path3 三个字段都必须不为空，否则验证失败。

## 国家代码处理

系统会自动处理国家代码转换：

### 国家缓存机制
- source 流程：程序启动时加载国家缓存文件 `country_cache_{env}.json`，超过1天（CACHE_TTL=86400）自动从源库Country表刷新
- 缓存包含：中文名 / 二字码 / 英文名 → CountryName_en（英文名）的映射
- 缓存文件在程序退出时自动清理，重启时重新生成
- API 流程：不读源库，使用内置 ISO 3166 国家表 + 业务 Country 表快照覆盖层（`StaticCountryService`），英文名与 source 流程保持一致

### 国家验证和转换
- 支持三种输入格式：
  - **中文名**: 如"中国"、"美国"
  - **二字码**: 如"CN"、"US"  
  - **英文名**: 如"China"、"United States"
- 验证输入值是否在Country表中存在（任一格式匹配即可）
- 统一转换为英文名存储到目标表：
  - `LegalPersonCountry` → `legal_person_country`
  - `Country` → `office_country`
- 如果输入值在Country表中不存在，验证失败并记录错误

## 邮箱创建功能

系统会自动为每个VAT注册记录创建专用邮箱：

### 邮箱生成规则
- **格式**: registrationvatXXXX@usaeu.com
- **序号**: 自动递增，从1开始
- **密码**: 固定为 meiou@2025
- **示例**: registrationvat1@usaeu.com, registrationvat1999@usaeu.com

### 邮箱管理
- 使用 `email_sequence_manager` 表管理序号
- 支持并发安全的序号生成
- 自动处理邮箱重复情况
- 失败时自动重试下一个序号

### 数据回写
- 成功创建后回写到源数据库：
  - `GBVAT_Register.RegisterEmail`
  - `GBVAT_Register.RegisterEmailPassword`
- 同时存储到目标数据库：
  - `uk_vat_register.customer_email`
  - `uk_vat_register.customer_email_password`

## 提交月份计算

系统会自动计算VAT提交月份：

### 计算规则
- **基准日期**: GBVAT_Register.VATStartDate
- **计算方式**: VAT生效日期 + 2个月
- **存储格式**: 英文月份全名
- **示例**: 
  - VAT生效日期: 2024-01-15 → 提交月份: March
  - VAT生效日期: 2024-11-01 → 提交月份: January (次年)

### 月份映射
- 1月 → January, 2月 → February, 3月 → March
- 4月 → April, 5月 → May, 6月 → June  
- 7月 → July, 8月 → August, 9月 → September
- 10月 → October, 11月 → November, 12月 → December

## 字段映射说明

### 当前数据同步阶段处理的字段

**源数据库字段映射**:
- `bc.NameEng` → `company_name_pinyin`
- `gr.VATStartDate` → `vat_effective_date_day/month/year`
- `bc.LegalPersonFullNamePinYin` → `legal_person_first_name/last_name`
- `bc.LegalPersonBirthDate` → `legal_person_birthday_day/month/year`
- `bc.LegalPersonAddressLine1En/2En` → `legal_person_id_address1/2` & `office_address1/2`
- `bc.LegalPersonCountry` → `legal_person_country` (转换为英文名)
- `bc.LegalPersonPhone` → `customer_phone` (清理+/-符号)
- `bc.Country` → `office_country` (转换为英文名)
- `gr.ProductsRange` → `customer_sales_platforms`
- `bc.RegNumber` → `reg_number`
- `gr.AnnualGMV` → `annual_gmv`

**系统生成字段**:
- 邮箱服务 → `customer_email` & `customer_email_password`
- VATStartDate+2月 → `submission_month`
- 附件服务 → `upload_file_path1/2/3`
- 系统时间 → `created_date` & `updated_date`

### 后续其他部分处理的字段
- **MTD集成**: `mtd_account`, `mtd_password`, `mtd_key`（由 RPA 保存步骤 `python/rpa_save.py` 写入）
- **注册流程**: `registration_status`（0 待处理 / 1 处理中 / 2 成功 / 3 失败 / 4 重试达上限）、`vat_registration_number`、`registration_failure_count`

### 文件路径转换

系统会自动将文件路径从MainSitePath转换为对应环境的访问路径：

- **测试环境**: G:/fileAnnexesTest → http://testfile.usaeu.com
- **生产环境**: G:/fileAnnexes → https://file.usaeu.com

## 状态说明

源库 `VATRegInfo.PushTaxBureauStatus` 状态流转：

- `PushTaxBureauStatus = 1`: 待同步（被 sync 拉取）
- `PushTaxBureauStatus = 2`: 推送中（同步成功）
- `PushTaxBureauStatus = 3`: 跳过（目标库中该记录已完成注册，registration_status = 2）
- `PushTaxBureauStatus = -1`: 处理失败（校验或同步错误）

**注意**: 状态和错误信息都更新在源库 **`VATRegInfo`** 表的 `PushTaxBureauStatus` / `PushTaxBureauErrorMsg` 字段（不是 VATBusinessRecord / VATTaxNumber 表）。

目标库 `uk_vat_register.registration_status` 状态流转：`0` 待处理（sync/API 落库）→ `1` 处理中（rpa_get 领取）→ `2` 成功 / `3` 失败（rpa_save 保存）；`4` 重试达上限（rpa_get 置位，api 行同时经 delivery 回调发失败通知）。

## 日志文件

- `logs/app.log`: 应用运行日志
- `logs/error.log`: 错误日志
- 日志按天自动轮转

## 配置说明

### 应用配置 (config/app.php)

- `sync_interval`: 同步间隔秒数（默认5秒）
- `max_cycles`: 最大循环次数（默认0=持续运行；旧版限次模式已移除自动重启支持）
- `php_executable`: PHP可执行文件路径配置
- `file_paths`: 文件路径转换配置
- `timezone`: 时区设置

### 数据库配置 (config/database.php)

数据库配置已预设为项目环境：
- **生产服务器**: 172.16.16.13:1433（内网 SQL Server）
- **测试服务器**: gz-mssql-4jvvbair.sql.tencentcdb.com（腾讯云 SQL Server）
- **用户名**: rpa_sa
- **密码**: 见 `config/database.php`

**数据库映射**:
- 测试环境: vat_db_test → rpa_test（腾讯云主机）
- 生产环境: vat_db → rpa（内网主机）

### PHP路径配置

项目自动使用根目录下的 `php/php.exe`，无需手动配置。如需修改，可在 `config/app.php` 中调整：
```php
'php_executable' => [
    'test' => __DIR__ . '/../php/php.exe', // 测试环境PHP路径
    'prod' => __DIR__ . '/../php/php.exe'  // 生产环境PHP路径
],
```

## 注意事项

1. **首次使用必须配置** `config.bat` 中的 `PROJECT_ROOT` 路径
2. 确保SQL Server PDO扩展已正确安装
3. 数据库用户需要有相应的读写权限
4. 确保项目目录下存在 `php` 文件夹和 `php.exe`
5. 所有批处理脚本都在项目根目录
6. 运行 `test/` 目录下的独立测试脚本（`php test/test-*.php`）验证各项功能
7. 持续模式下直接前台运行即可实现长期稳定运行（`max_cycles = 0`）
8. 定期检查日志文件和心跳文件（`heartbeat_{env}.txt`），监控系统运行状态