Files
baiyue-firstleg/docs/公共导出与下载中心接入说明.md

152 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 公共导出与下载中心接入说明
本文描述当前已经实现的 RocketMQ + 事务 Outbox 版本。完整状态机、故障模型和容量门槛见[公共导出中心 RocketMQ 架构设计](./公共导出中心RocketMQ架构设计.md)。
## 1. 边界
- `file-service` 统一承载任务、可靠调度、通用 XLSX、存储、过期清理、下载鉴权和指标。
- File 不跨库读取 IAM、Order、Warehouse 等业务数据;业务服务只实现标准只读 Provider。
- MQ 只传任务标识,不传筛选条件、业务行、Token 或文件。
- 订单与客户存储是 `FILE_STORAGE_ROOT` 目录且按单节点部署;多实例前必须使用跨主机共享卷或对象存储。
- 当前首个类型为 `CUSTOMER_LIST`,由 IAM 提供数据。
## 2. 执行链路
```text
Admin
-> POST /api/platform/v1/files/export-tasks
-> file_export_task_guard 行锁下复核重复提交及活动上限
-> 同一事务写 file_export_task + file_export_outbox
-> Publisher 投递 RocketMQ(失败无限退避)
-> Consumer 按 dispatch_version + 数据库租约领取(并发 4)
-> POST /internal/exports/v1/{TYPE}/batches
-> 通用 SXSSFWorkbook 流式写盘
-> 条件提交 SUCCEEDED
-> GET /api/platform/v1/files/export-tasks/{id}/download
```
```text
PENDING -> RUNNING -> SUCCEEDED -> EXPIRED
-> PENDING(自动重试,dispatch_version + 1)
-> FAILED -> PENDING(人工重试,dispatch_version + 1)
```
消息补投保持同一版本;只有执行失败、执行租约恢复和人工重试递增版本。独立 watchdog 覆盖 Provider 拉取和最终写盘,最终成功 SQL 同时校验任务状态、版本、租约所有者与数据库 deadline。
Reconciler 负责补写缺失 Outbox、同版本重发长期 `SENT` 命令、恢复超时执行租约、释放超时 Outbox 租约及过期文件清理。旧 `ExportTaskWorker` 已删除,避免绕过版本和退避窗口。
## 3. 浏览器 API
- `POST /api/platform/v1/files/export-tasks`
- `GET /api/platform/v1/files/export-tasks`
- `GET /api/platform/v1/files/export-tasks/{id}`
- `GET /api/platform/v1/files/export-tasks/{id}/download`
- `POST /api/platform/v1/files/export-tasks/{id}/retry`
创建示例:
```json
{
"exportType": "CUSTOMER_LIST",
"format": "XLSX",
"taskName": "客户列表",
"parameters": { "keyword": "柏悦", "status": "ACTIVE" }
}
```
创建按注册表校验访问域、启用状态和来源权限。客户导出要求 `platform:customer:export`;列表和下载要求 `platform:download:list`。默认只能访问本人任务,`platform:download:all` 可访问同一租户与应用域的全部任务。人工重试只允许 `FAILED`,并重新校验类型启用状态和来源权限。
限制:单用户最多 3 个活动任务、单类型最多 5 个活动任务;同一用户 60 秒内相同类型和规范化参数返回已有活动任务。
## 4. Provider 契约
```http
POST /internal/exports/v1/{exportType}/batches
X-Internal-Service-Token: ${INTERNAL_SERVICE_TOKEN}
Content-Type: application/json
```
请求使用 `common-core` 的 `ExportBatchRequest`,必须包含 `taskId`、`tenantId`、`appCode`、`requestedBy`、`dispatchVersion`、`parameters`、`cursor`、`snapshotToken` 和 `limit`。Provider 必须按身份字段约束查询,不得信任 parameters 内的租户字段。
响应使用 `ExportBatchResponse`:首批返回 `schemaVersion`、`schemaHash`、`snapshotToken`、`columns`、`rows`、游标和估算总行数;后续批次必须保持 `schemaHash`,可省略 `columns`。File 在 Feign 解码流读取阶段限制单批 4MB,再校验游标推进、列/行/文件上限,并支持 `STRING`、`INTEGER`、`DECIMAL`、`BOOLEAN`、`DATE`、`DATETIME` 六类 Excel 单元格。
IAM `CUSTOMER_LIST` Provider 强制 `tenantId=0`、`appCode=PLATFORM`,采用 HMAC 签名的最大商户 ID 快照。因此不会混入开始后新增客户,但不冻结期间的修改与删除。
新增类型只需:业务服务实现标准 Provider、在 `baiyue.file.export.definitions` 注册受控服务/路径/权限/访问域/容量参数、补权限和测试。不得修改公共 Controller、Consumer 或 Writer。
## 5. MQ 与 DLQ
- Topic:`${MQ_TOPIC_PREFIX}baiyue-file-export-command-v1`,4 个队列。
- Consumer:`${MQ_TOPIC_PREFIX}baiyue-file-export-worker-v1`,同步并发 4。
- Producer:`${MQ_TOPIC_PREFIX}baiyue-file-export-publisher-v1`。
- `eventId`、`taskId`、`dispatchVersion`、`schemaVersion` 同时写入 User Properties。
- 业务失败从不进入 DLQ;只有无法反序列化或不支持版本的消息不确认消费。
- DLQ Handler 不解析损坏 body,只从四个属性定位,并在同一事务内把 Outbox 置 `QUARANTINED`、当前 PENDING 任务置 `FAILED`。缺少定位属性只记录 CRITICAL 和 messageId,不误伤任务。
- DLQ Handler 的数据库处置失败持续重试,不允许因有限次数耗尽进入无人消费的二级 DLQ。
## 6. 配置与部署
关键环境变量:
```text
ROCKETMQ_NAME_SERVER
ROCKETMQ_BROKER_IP
MQ_TOPIC_PREFIX
FILE_EXPORT_CONSUMER_CONCURRENCY
FILE_EXPORT_RECONCILER_ENABLED
FILE_EXPORT_PUBLISHER_ENABLED
FILE_EXPORT_CONSUMER_ENABLED
FILE_EXPORT_RETENTION_DAYS
FILE_STORAGE_ROOT
```
`MQ_TOPIC_PREFIX` 是 File 部署必填项但允许生产空值:测试环境显式填 `test-`,生产环境显式填空字符串。Compose 单服务部署会插值整份文件,因此 File 服务块本身使用空默认值,避免 Auth/IAM 等无关服务被 MQ 配置阻断;`deploy-services.sh` 只在部署 File 时按环境强校验该变量与 `ROCKETMQ_NAME_SERVER`。
当前 RocketMQ Compose 不映射 Broker 宿主机端口,File 与 Broker 必须同在 `infra-network`,因此 `ROCKETMQ_BROKER_IP` 填 `rocketmq-broker`。Topic 初始化会等待 `BaiyueCluster` 真正注册,任一幂等初始化命令失败后自动重试。
File 容器初始 JVM 为 `-Xms1g -Xmx2g -XX:+ExitOnOutOfMemoryError`。Provider 单批连接/读取超时分别为 3 秒/15 秒;执行租约 300 秒、watchdog 60 秒、deadline 30 分钟、消费超时 90 分钟、单文件 50MB。
开发阶段确认无保留任务后,重建并执行 `file/src/main/resources/db/file.sql`。RocketMQ 使用 `deploy/docker-compose.rocketmq.yml`;脚本会先校验并展开 `ROCKETMQ_BROKER_IP`,再显式创建 4 队列命令 Topic、Worker 消费组、DLQ Handler 消费组以及可读写的 Worker DLQ。上线顺序:Broker -> SQL -> Nacos 注册表 -> IAM Provider -> File 三开关全关 -> Reconciler -> Publisher -> Consumer -> Admin。
生产启用并发 4 前必须完成 4 个 20 万行任务并发压测;若 CPU、堆、磁盘、Provider 或 IAM 普通接口延迟不达设计门槛,回退并发 2。
## 7. 观测与验收
Prometheus 指标包括任务创建/完成/耗时、队列年龄、Outbox 数量/年龄/最大尝试次数、Reconciler 动作与 `export_dlq_messages_total`;日志携带任务与版本标识但不打印完整数据或令牌。
最低验收:任务与 Outbox 同事务;Broker 停机恢复后追平;重复消息只生成一个有效文件;watchdog 覆盖最终写盘;deadline 竞态不能提交成功;同版本补投不递增版本;毒消息只隔离一次;IAM 快照不混入新增客户;生成的 XLSX 可真实打开且日期、金额为可计算类型;普通用户不能访问他人任务。
### 7.1 测试环境自动验收入口
`scripts/export-center-test-env-acceptance.ps1` 通过 Gateway 创建真实 `CUSTOMER_LIST` 任务,轮询终态,下载文件并校验 XLSX 的 ZIP 签名。Token 只通过参数或环境变量注入,脚本不会登录、部署、停止 Broker 或改数据库。
单任务冒烟:
```powershell
$env:ADMIN_ACCESS_TOKEN = '<测试环境平台用户Token>'
.\scripts\export-center-test-env-acceptance.ps1 `
-GatewayBaseUrl 'http://8.163.30.128:9000'
```
并发 4 容量验收必须使用 4 个不同申请用户,避免 60 秒重复提交合并,并要求每个账号都具备 `platform:customer:export` 与 `platform:download:list`:
```powershell
$tokens = @($env:EXPORT_TOKEN_1, $env:EXPORT_TOKEN_2, $env:EXPORT_TOKEN_3, $env:EXPORT_TOKEN_4)
.\scripts\export-center-test-env-acceptance.ps1 `
-GatewayBaseUrl 'http://8.163.30.128:9000' `
-AccessTokens $tokens -TaskCount 4 -MinimumRows 200000 -TimeoutSeconds 3600
```
故障注入分两段执行:故障期间使用 `-CreateOnly` 创建并保存任务 ID;恢复 Broker、File 或 IAM 后,用相同申请人的 Token 和 `-TaskIds` 继续轮询与下载。这样停止/恢复基础设施仍由有权限的运维操作,脚本只验证任务是否最终追平:
```powershell
.\scripts\export-center-test-env-acceptance.ps1 `
-GatewayBaseUrl 'http://8.163.30.128:9000' -AccessTokens $tokens -TaskCount 4 -CreateOnly
.\scripts\export-center-test-env-acceptance.ps1 `
-GatewayBaseUrl 'http://8.163.30.128:9000' -AccessTokens $tokens `
-TaskIds @('<taskId1>', '<taskId2>', '<taskId3>', '<taskId4>') -MinimumRows 200000
```
脚本通过只证明 API 创建、数据库状态机、MQ 触发、Provider、XLSX 写入和下载主链路。CPU/堆/磁盘、Provider P95/P99、IAM 普通接口退化比例仍须同时从 Prometheus/Grafana 取证;DLQ 毒消息必须由 `mqadmin` 按 §19.2 投递,不能由普通验收账号伪造。