实现Excel转PDF手动转换接口
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
| `../../README.md` | 当前有效 | 项目根说明,记录目录、启动命令、健康检查、Debug EML 和 MCP 基础说明。 |
|
||||
| `backend-development-guidelines.md` | 当前有效 | 当前项目后端专属规范。 |
|
||||
| `backend-time-design.md` | 当前有效 | 当前项目时间设计说明,记录数据库 UTC、API `Z` 时间、酒店时区展示和本地日期边界。 |
|
||||
| `security-access-control-boundary.md` | 当前有效 | 当前项目接口暴露、权限码、酒店隔离和审计边界总表;新增或修改接口时必须同步。 |
|
||||
| `frontend-development-guidelines.md` | 当前有效 | 当前项目前端专属规范。 |
|
||||
| `frontend-backend/README.md` | 当前有效 | 前后端协作入口,记录接口契约来源、字段白名单和当前后置事项。 |
|
||||
| `go-live-notes.md` | 当前有效 | 当前项目上线注意事项,记录上线前检查、环境变量、安全、AgentBus、验证和回滚。 |
|
||||
@@ -42,6 +43,7 @@
|
||||
| `requirements/M005-hotel-context-unification-plan.md` | 当前有效 | M005 酒店上下文统一收口方案。 |
|
||||
| `requirements/M006-system-admin-management-console-v1.md` | 草案 | M006 系统管理后台方案,覆盖用户、角色、权限、菜单、酒店和用户酒店授权维护。 |
|
||||
| `requirements/M007-agentbus-superagent-auto-dispatch-v1.md` | 当前有效 | M007 AgentBus 新邮件入库后异步分发 SuperAgent 的后端 V1 方案,当前默认关闭,等待测试机联调。 |
|
||||
| `requirements/M008-excel-to-pdf-conversion-v1.md` | 当前有效 | M008 Excel 转 PDF 文件转换能力方案,记录 LibreOffice headless、手动上传转换、邮件附件自动派生 PDF 和部署要求;CP2 已实现手动上传后端接口。 |
|
||||
|
||||
## 集成契约
|
||||
|
||||
@@ -70,6 +72,7 @@
|
||||
|
||||
- SuperAgent 对外 HTTP 接口以 `integrations/superagent-api-contract.md` 为权威来源。
|
||||
- SuperAgent MCP 文档以 `integrations/superagent-mcp/` 为对外交付资料包,但字段语义应跟随 HTTP 总契约。
|
||||
- 接口暴露、权限、酒店隔离和审计边界以 `security-access-control-boundary.md` 为总检查清单;具体 SuperAgent / MCP / AgentBus 请求响应契约仍以 `integrations/` 下对应文档为准。
|
||||
- M002 V1 只作为历史参考;V2 记录当前阶段实现;后续 M002 新开发以 `requirements/M002-order-task-workflow-v3.md` 为开发基线。
|
||||
- 前端展示 / 编辑字段以 2026-07-11 P0 冻结基线中的前端字段表、0712 字段控件说明和 `requirements/M002-task-field-control-contract-v1.md` 为白名单和控件契约基线;后端完整校验和 OPERA 映射仍以任务卡完整矩阵、0711 runtime 契约和后端规则为准。
|
||||
- 时间点语义以 `backend-time-design.md` 为准;数据库时间点按 UTC 理解,API 返回带 `Z` 的 UTC 时间,页面再按酒店或用户时区展示。
|
||||
|
||||
@@ -65,8 +65,8 @@
|
||||
| `GET /api/reservation/tasks/{taskId}/audits` | 查询任务审计流水 | 必须带 Bearer token,需要 `RESERVATION_AUDIT_READ`,后端按任务所属酒店做访问校验;用于展示人工确认、转换、模拟操作等轨迹。 |
|
||||
| `GET /api/source-messages` | 查询来源消息安全摘要 | 必须带 Bearer token,需要 `SOURCE_MESSAGE_READ`;列表不返回邮件正文、HTML、附件 URL 或原始 payload;查询参数以 `hotel_id`、`external_message_id`、`external_conversation_id`、`page_num`、`page_size` 为准,后端暂兼容早期 camelCase 参数。 |
|
||||
| `GET /api/source-messages/{id}` | 查询来源消息安全详情 | 必须带 Bearer token,需要 `SOURCE_MESSAGE_READ`,后端按消息所属酒店做访问校验;只用于安全摘要详情。 |
|
||||
| `GET /api/source-messages/{id}/original` | 读取来源消息原文 | 需要受控访问头,返回 HTML 时前端展示前必须 sanitize。 |
|
||||
| `GET /api/source-messages/{sourceMessageId}/conversation` | 读取邮件会话详情 | 返回同一外部会话全部邮件的完整 text/html、`html_body_sanitized`、附件外链、内联图片和关联订单 / 任务摘要;前端不传原文读取 key,展示 HTML 时优先使用 `html_body_sanitized`。 |
|
||||
| `GET /api/source-messages/{id}/original` | 读取来源消息原文 | 必须带 Bearer token,需要同时拥有 `SOURCE_MESSAGE_READ` 和 `SOURCE_MESSAGE_ORIGINAL_READ`;后端按消息所属酒店做访问校验;返回 HTML 时前端展示前必须 sanitize。 |
|
||||
| `GET /api/source-messages/{sourceMessageId}/conversation` | 读取邮件会话详情 | 必须带 Bearer token,需要同时拥有 `SOURCE_MESSAGE_READ` 和 `SOURCE_MESSAGE_ORIGINAL_READ`;返回同一外部会话全部邮件的完整 text/html、`html_body_sanitized`、附件外链、内联图片和关联订单 / 任务摘要;前端不传原文读取 key,展示 HTML 时优先使用 `html_body_sanitized`。 |
|
||||
| `POST /api/system/debug/eml-superagent-runs` | Debug 页面上传 `.eml` 并调用 SuperAgent | 仅 dev/test 受控调试使用;会写入 SourceMessage Inbox,但不创建订单和任务。 |
|
||||
| `GET/POST/PUT /api/admin/users...` | 系统管理用户维护 | 需要 Bearer token 和 `SYSTEM_USER_MANAGE`;用户 ID 返回字符串;禁用用户会撤销其 ACTIVE session。 |
|
||||
| `GET/POST/PUT /api/admin/roles...` | 系统管理角色权限维护 | 需要 `SYSTEM_ROLE_MANAGE`;内置角色只读,自定义角色可新增、编辑和分配权限。 |
|
||||
@@ -107,7 +107,7 @@ POST /api/auth/logout
|
||||
- 当前后端已强制拦截第一批 Reservation / SourceMessage 只读接口:任务列表、订单列表、订单详情、任务详情、任务审计列表、SourceMessage 摘要列表和摘要详情。调用这些接口必须带 Bearer token。
|
||||
- 第一批只读接口权限码分别是:`RESERVATION_TASK_READ`、`RESERVATION_ORDER_READ`、`RESERVATION_AUDIT_READ`、`SOURCE_MESSAGE_READ`。前端菜单、按钮和路由守卫应使用 `/api/auth/me` 返回的 `permissions[]` 与 `menus[]`。
|
||||
- 后端会按当前登录用户的可访问酒店集合做隔离;显式传 `hotel_id` 时会校验该酒店是否可访问,按 `orderId`、`taskId`、`sourceMessageId` 定位的详情接口会反查对象实际所属酒店并校验访问权。
|
||||
- Reservation 写操作、邮件原文 / conversation 完整正文、Debug / Demo / Replay / Probe 等接口仍按 `../security-access-control-boundary.md` 的分阶段计划继续收口,前端不要自行假设它们和第一批只读接口完全一致。
|
||||
- 邮件原文 / conversation 完整正文接口已完成权限收口,必须带 Bearer token 且同时需要 `SOURCE_MESSAGE_READ` 和 `SOURCE_MESSAGE_ORIGINAL_READ`;Reservation 写操作、Debug / Demo / Replay / Probe 等接口仍按 `../security-access-control-boundary.md` 的分阶段计划继续收口。
|
||||
- `/api/auth/me` 返回 `user`、`default_hotel_id`、`hotels[]`、`permissions[]`、`menus[]`;菜单入口应优先使用 `menus[]`,不要继续硬编码订单列表、任务队列、Debug EML。
|
||||
- `menus[]` 只包含可见菜单;订单详情、任务详情和邮件会话详情是隐藏详情路由,不会作为菜单项返回。
|
||||
- `DEBUG_EML_SUPERAGENT` 菜单第一版只授予 `SYSTEM_ADMIN`;这只表示页面入口是否可见,不代表后端会把 `X-TH-Hotel-Debug-Upload-Key` 下发给前端。
|
||||
@@ -131,14 +131,14 @@ POST /api/auth/logout
|
||||
|
||||
### 5.4 邮件会话详情接入注意
|
||||
|
||||
- `GET /api/source-messages/{sourceMessageId}/conversation` 只接收路径参数 `sourceMessageId`;第一版不接收 `hotelId`、`includeBody`、`includeRelated`。
|
||||
- `GET /api/source-messages/{sourceMessageId}/conversation` 只接收路径参数 `sourceMessageId`;第一版不接收 `hotelId`、`includeBody`、`includeRelated`;请求必须带 `Authorization: Bearer <access_token>`,且当前用户需要同时拥有 `SOURCE_MESSAGE_READ` 和 `SOURCE_MESSAGE_ORIGINAL_READ`。
|
||||
- Reservation 列表、任务列表和订单详情默认不需要前端传 `hotel_id`;如果前端已经接入酒店选择器,可以把当前选中酒店作为可选 `hotel_id` 传给后端。任务详情、任务写操作和邮件会话详情当前仍按对象 ID 定位,不接收该参数。
|
||||
- 后端会根据 `sourceMessageId` 定位 `external_conversation_id`,并返回同一会话下全部邮件;如果来源消息没有外部会话 ID,会降级返回当前单封邮件。
|
||||
- `messages[]` 按邮件来源接收时间正序返回,前端不要重新按创建时间或任务时间排序。
|
||||
- 返回内容包含完整 `text_body`、`html_body`、`inline_images[]`、`attachments[]`、`related_orders[]`、`related_tasks[]`。
|
||||
- `html_body` 是原始 HTML 兼容字段;`html_body_sanitized` 是后端第一版清洗结果,已移除脚本标签、事件属性和危险协议链接。前端生产展示必须优先使用 `html_body_sanitized`,并可用 `html_render_mode=SANITIZED_HTML` 判断渲染模式。
|
||||
- 第一版仅处理 HTML 内容安全;`inline_images[]` 和 `attachments[]` 的 `externalUrl` 来自本系统 OSS 服务,暂不做额外拦截,但前端仍不得写入普通日志、错误上报、localStorage 或 URL query。
|
||||
- 会话详情接口由后端内部写原文读取审计,前端不传 `X-TH-Hotel-Source-Original-Read-Key`。
|
||||
- 会话详情接口由后端内部写原文读取审计,actor 使用当前登录用户稳定 ID;前端不传 `X-TH-Hotel-Source-Original-Read-Key`、`X-TH-Hotel-Actor` 或 `X-TH-Hotel-Access-Scene`。
|
||||
- 会话详情外层字段主要是 snake_case,但媒体对象沿用原文读取接口字段,当前是 `mediaType`、`fileName`、`contentType`、`sizeBytes`、`externalUrl`、`externalMediaId` 这种 camelCase,前端类型定义需要单独处理。
|
||||
|
||||
### 5.5 订单列表接入注意
|
||||
@@ -336,6 +336,47 @@ run_label: 可选调试标签
|
||||
- 单酒店阶段只允许一家 `ACTIVE` 酒店,后端会拒绝启用第二家 `ACTIVE`,也会拒绝禁用最后一家 `ACTIVE`。
|
||||
- 系统管理写操作会写入 `platform_admin_audit_log`;审计接口 `GET /api/admin/audits` 可按 `target_type`、`target_id`、`action` 查询。
|
||||
|
||||
### 5.10 Excel 转 PDF 手动上传接口接入注意
|
||||
|
||||
后端已提供 M008 CP2 Excel 转 PDF 手动上传接口:
|
||||
|
||||
```text
|
||||
POST /api/system/document-conversions/excel-to-pdf
|
||||
Header: X-TH-Hotel-Document-Conversion-Key: <文件转换访问口令>
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
file: .xls / .xlsx 文件
|
||||
hotel_id: 可选;用于 OSS 对象路径分组
|
||||
```
|
||||
|
||||
成功响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"conversion_status": "SUCCEEDED",
|
||||
"source_file_name": "booking-request.xlsx",
|
||||
"source_size_bytes": 12345,
|
||||
"pdf_file_name": "booking-request.pdf",
|
||||
"pdf_url": "https://oss.example.test/document-conversions/excel-to-pdf/HOTEL-TEST/2026-07-16/.../booking-request.pdf",
|
||||
"object_key": "document-conversions/excel-to-pdf/HOTEL-TEST/2026-07-16/.../booking-request.pdf",
|
||||
"content_type": "application/pdf",
|
||||
"pdf_size_bytes": 67890,
|
||||
"duration_millis": 1200
|
||||
}
|
||||
```
|
||||
|
||||
前端注意:
|
||||
|
||||
- 该接口当前属于受控调试 / 后台工具能力,不是普通公开上传接口。
|
||||
- `X-TH-Hotel-Document-Conversion-Key` 不能写入 `VITE_*`、源码、构建产物、URL query、localStorage、错误上报或普通日志。
|
||||
- 只允许上传 `.xls` / `.xlsx`;`.xlsm` 第一版不支持。后端会做扩展名和文件头轻量校验,改后缀的非 Excel 文件会返回 `DOCUMENT_CONVERSION_FILE_CONTENT_INVALID`。
|
||||
- 后端默认大小限制是 20 MB,测试机可以通过环境变量调整。
|
||||
- `pdf_url` 来自本系统 OSS,可用于预览或下载,但不要写入普通日志、埋点、错误上报或 URL query。
|
||||
- `DOCUMENT_CONVERSION_DISABLED` 表示后端未开启文件转换能力,页面应提示联系管理员或切换到已开启环境。
|
||||
- `DOCUMENT_CONVERSION_BUSY` 表示后端 LibreOffice 并发已满,页面可以提示稍后重试。
|
||||
- `DOCUMENT_CONVERSION_TIMEOUT` / `DOCUMENT_CONVERSION_FAILED` 通常需要后端排查 LibreOffice、字体、文件格式或临时目录权限。
|
||||
- CP2 不会创建转换任务记录,也不会自动处理邮件附件;邮件附件自动派生 PDF 是 M008 后续 checkpoint。
|
||||
|
||||
## 6. 不给前端直接调用的接口
|
||||
|
||||
- `POST /api/system/reservation/demo-data` 只用于 dev/test 联调造数,不是生产业务页面接口;访问口令不能进入前端代码。
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
- Reservation OPERA 模拟骨架:已确认任务固定生成两条模拟操作,支持执行、失败重试、attempt 记录和任务审计列表。
|
||||
- SuperAgent 查询上下文接口 1、2:支持 HMAC 鉴权的订单上下文查询和对象详情查询。
|
||||
- Debug EML 上传到 SuperAgent 调试链路:受控上传 `.eml`、转存阿里云 OSS、写入 SourceMessage Inbox、调用 SuperAgent Open API 并返回调试结果。
|
||||
- Excel 转 PDF 手动上传接口:受控上传 `.xls` / `.xlsx`,通过 LibreOffice headless 转 PDF 后上传阿里云 OSS 并返回 PDF URL。
|
||||
- 登录权限底座:支持用户名密码登录、登出、当前用户上下文、数据库 session token、可访问酒店、权限码和可见菜单。
|
||||
- 系统管理后台 V1:支持用户、角色权限、菜单、酒店和管理操作审计的受控维护接口与前端页面。
|
||||
|
||||
@@ -34,6 +35,7 @@
|
||||
- 现有业务接口强制登录和强制权限拦截。
|
||||
- 业务审计 actor 全量迁移到当前登录用户。
|
||||
- Debug EML 上传链路不属于生产普通业务页面能力,生产默认关闭;即使已有登录权限,也不要开放给普通用户。
|
||||
- Excel 转 PDF 当前只完成手动上传后端接口;邮件附件自动转换、持久化转换任务和 worker 尚未实现。生产默认关闭,启用前必须确认 LibreOffice、字体、OSS、临时目录和访问口令。
|
||||
|
||||
## 2. 上线前必须确认
|
||||
|
||||
@@ -91,17 +93,13 @@
|
||||
|
||||
### 3.3 SourceMessage
|
||||
|
||||
| 变量 | 是否 Secret | 上线注意事项 |
|
||||
| --- | --- | --- |
|
||||
| `SOURCE_MESSAGE_DEV_ORIGINAL_READ_ACCESS_KEY` | 是 | dev 原文读取临时访问 key。未配置时可兜底使用旧通用变量。 |
|
||||
| `SOURCE_MESSAGE_TEST_ORIGINAL_READ_ACCESS_KEY` | 是 | test 原文读取临时访问 key。未配置时可兜底使用旧通用变量。 |
|
||||
| `SOURCE_MESSAGE_PROD_ORIGINAL_READ_ACCESS_KEY` | 是 | prod 原文读取临时访问 key。未配置时原文读取默认关闭。 |
|
||||
| `SOURCE_MESSAGE_ORIGINAL_READ_ACCESS_KEY` | 是 | 旧通用原文读取 key,仅作为兼容兜底。 |
|
||||
SourceMessage 原文和邮件会话完整正文已迁移到登录权限体系:
|
||||
|
||||
注意:
|
||||
|
||||
- 原文读取 key 不是用户体系,后续接入正式登录和角色权限后应替换。
|
||||
- 任何能读取原文的调用都必须有调用方和访问场景,并写入审计表。
|
||||
- 前端请求必须携带 `Authorization: Bearer <access_token>`。
|
||||
- 当前用户必须同时拥有 `SOURCE_MESSAGE_READ` 和 `SOURCE_MESSAGE_ORIGINAL_READ`。
|
||||
- 后端按 SourceMessage 实际所属酒店校验酒店访问权。
|
||||
- 后端内部写入原文读取审计,actor 使用当前登录用户稳定 ID。
|
||||
- 旧 `SOURCE_MESSAGE_DEV_ORIGINAL_READ_ACCESS_KEY`、`SOURCE_MESSAGE_TEST_ORIGINAL_READ_ACCESS_KEY`、`SOURCE_MESSAGE_PROD_ORIGINAL_READ_ACCESS_KEY`、`SOURCE_MESSAGE_ORIGINAL_READ_ACCESS_KEY` 已废弃,不再作为部署必备 Secret。
|
||||
|
||||
### 3.4 AgentBus
|
||||
|
||||
@@ -201,6 +199,31 @@
|
||||
- Debug EML 和 AgentBus 自动分发复用同一个 SuperAgent Open API SSE 稳定客户端;上线前必须验证 `run.completed + end + final answer` 严格成功条件和 EOF 后 `/events` 恢复。
|
||||
- 当前共享 Open API client 会自动携带临时 CSRF double-submit header / cookie;如果测试机仍返回 `CSRF token missing`,优先检查部署包版本和反向代理是否转发 `X-CSRF-Token`、`Cookie`。
|
||||
|
||||
### 3.7 Excel 转 PDF / LibreOffice
|
||||
|
||||
| 变量 | 是否 Secret | 上线注意事项 |
|
||||
| --- | --- | --- |
|
||||
| `DOCUMENT_CONVERSION_DEV_ENABLED` / `DOCUMENT_CONVERSION_TEST_ENABLED` / `DOCUMENT_CONVERSION_PROD_ENABLED` | 否 | 是否启用 Excel 转 PDF 手动上传接口;prod 默认必须保持 `false`,确认运行环境后再开启。 |
|
||||
| `DOCUMENT_CONVERSION_DEV_ACCESS_KEY` / `DOCUMENT_CONVERSION_TEST_ACCESS_KEY` / `DOCUMENT_CONVERSION_PROD_ACCESS_KEY` | 是 | 文件转换访问口令;未配置时可兜底 `DOCUMENT_CONVERSION_ACCESS_KEY`。不得进入前端源码、镜像、普通日志或文档真实值。 |
|
||||
| `DOCUMENT_CONVERSION_SOFFICE_PATH` / `DOCUMENT_CONVERSION_*_SOFFICE_PATH` | 否 | `soffice` 可执行文件路径,默认 `soffice`。测试机和生产机路径可能不同。 |
|
||||
| `DOCUMENT_CONVERSION_TEMP_DIR` / `DOCUMENT_CONVERSION_*_TEMP_DIR` | 否 | 文件转换临时目录。后端运行用户必须有读写权限,目录应有系统清理策略。 |
|
||||
| `DOCUMENT_CONVERSION_MAX_FILE_BYTES` / `DOCUMENT_CONVERSION_*_MAX_FILE_BYTES` | 否 | 单个 Excel 文件大小上限,默认 `20971520`。 |
|
||||
| `DOCUMENT_CONVERSION_TIMEOUT_SECONDS` / `DOCUMENT_CONVERSION_*_TIMEOUT_SECONDS` | 否 | 单次 LibreOffice 转换超时秒数,默认 `60`。 |
|
||||
| `DOCUMENT_CONVERSION_MAX_CONCURRENT` / `DOCUMENT_CONVERSION_*_MAX_CONCURRENT` | 否 | 最大并发转换数,默认 `2`。不要盲目调大,避免 LibreOffice 进程拖垮后端。 |
|
||||
| `DOCUMENT_CONVERSION_OUTPUT_OSS_PREFIX` / `DOCUMENT_CONVERSION_*_OUTPUT_OSS_PREFIX` | 否 | PDF 输出 OSS 前缀,默认 `document-conversions/excel-to-pdf/`。 |
|
||||
| `DOCUMENT_CONVERSION_WORKER_ENABLED` / `DOCUMENT_CONVERSION_*_WORKER_ENABLED` | 否 | 自动转换 worker 预留开关;CP2 不使用,默认关闭。 |
|
||||
| `DOCUMENT_CONVERSION_MULTIPART_MAX_FILE_BYTES` / `DOCUMENT_CONVERSION_MULTIPART_MAX_REQUEST_BYTES` | 否 | Spring multipart 框架上限;默认分别为 `25165824` / `29360128`,应高于业务 `MAX_FILE_BYTES`,否则请求会在进入 Controller 前被框架拦截。 |
|
||||
|
||||
注意:
|
||||
|
||||
- 服务器必须安装 LibreOffice / LibreOffice Calc,并确认后端运行用户可以执行 `soffice`。
|
||||
- 服务器必须安装中文、英文、泰文等业务字体;缺少字体会导致 PDF 乱码、缺字或分页变化。
|
||||
- 每次转换会创建独立临时目录和 LibreOffice profile;正常结束、失败或超时后后端会清理,但仍建议运维配置临时目录兜底清理。
|
||||
- 当前接口路径为 `POST /api/system/document-conversions/excel-to-pdf`,Header 为 `X-TH-Hotel-Document-Conversion-Key`。
|
||||
- 当前接口只支持 `.xls` / `.xlsx`,不支持 `.xlsm`;后端会校验扩展名和文件头,改后缀的非 Excel 文件会返回受控错误。
|
||||
- PDF 上传到阿里云 OSS,返回 `pdf_url`、`object_key`、`pdf_file_name`、`pdf_size_bytes` 和 `duration_millis`。
|
||||
- CP2 不落库,不提供转换历史查询;如果需要自动处理邮件附件,应先进入 M008 后续持久化任务和 worker checkpoint。
|
||||
|
||||
## 4. 数据库上线注意事项
|
||||
|
||||
当前 SourceMessage 相关 migration:
|
||||
@@ -308,9 +331,8 @@ SourceMessage 普通列表和普通详情只能返回安全摘要:
|
||||
|
||||
```text
|
||||
GET /api/source-messages/{id}/original
|
||||
Header: X-TH-Hotel-Source-Original-Read-Key
|
||||
Header: X-TH-Hotel-Actor
|
||||
Header: X-TH-Hotel-Access-Scene
|
||||
Header: Authorization: Bearer <access_token>
|
||||
Required permission: SOURCE_MESSAGE_READ + SOURCE_MESSAGE_ORIGINAL_READ
|
||||
```
|
||||
|
||||
前端展示 `htmlBody` 前必须 sanitize。后端返回 `htmlSanitizeRequired=true` 是提醒前端不要直接信任 HTML。
|
||||
@@ -418,15 +440,13 @@ GET /api/source-messages/{id}/original
|
||||
```text
|
||||
AGENTBUS_PROBE_ENABLED=false
|
||||
AGENTBUS_CAPTURE_ENABLED=false
|
||||
SOURCE_MESSAGE_PROD_ORIGINAL_READ_ACCESS_KEY=
|
||||
SOURCE_MESSAGE_ORIGINAL_READ_ACCESS_KEY=
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 关闭 `AGENTBUS_PROBE_ENABLED` 可以停止 WebSocket 入站连接。
|
||||
- 关闭 `AGENTBUS_CAPTURE_ENABLED` 可以保留连接但暂停写入 Inbox。
|
||||
- 清空当前 profile 对应的 `SOURCE_MESSAGE_*_ORIGINAL_READ_ACCESS_KEY`,且不配置旧通用变量,可以关闭原文读取接口。
|
||||
- 如需临时关闭前端邮件原文 / 会话完整正文读取,应从相关角色移除 `SOURCE_MESSAGE_ORIGINAL_READ` 或 `SOURCE_MESSAGE_READ`,或禁用对应用户入口;旧原文读取 key 已废弃,不能作为关闭开关。
|
||||
|
||||
数据库回滚注意:
|
||||
|
||||
|
||||
318
docs/project/requirements/M008-excel-to-pdf-conversion-v1.md
Normal file
318
docs/project/requirements/M008-excel-to-pdf-conversion-v1.md
Normal file
@@ -0,0 +1,318 @@
|
||||
# M008 Excel 转 PDF 文件转换能力 V1
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 文档状态 | 当前有效 |
|
||||
| 适用范围 | 手动上传 Excel 转 PDF、邮件附件 Excel 自动派生 PDF |
|
||||
| 推荐方案 | LibreOffice headless 作为后端受控转换引擎 |
|
||||
| 当前目标 | CP2 已实现手动上传 Excel 转 PDF 后端接口;邮件附件自动转换、持久化转换任务和 worker 后置 |
|
||||
|
||||
## 1. 背景
|
||||
|
||||
系统后续会出现两类 Excel 转 PDF 需求:
|
||||
|
||||
- 前端 Debug / 后台页面手动上传 `.xls` / `.xlsx`,后端转换为 PDF 后返回下载或 OSS URL。
|
||||
- 邮件来源链路中,AgentBus 或 Debug EML 解析到 Excel 附件后,系统自动派生 PDF,便于前端预览、人工复核或后续证据沉淀。
|
||||
|
||||
这两类场景不应各自实现转换逻辑。第一版应把 Excel 转 PDF 做成平台级文件转换能力,手动上传和自动转换都调用同一套 Service / Adapter。
|
||||
|
||||
## 2. 设计原则
|
||||
|
||||
- 文件转换属于 `platform` 通用能力,不绑定 `reservation` 订单任务流程。
|
||||
- 业务代码只依赖内部稳定 Service,不直接调用 `soffice` 命令。
|
||||
- LibreOffice 作为外部运行时依赖,需要通过 Adapter 隔离。
|
||||
- 手动上传接口和邮件附件自动转换共享同一套校验、超时、并发、临时文件清理、OSS 上传和错误处理规则。
|
||||
- 转换失败不能影响 SourceMessage 原始邮件入库;自动转换失败只记录失败原因和可重试状态。
|
||||
- Excel 原文件和 PDF 派生文件必须能建立来源关系,便于审计和排查。
|
||||
- 默认不支持宏执行,不支持外部链接刷新,不支持把 Excel 内容写入普通日志。
|
||||
|
||||
## 3. 方案选择
|
||||
|
||||
### 3.1 推荐方案:LibreOffice headless
|
||||
|
||||
后端把 Excel 文件写入隔离临时目录,然后调用 LibreOffice headless:
|
||||
|
||||
```bash
|
||||
soffice --headless --nologo --nofirststartwizard --norestore \
|
||||
-env:UserInstallation=file:///tmp/th-hotel-lo-profile-xxx \
|
||||
--convert-to pdf \
|
||||
--outdir /tmp/th-hotel-convert-output-xxx \
|
||||
/tmp/th-hotel-convert-input-xxx/source.xlsx
|
||||
```
|
||||
|
||||
中文说明:
|
||||
|
||||
- `soffice` 负责按照 Excel 自身页面设置、分页、合并单元格、图片和样式生成 PDF。
|
||||
- `UserInstallation` 必须为每次转换使用独立临时目录,降低并发转换时的 profile 冲突风险。
|
||||
- 后端必须设置转换超时;超时后销毁进程并清理临时文件。
|
||||
|
||||
### 3.2 不推荐第一版使用 POI 手工绘制 PDF
|
||||
|
||||
Apache POI 可以读取 Excel,但要完整还原页面设置、分页、图表、图片、字体、合并单元格和打印区域,需要大量手工逻辑,维护成本高,不适合作为第一版主方案。
|
||||
|
||||
### 3.3 商业库作为备选
|
||||
|
||||
Aspose / Spire 等商业库可以降低运行时部署复杂度,但需要确认授权、费用和商用合规。除非项目明确采购,否则第一版不采用。
|
||||
|
||||
## 4. 后端模块边界
|
||||
|
||||
建议新增平台模块:
|
||||
|
||||
```text
|
||||
server/src/main/java/cn/nianxx/thhotel/platform/documentconversion
|
||||
├── control
|
||||
├── service
|
||||
│ └── impl
|
||||
├── domain
|
||||
├── mapper
|
||||
├── repository
|
||||
└── common
|
||||
├── dto
|
||||
├── request
|
||||
├── result
|
||||
└── enums
|
||||
|
||||
server/src/main/java/cn/nianxx/thhotel/integrations/document/libreoffice
|
||||
└── adapter
|
||||
```
|
||||
|
||||
中文说明:
|
||||
|
||||
| 模块 | 中文职责 |
|
||||
| --- | --- |
|
||||
| `platform.documentconversion` | 平台文件转换能力,负责请求校验、转换任务、OSS 输出、审计和结果查询 |
|
||||
| `integrations.document.libreoffice.adapter` | LibreOffice 外部进程适配器,只负责调用 `soffice`、收集退出码和输出文件 |
|
||||
| `integrations.storage.aliyunoss` | 继续作为 OSS 上传端口,转换服务只依赖 `ObjectStorageService` |
|
||||
|
||||
后端实现时继续遵守当前项目包结构规范:`control`、`service`、`service.impl`、`domain`、`mapper`、`repository`、`common.request`、`common.result`、`common.dto`、`common.enums`。
|
||||
|
||||
## 5. 第一版数据流
|
||||
|
||||
### 5.1 手动上传转换
|
||||
|
||||
```text
|
||||
前端上传 Excel
|
||||
-> 后端校验文件类型和大小
|
||||
-> 写入隔离临时目录
|
||||
-> 调用 LibreOffice headless 转 PDF
|
||||
-> 校验 PDF 输出存在且非空
|
||||
-> 上传 PDF 到 OSS 或直接返回文件流
|
||||
-> 返回转换结果
|
||||
-> 清理临时文件
|
||||
```
|
||||
|
||||
建议第一版优先返回 OSS URL,原因:
|
||||
|
||||
- 与现有 Debug EML 附件处理和阿里云 OSS 上传能力一致。
|
||||
- PDF 可以被前端预览或下载。
|
||||
- 转换结果可以被后续审计和自动转换链路复用。
|
||||
|
||||
### 5.2 邮件附件自动转换
|
||||
|
||||
```text
|
||||
SourceMessage 入库 / EML 调试链路解析附件
|
||||
-> 识别 Excel 附件
|
||||
-> 创建文件转换任务
|
||||
-> 异步 worker 执行转换
|
||||
-> PDF 上传 OSS
|
||||
-> 回写转换任务状态和派生 PDF 结果
|
||||
```
|
||||
|
||||
自动转换不应阻塞邮件入库、AgentBus WebSocket 回调、SuperAgent dispatch 或任务结果入站。第一版如果转换失败,只记录 `FAILED` / `RETRYABLE_FAILED` 和安全错误摘要,不影响原附件可见。
|
||||
|
||||
## 6. 建议接口边界
|
||||
|
||||
### 6.1 手动上传接口
|
||||
|
||||
CP2 已实现:
|
||||
|
||||
```text
|
||||
POST /api/system/document-conversions/excel-to-pdf
|
||||
```
|
||||
|
||||
中文说明:
|
||||
|
||||
- 接口分类:`FRONTEND_DEBUG` 或 `FRONTEND_ADMIN`,不能作为普通公开接口。
|
||||
- 鉴权方式:CP2 使用环境开关 + 受控 access key;后续可统一迁移到登录权限体系。
|
||||
- Header:`X-TH-Hotel-Document-Conversion-Key: <文件转换访问口令>`。
|
||||
- 请求类型:`multipart/form-data`。
|
||||
- 文件字段:`file`。
|
||||
- 可选字段:`hotel_id`,用于 OSS 对象路径分组;CP2 不做酒店访问权校验,生产启用前应迁移到登录权限体系。
|
||||
- 返回:转换状态、PDF OSS URL、对象路径、文件名、大小、转换耗时、安全错误摘要。
|
||||
|
||||
请求示例:
|
||||
|
||||
```bash
|
||||
curl -X POST "$BASE_URL/api/system/document-conversions/excel-to-pdf" \
|
||||
-H "X-TH-Hotel-Document-Conversion-Key: $DOCUMENT_CONVERSION_ACCESS_KEY" \
|
||||
-F "hotel_id=HOTEL-TEST" \
|
||||
-F "file=@booking-request.xlsx"
|
||||
```
|
||||
|
||||
成功响应示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"conversion_status": "SUCCEEDED",
|
||||
"source_file_name": "booking-request.xlsx",
|
||||
"source_size_bytes": 12345,
|
||||
"pdf_file_name": "booking-request.pdf",
|
||||
"pdf_url": "https://oss.example.test/document-conversions/excel-to-pdf/HOTEL-TEST/2026-07-16/.../booking-request.pdf",
|
||||
"object_key": "document-conversions/excel-to-pdf/HOTEL-TEST/2026-07-16/.../booking-request.pdf",
|
||||
"content_type": "application/pdf",
|
||||
"pdf_size_bytes": 67890,
|
||||
"duration_millis": 1200
|
||||
}
|
||||
```
|
||||
|
||||
错误码:
|
||||
|
||||
| HTTP | 错误码 | 中文含义 |
|
||||
| --- | --- | --- |
|
||||
| `401` | `DOCUMENT_CONVERSION_KEY_INVALID` | 文件转换访问口令缺失或错误 |
|
||||
| `404` | `DOCUMENT_CONVERSION_DISABLED` | 文件转换接口未启用 |
|
||||
| `400` | `DOCUMENT_CONVERSION_FILE_REQUIRED` | 缺少上传文件或文件为空 |
|
||||
| `400` | `DOCUMENT_CONVERSION_FILE_TYPE_UNSUPPORTED` | 不是 `.xls` / `.xlsx` |
|
||||
| `400` | `DOCUMENT_CONVERSION_FILE_CONTENT_INVALID` | 文件扩展名是 Excel,但文件头不是 `.xlsx` ZIP / `.xls` OLE 格式 |
|
||||
| `413` | `DOCUMENT_CONVERSION_FILE_TOO_LARGE` | 超过配置的文件大小限制 |
|
||||
| `429` | `DOCUMENT_CONVERSION_BUSY` | 转换并发已满,稍后重试 |
|
||||
| `502` | `DOCUMENT_CONVERSION_FAILED` | LibreOffice 转换失败或未生成有效 PDF |
|
||||
| `502` | `DOCUMENT_CONVERSION_OSS_UPLOAD_FAILED` | PDF 上传 OSS 失败 |
|
||||
| `504` | `DOCUMENT_CONVERSION_TIMEOUT` | LibreOffice 转换超时 |
|
||||
|
||||
### 6.2 自动转换内部入口
|
||||
|
||||
建议后续只暴露内部 Service,不提供给前端直接调用:
|
||||
|
||||
```text
|
||||
DocumentConversionService.createExcelToPdfRun(...)
|
||||
DocumentConversionWorker.processPendingRuns(...)
|
||||
```
|
||||
|
||||
中文说明:
|
||||
|
||||
- 邮件链路、Debug EML 链路和未来其他附件来源都只创建转换任务。
|
||||
- worker 统一处理重试、超时和状态流转。
|
||||
|
||||
## 7. 建议数据模型
|
||||
|
||||
如需要持久化转换记录,建议新增表:
|
||||
|
||||
```text
|
||||
platform_document_conversion_run
|
||||
```
|
||||
|
||||
建议核心字段:
|
||||
|
||||
| 字段 | 中文含义 |
|
||||
| --- | --- |
|
||||
| `id` | 内部转换任务 ID |
|
||||
| `hotel_id` | 酒店 ID,手动上传或来源消息所属酒店 |
|
||||
| `conversion_type` | 转换类型,第一版为 `EXCEL_TO_PDF` |
|
||||
| `source_type` | 来源类型,例如 `MANUAL_UPLOAD`、`SOURCE_MESSAGE_ATTACHMENT`、`DEBUG_EML_ATTACHMENT` |
|
||||
| `source_object_id` | 来源对象 ID,例如 SourceMessage ID 或 Debug run ID |
|
||||
| `source_file_name` | 原始文件名 |
|
||||
| `source_content_type` | 原始文件 MIME |
|
||||
| `source_oss_url` | 原文件 OSS URL;手动上传如不保留原文件可为空 |
|
||||
| `target_file_name` | 生成 PDF 文件名 |
|
||||
| `target_oss_url` | 生成 PDF OSS URL |
|
||||
| `status` | 转换状态 |
|
||||
| `attempt_count` | 已尝试次数 |
|
||||
| `safe_error_code` | 安全错误码 |
|
||||
| `safe_error_summary` | 安全错误摘要,不包含文件内容和敏感 URL |
|
||||
| `created_by` | 创建人或机器身份 |
|
||||
| `created_at` / `updated_at` | UTC 时间 |
|
||||
|
||||
建议状态:
|
||||
|
||||
| 状态 | 中文含义 |
|
||||
| --- | --- |
|
||||
| `PENDING` | 等待转换 |
|
||||
| `RUNNING` | 正在转换 |
|
||||
| `SUCCEEDED` | 转换成功 |
|
||||
| `RETRYABLE_FAILED` | 可重试失败 |
|
||||
| `FAILED` | 不可重试失败 |
|
||||
| `CANCELED` | 已取消 |
|
||||
|
||||
## 8. 配置与部署要求
|
||||
|
||||
建议配置项:
|
||||
|
||||
| 配置 | 是否 Secret | 中文说明 |
|
||||
| --- | --- | --- |
|
||||
| `DOCUMENT_CONVERSION_DEV_ENABLED` / `DOCUMENT_CONVERSION_TEST_ENABLED` / `DOCUMENT_CONVERSION_PROD_ENABLED` | 否 | 是否启用文件转换能力 |
|
||||
| `DOCUMENT_CONVERSION_DEV_ACCESS_KEY` / `DOCUMENT_CONVERSION_TEST_ACCESS_KEY` / `DOCUMENT_CONVERSION_PROD_ACCESS_KEY` | 是 | 手动上传转换访问口令;未配置时可兜底 `DOCUMENT_CONVERSION_ACCESS_KEY` |
|
||||
| `DOCUMENT_CONVERSION_SOFFICE_PATH` | 否 | `soffice` 可执行文件路径,默认 `soffice` |
|
||||
| `DOCUMENT_CONVERSION_TEMP_DIR` | 否 | 临时文件根目录 |
|
||||
| `DOCUMENT_CONVERSION_MAX_FILE_BYTES` | 否 | 单个 Excel 最大字节数 |
|
||||
| `DOCUMENT_CONVERSION_TIMEOUT_SECONDS` | 否 | 单次转换超时时间 |
|
||||
| `DOCUMENT_CONVERSION_MAX_CONCURRENT` | 否 | 最大并发转换数 |
|
||||
| `DOCUMENT_CONVERSION_OUTPUT_OSS_PREFIX` | 否 | PDF 输出 OSS 前缀 |
|
||||
| `DOCUMENT_CONVERSION_WORKER_ENABLED` | 否 | 自动转换 worker 是否启用 |
|
||||
|
||||
服务器需要准备:
|
||||
|
||||
- 安装 LibreOffice / LibreOffice Calc。
|
||||
- 安装中文、英文、泰文等业务需要字体,例如 Noto CJK、Noto Thai、Arial 兼容字体。
|
||||
- 确认运行后端的系统用户可以执行 `soffice`,并有临时目录读写权限。
|
||||
- 确认临时目录磁盘空间、清理策略和权限隔离。
|
||||
- 测试机和生产机 LibreOffice 版本尽量一致,避免同一 Excel 输出 PDF 差异过大。
|
||||
|
||||
## 9. 安全与稳定性要求
|
||||
|
||||
- 只允许 `.xls` / `.xlsx`;第一版不支持 `.xlsm`。后端会同时校验扩展名和文件头:`.xlsx` 必须是 ZIP / OOXML 头,`.xls` 必须是 OLE 复合文档头。
|
||||
- 文件大小必须有限制,默认建议不超过 20 MB,具体值实现前再确认。
|
||||
- 使用 `ProcessBuilder` 参数数组调用外部进程,禁止拼接 shell 字符串。
|
||||
- 每次转换使用独立输入目录、输出目录和 LibreOffice profile 目录。
|
||||
- 转换完成、失败或超时后必须清理临时文件。
|
||||
- 日志只记录转换 ID、文件类型、大小、耗时、状态和安全错误摘要,不记录 Excel 内容、PDF 内容、OSS 签名 URL 或客户敏感信息。
|
||||
- 自动转换 worker 必须限制并发,避免大量附件同时转换拖垮后端。
|
||||
- 生产环境默认关闭,待测试机验证转换质量、字体和性能后再开启。
|
||||
|
||||
## 10. Checkpoint 规划
|
||||
|
||||
### CP1:方案和部署验证
|
||||
|
||||
- 落地本文档。
|
||||
- 测试机安装 LibreOffice 和字体。
|
||||
- 用真实业务样式 Excel 手工执行 `soffice` 命令,确认 PDF 保真度。
|
||||
- 明确 PDF 输出是直接下载还是 OSS URL,建议第一版 OSS URL。
|
||||
|
||||
### CP2:手动上传转换接口
|
||||
|
||||
- 新增平台文件转换模块。
|
||||
- 实现 `POST /api/system/document-conversions/excel-to-pdf`。
|
||||
- 接入 OSS 输出。
|
||||
- 补充文件类型、大小、超时、临时文件清理和错误返回测试。
|
||||
- 更新前后端沟通文档和上线注意事项。
|
||||
|
||||
当前状态:已完成第一版后端接口。CP2 不落库、不做 worker、不自动处理邮件附件;返回的 PDF 会上传 OSS,接口受 `document-conversion.enabled` 和 `X-TH-Hotel-Document-Conversion-Key` 控制。
|
||||
|
||||
### CP3:持久化转换任务和 worker
|
||||
|
||||
- 新增 `platform_document_conversion_run`。
|
||||
- 实现 PENDING / RUNNING / SUCCEEDED / RETRYABLE_FAILED / FAILED 状态流转。
|
||||
- 支持自动重试、错误摘要和结果查询。
|
||||
|
||||
### CP4:邮件附件自动转换
|
||||
|
||||
- SourceMessage / Debug EML 链路识别 Excel 附件后创建转换任务。
|
||||
- PDF 派生结果和原附件建立来源关系。
|
||||
- 自动转换失败不影响原邮件入库和业务主流程。
|
||||
|
||||
### CP5:前端展示和下载
|
||||
|
||||
- 手动上传页面展示转换进度和 PDF 链接。
|
||||
- 邮件会话或附件区域展示 Excel 原件和派生 PDF。
|
||||
- 前端不得把 OSS URL 写入普通日志、埋点或 URL query。
|
||||
|
||||
## 11. 当前未确定事项
|
||||
|
||||
实现前需要确认:
|
||||
|
||||
- 手动上传第一版返回 PDF 文件流,还是上传 OSS 后返回 URL;当前建议 OSS URL。
|
||||
- 手动上传接口是放在 Debug 页面、系统管理页面,还是单独工具页面。
|
||||
- 第一版最大文件大小和最大转换时长;当前代码默认 20 MB 和 60 秒,可通过环境变量调整。
|
||||
- 自动转换是否只处理邮件附件,还是也处理用户手动上传后保存的 Excel。
|
||||
- PDF 是否需要长期保存;如果长期保存,需要确认 OSS 生命周期和清理策略。
|
||||
- 是否需要把 Excel 原文件也统一转存 OSS;自动附件场景通常已有 OSS URL,手动上传场景需要确认。
|
||||
@@ -61,8 +61,8 @@
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `GET /api/source-messages` | `FRONTEND_USER` | 已强制 Bearer 登录 + `SOURCE_MESSAGE_READ`;列表条件中的酒店按当前用户可访问酒店校验 | 保持登录 + `SOURCE_MESSAGE_READ` + 酒店访问权 | 只读摘要不写审计 |
|
||||
| `GET /api/source-messages/{id}` | `FRONTEND_USER` | 已强制 Bearer 登录 + `SOURCE_MESSAGE_READ`;按消息实际所属酒店校验访问权 | 保持登录 + `SOURCE_MESSAGE_READ` + 消息所属酒店访问权 | 只读摘要不写审计 |
|
||||
| `GET /api/source-messages/{id}/conversation` | `FRONTEND_USER` | 返回会话完整 text/html 和媒体 URL;后端内部写原文读取审计 | 登录 + `SOURCE_MESSAGE_READ`,如返回完整正文则还需 `SOURCE_MESSAGE_ORIGINAL_READ` | 必须写原文读取审计 |
|
||||
| `GET /api/source-messages/{id}/original` | `FRONTEND_USER` | 当前使用受控原文读取 key | 迁移为登录 + `SOURCE_MESSAGE_ORIGINAL_READ` + 酒店访问权,access key 仅作兼容或关闭 | 必须写原文读取审计 |
|
||||
| `GET /api/source-messages/{id}/conversation` | `FRONTEND_USER` | 已强制 Bearer 登录 + `SOURCE_MESSAGE_READ` + `SOURCE_MESSAGE_ORIGINAL_READ`;按消息实际所属酒店校验访问权;返回会话完整 text/html 和媒体 URL | 保持登录 + `SOURCE_MESSAGE_READ` + `SOURCE_MESSAGE_ORIGINAL_READ` + 消息所属酒店访问权 | 必须写原文读取审计,actor 使用当前登录用户稳定 ID |
|
||||
| `GET /api/source-messages/{id}/original` | `FRONTEND_USER` | 已强制 Bearer 登录 + `SOURCE_MESSAGE_READ` + `SOURCE_MESSAGE_ORIGINAL_READ`;按消息实际所属酒店校验访问权;不再使用原文读取 access key | 保持登录 + `SOURCE_MESSAGE_READ` + `SOURCE_MESSAGE_ORIGINAL_READ` + 消息所属酒店访问权 | 必须写原文读取审计,actor 使用当前登录用户稳定 ID |
|
||||
|
||||
### 3.4 系统管理后台接口
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
| `POST /api/system/debug/eml-superagent-runs` | `FRONTEND_DEBUG` | 环境开关 + `X-TH-Hotel-Debug-Upload-Key` | dev/test 可保留 access key;长期目标为登录 + `SYSTEM_DEBUG_EML_RUN` + 环境开关 | 写 Debug run,必要时补管理 / 调试审计 |
|
||||
| `GET /api/system/debug/eml-superagent-runs/{runId}` | `FRONTEND_DEBUG` | 环境开关 + access key | 登录 + `SYSTEM_DEBUG_EML_RUN`;避免向普通用户暴露 AI 原始结果 | 只读调试可记录访问日志 |
|
||||
| `POST /api/system/debug/eml-superagent-runs/stream` | `FRONTEND_DEBUG` | 环境开关 + access key | 登录 + `SYSTEM_DEBUG_EML_RUN`;生产默认关闭 | 写 Debug run 和安全错误摘要 |
|
||||
| `POST /api/system/document-conversions/excel-to-pdf` | `FRONTEND_DEBUG` | 环境开关 + `X-TH-Hotel-Document-Conversion-Key`;只支持 `.xls` / `.xlsx`,PDF 输出到 OSS | 长期目标为登录 + 文件转换调试权限 + 环境开关;生产默认关闭 | CP2 不落库;必要时通过网关访问日志和 OSS 对象路径追踪,后续自动转换任务落库后补转换审计 |
|
||||
| `GET /api/system/agentbus-probe` | `FRONTEND_DEBUG` | 当前系统状态接口 | 登录 + `SYSTEM_AGENTBUS_PROBE_READ` 或系统管理入口权限 | 不返回 Token、raw frame 或邮件正文 |
|
||||
| `POST /api/system/reservation/demo-data` | `FRONTEND_DEBUG` | 环境开关 + demo access key | dev/test 使用;生产必须关闭 | 写入演示数据时建议记录调试审计 |
|
||||
|
||||
@@ -122,7 +123,7 @@
|
||||
| `RESERVATION_OPERA_SIM_EXECUTE` | 执行或重试 OPERA 模拟 / 未来真实操作 | OPERA execute / retry |
|
||||
| `RESERVATION_AUDIT_READ` | 查看业务审计流水 | 任务审计列表 |
|
||||
| `SOURCE_MESSAGE_READ` | 查看来源邮件安全摘要 | SourceMessage 列表、详情、会话摘要 |
|
||||
| `SOURCE_MESSAGE_ORIGINAL_READ` | 查看邮件正文、HTML 和附件外链 | original / conversation 完整正文 |
|
||||
| `SOURCE_MESSAGE_ORIGINAL_READ` | 查看邮件正文、HTML 和附件外链 | original / conversation 完整正文;必须叠加 `SOURCE_MESSAGE_READ` 使用 |
|
||||
| `SYSTEM_DEBUG_EML_RUN` | 使用 Debug EML 调试链路 | Debug EML 上传、查询、stream |
|
||||
| `SYSTEM_AGENTBUS_PROBE_READ` | 查看 AgentBus 安全状态 | AgentBus probe |
|
||||
|
||||
@@ -133,6 +134,31 @@
|
||||
3. `docs/project/security-access-control-boundary.md`。
|
||||
4. 前后端协作文档中对应页面按钮或菜单说明。
|
||||
|
||||
### 4.1 新增接口权限固定流程
|
||||
|
||||
后续新增需要前端用户或管理员调用的接口时,必须按同一套流程维护权限,避免“接口能调用但系统设置里管不了”或“前端隐藏了但后端没拦”的不一致。
|
||||
|
||||
固定流程:
|
||||
|
||||
1. **确认接口分类。** 先判断接口属于 `FRONTEND_USER`、`FRONTEND_ADMIN`、`FRONTEND_DEBUG`、第三方机器接口还是 `INTERNAL_ONLY`。只有前端用户和管理员接口进入用户角色权限模型;SuperAgent、AgentBus、MCP 继续使用机器鉴权,不使用用户 Bearer 权限码。
|
||||
2. **定义稳定权限码。** 在 `PlatformPermissionCode` 增加稳定英文权限码,例如 `RESERVATION_TASK_ASSIGN`。权限码只表达能力边界,不绑定中文文案、不绑定某个按钮样式。
|
||||
3. **补启动同步元数据。** 在权限启动同步逻辑中补权限名称、权限分组和状态,确保 `platform_permission` 能自动拥有该权限码。
|
||||
4. **补内置角色默认矩阵。** 明确 `SYSTEM_ADMIN`、业务操作员、只读角色等内置角色是否默认拥有该权限。内置角色矩阵仍以代码为准;自定义角色后续通过系统设置页面分配。
|
||||
5. **后端接口强制校验。** 在 Controller 或统一入口中显式调用对应鉴权服务,例如 `requirePermission(PlatformPermissionCode.X.name())`。只读接口同时校验对象所属酒店;写接口还要校验状态、幂等、事务和审计。
|
||||
6. **前端类型和交互同步。** 在前端权限类型中加入新权限码,路由、菜单、按钮和操作入口按 `/api/auth/me` 返回的 `permissions[]` 控制展示。前端控制只提升体验,不能替代后端权限校验。
|
||||
7. **系统设置可分配。** 权限启动同步后,系统设置的角色权限页面应能看到该权限码;需要给自定义角色授权时,通过系统设置勾选,用户重新登录或刷新上下文后生效。
|
||||
8. **补测试。** 至少覆盖无 token、无权限、有权限、跨酒店或对象归属校验。第三方接口要补“不被用户登录拦截误伤”的回归测试。
|
||||
9. **补文档。** 本文矩阵中登记接口分类、权限码、酒店隔离和审计要求;影响前端时同步 `docs/project/frontend-backend/backend-to-frontend-notes.md`;影响 SuperAgent / MCP / AgentBus 时同步对应集成契约。
|
||||
|
||||
最小验收口径:
|
||||
|
||||
- 新接口没有 token 时返回该分类约定的 401。
|
||||
- 已登录但缺权限时返回该分类约定的 403。
|
||||
- 有权限但访问无权酒店或无权对象时返回酒店 / 对象访问拒绝。
|
||||
- 权限码能在系统设置角色权限页面看到并分配给自定义角色。
|
||||
- 前端只做路由、菜单、按钮显示控制,后端仍能拦截直接调用。
|
||||
- 第三方机器接口不被用户 Bearer 权限模型误拦截。
|
||||
|
||||
## 5. 酒店隔离规则
|
||||
|
||||
- 前端用户接口必须从当前登录用户解析可访问酒店集合。
|
||||
@@ -184,7 +210,7 @@
|
||||
后续开发权限收口时建议按以下顺序推进:
|
||||
|
||||
1. 已完成 CP1:Reservation / SourceMessage 第一批只读查询接口已收口登录、权限码和酒店访问权,包括任务列表、订单列表、订单详情、任务详情、任务审计列表、SourceMessage 摘要列表和摘要详情。
|
||||
2. 再收口邮件原文和会话完整正文读取:迁移到 `SOURCE_MESSAGE_ORIGINAL_READ`,保留审计。
|
||||
2. 已完成 CP2:邮件原文和会话完整正文读取已迁移到 `SOURCE_MESSAGE_READ` + `SOURCE_MESSAGE_ORIGINAL_READ`,并按消息所属酒店校验访问权;原文读取审计 actor 使用当前登录用户稳定 ID。
|
||||
3. 再收口 Reservation 写操作:草稿、确认、人工复核、OPERA 模拟。
|
||||
4. 迁移业务审计 actor 到当前登录用户。
|
||||
5. 最后处理 Debug、Demo、Replay、AgentBus Probe 等系统调试入口。
|
||||
|
||||
Reference in New Issue
Block a user