docs: 增加项目规范和邮件来源入口PRD

This commit is contained in:
andy
2026-07-06 17:39:07 +08:00
parent a75ac19662
commit 5a498635d8
14 changed files with 3451 additions and 0 deletions

12
docs/project/README.md Normal file
View File

@@ -0,0 +1,12 @@
# 当前项目专属文档
本目录保存只适用于当前项目的业务背景、架构边界、外部系统约束和实现决策。
这些文档可以作为后续项目参考,但不应整份复制到新项目。新项目可复用内容应优先沉淀到 `docs/import/reusable/`
## 文档清单
- `backend-development-guidelines.md`:当前项目后端专属规范。
- `frontend-development-guidelines.md`:当前项目前端专属规范。
- `requirements/M001-source-message-inbox-prd.md`M001 邮件来源入口 PRD记录 AgentBus 邮件 JSON 入库、历史查询、原文读取和媒体引用边界。
- `integrations/superagent-agentbus-project-integration-guide.md`:当前项目 SuperAgent 与 AgentBus 验证记录和项目级接入细节。

View File

@@ -0,0 +1,265 @@
# TH Hotel 后端开发规范
## 1. 文档定位
本文整理 TH Hotel 当前后端开发约定,供本项目后端开发和其他 agent 协作参考。
本文是当前项目专属补充,不应整份复制到其他项目。可复用的通用后端规则应沉淀到 `docs/import/reusable/backend-development-guidelines.md`
本项目后端是酒店多部门 AI 业务流程与 Case 协同平台的服务端。这里的 AI 只表示调用外部
模型或 Agent 能力提供方并消费其结构化结果本项目不开发模型、Prompt 优化平台、Agent
Runtime、Planner、Memory 或通用工具调用框架。
## 2. 技术栈
`server/pom.xml` 为准,当前后端技术栈如下:
| 类别 | 当前选择 |
| --- | --- |
| Java | Java 17 LTS语法版本 Java 17 |
| 框架 | Spring Boot 3.5.15 |
| Web | Spring MVC |
| 构建 | Maven Wrapper |
| 外部 HTTP | Spring `RestClient` 优先 |
| 数据库 | MySQL 8.0+ |
| ORM / Mapper | MyBatis-Plus 3.5.16Spring Boot 3 使用 `mybatis-plus-spring-boot3-starter` |
| 数据库迁移 | Flyway |
| OpenAPI | springdoc-openapi 2.8.17 |
| 测试 | JUnit 5、Spring Boot Test、H2 MySQL Mode |
升级 Java、Spring Boot、MyBatis-Plus、springdoc 或 Flyway 前,必须先做兼容性验证。
Maven 编译配置应显式使用 `maven.compiler.release=17`,并开启参数名保留,例如 `parameters=true`。除非单独确认升级方案,否则不得使用 Java preview 特性或 Java 21 / Java 25 专属语法。
引入 MyBatis-Plus 后,不再额外引入 `mybatis-spring-boot-starter``MyBatis-Spring` 或重复的原生 MyBatis starter避免 starter 版本差异造成运行期问题。
## 3. 后端分层与包边界
推荐边界:
```text
platform
├── ai
├── audit
├── evidence
├── message
├── operation
├── receipt
└── system
workflows
└── reservation
integrations
├── ai
├── document
├── external
├── messaging
└── ohip
```
中文说明:
| 层级 | 中文职责 | 依赖规则 |
| --- | --- | --- |
| `platform` | 平台通用能力层保存消息、证据、AI 调用审计、Operation、Receipt、审计等部门中立能力 | 可以被业务流程调用,不能反向依赖预订部等具体业务流程 |
| `workflows` | 业务流程层,保存具体部门或业务线的流程编排,当前优先是预订流程 | 可以依赖 `platform` 和稳定端口,不直接依赖外部系统 DTO |
| `integrations` | 外部系统适配层,隔离 OHIP、SuperAgent、AgentBus 等外部协议和认证细节 | 可以实现平台或业务端口,不能把外部 DTO 泄漏到领域模型 |
后续输出包结构、目录树、数据模型、字段映射或接口示例时,必须补充中文说明,说明每层职责、依赖方向、可调用对象和禁止事项,不能只依赖英文命名表达含义。
职责规则:
- `platform` 保存部门中立能力例如消息、证据、AI 调用审计、Operation、Receipt、审计。
- `workflows` 保存部门业务流程,当前明确的是 `reservation`
- `integrations` 保存外部系统适配器,例如 OHIP、SuperAgent、AgentBus。
- 平台核心不得依赖预订部专有字段。
- 部门工作流可以依赖平台核心,平台核心不能反向依赖部门模块。
- 外部系统 DTO、Oracle DTO、Provider DTO、领域模型和 API Response 必须分离。
- Oracle DTO 或生成代码只能位于 `integrations.ohip`,不得进入平台领域模型。
## 4. Controller / Service / Repository 规则
- Controller 只做 HTTP 契约、参数校验、权限入口和响应映射。
- Controller 不直接访问 Mapper。
- Controller 不直接调用外部适配器。
- Application Service 编排事务、领域对象、Repository 和外部端口。
- Domain 对象表达稳定业务语义,不引入 HTTP、JSON、MyBatis 或外部 Provider 细节。
- Repository 是领域侧持久化接口。
- Infrastructure / Persistence 负责 Entity、Mapper 和数据库细节。
- 外部调用通过端口和 Adapter 隔离,业务层不依赖厂商 SDK 或厂商 DTO。
## 5. 数据建模规则
必须分别建模以下业务标识,不能共用模糊字段:
```text
caseId
blockId
reservationId
confirmationNumber
groupCode
```
其他规则:
- 房型、房量、人数、金额、日期必须使用结构化字段,不只保存展示字符串。
- 状态使用稳定英文代码,中文和英文只用于显示。
- 动态参数必须有稳定 `fieldKey` 和可国际化 `labelKey`
- 不能把某一种语言的显示文本当作接口契约或业务判断依据。
- 至少区分外部提供方建议值、人工确认值、最终执行值。
- Receipt 生成后不可覆盖修改;纠错通过新记录表达。
- Task 执行必须支持幂等、并发版本校验和审计。
## 6. SourceMessage / AI / Task 边界
- `SourceMessage` 表示 Email、LINE、附件等渠道输入的原始来源事实。
- AI 抽取输出的业务事件不是来源消息,也不是正式 Task。
- 一条 SourceMessage 可以产生多个 AI Task Result、多个候选或多张 Task。
- Task 与 SourceMessage、AI MessageEvent、Evidence 通过来源关联建模。
- 不得用单个 `createdFromEventId` 固化一对一关系。
- Case 匹配不明确时,必须创建可持久化、可审计的 Preflight / Need Manual Review 对象。
- 只有人工关联已有 Case 或确认新增 Case 后,才创建正式 TaskCard。
- Preflight / Need Manual Review 被拒绝或终止后不得删除,必须保留来源、候选版本、处理人、原因和时间。
## 7. AI / Agent Provider 边界
- 业务模块只依赖 `AiCapabilityPort` 一类稳定调用契约。
- SuperAgent、模型 ID、Agent ID、认证配置位于 Adapter 层。
- 真实 Provider 返回内容不得直接生成 Recognition、Case、Task、Operation、Receipt 或 OHIP 写入。
- Provider 输出只能作为建议或证据。
- 所有会引起业务写操作的参数必须经过规则校验和人工确认。
- 平台可记录 provider、capability、request id、版本、耗时和用量但不实现模型训练、Prompt 优化或 Agent 编排。
## 8. AgentBus 边界
- AgentBus 是消息入口适配器,不是 AI Provider。
- 实时 AgentBus 链路只写 `platform_source_message_inbox`
- 不直接生成 `platform_message_event`、Evidence、Recognition、Case、Task、Operation、Receipt 或 OHIP 写入。
- 不自动发送 ACK、`task.result` 或客户回复。
- SourceMessage Inbox 到 MessageEvent / Evidence 必须通过受控 replay。
- 原始 frame 本地采样默认关闭,生产不常态保存 raw frame。
## 9. OHIP / OPERA Cloud 边界
开发任何 OHIP 页面或执行器前,必须先更新字段映射文档,明确:
- 页面字段
- 领域字段
- 内部 API 字段
- OHIP 字段或 JSONPath
- 来源接口和版本
- 是否必填
- fallback
- Sandbox 验证状态
- UAT 三方核对状态
禁止根据字段名猜 Oracle API 字段。未通过官方文档或真实响应确认的内容必须标记为“待确认”。
浏览器不得直接调用 OHIP。OHIP Secret 只能由后端环境变量或部署平台 Secret 注入。
## 10. 数据库与 Flyway 规范
- 新建表和改表必须通过 Flyway migration。
- 已发布 migration 禁止直接修改。
- 修正注释、索引或约束必须新增 migration。
- 每张业务表必须有中文表级 `COMMENT`
- 每个业务字段必须有中文字段级 `COMMENT`
- SQL 文件应使用中文行注释划分表、索引、约束等主要结构。
- 注释必须说明业务含义、来源或代码值范围。
- 禁止使用“字段1”“备用字段”等模糊注释。
- 业务时间以 UTC 写入数据库API 层负责返回 ISO 8601。
- JSON 字段只用于扩展元数据,不替代需要查询、约束或索引的正式列。
## 11. 后端代码注释规范
后端 Java 代码必须提供必要且准确的中文注释:
- 架构文档、设计文档、目录结构、数据模型、字段映射和接口示例必须优先使用中文说明业务含义。
- 领域对象、应用服务、外部适配器、Controller、配置属性和复杂参数对象应说明业务含义、边界或调用约束。
- 复杂流程、幂等键、并发控制、事务边界、错误转换、外部系统字段映射和安全脱敏逻辑必须说明原因。
- 注释不得只复述类名、方法名或字段名。
- 不得用“TODO 待完善”替代真实说明。
- 生成新后端代码时必须同步生成中文注释。
- 修改旧代码时,应补齐触达代码的必要中文注释。
## 12. 安全与日志
Secret 只能通过 `.env`、环境变量或部署平台 Secret 注入。仓库只提交无真实值的 `.env.example`
禁止提交:
- 真实酒店凭证
- Oracle Client Secret、Application Key、Integration Password
- SuperAgent API Key
- AgentBus Token
- Cookie、Access Token
- 真实客户邮件、附件 URL、个人数据
- 完整支付信息
日志必须脱敏:
- Authorization、Cookie
- Client Secret、Integration Password、Application Key
- 客人姓名、邮箱、电话、证件信息
- 支付卡和账务敏感数据
- 原始消息正文、HTML、附件 URL
## 13. 配置规范
- Spring Boot 不会自动读取 `.env`,本地启动需由 shell、IDE、容器或部署平台注入环境变量。
- 本地开发可复制根目录 `.env.example``.env`,真实值只留本机。
- `TH_HOTEL_DB_URL` 包含 `&` 时必须加引号。
- 后端 Secret 不得放入前端 `VITE_*`
- 生产环境应拆分 ConfigMap / 非敏感环境变量与 Secret / 密钥管理系统。
## 14. API 设计规范
- API 返回稳定代码,不返回中文或英文文本作为前端业务判断依据。
- 动态表单字段使用稳定 `fieldKey` 和可国际化 `labelKey`
- 错误响应不得回显 Secret、原始正文、附件 URL 或个人信息。
- 写接口必须考虑幂等、并发版本、审计和失败恢复。
- 外部写操作结果不明确时,禁止盲目重试。
- 调试接口必须默认关闭,并使用独立访问密钥。
## 15. 测试与检查命令
后端最低检查:
```bash
cd server
./mvnw test
./mvnw verify
```
聚焦开发时可先运行相关测试:
```bash
cd server
./mvnw -Dtest=SomeFocusedTest test
```
如果命令尚未配置或因环境问题无法运行,必须明确说明,不能假装通过。
## 16. Git 与协作流程
- 改动前说明目标、范围和将修改的文件。
- 每次只处理一个模块或一个纵向切片。
- 需求基准变化时,先做差异和影响分析。
- 修改接口前确认领域模型和数据映射。
- 不修改与当前任务无关的用户变更。
- 修改后运行项目已配置的检查命令。
- Git commit message 使用中文,清楚说明本次提交的业务或技术变更。
## 17. 后端提交前检查清单
- [ ] 是否遵守 platform / workflows / integrations 分层?
- [ ] Controller 是否没有直接访问 Mapper 或外部 Adapter
- [ ] 外部 DTO 是否没有进入领域模型?
- [ ] 写接口是否有幂等、版本或审计设计?
- [ ] Flyway SQL 是否有规范中文注释?
- [ ] Java 复杂逻辑是否有必要中文注释?
- [ ] 是否没有提交真实 Secret 或客户数据?
- [ ] 是否没有把 Provider 输出直接当业务事实?
- [ ] 是否运行了 `./mvnw test` 或说明了无法运行原因?
- [ ] 是否运行了 `./mvnw verify` 或说明了无法运行原因?

View File

@@ -0,0 +1,282 @@
# TH Hotel 前端开发规范
## 1. 文档定位
本文整理 TH Hotel 当前前端开发约定,供本项目 UI 开发和其他 agent 协作参考。
本文是当前项目专属补充,不应整份复制到其他项目。可复用的通用前端规则应沉淀到 `docs/import/reusable/frontend-development-guidelines.md`
前端只负责展示、交互、人工确认、调试入口和调用本项目后端。前端不得直接调用 OHIP、
SuperAgent、AgentBus 或任何持有 Secret 的外部系统。
## 2. 技术栈
`client/package.json` 为准,当前前端技术栈如下:
| 类别 | 当前选择 |
| --- | --- |
| 运行时 | Node.js 22.13+ LTS |
| 框架 | Vue 3.5.x |
| 语言 | TypeScript ~6.0.xStrict 模式 |
| 构建 | Vite 8.x |
| 组件写法 | Composition API`<script setup lang="ts">` |
| 路由 | Vue Router 5.x |
| 客户端状态 | Pinia 3.x |
| 服务端状态 | TanStack Vue Query 5.x |
| UI 组件库 | PrimeVue 4.x |
| 图标 | PrimeIcons |
| 国际化 | vue-i18n 11.x |
| Vue SFC 类型检查 | vue-tsc |
| 测试 | Vitest 4.x、Vue Test Utils 2.x、jsdom |
| Lint | ESLint 10.x、typescript-eslint 8.x、eslint-plugin-vue |
不引入通用 Admin Template。新增 UI 应延续现有设计语言和样式 token。
兼容性要求:
- Vite、ESLint 和 Vitest 的 Node.js 要求必须同时满足,当前统一使用 Node.js 22.13+ LTS。
- TypeScript 版本必须与 `typescript-eslint` 支持范围一致;在确认 `typescript-eslint` 支持更高版本前TypeScript 锁定为 `~6.0.x`,不得使用宽泛的 `^6.0.0`
- Vue SFC 项目必须配置 `vue-tsc` 做类型检查,不能只依赖 `tsc`
- 升级 Vue、Vite、TypeScript、ESLint、Vitest、PrimeVue、Pinia、Vue Router 或 Vue Query 前,必须先检查版本兼容性并更新本文。
## 3. 目录约定
当前前端目录:
```text
client/src
├── components
│ ├── common
│ └── reservation
├── composables
├── i18n
│ └── locales
├── layouts
├── router
├── services
├── stores
├── styles
├── tests
├── types
└── views
└── reservation
```
目录规则:
- API 请求封装放入 `src/services`
- 手写类型定义放入 `src/types`
- 可复用组合逻辑放入 `src/composables`
- 路由定义放入 `src/router`
- Pinia Store 放入 `src/stores`
- i18n 入口和语言包放入 `src/i18n`
- 页面级组件放入 `src/views`
- 可复用业务组件放入 `src/components/<domain>`
- 通用布局放入 `src/layouts`
- 全局样式和设计 token 放入 `src/styles`
- 未来 OpenAPI 生成代码放入 `src/generated`,禁止手工修改。
## 4. 组件规范
- 页面和组件统一使用 Vue 3 Composition API。
- `.vue` 文件使用 `<script setup lang="ts">`
- Props、Emits、响应式状态和服务返回值必须有明确类型。
- 避免在模板中写复杂业务逻辑,复杂逻辑放入 computed、composable 或服务层。
- 不在组件里直接拼接后端 URL统一通过 services。
- 不在组件里直接访问浏览器全局存储保存业务事实。
- 不把中文或英文显示文案作为业务判断依据。
- 不为未知的财务部、前台流程硬编码页面、状态或菜单。
## 5. 状态管理规则
Pinia 只保存客户端状态,例如:
- 当前用户上下文
- 当前酒店
- 当前部门
- 语言和页面偏好
- 轻量 UI 状态
服务端数据使用 TanStack Vue Query
- 列表、详情、Timeline、Task、Preflight、SourceMessage 等后端数据通过 Query 获取。
- Mutation 完成后按 query key 精准失效或更新缓存。
- 不把服务端数据长期复制进 Pinia。
- 不用 Pinia 绕过后端权限、状态机或校验。
## 6. API 请求规范
- 浏览器只能调用本项目后端。
- 所有请求封装到 `src/services`
- 服务函数返回明确 TypeScript 类型。
- 统一使用 `httpClient` 处理 JSON、错误和 Problem Detail。
- 前端不直接调用 OHIP、SuperAgent、AgentBus、数据库或对象存储。
- 前端不发送后端 Secret、replay access key、Provider API Key 或 Oracle 凭证。
- 调试页面如果需要触发受控后端能力,应由后端提供 debug-only 包装接口Secret 保留在后端。
## 7. 国际化规范
首期交付:
```text
zh-CN
en-US
```
架构预留:
```text
th-TH
```
规则:
- 新增页面和组件不得硬编码中英文业务文案。
- 系统固定文案必须使用稳定 i18n key。
- 后端返回稳定业务代码和必要 `labelKey`,前端按 key 显示。
- 前端不得把中文或英文文本作为业务判断依据。
- 酒店配置名称、客人消息、姓名、备注、邮件正文和附件内容保持原文。
- 日期、时间、数字和货币按当前语言、酒店时区和币种配置格式化。
- API 仍传递结构化原始值,不传本地化展示字符串作为业务参数。
## 8. 类型与业务代码
- TypeScript 类型应贴近后端 API 契约。
- 稳定业务代码使用 string union 或枚举型常量,避免散落 magic string。
- 动态操作参数使用稳定 `fieldKey`
- 表单字段展示使用 `labelKey` 或 i18n key。
- 不用 `label`、中文标题或英文标题做字段标识。
- 接口字段变更时,同步更新 `src/types`、services、页面和测试。
## 9. UI 与交互规范
- UI 组件库使用 PrimeVue。
- 避免引入大型 Admin Template。
- 页面应优先表达业务主线,而不是堆叠技术字段。
- Debug 页面可以显示技术 ID但业务页面应优先展示可理解的业务状态和来源摘要。
- 预检、人工复核、任务确认要清楚区分:
- 外部 Provider 建议
- 人工确认值
- 最终执行结果
- 邮件原文、附件详情和外部响应详情应通过受控详情接口读取,不默认塞进所有 Task 页面。
- 涉及客户信息、付款信息和附件时,应默认最小展示。
## 10. 前端安全规范
- `VITE_*` 变量会暴露到浏览器构建产物,不能保存 Secret。
- 前端不得保存数据库密码、OHIP 凭证、SuperAgent API Key、AgentBus Token、replay key。
- 不在 LocalStorage、SessionStorage 或 URL 中保存敏感业务数据。
- 错误提示不展示 Authorization、Cookie、Token、原始邮件全文或附件 URL。
- 测试夹具不得使用真实客人、真实酒店或真实支付信息。
## 11. 配置规范
常用公开配置:
```text
VITE_API_BASE_URL=http://localhost:8080
VITE_APP_ENV=local
VITE_DEFAULT_LOCALE=zh-CN
VITE_ENABLE_MOCKS=false
```
本地开发如需代理后端:
```text
VITE_API_PROXY_TARGET=http://127.0.0.1:8081
```
规则:
- 公开配置可以放 `VITE_*`
- Secret 一律不进入 `VITE_*`
- 生产需要运行时配置时,应由部署系统生成公开配置文件,例如 `/app-config.json`
- 需要秘密的外部调用一律经后端代理或适配器。
## 12. 路由规范
- Vue Router 管理部门级路由。
- 新增业务路由前确认对应后端 API 和权限边界。
- 平台 Shell 只保留已确认部门入口,不硬编码未知流程。
- 详情页路由参数使用稳定 ID。
- 页面刷新后必须能通过后端详情接口恢复必要状态,不依赖内存临时状态。
## 13. 表单与人工确认规范
- 人工确认表单优先使用后端返回的字段契约。
- 保存草稿时使用稳定 `fieldKey`
- 不把显示文案作为提交字段名。
- 表单应展示字段来源Provider 建议、来源消息、人工修正、最终值。
- 提交前前端可做基础格式校验,但最终业务校验在后端。
- 发生版本冲突时,应提示用户刷新或重新确认,不静默覆盖。
## 14. Agent 前端开发 Skill 使用
当前项目后续如果由 Codex 或其他 agent 开发前端,涉及 UI、交互、视觉或浏览器验证时应优先使用当前环境可用的前端相关 skill。Skill 只作为辅助,不能覆盖本项目 Vue、TypeScript、Vite、PrimeVue、Pinia、Vue Query、i18n、接口契约和安全边界。
当前项目建议:
- 新页面、复杂交互或业务页面信息架构:优先使用 `design-taste-frontend``ui-ux-pro-max`
- 设计 token、组件规范和样式体系优先使用 `design-system`
- 已有页面视觉重构:优先使用 `redesign-existing-projects`
- 根据截图、设计稿或视觉参考还原页面:优先使用 `image-to-code`
- 浏览器验证、截图检查、点击路径和响应式检查:优先使用 `browser:control-in-app-browser`
- 需要生成插图、图片或占位视觉资产:可使用 `imagegen`
- `ui-styling` 可参考其可访问性、组件状态和样式组织原则,但本项目未确认使用 shadcn 或 Tailwind 前,不得因此引入 shadcn、Tailwind 或替换 PrimeVue。
- `superpowers:brainstorming``superpowers:writing-plans``superpowers:test-driven-development``superpowers:verification-before-completion` 用于需求澄清、计划、测试和验收,不属于 UI 框架选择依据。
交付要求:
- 涉及 UI 或交互变更时,应说明使用了哪些 skill或说明为什么未使用。
- 涉及可视化结果时,应尽量提供浏览器截图、响应式检查或交互验证结果。
- 如果 skill 建议与本项目规范冲突,以本项目规范、接口契约和用户明确要求为准。
## 15. 测试与检查命令
前端最低检查:
```bash
cd client
pnpm lint
pnpm typecheck
pnpm test
pnpm build
```
`pnpm typecheck` 应执行 `vue-tsc --noEmit` 或等价命令,确保 `.vue` 单文件组件也被类型检查覆盖。
聚焦开发时可运行:
```bash
cd client
pnpm test -- SomeSpecName.spec.ts
```
如果命令尚未配置或因环境问题无法运行,必须明确说明,不能假装通过。
## 16. Git 与协作流程
- 改动前说明目标、范围和将修改的文件。
- 每次只处理一个模块、一个页面或一个纵向切片。
- 不修改与当前任务无关的用户变更。
- 前后端接口变化前,先确认 API 契约和字段映射。
- 修改后运行项目已配置的检查命令。
- Git commit message 使用中文,清楚说明本次提交的业务或技术变更。
## 17. 前端提交前检查清单
- [ ] 是否只调用本项目后端?
- [ ] 是否没有把 Secret 放入 `VITE_*`、源码、测试或 URL
- [ ] 新文案是否已进入 i18n
- [ ] 是否避免用中文或英文显示文本做业务判断?
- [ ] API 请求是否放在 `src/services`
- [ ] 手写类型是否放在 `src/types`
- [ ] 可复用逻辑是否放在 `src/composables`
- [ ] 服务端数据是否使用 Vue Query而不是长期塞进 Pinia
- [ ] 是否没有手工修改 `src/generated`
- [ ] 涉及 UI、交互或视觉变更时是否使用或说明未使用合适的前端 / 设计 skill
- [ ] 涉及可视化结果时,是否做过浏览器、截图或响应式验证?
- [ ] 是否运行了 `pnpm lint` 或说明了无法运行原因?
- [ ] 是否运行了 `pnpm typecheck` 或说明了无法运行原因?
- [ ] 是否运行了 `pnpm test` 或说明了无法运行原因?
- [ ] 是否运行了 `pnpm build` 或说明了无法运行原因?

View File

@@ -0,0 +1,612 @@
# TH Hotel SuperAgent 与 AgentBus 项目接入记录
## 1. 文档定位
本文保存 TH Hotel 项目中已经验证过的 SuperAgent 与 AgentBus 接入经验、项目路径、表名和
验证记录。
本文是当前项目专属记录,不应整份复制到其他项目。可复用的通用接入规则应沉淀到
`docs/import/reusable/integrations/superagent-agentbus-portable-integration-guide.md`
本文不是 SuperAgent 或 AgentBus 官方协议文档,也不记录任何真实 Token、API Key、
Session ID、Run ID、邮箱正文、附件 URL 或客户个人信息。其他项目接入时,应把本文作为
工程边界、配置清单和验证顺序参考;具体字段仍以提供方最新协议和真实测试响应为准。
相关本项目验证记录:
- `docs/superagent-integration-notes.md`
- `docs/agentbus-integration-notes.md`
- `docs/agentbus-production-flow.md`
- `docs/architecture/ADR-003-ai-provider-boundary.md`
## 2. 两条链路的职责边界
SuperAgent 和 AgentBus 不应被设计成同一个模块。
```text
AgentBus
→ 接收 Email / LINE 等外部渠道消息
→ 保存 SourceMessage Inbox
→ 受控 Replay 为 MessageEvent / Evidence
→ 调用 AI 能力端口
→ SuperAgent Provider Adapter
→ 保存 AI Capability Invocation
→ 业务 Schema 校验
→ 人工确认
→ Case / Task / Operation / Receipt
```
| 能力 | 定位 | 负责什么 | 不负责什么 |
| --- | --- | --- | --- |
| AgentBus | 外部消息通道适配器 | WebSocket 连接、接收入站 frame、保存原始来源事实 | 不做 AI 抽取、不创建业务 Task、不回复客户、不调用业务写接口 |
| SuperAgent | 外部 AI / Agent 能力提供方 | 创建 Agent Session、发送消息、解析 SSE、返回建议或回答 | 不决定业务动作、不绕过人工确认、不直接写业务系统 |
| SourceMessage Inbox | 平台缓冲层 | 不可变保存来源消息和捕获状态 | 不表达 AI 结论或业务归属 |
| AiCapabilityPort | 平台能力端口 | 隔离业务层和具体 Provider SDK / HTTP 协议 | 不暴露 Provider DTO 给领域层 |
核心原则:
- 前端不直接调用 SuperAgent 或 AgentBus不接触任何 Provider Secret。
- AgentBus 实时链路只落来源事实,不直接生成 Case、Task、Operation 或客户回复。
- SuperAgent 返回内容只能作为建议、证据或审计结果,不能直接改变业务最终状态。
- 业务写操作必须经过平台规则校验、权限控制、幂等控制和人工确认。
## 3. 推荐模块拆分
其他 Java / Spring Boot 项目接入时,建议按以下模块复制思路,而不是复制 TH Hotel 的
预订部业务代码。
```text
platform
├── message
│ ├── SourceMessageInbox
│ ├── MessageEvent
│ └── Evidence
├── ai
│ ├── AgentCapabilityPort
│ ├── AgentCapabilityRequest
│ ├── AgentCapabilityResult
│ └── AiCapabilityInvocation
└── system
├── SuperAgentProbeController
├── AgentBusProbeStatusController
└── SourceMessageReplayController
integrations
├── ai
│ └── superagent
└── messaging
└── agentbus
```
TH Hotel 当前代码中的可参考文件:
| 目的 | 参考文件 |
| --- | --- |
| SuperAgent 配置 | `server/src/main/java/cn/nianxx/thhotel/integrations/ai/deerflow/SuperAgentProbeProperties.java` |
| SuperAgent HTTP 客户端 | `server/src/main/java/cn/nianxx/thhotel/integrations/ai/deerflow/SuperAgentOpenApiClient.java` |
| SuperAgent SSE 解析 | `server/src/main/java/cn/nianxx/thhotel/integrations/ai/deerflow/SuperAgentSseParser.java` |
| SuperAgent 能力适配器 | `server/src/main/java/cn/nianxx/thhotel/integrations/ai/deerflow/SuperAgentCapabilityAdapter.java` |
| SuperAgent 探针 API | `server/src/main/java/cn/nianxx/thhotel/platform/system/api/SuperAgentProbeController.java` |
| AI 调用审计 API | `server/src/main/java/cn/nianxx/thhotel/platform/ai/api/AiCapabilityInvocationController.java` |
| AgentBus WebSocket 客户端 | `server/src/main/java/cn/nianxx/thhotel/integrations/messaging/agentbus/AgentBusProbeWebSocketClient.java` |
| AgentBus frame 处理 | `server/src/main/java/cn/nianxx/thhotel/integrations/messaging/agentbus/AgentBusFrameProcessor.java` |
| AgentBus 到 SourceMessage 映射 | `server/src/main/java/cn/nianxx/thhotel/integrations/messaging/agentbus/AgentBusSourceMessageMapper.java` |
| AgentBus 入站持久化 | `server/src/main/java/cn/nianxx/thhotel/integrations/messaging/agentbus/PersistingAgentBusSourceMessageCaptureService.java` |
| SourceMessage Replay | `server/src/main/java/cn/nianxx/thhotel/platform/message/application/SourceMessageReplayApplicationService.java` |
## 4. SuperAgent 对接
### 4.1 运行时配置
最小配置建议:
```text
AI_PROVIDER_ENABLED=false
DEERFLOW_BASE_URL=https://superagent.nianxx.cn
DEERFLOW_OPEN_API_KEY=
SUPERAGENT_PROBE_ENABLED=false
SUPERAGENT_PROBE_ACCESS_KEY=
SUPERAGENT_CONNECT_TIMEOUT=15s
SUPERAGENT_READ_TIMEOUT=180s
SUPERAGENT_MAX_MESSAGE_CHARS=4000
SUPERAGENT_EXTERNAL_SUBJECT_ID=your-project-superagent-probe
```
变量说明:
| 变量 | 是否 Secret | 说明 |
| --- | --- | --- |
| `AI_PROVIDER_ENABLED` | 否 | 是否启用真实 SuperAgent Provider Adapter。默认关闭。 |
| `DEERFLOW_BASE_URL` | 否 | SuperAgent / DeerFlow Open API 地址。 |
| `DEERFLOW_OPEN_API_KEY` | 是 | Open API Key应只存在后端环境变量或 Secret Manager。 |
| `SUPERAGENT_PROBE_ENABLED` | 否 | 是否开放本项目自己的探针接口。生产默认关闭。 |
| `SUPERAGENT_PROBE_ACCESS_KEY` | 是 | 调用探针接口的本地访问密钥,不是 Provider API Key。 |
| `SUPERAGENT_CONNECT_TIMEOUT` | 否 | 建立连接超时。 |
| `SUPERAGENT_READ_TIMEOUT` | 否 | SSE 读取超时。 |
| `SUPERAGENT_MAX_MESSAGE_CHARS` | 否 | 单次发送给 Provider 的消息长度上限。 |
| `SUPERAGENT_EXTERNAL_SUBJECT_ID` | 否 | 创建 Agent Session 时使用的外部主体标识。 |
### 4.2 当前已验证的 Open API 调用形态
当前 TH Hotel 已验证的流程:
```text
POST /api/open/agent-sessions
→ 获取 session_id
→ POST /api/open/agent-sessions/{sessionId}/messages/stream
→ 读取 text/event-stream
→ 解析最终 answer、run、profile、model、token usage
```
状态变更请求需要 CSRF double-submit
```text
X-CSRF-Token: <random-csrf-token>
Cookie: csrf_token=<same-random-csrf-token>
Authorization: Bearer <DEERFLOW_OPEN_API_KEY>
```
CSRF Token 由客户端实例临时生成,不需要写入配置,也不能当作 Secret 长期保存。
### 4.3 Session 请求示例
```json
{
"external_subject_id": "your-project-superagent-probe",
"idempotency_key": "your-project-superagent-session-<correlation-id>",
"metadata": {
"source": "your-project",
"purpose": "provider-connectivity-test"
}
}
```
### 4.4 SSE 消息请求示例
```json
{
"message": "请介绍一下你是谁。",
"idempotency_key": "your-project-superagent-message-<correlation-id>",
"metadata": {
"source": "your-project",
"purpose": "provider-flow-test"
}
}
```
### 4.5 SSE 解析口径
TH Hotel 当前观测并处理的事件类型:
| Event | 用途 |
| --- | --- |
| `metadata` | 读取 Run、Thread、Profile 等调用元数据 |
| `messages` | 流式消息增量或中间消息 |
| `values` | 阶段性或最终聚合状态 |
| `end` | SSE 正常结束标志 |
解析最终答案时,不要简单拼接所有 `messages`。当前实现从后期 `values.messages[]` 中选择:
```text
type = ai
response_metadata.finish_reason = stop
content 非空
```
并记录:
- `run_id`
- `resolved_profile_id`
- `resolved_profile_version_id`
- `response_metadata.model_name`
- `usage_metadata.input_tokens`
- `usage_metadata.output_tokens`
- `usage_metadata.total_tokens`
- 已出现的 SSE event types
如果没有收到 `end`,或无法找到最终 AI 回答,应视为协议失败,不要伪造成成功结果。
### 4.6 平台能力端口
其他项目建议定义一个稳定端口,例如:
```java
public interface AgentCapabilityPort {
AgentCapabilityResult invoke(AgentCapabilityRequest request);
}
```
领域层只依赖这个端口,不依赖 SuperAgent HTTP DTO、SSE event、Profile ID 或厂商 SDK。
建议 `AgentCapabilityResult` 至少包含:
```text
providerCode
responseSchemaVersion
providerSessionId
providerRequestId
providerProfileId
providerProfileVersionId
providerModelId
outputText 或 outputReference
usageMetadata
```
### 4.7 调用审计
建议每次外部能力调用都写入不可变审计表。TH Hotel 当前表为
`platform_ai_capability_invocation`,参考:
- `server/src/main/resources/db/migration/V2__create_ai_capability_invocation.sql`
- `docs/database/ai-capability-invocation-data-dictionary.md`
审计表应记录成功与失败,不应记录:
- Provider API Key
- Cookie
- Authorization
- Chain of Thought
- Provider 内部 Plan / Memory
- 未脱敏的个人信息
## 5. AgentBus 对接
### 5.1 运行时配置
最小配置建议:
```text
AGENTBUS_PROBE_ENABLED=false
AGENTBUS_WS_URL=wss://mesh.nianxx.cn/ws
AGENTBUS_WS_TOKEN=
AGENTBUS_BOT_ADDRESS=bot:external:listener
AGENTBUS_WS_RECONNECT_DELAY=5s
AGENTBUS_CONNECT_TIMEOUT=15s
AGENTBUS_SAMPLE_ENABLED=false
AGENTBUS_SAMPLE_DIR=var/agentbus-samples
AGENTBUS_MAX_FRAME_BYTES=1048576
AGENTBUS_MAX_SAMPLES=100
AGENTBUS_CAPTURE_ENABLED=true
AGENTBUS_DEFAULT_HOTEL_ID=HOTEL-TEST
AGENTBUS_REPLY_MODE=NONE
```
变量说明:
| 变量 | 是否 Secret | 说明 |
| --- | --- | --- |
| `AGENTBUS_PROBE_ENABLED` | 否 | 是否启用 AgentBus WebSocket 监听。默认关闭。 |
| `AGENTBUS_WS_URL` | 否 | AgentBus WebSocket 地址。 |
| `AGENTBUS_WS_TOKEN` | 是 | WebSocket 鉴权 Token。 |
| `AGENTBUS_BOT_ADDRESS` | 否 | 当前 Bot / Listener 地址。 |
| `AGENTBUS_WS_RECONNECT_DELAY` | 否 | 断线后的重连间隔。 |
| `AGENTBUS_CONNECT_TIMEOUT` | 否 | WebSocket 连接超时。 |
| `AGENTBUS_SAMPLE_ENABLED` | 否 | 是否保存本地原始 frame 样本。生产应默认关闭。 |
| `AGENTBUS_SAMPLE_DIR` | 否 | 本地样本目录,可能含 PII不得提交。 |
| `AGENTBUS_MAX_FRAME_BYTES` | 否 | 单个入站 frame 最大字节数。 |
| `AGENTBUS_MAX_SAMPLES` | 否 | 最多保留的本地样本数。 |
| `AGENTBUS_CAPTURE_ENABLED` | 否 | 是否写入 SourceMessage Inbox。 |
| `AGENTBUS_DEFAULT_HOTEL_ID` | 否 | AgentBus 未提供租户上下文时的默认业务上下文。 |
| `AGENTBUS_REPLY_MODE` | 否 | 调试回复模式。真实客户渠道应保持 `NONE`。 |
### 5.2 WebSocket 连接
当前实现使用 JDK `HttpClient` 的 WebSocket
```text
Authorization: Bearer <AGENTBUS_WS_TOKEN>
GET <AGENTBUS_WS_URL>?ready=1
```
连接成功后应能收到 `session.ready`
状态查询接口示例:
```text
GET /api/system/agentbus-probe
```
响应只应返回连接状态、计数器和最近错误代码,不返回 Token 或原始消息。
### 5.3 入站 frame 处理边界
推荐处理顺序:
```text
收到 raw WebSocket frame
→ 限制单帧大小
→ 可选本地采样
→ JSON 解析
→ 忽略 session.ready / task.progress / task.result 等控制事件
→ 将业务 payload 映射为 CaptureSourceMessageCommand
→ 写入 SourceMessage Inbox
```
实时链路禁止:
- 自动发送 ACK。
- 自动发送 `task.result`
- 自动回复客户。
- 直接创建 MessageEvent、Evidence、AI Recognition、Case、Task、Operation 或 Receipt。
- 直接调用 OHIP、ERP、支付系统等业务写接口。
### 5.4 当前已确认的 Outlook Payload 关键字段
AgentBus 后续确认的 Outlook 邮件 payload 包括:
```text
text
body.content_type
body.html
body.text
inline_images[]
attachments[]
source.channel
source.channel_account
source.external_message_id
source.external_conversation_id
source.sender
source.subject
source.web_link
reply_policy.mode
reply_policy.final_only
```
当前 SourceMessage 捕获只依赖少量稳定字段:
| AgentBus 字段 | 平台字段 |
| --- | --- |
| `source.channel` | `channel`,例如 `EMAIL` |
| `source.external_message_id` | `externalMessageId`,作为幂等键组成部分 |
| `source.external_conversation_id` | `externalConversationId` |
| frame `id` | `agentbusFrameId` |
| frame `session_id` | `agentbusSessionId` |
| `payload` 规范 JSON | `payloadJson``payloadSha256` |
不要根据 envelope 的 `from``to``conversation_id` 猜测酒店、业务 Case 或下游 Task。
### 5.5 SourceMessage Inbox
建议单独建表保存 AgentBus 入站事实。TH Hotel 当前表为:
- `platform_source_message_inbox`
- `platform_source_message_replay_attempt`
参考:
- `server/src/main/resources/db/migration/V16__create_source_message_inbox.sql`
- `docs/agentbus-production-flow.md`
推荐幂等键:
```text
hotel_id + provider + channel + external_message_id
```
重复投递时返回已有 Inbox不覆盖原始 payload不创建重复记录。
如果 payload 缺少必要字段或格式不符合预期,也应保存为 `FAILED` Inbox并记录安全错误
摘要。错误摘要不得包含:
- 邮件正文
- HTML
- 附件 URL
- 完整邮箱地址
- Token / Cookie / Secret
### 5.6 Replay 到 MessageEvent / Evidence
SourceMessage Inbox 不应等同于正式业务消息。推荐增加受控 Replay
```text
POST /api/system/source-message-inbox/{inboxId}/replay
Header: X-TH-Hotel-Source-Replay-Key
```
Replay 负责:
- 从 Inbox payload 提取 MessageEvent 字段。
- 保存正文或正文引用。
- 保存 Evidence 摘要或附件引用。
- 记录 replay attempt。
- 返回 MessageEvent ID 和状态。
Replay 接口默认关闭仅在本地、UAT 或受控生产运维场景开启。
## 6. 其他项目最小落地顺序
### 阶段 1SuperAgent 连通性
目标:
```text
后端探针
→ 创建 SuperAgent Session
→ 发送一条无 PII 测试消息
→ 解析 SSE 最终回答
→ 返回非敏感元数据
```
验收:
- HTTP 连接成功。
- SSE 收到 `end`
- 最终回答非空。
- 日志不出现 API Key、Cookie、Session 原始值或客户信息。
### 阶段 2AgentBus 连接
目标:
```text
AgentBus WebSocket
→ session.ready
→ 状态接口可见 connected/sessionReady
```
验收:
- 连接成功。
- 可断线重连。
- 不发送客户回复。
- 不保存本地 raw sample除非临时排障。
### 阶段 3SourceMessage Inbox
目标:
```text
AgentBus 入站业务 frame
→ SourceMessage Inbox
```
验收:
- 正常 payload 保存为 `RECEIVED`
- 无效 payload 保存为 `FAILED`
- 重复外部消息不重复入库。
- 查询接口只返回安全摘要。
### 阶段 4手动 Replay
目标:
```text
SourceMessage Inbox
→ MessageEvent / Evidence
```
验收:
- 同一 Inbox 可以按不同 `replayRunId` 多次 replay。
- 相同 `replayRunId` 幂等。
- Replay 失败有 attempt 记录。
- 响应不返回客户正文、HTML、附件 URL 或 Token。
### 阶段 5业务接入 SuperAgent
目标:
```text
MessageEvent
→ AgentCapabilityPort
→ SuperAgent Adapter
→ AiCapabilityInvocation
→ 业务 Schema 校验
```
验收:
- Provider 返回记录为审计,不直接触发业务写操作。
- 结构化输出必须通过 Schema 校验。
- 无法映射或不可信结果进入人工处理。
## 7. 安全与日志清单
必须放入 Secret 管理,不得提交仓库:
- `DEERFLOW_OPEN_API_KEY`
- `SUPERAGENT_PROBE_ACCESS_KEY`
- `AGENTBUS_WS_TOKEN`
- `SOURCE_MESSAGE_REPLAY_ACCESS_KEY`
- 数据库密码
- 任何真实客户渠道 Token
普通日志和错误响应不得输出:
- Authorization
- Cookie
- CSRF Token
- Provider API Key
- AgentBus Token
- 邮件正文和 HTML
- 附件 URL
- 客人姓名、邮箱、电话、证件号
- 支付信息
本地采样要求:
- `AGENTBUS_SAMPLE_ENABLED` 默认 `false`
- 只在隔离测试或排障时临时开启。
- 样本目录必须被 `.gitignore` 忽略。
- 排障结束后删除样本。
## 8. 测试建议
SuperAgent 建议覆盖:
- 缺失 API Key 时启动或调用失败。
- CSRF Header / Cookie 不一致时转换为受控错误。
- 创建 Session 成功。
- SSE 正常结束并解析最终回答。
- SSE 缺少 `end` 时失败。
- SSE 缺少最终回答时失败。
- HTTP 401 / 403 / 404 / 409 / 5xx 错误转换。
- 连接超时和读取超时。
AgentBus 建议覆盖:
- `session.ready` 只更新状态,不写 Inbox。
- 控制事件不写 Inbox。
- 正常 Outlook payload 写入 Inbox。
- `captureEnabled=false` 时忽略业务 frame。
- 无效 payload 写入 `FAILED` Inbox。
- 超大 frame 被拒绝并记录错误代码。
- 重复外部消息保持幂等。
- 查询接口不返回原始 payload。
- Replay 相同 run id 幂等。
## 9. 常见误区
### 9.1 把 AgentBus 当 AI Provider
AgentBus 是消息入口,不是抽取模型。它可以传递 Email / LINE 原始事实,但不应该直接产生
业务最终判断。
### 9.2 把 SuperAgent 返回当业务事实
SuperAgent 返回的是 Provider 输出。即使未来返回结构化 JSON也必须经过平台 Schema、
业务规则、Case 匹配和人工确认。
### 9.3 让浏览器直接调用 Provider
浏览器不能持有 Provider Key、AgentBus Token 或 replay access key。前端只调用本项目后端。
### 9.4 实时入口直接生成 Task
实时 AgentBus 链路如果直接创建 Task会导致重复投递、字段不完整、后续协议变化和人工
回溯都难处理。先落 Inbox再 Replay是更稳的路线。
### 9.5 在文档或测试里保存真实邮件
真实邮件、附件 URL、客户姓名和联系方式都可能是敏感数据。测试夹具应使用合成数据。
## 10. 接入前检查清单
接入 SuperAgent 前确认:
- [ ] 已获得 Open API Key 和允许访问的 Base URL。
- [ ] 已确认是否需要 CSRF double-submit。
- [ ] 已确认 Session、Message、Run 的生命周期。
- [ ] 已确认 SSE 最终答案或结构化结果所在字段。
- [ ] 已定义 `AgentCapabilityPort` 和调用审计表。
- [ ] 已确认 Provider 输出不会直接触发业务写操作。
接入 AgentBus 前确认:
- [ ] 已获得 WebSocket URL、Token 和 Bot Address。
- [ ] 已确认真实渠道 payload 字段。
- [ ] 已确认外部消息稳定幂等键。
- [ ] 已确认断线重连和重复投递语义。
- [ ] 已确认是否允许 ACK 或客户回复;默认按禁止处理。
- [ ] 已建立 SourceMessage Inbox 和 Replay attempt。
- [ ] 已定义原始 payload 的保存、访问、保留和删除策略。
进入生产前确认:
- [ ] 所有 Secret 均通过环境变量或 Secret Manager 注入。
- [ ] `.env.example` 只有占位值。
- [ ] 日志脱敏已验证。
- [ ] 自动回复保持关闭。
- [ ] 自动业务写操作保持关闭,除非经过单独评审。
- [ ] 监控至少覆盖连接状态、失败次数、Replay 失败和 Provider 调用失败。

View File

@@ -0,0 +1,397 @@
# M001 SourceMessage Inbox 邮件来源入口 PRD
## 文档信息
| 项目 | 内容 |
| --- | --- |
| 文档版本 | 0.1 |
| 日期 | 2026-07-06 |
| 状态 | 草稿 |
| 适用范围 | TH Hotel 项目邮件来源入口、邮件历史查询、邮件原文读取能力 |
| 主要读者 | 产品、后端、前端、测试、后续协作 agent |
## 已确认结论
- 历史邮件范围:第一阶段只查询“本项目已经接收并落库过的邮件”,不主动拉取上线前邮箱历史邮件。
- 邮件唯一标识AgentBus 返回 `source.external_message_id`,对应单封邮件外部唯一 ID。
- 邮件链标识AgentBus 返回 `source.external_conversation_id`,对应邮件会话或邮件链 ID。
- AgentBus frame `id``session_id` 只表示 AgentBus 推送帧和连接会话,不能等同于邮件 ID 或邮件链 ID。
- 正文图片和附件AgentBus 返回的是已经处理过的新 URL且 URL 长期有效。
- 本项目第一阶段不下载附件、不转存文件、不重新生成媒体 URL、不做 URL 刷新机制。
- 邮件原文读取能力是平台通用能力,不按“内部人员 / 业务人员”拆成两套能力,而是按权限、场景、审计和数据最小化原则管控。
- 邮件原文后续在哪个功能或哪个前端页面展示暂时待定,但平台侧应先把能力和数据边界设计清楚。
## 1. Executive Summary
本项目需要建设 `SourceMessage Inbox` 邮件来源入口,用于接收 AgentBus 推送的邮件 JSON并把它沉淀为本项目可查询、可去重、可追溯、可扩展的来源事实。该能力为后续 AI 识别、人工处理、Case / Task 流程提供稳定输入,但第一阶段不直接做 AI 抽取、不自动创建业务任务、不自动回复客户,也不直接调用外部业务系统。平台同时需要提供受控的邮件原文读取能力,后续具体页面由业务功能决定。
## 2. Problem Statement
### 谁有这个问题
- 业务处理人员:后续需要确认某封邮件是否已经进入系统,并在具体业务流程中查看邮件原文或上下文。
- 系统运维 / 管理人员:需要排查 AgentBus 是否成功推送、是否重复投递、payload 是否解析失败。
- 后续 AI / 业务流程:需要一个稳定、可审计的来源输入,而不是直接依赖 AgentBus 原始 DTO。
### 当前问题
如果 AgentBus 邮件 JSON 只被临时消费,或者直接进入 AI / Case / Task 业务流程,会带来以下问题:
- 历史邮件不可查,无法确认邮件是否已经进入系统。
- 重复投递难以幂等处理,可能导致重复任务或重复业务动作。
- AgentBus 协议细节会污染业务模块,后续更换来源方式成本高。
- 邮件原文、HTML、正文图片、附件 URL 如果缺少统一管控,容易在列表、日志、错误响应或前端页面里过度暴露。
- 后续 AI 识别和人工处理缺少统一来源事实,审计链路不完整。
### 为什么痛
- 业务影响:邮件是后续业务流程的起点,入口不稳定会影响 Case 匹配、任务生成、人工复核和追溯。
- 工程影响:入口层和业务层耦合过深,会让后续接入 Microsoft Graph、IMAP、Webhook、手工导入等来源变困难。
- 安全影响原文、HTML、图片和附件 URL 都可能包含客户信息,需要统一权限、审计和最小化返回策略。
### 证据
- 已有 AgentBus 对接文档确认 Outlook 邮件 payload 包含 `source.external_message_id``source.external_conversation_id``body.html``body.text``inline_images[]``attachments[]` 等字段。
- 已有后端规范要求 AgentBus 实时链路只写 SourceMessage Inbox不直接生成 MessageEvent、Evidence、Case、Task、Operation 或 Receipt。
- 🔶 Assumption业务人员后续会在某些业务功能中查看邮件原文但具体功能和页面还未确定。
## 3. Target Users & Personas
### Primary Persona业务处理人员
- 角色:酒店业务流程中的处理人员,例如预订、客服或后续明确的部门角色。
- 目标:在处理具体业务时,能够查看相关邮件原文和附件信息,理解客户真实意图。
- 痛点:如果只能看到摘要,遇到复杂邮件、转发链、附件说明时可能无法做准确判断。
- 当前行为:业务人员可能通过其他方式直接查看邮箱原文;本项目后续需要把原文读取能力接入业务流程。
### Secondary Persona系统运维 / 管理人员
- 角色:负责排查 AgentBus 接入、消息落库、重复投递、失败 payload 的技术或管理人员。
- 目标:确认系统是否收到邮件、是否重复、是否解析失败、失败原因是否安全可见。
- 痛点:如果没有入站记录和幂等状态,排查只能依赖外部日志或临时样本。
### System Persona后续 AI / 业务流程
- 角色:后续的 AI 识别流程、人工复核流程、Case / Task 编排流程。
- 目标:使用稳定的 SourceMessage ID 和受控内容读取能力,而不是直接依赖 AgentBus 原始 payload。
- 痛点:如果来源事实不稳定,后续业务对象无法可靠追溯到原始邮件。
## 4. Strategic Context
### 业务目标
- 建立邮件作为业务流程起点的稳定入口。
- 为后续 AI 识别、人工确认、Case / Task 流程提供可追溯来源。
- 保持低耦合和高可维护性,避免把 AgentBus 协议绑定到业务流程里。
### 为什么现在做
当前项目还处于需求和规范阶段,尚未大量生成前后端代码。此时先确定 SourceMessage Inbox 的产品边界、数据边界和安全边界,可以降低后续重构成本。
## 5. Solution Overview
### 方案概述
建设平台级 `SourceMessage Inbox` 能力。AgentBus Adapter 接收邮件 JSON 后先通过统一捕获端口保存为来源事实。后续所有查询、原文读取、AI 处理、业务处理都围绕本项目内部 SourceMessage ID 展开。
```text
AgentBus 邮件 JSON
// 外部消息入口,负责把邮件 payload 推送给本项目
→ integrations.messaging.agentbus
// AgentBus 适配层:解析 AgentBus frame提取稳定字段不做业务判断
→ platform.message SourceMessage Capture
// 平台消息捕获能力:做幂等、落库、状态记录、安全摘要生成
→ SourceMessage Inbox
// 邮件来源事实:可查询、可追溯、不可直接等同于业务任务
→ 后续受控读取 / AI 识别 / 人工处理
// 后续流程只依赖内部 SourceMessage ID 和平台接口
```
### 核心能力
- 邮件入站捕获:接收 AgentBus 邮件 JSON 并保存来源事实。
- 幂等去重:同一酒店、同一 provider、同一 channel、同一外部邮件 ID 不重复建记录。
- 历史查询:查询本项目已经接收并落库过的邮件。
- 邮件链查询:按 `externalConversationId` 查询同一邮件链下的记录。
- 安全摘要:列表只返回主题、发送人摘要、时间、状态、短摘要等安全信息。
- 原文读取:提供统一受控接口读取 text、HTML、正文图片 URL、附件 URL。
- 媒体引用保存:保存 AgentBus 已处理好的长期有效媒体 URL不下载、不转存、不刷新。
- 扩展来源:后续可以接入 Microsoft Graph、IMAP、Webhook、手工导入等来源。
### 数据模型边界
```text
source_message_inbox
// 邮件来源索引表:保存单封邮件的外部 ID、邮件链 ID、来源、状态、摘要和幂等信息
source_message_payload
// 原始载荷表:保存 AgentBus 推来的规范 JSON用于追溯、排查和后续重新处理
source_message_body
// 邮件正文表:保存 text/html 正文。列表不直接返回正文,原文读取接口按权限返回
source_message_media
// 邮件媒体引用表:保存 AgentBus 已处理好的长期有效 URL包括正文图片和附件不保存二进制文件
```
#### `source_message_inbox` 字段说明
| 字段 | 中文说明 |
| --- | --- |
| `id` | 本项目内部 SourceMessage ID后续业务流程使用该 ID 关联来源邮件 |
| `hotelId` | 酒店或业务上下文 ID避免不同酒店邮件 ID 冲突 |
| `provider` | 来源提供方,例如 `AGENTBUS` |
| `channel` | 来源渠道,例如 `EMAIL` |
| `externalMessageId` | 外部邮件系统中的单封邮件唯一 ID对应 AgentBus `source.external_message_id` |
| `externalConversationId` | 外部邮件系统中的邮件链 / 会话 ID对应 AgentBus `source.external_conversation_id` |
| `providerFrameId` | AgentBus frame ID用于排查推送帧不作为邮件唯一 ID |
| `providerSessionId` | AgentBus session ID用于排查连接会话不作为邮件链 ID |
| `payloadSha256` | 规范 JSON 的哈希值,用于排查 payload 是否变化 |
| `captureStatus` | 捕获状态,例如 `RECEIVED``FAILED` |
| `receivedAt` | 本项目接收时间 |
| `sourceSentAt` | 邮件来源发送时间,如果 payload 提供则保存 |
| `senderSummary` | 发送人安全摘要,列表可展示,不保存或不返回完整敏感信息 |
| `subject` | 邮件主题 |
| `safeSnippet` | 邮件安全摘要不能包含完整正文、HTML、附件 URL 或敏感凭证 |
推荐幂等键:
```text
hotelId + provider + channel + externalMessageId
// 同一业务上下文、同一来源、同一渠道、同一封外部邮件只保存一次
```
#### `source_message_payload` 字段说明
| 字段 | 中文说明 |
| --- | --- |
| `inboxId` | 所属 SourceMessage Inbox 记录 |
| `payloadJson` | AgentBus 规范 JSON 原始载荷 |
| `payloadSha256` | payload 哈希,用于一致性检查 |
| `schemaVersion` | payload 结构版本AgentBus 有提供则保存,否则使用本项目捕获版本 |
#### `source_message_body` 字段说明
| 字段 | 中文说明 |
| --- | --- |
| `inboxId` | 所属 SourceMessage Inbox 记录 |
| `contentType` | 正文类型,例如 `TEXT``HTML``MIXED` |
| `textBody` | 纯文本正文,用于摘要、搜索和 AI 处理更安全 |
| `htmlBody` | HTML 原文,前端展示前必须做 sanitize |
| `bodySha256` | 正文哈希,用于排查正文是否变化 |
#### `source_message_media` 字段说明
| 字段 | 中文说明 |
| --- | --- |
| `inboxId` | 所属 SourceMessage Inbox 记录 |
| `mediaType` | 媒体类型:`INLINE_IMAGE` 表示正文内图片,`ATTACHMENT` 表示附件 |
| `fileName` | 文件名,附件一般有,正文图片可能为空 |
| `contentType` | 文件 MIME 类型,例如 `image/png``application/pdf` |
| `sizeBytes` | 文件大小AgentBus 有返回则保存,没有则为空 |
| `externalUrl` | AgentBus 返回的长期有效访问 URL只能通过受控详情接口返回 |
| `externalMediaId` | AgentBus 或邮件系统中的媒体 ID有则保存便于排查和追溯 |
### 接口能力边界
```text
GET /api/source-messages
// 邮件历史列表只返回安全摘要不返回完整正文、HTML、附件 URL
GET /api/source-messages/{id}
// 邮件详情摘要:返回来源字段、状态、主题、摘要、邮件链 ID 等,不默认返回原文
GET /api/source-messages/{id}/original
// 邮件原文读取:按权限返回 textBody、htmlBody、正文图片 URL 和附件 URL
GET /api/source-messages?externalConversationId=...
// 邮件链查询:按外部邮件链 ID 查询本项目已接收的同链邮件
```
权限建议:
```text
SOURCE_MESSAGE_READ
// 邮件来源记录读取权限:允许查看列表和摘要详情
SOURCE_MESSAGE_ORIGINAL_READ
// 邮件原文读取权限允许读取正文、HTML、正文图片 URL 和附件 URL
```
## 6. Success Metrics
### Primary Metric
- 邮件入库可靠性:合法 AgentBus 邮件 payload 能稳定保存为 `RECEIVED` SourceMessage Inbox。
- 🔵 Open Question第一阶段是否需要量化目标例如测试环境 100% 合法样本入库成功。
### Secondary Metrics
- 重复投递去重成功率:重复外部邮件不会重复创建 SourceMessage。
- 查询可用性:可以按时间、状态、外部邮件 ID、外部邮件链 ID 查询本项目已接收邮件。
- 原文读取可控性:只有具备 `SOURCE_MESSAGE_ORIGINAL_READ` 权限的调用方可以读取原文和媒体 URL。
### Guardrail Metrics
- 列表接口、普通详情接口、日志、错误响应不得暴露完整正文、HTML、附件 URL、Token 或 Secret。
- AgentBus 实时链路不得直接创建 Case、Task、Operation、Receipt 或客户回复。
## 7. User Stories & Requirements
### Epic Hypothesis
我们相信建设 SourceMessage Inbox 邮件来源入口,可以让本项目稳定保存、查询和追溯 AgentBus 邮件来源事实,因为业务流程需要一个不依赖 AgentBus DTO 的内部起点。成功标准是合法邮件可入库、重复邮件可幂等、已入库邮件可查询、邮件原文可按权限读取。
### Story 1接收并保存 AgentBus 邮件 JSON
As a 系统
I want to 把 AgentBus 推送的邮件 JSON 保存为 SourceMessage Inbox
so that 后续业务流程可以基于本项目内部来源记录继续处理
Acceptance Criteria:
- Given AgentBus 推送合法邮件 JSON
When 系统处理该 JSON
Then 系统保存一条 `RECEIVED` SourceMessage Inbox 记录
- Given payload 包含 `source.external_message_id`
When 系统保存记录
Then `externalMessageId` 保存该单封邮件外部唯一 ID
- Given payload 包含 `source.external_conversation_id`
When 系统保存记录
Then `externalConversationId` 保存该邮件链 ID
- Given payload 包含正文、正文图片和附件 URL
When 系统保存记录
Then 系统保存正文和媒体引用,但不下载、不转存、不刷新 URL
### Story 2重复投递保持幂等
As a 系统运维 / 管理人员
I want to 系统识别重复投递的同一封邮件
so that 不会产生重复来源记录和后续重复业务处理
Acceptance Criteria:
- Given 已存在相同 `hotelId + provider + channel + externalMessageId` 的记录
When 系统再次收到该邮件 payload
Then 系统不创建新的 SourceMessage Inbox
- Given 重复投递 payload 与原 payload 内容不同
When 系统识别到同一外部邮件 ID
Then 系统不覆盖原始 payload并保留可排查的安全状态或摘要
### Story 3查询已接收邮件历史
As a 具备邮件来源读取权限的用户或系统
I want to 查询本项目已经接收并落库的邮件历史
so that 我可以确认邮件是否已经进入系统
Acceptance Criteria:
- Given 系统中已有 SourceMessage Inbox
When 调用邮件历史列表接口
Then 返回分页列表和安全摘要
- Given 调用方按 `externalMessageId` 查询
When 邮件已存在
Then 返回对应邮件摘要记录
- Given 调用方按 `externalConversationId` 查询
When 同一邮件链已有多封邮件
Then 返回本项目已接收的同链邮件记录
- Given 列表返回结果
Then 不包含完整正文、HTML、附件 URL、Token 或 Secret
### Story 4按权限读取邮件原文
As a 具备邮件原文读取权限的调用方
I want to 通过统一接口读取邮件原文、正文图片和附件 URL
so that 后续业务功能可以在需要时展示完整邮件上下文
Acceptance Criteria:
- Given 调用方具备 `SOURCE_MESSAGE_ORIGINAL_READ` 权限
When 调用邮件原文读取接口
Then 返回 `textBody``htmlBody``inlineImages[]``attachments[]`
- Given 调用方不具备 `SOURCE_MESSAGE_ORIGINAL_READ` 权限
When 调用邮件原文读取接口
Then 系统拒绝访问
- Given 原文接口返回 `htmlBody`
Then 接口说明必须标注前端展示前需要 sanitize
- Given 原文接口返回媒体 URL
Then 只返回 AgentBus 已处理的长期有效 URL不返回 Token 或后端 Secret
- Given 发生原文读取
Then 系统应记录访问审计至少包含调用方、SourceMessage ID、访问时间和访问场景
## 8. Out of Scope
第一阶段不包含:
- 主动拉取上线前邮箱历史邮件。
- 下载附件、转存对象存储、重新生成图片或附件 URL。
- URL 过期刷新机制,因为当前已确认 AgentBus 返回 URL 长期有效。
- AI 抽取、意图识别、Case 匹配、Task 创建、Operation、Receipt。
- 自动回复客户、自动发送 ACK 或 `task.result`
- 具体业务前端页面设计。原文读取能力先作为平台接口能力沉淀,页面位置后续再定。
- 真实客户邮件样本入库测试。测试夹具必须使用合成数据。
未来可考虑:
- 接入 Microsoft Graph、IMAP、Webhook、手工导入等其他来源。
- 全文检索和更复杂的邮件链展示。
- 原文访问审批、敏感字段遮罩、附件预览策略。
- SourceMessage Replay 到 MessageEvent / Evidence。
## 9. Dependencies & Risks
### Dependencies
- AgentBus payload 字段稳定性:依赖 `source.external_message_id``source.external_conversation_id``body.text``body.html``inline_images[]``attachments[]`
- 权限体系:需要后续确认 `SOURCE_MESSAGE_READ``SOURCE_MESSAGE_ORIGINAL_READ` 如何落到角色。
- 审计体系:需要记录原文读取行为,具体表结构可与平台审计能力统一设计。
- HTML 安全展示:前端展示 HTML 前必须 sanitize后端接口文档也要明确该约束。
### Risks & Mitigations
- RiskAgentBus 字段变更导致映射失败。
Mitigation保存原始 payload字段映射失败也落 `FAILED` 记录和安全错误摘要。
- Risk重复投递 payload 内容不同,覆盖原始事实会破坏追溯。
Mitigation幂等命中后不覆盖原 payload差异通过安全摘要或后续 attempt 记录表达。
- Risk邮件原文或媒体 URL 在列表、日志、错误响应中过度暴露。
Mitigation列表只返回安全摘要原文接口独立权限日志和错误响应脱敏。
- Risk业务模块直接依赖 AgentBus DTO。
Mitigation业务模块只依赖 SourceMessage ID 和平台接口AgentBus DTO 只留在 `integrations.messaging.agentbus`
- RiskHTML 原文直接渲染带来安全问题。
Mitigation前端展示前 sanitize必要时使用受控容器和链接安全策略。
## 10. Open Questions
| 问题 | 负责人 | 状态 |
| --- | --- | --- |
| 第一个 checkpoint 是否直接实现 `/api/source-messages/{id}/original` 原文读取接口,还是只写接口契约并后置实现? | 产品 / 后端 | Open |
| `SOURCE_MESSAGE_READ``SOURCE_MESSAGE_ORIGINAL_READ` 第一阶段如何映射到角色? | 产品 / 后端 | Open |
| 原文读取审计是否使用独立表,还是复用后续统一审计能力? | 后端 | Open |
| HTML sanitize 主要在前端完成,还是后端也提供清洗后的安全 HTML | 前端 / 后端 | Open |
| 是否需要第一阶段支持按正文摘要关键字搜索,还是只按 ID、时间、状态、邮件链查询 | 产品 | Open |
## PRD Self-Assessment
### Strongest Section
数据边界和安全边界较清晰:已经明确 AgentBus 字段、邮件唯一 ID、邮件链 ID、媒体 URL 长期有效、原文读取权限和列表最小化返回策略。
### Weakest Section
成功指标和角色权限仍需细化。当前只有定性验收标准,缺少真实环境中的量化目标和角色映射。
### Top Assumptions to Validate
| # | Assumption | Section | Risk if Wrong | Proposed Validation |
| --- | --- | --- | --- | --- |
| 1 | 业务人员后续会在某些功能中查看邮件原文,但页面位置未定 | Section 2 / 8 | 如果不需要展示原文,原文接口优先级可能过高 | 与业务流程设计一起复核 |
| 2 | AgentBus 媒体 URL 长期有效 | Section 5 / 8 | 如果 URL 后续变为临时 URL需要增加刷新或代理机制 | 与 AgentBus 提供方确认并记录 |
| 3 | 第一阶段不需要全文搜索 | Section 10 | 如果业务排查强依赖搜索,查询体验不足 | 需求评审时确认查询条件 |
### Recommended Next Step
先确认第一个 checkpoint 范围:是否把原文读取接口纳入第一阶段实现。确认后,再把本 PRD 拆成用户故事和后端实现计划。