实现手工发票生成后端接口
This commit is contained in:
@@ -44,6 +44,7 @@
|
||||
| `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 已实现手动上传后端接口。 |
|
||||
| `requirements/M009-manual-invoice-generation-v1.md` | 当前有效 | M009 Manual Invoice 手工开票生成方案;后端 CP2 已支持无订单 / 无任务手工填写、填充 Excel 模板、转 PDF、OSS 输出和生成记录。 |
|
||||
|
||||
## 集成契约
|
||||
|
||||
@@ -67,6 +68,7 @@
|
||||
| 文档 | 状态 | 中文说明 |
|
||||
| --- | --- | --- |
|
||||
| `../superpowers/plans/2026-07-10-m006-system-admin-v1.md` | 阶段记录 | M006 系统管理后台实现计划,作为执行 checkpoint 参考,不替代需求文档。 |
|
||||
| `../superpowers/plans/2026-07-17-m009-manual-invoice-backend.md` | 阶段记录 | M009 Manual Invoice 后端 CP2 实现计划,记录本次接口、表、权限、模板和测试范围。 |
|
||||
|
||||
## 权威来源说明
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
| `frontend-to-backend-api-requests.md` | 前端提醒后端需要增加或补齐的接口,包含建议入参和返参草案。 |
|
||||
| `debug-eml-page-integration-guide.md` | Debug EML 页面前端对接指南,包含页面结构、上传接口、响应展示、错误处理和安全注意事项。 |
|
||||
| `../backend-time-design.md` | 时间设计说明,包含数据库 UTC、API `Z` 时间、酒店时区展示和本地日期筛选规则。 |
|
||||
| `../security-access-control-boundary.md` | 接口暴露、权限和审计边界总表,前端判断普通业务、系统管理、Debug 和第三方接口边界时必须参考。 |
|
||||
|
||||
## 3. 当前字段来源分工
|
||||
|
||||
@@ -39,10 +40,12 @@
|
||||
| --- | --- | --- |
|
||||
| SuperAgent HTTP 对外总契约 | `docs/project/integrations/superagent-api-contract.md` | 权威契约,包含查询上下文、对象详情、邮件会话任务、邮件会话正文、任务结果通知和统一 HMAC 规则。 |
|
||||
| SuperAgent MCP tools | `docs/project/integrations/superagent-mcp/README.md` | MCP 对外交付资料包,tools 字段语义应跟随 SuperAgent HTTP 对外总契约。 |
|
||||
| 接口暴露、权限和审计边界 | `docs/project/security-access-control-boundary.md` | 前端判断普通业务、系统管理、Debug、第三方接口、权限码和敏感数据边界的总表。 |
|
||||
| SuperAgent 任务结果入站接口 | `docs/project/requirements/M002-superagent-task-result-api-contract.md` | 阶段记录,用于理解 M002 接收 AI 结果的落地细节;如与总契约冲突,以总契约为准。 |
|
||||
| SuperAgent 查询上下文接口 1、2 | `docs/project/requirements/M002-ai-query-minimal-fields.md` | 阶段记录,用于理解接口 1、2 的最小字段实现;如与总契约冲突,以总契约为准。 |
|
||||
| 订单任务主流程 V3 | `docs/project/requirements/M002-order-task-workflow-v3.md` | 当前开发基线,基于 0711 P0 冻结基线和 0712 P0.1 Parent Group 修订,覆盖 S10/S99、40 路由、方案 C、type-known manual review 同卡解阻和 fail-closed。 |
|
||||
| 任务卡字段控件契约 V1 | `docs/project/requirements/M002-task-field-control-contract-v1.md` | 后端已返回 `fields[]` 控件元数据,规定人工复核控件复用和前后端边界;前端待接入。 |
|
||||
| Manual Invoice 手工开票生成 | `docs/project/requirements/M009-manual-invoice-generation-v1.md` | 当前有效;后端 CP2 已支持无订单 / 无任务手工填写字段、填 Excel 模板、转 PDF、OSS 输出和生成记录。 |
|
||||
| 订单任务主流程 V2 | `docs/project/requirements/M002-order-task-workflow-v2.md` | 已实现阶段记录,保留用于理解当前代码中的 S000/S999、订单任务流转和 OPERA 模拟骨架。 |
|
||||
| 后端 checkpoint | `docs/project/requirements/M002-backend-checkpoint-plan.md` | 阶段记录,用于理解后端拆分和验收。 |
|
||||
| 前端可用接口与待补接口 | `docs/project/frontend-backend/frontend-to-backend-api-requests.md` | 前后端协作清单,已区分可用、后置和历史候选路径,不替代后端权威契约。 |
|
||||
@@ -57,6 +60,7 @@
|
||||
- 用户 / 权限底座后端 CP1 已完成;前端登录页、动态菜单、管理后台和业务审计 actor 全量迁移仍后置。
|
||||
- 真实 OPERA / OHIP 接入后置;当前仅有 OPERA 模拟骨架。
|
||||
- 任务卡字段控件契约 V1 后端第一版已完成,任务详情 `fields[]` 已返回 `control_type/edit_scope/write_target/options_source/raw_readonly/control_hint`;前端后续按契约接入,不要硬编码 PMS 房型、Rate Code 或未冻结枚举。
|
||||
- Manual Invoice 第一阶段按 M009 推进:后端已提供 `POST /api/reservation/invoices/manual-generations`,页面可以不依赖订单或任务,用户手工填写 / 选择字段后由后端业务接口填充 Excel 模板并生成 PDF;前端不得直接调用 M008 的调试上传转换接口来完成业务开票。
|
||||
|
||||
## 6. 前端开发注意事项
|
||||
|
||||
|
||||
@@ -377,6 +377,28 @@ hotel_id: 可选;用于 OSS 对象路径分组
|
||||
- `DOCUMENT_CONVERSION_TIMEOUT` / `DOCUMENT_CONVERSION_FAILED` 通常需要后端排查 LibreOffice、字体、文件格式或临时目录权限。
|
||||
- CP2 不会创建转换任务记录,也不会自动处理邮件附件;邮件附件自动派生 PDF 是 M008 后续 checkpoint。
|
||||
|
||||
### 5.11 Manual Invoice 手工开票页面接入方向
|
||||
|
||||
M009 后端 CP2 已实现:页面可不依赖订单或任务,用户手工填写 / 选择字段后由后端填充 Excel 模板、转换 PDF 并上传 OSS。
|
||||
|
||||
前端注意:
|
||||
|
||||
- 当前 `invoice.html` 可以作为交互原型和控件参考,但不能直接作为生产页面上线。
|
||||
- 第一阶段入口建议是独立页面,例如 `/reservation/invoices/new` 或 `/invoices/new`;不要求必须从任务详情或订单详情进入。
|
||||
- 无订单 / 无任务时,页面按 `source_type=MANUAL` 提交,`task_id` 和 `order_id` 可以为空。
|
||||
- 从任务进入时,后续可以使用 `GET /api/reservation/tasks/{taskId}` 的 `fields[]`、草稿或确认 payload 预填;从订单进入时,需要明确选择具体任务或提示仅使用订单摘要,避免一个订单多任务时字段来源不清。
|
||||
- Company、Attention、Address、Tel、Email、Booking Date 第一阶段可以按“选择 + Manual 手填”控件处理。
|
||||
- Company、Attention、Address、Tel、Email 是一组收件方联系人档案,不是五个互相独立字段;选择 Company 后应刷新 Attention 候选,选择 Attention 后应带出 Address、Tel、Email。Booking Date 展示在同一区域,但不属于联系人档案,应作为 `document.booking_date` 独立提交。
|
||||
- 第一阶段已确认三组客户 / 旅行社种子数据:`LIAN_TAI` / `LIAN TAI TRAVEL (THAILAND) CO., LTD.` + `Khun Ann`;`QBD` / `Q.B.D. TRAVEL GROUP CO., LTD` + `Jitdanun Panaphuchong`;`HANATOUR` / `HANATOUR TD CO., LTD.` + 7 个联系人。完整数据以 `docs/project/requirements/M009-manual-invoice-generation-v1.md` 为准。
|
||||
- Room Type、Room Rate、Extra Bed 建议也预留“选择 + 可手填 / 可覆盖”能力,后续接 PMS 房型、Rate Code 或价格配置。
|
||||
- Amount、Sub-Total、VAT、Total 是只读计算字段;前端可展示预览,但最终金额以后端计算和模板公式为准。
|
||||
- 酒店名称、Tax ID、法人主体、银行账户、Logo 和固定付款文案不应作为每张 Invoice 的手工输入;第一阶段可由模板或酒店发票配置提供。
|
||||
- 正式业务生成接口为 `POST /api/reservation/invoices/manual-generations`,需要 Bearer token、酒店访问权和 `RESERVATION_INVOICE_GENERATE` 权限。
|
||||
- 第一版后端只支持 `source_type=MANUAL`,`task_id` / `order_id` 可以为空;传入时后端会反查对象所属酒店;如果两者同时传入,任务必须属于该订单,否则返回 `RESERVATION_INVOICE_CONTEXT_MISMATCH`。
|
||||
- 第一版后端最多支持 10 条费用明细;超过 10 条会返回 `RESERVATION_INVOICE_VALIDATION_FAILED`。
|
||||
- 第一版暂未提供 Invoice 历史列表、详情查询、任务 / 订单预填接口和客户联系人目录查询接口;前端客户 / 联系人候选可先按 M009 文档中的种子数据实现。
|
||||
- 前端不得直接调用 `POST /api/system/document-conversions/excel-to-pdf` 来完成业务开票;该接口是 M008 调试 / 后台工具能力,受 access key 控制,不具备业务开票审计和权限边界。
|
||||
|
||||
## 6. 不给前端直接调用的接口
|
||||
|
||||
- `POST /api/system/reservation/demo-data` 只用于 dev/test 联调造数,不是生产业务页面接口;访问口令不能进入前端代码。
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
- SuperAgent 查询上下文接口 1、2:支持 HMAC 鉴权的订单上下文查询和对象详情查询。
|
||||
- Debug EML 上传到 SuperAgent 调试链路:受控上传 `.eml`、转存阿里云 OSS、写入 SourceMessage Inbox、调用 SuperAgent Open API 并返回调试结果。
|
||||
- Excel 转 PDF 手动上传接口:受控上传 `.xls` / `.xlsx`,通过 LibreOffice headless 转 PDF 后上传阿里云 OSS 并返回 PDF URL。
|
||||
- Reservation Manual Invoice 后端生成接口:登录用户可通过 `POST /api/reservation/invoices/manual-generations` 手工生成 Proforma Invoice,后端填充受控 Excel 模板、转 PDF、上传 OSS,并写入生成记录和业务审计。
|
||||
- 登录权限底座:支持用户名密码登录、登出、当前用户上下文、数据库 session token、可访问酒店、权限码和可见菜单。
|
||||
- 系统管理后台 V1:支持用户、角色权限、菜单、酒店和管理操作审计的受控维护接口与前端页面。
|
||||
|
||||
@@ -35,7 +36,7 @@
|
||||
- 现有业务接口强制登录和强制权限拦截。
|
||||
- 业务审计 actor 全量迁移到当前登录用户。
|
||||
- Debug EML 上传链路不属于生产普通业务页面能力,生产默认关闭;即使已有登录权限,也不要开放给普通用户。
|
||||
- Excel 转 PDF 当前只完成手动上传后端接口;邮件附件自动转换、持久化转换任务和 worker 尚未实现。生产默认关闭,启用前必须确认 LibreOffice、字体、OSS、临时目录和访问口令。
|
||||
- Excel 转 PDF 当前只完成手动上传后端接口和 M009 Manual Invoice 内部复用;邮件附件自动转换、持久化转换任务和 worker 尚未实现。生产启用前必须确认 LibreOffice、字体、OSS、临时目录和访问口令。
|
||||
|
||||
## 2. 上线前必须确认
|
||||
|
||||
@@ -223,6 +224,10 @@ SourceMessage 原文和邮件会话完整正文已迁移到登录权限体系:
|
||||
- 当前接口只支持 `.xls` / `.xlsx`,不支持 `.xlsm`;后端会校验扩展名和文件头,改后缀的非 Excel 文件会返回受控错误。
|
||||
- PDF 上传到阿里云 OSS,返回 `pdf_url`、`object_key`、`pdf_file_name`、`pdf_size_bytes` 和 `duration_millis`。
|
||||
- CP2 不落库,不提供转换历史查询;如果需要自动处理邮件附件,应先进入 M008 后续持久化任务和 worker checkpoint。
|
||||
- M009 Manual Invoice 不调用上述调试接口,也不使用 `X-TH-Hotel-Document-Conversion-Key`;它通过登录 Bearer token、`RESERVATION_INVOICE_GENERATE` 权限和后端内部转换 Adapter 生成 PDF。
|
||||
- M009 Manual Invoice 当前模板资源为 `server/src/main/resources/templates/reservation-invoice/proforma-invoice-v1.xlsx`,部署包必须包含该资源。
|
||||
- M009 Manual Invoice 会写入 `workflow_reservation_invoice_generation`,上线前需确认 Flyway 已执行到 V22。
|
||||
- M009 Manual Invoice 失败记录也可能保留已上传成功的 Excel / PDF object key;排查或清理 OSS 生成物时应以生成记录为主,不只看 `SUCCEEDED` 状态。
|
||||
|
||||
## 4. 数据库上线注意事项
|
||||
|
||||
@@ -253,6 +258,10 @@ SourceMessage 原文和邮件会话完整正文已迁移到登录权限体系:
|
||||
|
||||
- `server/src/main/resources/db/migration/V20__create_superagent_dispatch_run.sql`
|
||||
|
||||
当前 M009 Manual Invoice 相关 migration:
|
||||
|
||||
- `server/src/main/resources/db/migration/V22__create_reservation_invoice_generation.sql`
|
||||
|
||||
当前 M003 登录权限相关 migration:
|
||||
|
||||
- `server/src/main/resources/db/migration/V9__create_identity_access_hotel_menu.sql`
|
||||
@@ -268,6 +277,7 @@ SourceMessage 原文和邮件会话完整正文已迁移到登录权限体系:
|
||||
- 目标数据库为空库或 Flyway history 与当前代码一致。
|
||||
- 如果某个环境已经在缺少 V10 的临时提交上执行过 V11 / V12,不能直接用默认 Flyway 策略补跑 V10;应先重建测试库,或按运维窗口明确 out-of-order / repair 策略。
|
||||
- V21 会为 `workflow_reservation_order` 增加 `latest_activity_at`,并按订单更新时间和历史任务最新来源 / 创建时间回填一次;上线后订单列表依赖该字段排序,不再在列表查询时聚合全量任务。发布后需要确认 Flyway 已执行到 V21,且订单列表能按最新业务活动倒序返回。
|
||||
- V22 会新增 `workflow_reservation_invoice_generation`,用于记录 Manual Invoice 生成状态、Excel / PDF OSS 对象、金额摘要和安全错误摘要;发布后需要确认 `RESERVATION_INVOICE_GENERATE` 权限已由启动同步写入平台权限表,预订操作员或目标角色已拥有该权限。
|
||||
- MySQL 版本满足项目要求,默认使用 MySQL 8.0+。
|
||||
- migration 在 UAT 或测试库已经跑过。
|
||||
- 表和字段中文注释能正常创建。
|
||||
|
||||
401
docs/project/requirements/M009-manual-invoice-generation-v1.md
Normal file
401
docs/project/requirements/M009-manual-invoice-generation-v1.md
Normal file
@@ -0,0 +1,401 @@
|
||||
# M009 Manual Invoice 手工开票生成 V1
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 文档状态 | CP2 后端已实现,前端 CP3 待开发 |
|
||||
| 适用范围 | 无订单 / 无任务场景下的手工 Invoice 创建,以及后续从任务或订单预填 Invoice |
|
||||
| 当前目标 | 第一阶段基于现有 `invoice.html` 原型落地 Manual Invoice Mode;后端已支持手工生成 PDF,前端页面待接入 |
|
||||
| 依赖能力 | M008 Excel 转 PDF 平台转换能力、阿里云 OSS、登录权限和酒店上下文 |
|
||||
|
||||
## 1. 背景
|
||||
|
||||
当前系统的主业务数据来自 AgentBus、SuperAgent、订单和任务。但实际运营中可能出现极端情况:
|
||||
|
||||
- 邮件还没有被 SuperAgent 正确识别成订单 / 任务。
|
||||
- 订单或任务尚未入库,用户仍需要先出一张 Proforma Invoice。
|
||||
- 历史、线下、电话或临时沟通产生的开票需求没有可关联的任务。
|
||||
- SuperAgent 或主链路故障时,需要人工兜底生成 PDF。
|
||||
|
||||
因此第一阶段需要支持一个独立的 `Manual Invoice Mode`:用户可以不选择订单、不选择任务,直接填写或选择 Invoice 字段,由后端生成 Excel,再通过 LibreOffice 转为 PDF 并上传 OSS。
|
||||
|
||||
## 2. 第一阶段定位
|
||||
|
||||
第一阶段不是完整财务发票系统,而是酒店预订业务中的 Proforma Invoice 生成能力。
|
||||
|
||||
第一阶段应做到:
|
||||
|
||||
- 页面可从独立入口进入,例如 `/reservation/invoices/new` 或 `/invoices/new`。
|
||||
- 不要求 `task_id` 或 `order_id`。
|
||||
- 用户手工填写 / 选择字段后,后端生成 PDF。
|
||||
- 后端重新计算金额、税额和合计,不信任前端提交的计算结果。
|
||||
- 酒店、法人、税号、银行账户、Logo 和固定文案优先来自模板或酒店发票配置。
|
||||
- 生成行为必须有权限、酒店隔离和审计记录。
|
||||
|
||||
第一阶段不做:
|
||||
|
||||
- 不接真实 OPERA / OHIP。
|
||||
- 不要求必须挂靠订单或任务。
|
||||
- 不做应收账款、收款核销、发票税务编号或财务系统入账。
|
||||
- 不把 SuperAgent AI 原始 JSON 直接渲染成 Invoice 表单。
|
||||
- 不允许前端直接调用 M008 调试上传接口来伪造业务开票。
|
||||
|
||||
## 3. 与现有流程关系
|
||||
|
||||
### 3.1 手工模式
|
||||
|
||||
```text
|
||||
用户打开手工 Invoice 页面
|
||||
-> 前端读取发票表单配置和默认值
|
||||
-> 用户填写 / 选择字段
|
||||
-> 前端提交 invoice_payload
|
||||
-> 后端校验、计算金额和税额
|
||||
-> 后端填充 Excel 模板
|
||||
-> 调用 M008 内部转换能力生成 PDF
|
||||
-> PDF 上传 OSS
|
||||
-> 写入 Invoice 生成记录和审计
|
||||
-> 返回 PDF URL 和生成摘要
|
||||
```
|
||||
|
||||
### 3.2 任务或订单预填模式
|
||||
|
||||
后续可以支持从任务或订单进入:
|
||||
|
||||
```text
|
||||
任务详情 / 订单详情
|
||||
-> 打开 Invoice 页面并带 task_id 或 order_id
|
||||
-> 后端按 task_id / order_id 查询可用上下文
|
||||
-> 返回同一套 invoice_payload 默认值
|
||||
-> 用户确认或修改
|
||||
-> 后续生成流程与手工模式相同
|
||||
```
|
||||
|
||||
中文说明:
|
||||
|
||||
- `task_id` 和 `order_id` 只是可选上下文,不是第一阶段生成 Invoice 的必要条件。
|
||||
- 如果从任务进入,应优先使用任务详情 `fields[]`、`draft_payload_json` 或 `confirmed_payload_json` 中已确认的数据。
|
||||
- 如果从订单进入,应由用户选择具体任务或由页面显式展示“仅使用订单摘要生成”,避免一个订单多任务时字段来源不清。
|
||||
|
||||
## 4. 字段来源和控件分类
|
||||
|
||||
基于现有 `invoice.html` 原型和 Excel 模板,字段分为四类。
|
||||
|
||||
| 分类 | 中文说明 | 示例字段 | 第一阶段控件建议 |
|
||||
| --- | --- | --- | --- |
|
||||
| 用户输入字段 | 每张 Invoice 都可能不同,需要用户填写或修改 | Group Name、Arrival Date、Departure Date、Due Date、Room Rate Note、明细 Description | `input` / `date` / `textarea` |
|
||||
| 选择 + 可手填字段 | 有标准目录,但业务上允许人工覆盖 | Company、Attention、Address、Tel、Email、Booking Date、Room Type、Room Rate、Extra Bed | `select + Manual` 或后续 combobox |
|
||||
| 模板 / 配置字段 | 不是每张 Invoice 的业务输入,随酒店、法人或模板版本变化 | 酒店名称、Tax ID、法人公司、银行账号、Logo、固定付款文案、页脚 | 第一阶段可在模板或酒店发票配置中维护 |
|
||||
| 系统计算字段 | 不应由用户手填,后端或 Excel 公式计算 | Amount、Sub-Total、VAT、Total、Night(s) 汇总、Room(s) 汇总 | 前端只读预览,后端重新计算 |
|
||||
|
||||
### 4.1 Basic Information
|
||||
|
||||
| 字段 | 控件 | 后续来源 |
|
||||
| --- | --- | --- |
|
||||
| Company | 选择 + 可手填 | 客户 / 旅行社目录,第一阶段可使用后端配置或静态种子 |
|
||||
| Attention | 选择 + 可手填 | Company 下联系人目录 |
|
||||
| Address | 选择 + 可手填 | 联系人或公司账单地址 |
|
||||
| Tel | 选择 + 可手填 | 联系人电话 |
|
||||
| Email | 选择 + 可手填 | 联系人邮箱 |
|
||||
| Booking Date | 选择 + 可手填 | 默认使用当前酒店本地日期,也可用户调整 |
|
||||
|
||||
### 4.1.1 Recipient Directory 第一阶段种子数据
|
||||
|
||||
`Company`、`Attention`、`Address`、`Tel`、`Email` 不是五个互相独立的普通字段,而是一组收件方 / 旅行社联系人档案。第一阶段页面可以允许 Manual 覆盖,但默认选择逻辑必须保持关联关系:
|
||||
|
||||
- 选择 `Company` 后,刷新该公司下可选 `Attention` 列表。
|
||||
- 选择 `Attention` 后,同步带出该联系人的 `Address`、`Tel`、`Email`。
|
||||
- 公司只有一个联系人时,可以默认选中该联系人并自动带出地址、电话和邮箱。
|
||||
- `Address` 可在公司级和联系人级之间复用;如果同一公司多个联系人共用地址,不应在每个联系人手工重复维护多份无关数据。
|
||||
- 用户选择 `Manual` 时,才允许脱离目录手工填写;提交时仍保存最终文本值,目录 code 仅用于审计和后续追溯。
|
||||
|
||||
第一阶段确认的种子数据:
|
||||
|
||||
| company_code | Company | contact_id | Attention | Address | Tel | Email |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| `LIAN_TAI` | `LIAN TAI TRAVEL (THAILAND) CO., LTD.` | `LIAN_TAI_KHUN_ANN` | `Khun Ann` | `2/86 Rajpattana Road, Rajpattana, Sapansoong, Bangkok, TH, 10240` | `061-397-2675` | `op.liantaitravel@gmail.com` |
|
||||
| `QBD` | `Q.B.D. TRAVEL GROUP CO., LTD` | `QBD_JITDANUN_PANAPHUCHONG` | `Jitdanun Panaphuchong` | `2/90 Rajpattana,Rajpattana,Sapansoong, Bangkok, TH, 10240` | `089-032 0176` | `op.qbdtravel@gmail.com` |
|
||||
| `HANATOUR` | `HANATOUR TD CO., LTD.` | `HANATOUR_WICHIENPRAKARN` | `Wichienprakarn, Nuanphae, Khun.` | `HanaTour Bldg, 41,Insadong 5-gil, Jongno-gu, KR` | `066 124 - 7297` | `HI219@hanatour.com` |
|
||||
| `HANATOUR` | `HANATOUR TD CO., LTD.` | `HANATOUR_PHONGBUPPA` | `Phongbuppa, Buppachat` | `HanaTour Bldg, 41,Insadong 5-gil, Jongno-gu, KR` | `096 051 3587` | `HI223@hanatour.com` |
|
||||
| `HANATOUR` | `HANATOUR TD CO., LTD.` | `HANATOUR_KANG_SUNG_HWA` | `Kang,Sung Hwa` | `HanaTour Bldg, 41,Insadong 5-gil, Jongno-gu, KR` | `82 051 804 0707` | `k8040707@nave.com` |
|
||||
| `HANATOUR` | `HANATOUR TD CO., LTD.` | `HANATOUR_MINYOUNG_KIM` | `Minyoung Kim` | `HanaTour Bldg, 41,Insadong 5-gil, Jongno-gu, KR` | `82 010 6638 3345` | `mykim1220@hanayour.com` |
|
||||
| `HANATOUR` | `HANATOUR TD CO., LTD.` | `HANATOUR_BOLAM_JO` | `Bolam Jo` | `HanaTour Bldg, 41,Insadong 5-gil, Jongno-gu, KR` | `82 010 4182 4615` | `melissa0609@hanatour.com` |
|
||||
| `HANATOUR` | `HANATOUR TD CO., LTD.` | `HANATOUR_HWANG_SEONGSEOP` | `Hwang Seongseop` | `HanaTour Bldg, 41,Insadong 5-gil, Jongno-gu, KR` | `82 010 3167 8648` | `hwangpilot59@naver.com` |
|
||||
| `HANATOUR` | `HANATOUR TD CO., LTD.` | `HANATOUR_SONG_SAE_HWA` | `Song Sae Hwa` | `HanaTour Bldg, 41,Insadong 5-gil, Jongno-gu, KR` | `82 010 2400 6801` | `sh-tour2016@naver.com` |
|
||||
|
||||
中文说明:
|
||||
|
||||
- 以上数据第一阶段可以作为后端种子配置、数据库初始化数据或前端原型静态数据,但正式业务接口生成 PDF 时应以请求中的最终文本值为准。
|
||||
- 后续如果接客户 / 联系人维护后台,`company_code` 和 `contact_id` 应保持稳定,不使用 Company 或 Attention 展示文案做业务主键。
|
||||
- Booking Date 虽然展示在 Basic Information 区域,但它不是联系人档案的一部分,应独立保存在 `invoice_payload.document.booking_date`。
|
||||
|
||||
### 4.2 Reservation
|
||||
|
||||
| 字段 | 控件 | 后续来源 |
|
||||
| --- | --- | --- |
|
||||
| Group Name / Group Code | 输入框 | 手工填写;任务预填时来自 `case_keys.group_code` 或订单 `group_code` |
|
||||
| Arrival Date | 日期输入 | 手工填写;任务预填时来自任务字段 |
|
||||
| Departure Date | 日期输入 | 手工填写;任务预填时来自任务字段 |
|
||||
| Due Date | 日期输入 | 手工填写;可按酒店付款规则默认生成 |
|
||||
| Room Rate | 选择 + 可手填 | 第一阶段可手填;后续接 Rate Code / 房价配置 |
|
||||
| Room Rate Note | 输入框 | 例如 `includingBF` |
|
||||
| Extra bed | 选择 + 可手填 | 第一阶段可手填;后续接加床价格配置 |
|
||||
| No. of Room(s) | 输入框 / 只读派生 | 可由第一条明细同步,也允许手工覆盖 |
|
||||
| No. of Night(s) | 输入框 / 日期派生 | 可由 Arrival / Departure 派生,也允许手工覆盖 |
|
||||
|
||||
### 4.3 Description & Amount
|
||||
|
||||
| 字段 | 控件 | 后续来源 |
|
||||
| --- | --- | --- |
|
||||
| Description | 输入框 | 手工填写;默认可使用 Group Name 或 Booking No |
|
||||
| Room Types | 选择 + 可手填 | 第一阶段可手填;后续接 PMS 房型目录 |
|
||||
| Quantity(s) | 数字输入 | 后端要求正数 |
|
||||
| Rate | 数字输入 | 后端要求正数 |
|
||||
| Night(s) | 数字输入 | 后端要求正数 |
|
||||
| Amount | 只读 | `quantity * rate * nights` |
|
||||
| Sub-Total | 只读 | 后端按 VAT 规则计算 |
|
||||
| VAT 7% | 只读 | 后端按配置税率计算 |
|
||||
| Total amount | 只读 | 明细金额合计 |
|
||||
|
||||
## 5. 建议请求模型
|
||||
|
||||
第一阶段建议新增业务接口,不复用 M008 的调试上传接口:
|
||||
|
||||
```text
|
||||
POST /api/reservation/invoices/manual-generations
|
||||
Authorization: Bearer <access_token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
请求体草案:
|
||||
|
||||
```json
|
||||
{
|
||||
"hotel_id": "HOTEL-TEST",
|
||||
"source_type": "MANUAL",
|
||||
"task_id": null,
|
||||
"order_id": null,
|
||||
"template_code": "PROFORMA_INVOICE_V1",
|
||||
"invoice_payload": {
|
||||
"document": {
|
||||
"invoice_date": "2026-07-17",
|
||||
"booking_date": "2026-07-12",
|
||||
"due_date": "2026-07-22"
|
||||
},
|
||||
"recipient": {
|
||||
"company_code": "LIAN_TAI",
|
||||
"contact_id": "LIAN_TAI_KHUN_ANN",
|
||||
"company": "LIAN TAI TRAVEL (THAILAND) CO., LTD.",
|
||||
"attention": "Khun Ann",
|
||||
"address": "2/86 Rajpattana Road, Rajpattana, Sapansoong, Bangkok, TH, 10240",
|
||||
"telephone": "061-397-2675",
|
||||
"email": "op.liantaitravel@gmail.com"
|
||||
},
|
||||
"booking": {
|
||||
"group_name": "GRP-DEMO-0802",
|
||||
"arrival_date": "2026-08-02",
|
||||
"departure_date": "2026-08-05",
|
||||
"room_rate_note": "includingBF",
|
||||
"extra_bed_rate": 1200
|
||||
},
|
||||
"charges": [
|
||||
{
|
||||
"description": "GRP-DEMO-0802",
|
||||
"room_type": "Deluxe Room",
|
||||
"quantity": 2,
|
||||
"rate": 3000,
|
||||
"nights": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
中文说明:
|
||||
|
||||
- `source_type=MANUAL` 表示无订单 / 无任务的手工开票。
|
||||
- `task_id`、`order_id` 第一阶段允许为空。
|
||||
- 如果同时传入 `task_id` 和 `order_id`,后端会校验该任务必须挂靠在该订单下;不一致时返回 `RESERVATION_INVOICE_CONTEXT_MISMATCH`。
|
||||
- `invoice_payload` 中的日期是酒店本地业务日期,使用 `yyyy-MM-dd`。
|
||||
- 前端可以传金额预览,但后端不得信任;最终金额以服务端计算和模板公式为准。
|
||||
- 客户目录选择码和手填文本可以同时传;后端第一阶段以文本值生成 PDF,`company_code`、`contact_id` 用于审计和后续配置追溯。
|
||||
|
||||
成功响应草案:
|
||||
|
||||
```json
|
||||
{
|
||||
"invoice_generation_id": "2080000000000000001",
|
||||
"generation_status": "SUCCEEDED",
|
||||
"source_type": "MANUAL",
|
||||
"hotel_id": "HOTEL-TEST",
|
||||
"template_code": "PROFORMA_INVOICE_V1",
|
||||
"pdf_url": "https://oss.example.test/reservation-invoices/HOTEL-TEST/2026-07-17/.../proforma-invoice.pdf",
|
||||
"pdf_object_key": "reservation-invoices/HOTEL-TEST/2026-07-17/.../proforma-invoice.pdf",
|
||||
"generated_excel_object_key": "reservation-invoices/HOTEL-TEST/2026-07-17/.../proforma-invoice.xlsx",
|
||||
"totals": {
|
||||
"subtotal": 16822.43,
|
||||
"vat": 1177.57,
|
||||
"total": 18000.00,
|
||||
"currency": "THB"
|
||||
},
|
||||
"created_at": "2026-07-17T03:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 建议数据模型
|
||||
|
||||
第一阶段建议持久化生成记录,便于审计、排查和后续列表查询。
|
||||
|
||||
建议新增表:
|
||||
|
||||
```text
|
||||
workflow_reservation_invoice_generation
|
||||
```
|
||||
|
||||
核心字段:
|
||||
|
||||
| 字段 | 中文含义 |
|
||||
| --- | --- |
|
||||
| `id` | Invoice 生成记录 ID |
|
||||
| `hotel_id` | 酒店 ID |
|
||||
| `source_type` | 来源类型:`MANUAL`、`TASK`、`ORDER` |
|
||||
| `order_id` | 可选订单 ID |
|
||||
| `task_id` | 可选任务 ID |
|
||||
| `source_message_id` | 可选来源邮件 ID |
|
||||
| `template_code` | 模板编码 |
|
||||
| `template_version` | 模板版本 |
|
||||
| `invoice_payload_json` | 用户提交并经后端归一化后的业务字段 |
|
||||
| `calculated_totals_json` | 后端计算后的金额、税额和合计 |
|
||||
| `generated_excel_object_key` | 生成后的 Excel OSS 对象 Key |
|
||||
| `pdf_object_key` | PDF OSS 对象 Key |
|
||||
| `pdf_url` | PDF 访问 URL |
|
||||
| `generation_status` | 生成状态 |
|
||||
| `safe_error_code` | 安全错误码 |
|
||||
| `safe_error_summary` | 安全错误摘要 |
|
||||
| `created_by` | 创建人 |
|
||||
| `created_at` / `updated_at` | UTC 时间点 |
|
||||
|
||||
中文说明:
|
||||
|
||||
- `generated_excel_object_key`、`pdf_object_key`、`pdf_url` 不只在成功记录中出现;如果流程在后续步骤失败,后端也会保留已经成功上传的对象定位,避免 OSS 生成物变成不可追踪的孤立文件。
|
||||
- `SUCCEEDED` 只在模板填充、Excel 上传、PDF 转换、PDF 上传和业务审计均完成后写入;如果业务审计失败,不会先标记成功再覆盖为失败。
|
||||
|
||||
建议状态:
|
||||
|
||||
| 状态 | 中文含义 |
|
||||
| --- | --- |
|
||||
| `PENDING` | 已接收,等待生成 |
|
||||
| `RUNNING` | 正在填模板或转换 |
|
||||
| `SUCCEEDED` | 生成成功 |
|
||||
| `FAILED` | 生成失败 |
|
||||
|
||||
第一阶段也可以同步生成并直接返回,但仍建议落库,因为这属于业务文档生成,不是一次性调试转换。
|
||||
|
||||
## 7. 后端模块边界
|
||||
|
||||
建议放在 Reservation 工作流下,而不是平台文件转换模块:
|
||||
|
||||
```text
|
||||
server/src/main/java/cn/nianxx/thhotel/workflows/reservation/invoice
|
||||
├── control
|
||||
├── service
|
||||
│ └── impl
|
||||
├── domain
|
||||
├── mapper
|
||||
├── repository
|
||||
└── common
|
||||
├── dto
|
||||
├── request
|
||||
├── result
|
||||
└── enums
|
||||
```
|
||||
|
||||
中文说明:
|
||||
|
||||
- `reservation.invoice` 负责业务字段校验、模板字段映射、发票生成记录、权限和审计。
|
||||
- `platform.documentconversion` 继续作为底层文件转换能力,负责 Excel 到 PDF。
|
||||
- `integrations.storage.aliyunoss` 继续作为 OSS 能力。
|
||||
- 前端不能直接调用 `POST /api/system/document-conversions/excel-to-pdf` 来完成业务开票;那是调试 / 后台工具接口。
|
||||
|
||||
## 8. 模板和字段映射
|
||||
|
||||
第一阶段建议先使用一个受控模板:
|
||||
|
||||
```text
|
||||
server/src/main/resources/templates/reservation-invoice/proforma-invoice-v1.xlsx
|
||||
```
|
||||
|
||||
后续可迁移到 OSS + 数据库模板版本管理。
|
||||
|
||||
模板字段映射原则:
|
||||
|
||||
- 模板固定文案、Logo、银行信息、税号可以先保留在模板内。
|
||||
- 可动态变更但不属于单张 Invoice 的配置,后续迁移到酒店发票配置。
|
||||
- 用户输入字段通过后端模板填充器写入指定单元格。
|
||||
- 明细行需要支持 1 到 N 行;超过模板默认行数时,后端应复制行样式和公式。
|
||||
- 金额公式由后端统一写入或由模板公式统一生成,不复制历史案例中不一致的公式。
|
||||
|
||||
## 9. 权限、安全和审计
|
||||
|
||||
第一阶段建议:
|
||||
|
||||
- 接口分类:`FRONTEND_USER`,不是 `FRONTEND_DEBUG`。
|
||||
- 必须 Bearer 登录。
|
||||
- 权限码建议:`RESERVATION_INVOICE_GENERATE`。
|
||||
- 必须校验用户对 `hotel_id` 的访问权。
|
||||
- 如果传入 `task_id` 或 `order_id`,必须反查对象所属酒店并校验与 `hotel_id` 一致;如果两者同时传入,必须校验任务所属订单与 `order_id` 一致。
|
||||
- 写入业务审计,记录创建人、酒店、来源类型、可选订单 / 任务、模板版本和生成结果。
|
||||
- 日志不得输出完整 `invoice_payload_json`、客户邮箱、电话、OSS 签名 URL 或 PDF 内容。
|
||||
|
||||
## 10. Checkpoint 规划
|
||||
|
||||
### CP1:文档和页面方向确认
|
||||
|
||||
- 落地本文档。
|
||||
- 明确当前 `invoice.html` 原型可作为前端交互和控件参考。
|
||||
- 确认第一阶段是手工模式,不强依赖订单和任务。
|
||||
|
||||
### CP2:后端手工生成接口
|
||||
|
||||
- 已完成:新增业务接口 `POST /api/reservation/invoices/manual-generations`。
|
||||
- 已完成:新增生成记录表 `workflow_reservation_invoice_generation` 和业务审计。
|
||||
- 已完成:根据请求 payload 填充受控 Excel 模板 `templates/reservation-invoice/proforma-invoice-v1.xlsx`。
|
||||
- 已完成:复用平台 Excel 转 PDF Adapter 生成 PDF,并上传阿里云 OSS。
|
||||
- 已完成:返回 PDF URL、对象 Key、金额摘要和生成状态。
|
||||
- 已完成:失败记录保留已上传成功的 Excel / PDF object key;业务审计完成后才标记 `SUCCEEDED`。
|
||||
- 第一版限制:只支持 `source_type=MANUAL`、`template_code=PROFORMA_INVOICE_V1`、最多 10 条费用明细;暂不提供历史列表、详情查询、任务 / 订单预填和客户联系人目录查询接口。
|
||||
|
||||
### CP3:前端 Manual Invoice 页面
|
||||
|
||||
- 基于当前 `invoice.html` 原型改造成前端项目内页面。
|
||||
- 字段控件按“输入 / 选择 + 可手填 / 只读计算 / 模板配置”实现。
|
||||
- 提交到后端业务接口,不调用调试上传接口。
|
||||
- 展示 PDF 生成结果、错误提示和下载 / 预览入口。
|
||||
|
||||
### CP4:任务 / 订单预填
|
||||
|
||||
- 从任务详情进入时,使用任务详情 `fields[]`、草稿或确认 payload 预填。
|
||||
- 从订单详情进入时,要求用户选择具体任务或确认仅使用订单摘要。
|
||||
- 保持用户可以修改字段后再生成 PDF。
|
||||
|
||||
### CP5:配置化和历史查询
|
||||
|
||||
- 客户 / 联系人目录、房型、Rate、Extra Bed、银行账户和模板版本逐步配置化。
|
||||
- 增加 Invoice 生成历史列表和详情。
|
||||
- 明确 OSS 生命周期、重新生成、作废和审计策略。
|
||||
|
||||
## 11. 当前待确认事项
|
||||
|
||||
已确认并落地:
|
||||
|
||||
- 第一阶段接口路径采用 `POST /api/reservation/invoices/manual-generations`。
|
||||
- PDF 生成记录第一阶段必须落库。
|
||||
- 生成后的 Excel 同步上传 OSS 并保存对象 Key,便于排查。
|
||||
- Invoice Date 第一阶段允许前端提交,后端按酒店本地业务日期字段处理。
|
||||
- 发票模板初始文件使用当前 `Invoice Templete.xlsx` 改造成受控模板资源。
|
||||
- VAT 第一阶段固定按 7% 含税反算。
|
||||
|
||||
仍待后续确认:
|
||||
|
||||
- 客户 / 联系人目录是否进入后端配置 / 数据库维护,还是继续由前端临时静态维护。
|
||||
- 是否需要 Invoice 历史列表、详情、重新生成和作废接口。
|
||||
- 是否需要支持超过 10 条费用明细、复制模板行样式和分页。
|
||||
- 任务 / 订单预填时字段优先级和冲突提示规则。
|
||||
@@ -54,6 +54,7 @@
|
||||
| `POST /api/reservation/tasks/{taskId}/opera-operations/{operationId}/execute` | `FRONTEND_USER` | 当前为 OPERA 模拟 | 登录 + `RESERVATION_OPERA_SIM_EXECUTE` + 酒店访问权 | 必须写业务审计和 attempt |
|
||||
| `POST /api/reservation/tasks/{taskId}/opera-operations/{operationId}/retry` | `FRONTEND_USER` | 当前为 OPERA 模拟 | 登录 + `RESERVATION_OPERA_SIM_EXECUTE` + 酒店访问权 | 必须写业务审计和 attempt |
|
||||
| `GET /api/reservation/tasks/{taskId}/audits` | `FRONTEND_USER` | 已强制 Bearer 登录 + `RESERVATION_AUDIT_READ`;按任务实际所属酒店校验访问权 | 保持登录 + `RESERVATION_AUDIT_READ` + 酒店访问权 | 查询审计不再写审计 |
|
||||
| `POST /api/reservation/invoices/manual-generations` | `FRONTEND_USER` | 已实现 M009 CP2;强制 Bearer 登录 + `RESERVATION_INVOICE_GENERATE` + 酒店访问权;`task_id` / `order_id` 可为空,传入时反查对象所属酒店 | 保持登录 + `RESERVATION_INVOICE_GENERATE` + 酒店访问权;后续如增加历史列表或预填接口需单独登记权限 | 写业务审计,记录来源类型、模板版本、生成结果摘要;生成失败写入生成记录安全错误摘要 |
|
||||
|
||||
### 3.3 来源邮件接口
|
||||
|
||||
@@ -122,6 +123,7 @@
|
||||
| `RESERVATION_MANUAL_REVIEW_RESOLVE` | 处理人工复核和 Fallback 转换 | 复核解阻、Fallback 转换 |
|
||||
| `RESERVATION_OPERA_SIM_EXECUTE` | 执行或重试 OPERA 模拟 / 未来真实操作 | OPERA execute / retry |
|
||||
| `RESERVATION_AUDIT_READ` | 查看业务审计流水 | 任务审计列表 |
|
||||
| `RESERVATION_INVOICE_GENERATE` | 生成 Reservation Proforma Invoice | Manual Invoice 生成、未来任务 / 订单预填生成 |
|
||||
| `SOURCE_MESSAGE_READ` | 查看来源邮件安全摘要 | SourceMessage 列表、详情、会话摘要 |
|
||||
| `SOURCE_MESSAGE_ORIGINAL_READ` | 查看邮件正文、HTML 和附件外链 | original / conversation 完整正文;必须叠加 `SOURCE_MESSAGE_READ` 使用 |
|
||||
| `SYSTEM_DEBUG_EML_RUN` | 使用 Debug EML 调试链路 | Debug EML 上传、查询、stream |
|
||||
|
||||
Reference in New Issue
Block a user