# TH Hotel 后端开发规范 ## 1. 文档定位 本文整理 TH Hotel 当前后端开发约定,供本项目后端开发和其他 agent 协作参考。 本文是当前项目专属补充,不应整份复制到其他项目。可复用的通用后端规则应沉淀到 `docs/import/reusable/backend-development-guidelines.md`。 本项目后端是酒店多部门 AI 业务流程与 Case 协同平台的服务端。这里的 AI 只表示调用外部 模型或 Agent 能力提供方并消费其结构化结果;本项目不开发模型、Prompt 优化平台、Agent Runtime、Planner、Memory 或通用工具调用框架。 当前项目时间存储、接口返回、酒店时区展示和本地业务日期边界统一参考 `docs/project/backend-time-design.md`。 当前项目接口暴露、权限、酒店隔离和审计边界统一参考 `docs/project/security-access-control-boundary.md`。新增或修改 Controller、第三方入口、调试入口或 worker 触发入口时,必须同步检查该文档。 ## 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.16,Spring 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;外部协议转换类应放入具体集成模块的 `adapter` 包。 - 平台核心不得依赖预订部专有字段。 - 部门工作流可以依赖平台核心,平台核心不能反向依赖部门模块。 - 外部系统 DTO、Oracle DTO、Provider DTO、领域模型和 API Response 必须分离。 - Oracle DTO 或生成代码只能位于 `integrations.ohip`,不得进入平台领域模型。 - 外部协议到内部命令或 DTO 的转换类使用 `Adapter`、`Converter` 等能表达适配语义的后缀,不使用 `Mapper` 命名,避免和 MyBatis Mapper 混淆。 当前项目后端模块内部目录强制按以下结构组织: ```text ├── control ├── service │ └── impl ├── domain ├── mapper ├── repository └── common ├── dto ├── request ├── result └── enums ``` 中文说明: | 目录 | 中文职责 | 放置内容 | | --- | --- | --- | | `control` | HTTP 入口层 | Controller 具体实现类,只处理请求契约、参数校验和响应映射 | | `service` | 服务契约层 | Service 接口,只表达模块对外提供的稳定业务能力 | | `service.impl` | 服务实现层 | Service 实现类,负责编排事务、幂等、Repository 和外部端口 | | `domain` | 数据实体层 | Entity 类,字段必须有中文注释,不能与 Response 或外部 DTO 混用 | | `mapper` | MyBatis 访问层 | Mapper 类和语义化 Mapper 方法,Controller 与 Service 不直接跨层暴露 Mapper | | `repository` | 持久化封装层 | Repository 接口和具体实现,对 Service 屏蔽 Mapper 与数据库细节 | | `common.dto` | 通用数据载体层 | 模块内部跨层传递的 DTO、快照、草稿等数据载体 | | `common.request` | 请求模型层 | Controller、Service 或外部适配器输入侧的 Request、Command、Query 条件 | | `common.result` | 结果模型层 | Service 或 Controller 输出侧的 Result、分页结果、操作结果 | | `common.enums` | 模块枚举层 | 本模块稳定业务枚举、状态码和失败原因枚举 | 后续生成或移动 Java 代码时必须严格遵守上述目录,不再新增 `api`、`application`、`persistence` 等同义包来承载这些职责,除非先更新本规范并说明迁移原因。已有或迁移来的 `application` 包中的 Request、Result、DTO 类,应按语义移动到对应模块的 `common.request`、`common.result`、`common.dto`。如果类职责或目录归属不明确,必须先询问再继续。 ## 4. Controller / Service / Repository 规则 - Controller 只做 HTTP 契约、参数校验、权限入口和响应映射。 - Controller 不直接访问 Mapper。 - Controller 不直接调用外部适配器。 - Service 接口位于 `service` 包,表达模块对外提供的稳定业务能力。 - Service 实现类位于 `service.impl` 包,编排事务、Entity、Repository 和外部端口。 - Request、Command、Query 条件位于 `common.request` 包。 - Result、分页结果、操作结果位于 `common.result` 包。 - 跨层 DTO、Snapshot、Draft 等通用数据载体位于 `common.dto` 包。 - Entity 位于 `domain` 包,表达数据库表字段与业务含义,不与 DTO、Response 或外部 Provider DTO 混用。 - Mapper 位于 `mapper` 包,只负责本模块数据库表访问和语义化查询方法。 - Repository 位于 `repository` 包,负责封装 Mapper 和数据库细节,作为 Service 的持久化边界。 - 外部调用通过端口和 Adapter 隔离,业务层不依赖厂商 SDK 或厂商 DTO。 - 外部协议适配类位于对应 `integrations...adapter` 包,负责把外部 DTO 或 JSON 转换为内部稳定命令。 - 生成或迁移代码前必须先阅读本规范并查看同模块既有代码习惯;如果放置目录、类名后缀或依赖方向不确定,先询问再实现。 ## 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`;如需调用 SuperAgent,必须通过入库后的受控异步 dispatch / outbox 链路完成。 - 不直接生成 `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”“备用字段”等模糊注释。 - 新建 MySQL 表必须显式指定存储引擎、字符集和排序规则,默认使用 `ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='...'`。 - 本项目字符串字段默认大小写敏感,包含用户名、展示名、邮件主题、菜单名称、状态筛选、外部 ID、哈希、Token、nonce、幂等键、业务代码等。这样可以避免 MySQL 默认大小写不敏感 collation 把 `X` 和 `x` 之类的外部 opaque id 误判为同一个值。 - 已有表通过 `V13__make_existing_string_columns_case_sensitive.sql` 统一转为 `utf8mb4_bin`;后续新增表或新增字符串列不能重新退回默认 `*_ci` collation。 - 如果某个功能确实需要大小写不敏感搜索,不应改变整表默认 collation,应在查询层使用规范化字段、搜索索引、`LOWER()`/归一化值或专门搜索方案实现,并在需求文档和 migration 注释中写明原因。 - 新建表推荐模板: ```sql CREATE TABLE example_table ( id BIGINT NOT NULL COMMENT '内部主键 ID', external_id VARCHAR(256) NULL COMMENT '外部系统 ID,按大小写敏感保存和比较', created_at DATETIME(6) NOT NULL COMMENT '记录创建 UTC 时间', PRIMARY KEY (id), KEY idx_example_external_id (external_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='示例业务表'; ``` - 业务时间点以 UTC 写入数据库,API 层负责返回带 `Z` 的 ISO 8601 UTC 时间,例如 `2026-07-08T03:00:00Z`。 - 新增 API 响应 DTO 中的时间点字段优先使用 `OffsetDateTime`;从数据库 `LocalDateTime` 快照输出到接口时,应通过 `UtcTimeFormatter` 转换,禁止直接把无时区 `LocalDateTime` 暴露给前端或 SuperAgent。 - 酒店本地业务日期,例如入住日期、离店日期、营业日,优先使用 `LocalDate` 或明确酒店时区语义的字段,不和 UTC 时间点混用。 - JSON 字段只用于扩展元数据,不替代需要查询、约束或索引的正式列。 ## 11. 后端代码注释规范 后端 Java 代码必须提供必要且准确的中文注释: - 架构文档、设计文档、目录结构、数据模型、字段映射和接口示例必须优先使用中文说明业务含义。 - 领域对象、应用服务、外部适配器、Controller、配置属性和复杂参数对象应说明业务含义、边界或调用约束。 - `control`、`service`、`service.impl` 中的方法必须有中文注释,说明业务动作、调用边界、幂等或安全约束。 - `mapper` 中 MyBatis-Plus 自带继承方法不强制补中文注释;不要为了重命名 `selectById`、`insert`、`updateById` 等自带方法而写薄 default 包装。本项目自定义的语义化查询、写入、更新方法应根据业务复杂度合理补充中文注释。 - `domain` 中 Entity 的每个字段必须有中文注释,说明字段业务含义、来源、代码值范围或安全限制。 - `repository` 中对外暴露的方法必须有中文注释,说明持久化语义和是否返回安全字段。 - 复杂流程、幂等键、并发控制、事务边界、错误转换、外部系统字段映射和安全脱敏逻辑必须说明原因。 - 注释不得只复述类名、方法名或字段名。 - 不得用“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. 接口暴露、权限和审计边界 新增或修改后端入口前,必须先判定接口分类: ```text PUBLIC FRONTEND_USER FRONTEND_ADMIN FRONTEND_DEBUG THIRD_PARTY_SUPERAGENT THIRD_PARTY_AGENTBUS THIRD_PARTY_MCP INTERNAL_ONLY ``` 中文说明: | 分类 | 中文含义 | 后端落地要求 | | --- | --- | --- | | `PUBLIC` | 公开基础接口 | 只能返回健康、登录等非敏感信息,不返回业务数据和配置细节 | | `FRONTEND_USER` | 普通业务前端接口 | 目标状态必须登录、权限码和酒店访问权控制 | | `FRONTEND_ADMIN` | 系统管理后台接口 | 必须登录、管理权限码和管理审计 | | `FRONTEND_DEBUG` | Debug、Demo、Replay、Probe 等调试接口 | 必须有环境开关、受控 access key 或管理权限,生产默认关闭或严格限制 | | `THIRD_PARTY_SUPERAGENT` | SuperAgent 服务到服务接口 | 使用 HMAC、timestamp、nonce、body hash 等机器鉴权,不使用用户 Bearer token | | `THIRD_PARTY_AGENTBUS` | AgentBus 实时入口 | 使用 AgentBus Token、入库幂等和受控 dispatch,不直接改业务状态 | | `THIRD_PARTY_MCP` | MCP 工具入口 | 使用 Bearer Token 和工具级能力限制,不暴露无关业务接口 | | `INTERNAL_ONLY` | 后端内部能力 | 不提供外部 HTTP 入口,只能通过 Service、Port、Worker 或 Adapter 内部调用 | 落地规则: - Controller 方法新增前必须明确调用方、鉴权方式、权限码、酒店隔离方式、敏感字段返回边界和审计要求。 - 前端业务接口最终应使用登录态、权限码和酒店访问权;第三方机器接口不得误用用户登录态。 - 后端内部 Adapter、Repository、Mapper、Secret、外部系统 client、raw payload 和原始附件读取能力不得直接暴露给前端或第三方。 - 邮件正文、HTML、附件 URL、AI 原始 payload、trace、调试错误摘要等敏感数据返回前必须确认调用方和审计策略。 - 写接口必须明确 actor 来源;前端用户写操作使用当前登录用户,SuperAgent、MCP、AgentBus 使用机器身份。 - 新增或变更接口必须同步更新 `docs/project/security-access-control-boundary.md`。影响前端时同步更新 `docs/project/frontend-backend/backend-to-frontend-notes.md`,影响 SuperAgent / MCP / AgentBus 时同步更新对应集成契约文档。 - 新增接口权限时必须执行 `docs/project/security-access-control-boundary.md` 中“新增接口权限固定流程”:定义权限码、补启动同步、补内置角色矩阵、后端强制校验、前端权限类型和交互同步、系统设置可分配、补测试和文档。 ## 15. API 设计规范 - API 返回稳定代码,不返回中文或英文文本作为前端业务判断依据。 - 动态表单字段使用稳定 `fieldKey` 和可国际化 `labelKey`。 - 错误响应不得回显 Secret、原始正文、附件 URL 或个人信息。 - 写接口必须考虑幂等、并发版本、审计和失败恢复。 - 外部写操作结果不明确时,禁止盲目重试。 - 调试接口必须默认关闭,并使用独立访问密钥。 ## 16. 测试与检查命令 后端最低检查: ```bash cd server ./mvnw test ./mvnw verify ``` 聚焦开发时可先运行相关测试: ```bash cd server ./mvnw -Dtest=SomeFocusedTest test ``` 默认 `test` profile 使用 H2 MySQL Mode,保证普通测试不依赖本机 MySQL。需要连接真实测试 MySQL 时,使用 `test,test-mysql` profile,并通过 `TH_HOTEL_TEST_DB_URL`、`TH_HOTEL_TEST_DB_USERNAME`、`TH_HOTEL_TEST_DB_PASSWORD` 注入测试库连接信息。测试 MySQL 只能使用本地或 CI 测试库,不得连接生产库或包含真实酒店、客户、支付数据的数据库。 如果命令尚未配置或因环境问题无法运行,必须明确说明,不能假装通过。 ## 17. Git 与协作流程 - 改动前说明目标、范围和将修改的文件。 - 每次只处理一个模块或一个纵向切片。 - 需求基准变化时,先做差异和影响分析。 - 修改接口前确认领域模型和数据映射。 - 不修改与当前任务无关的用户变更。 - 修改后运行项目已配置的检查命令。 - Git commit message 使用中文,清楚说明本次提交的业务或技术变更。 ## 18. 后端提交前检查清单 - [ ] 是否遵守 platform / workflows / integrations 分层? - [ ] 是否已在 `docs/project/security-access-control-boundary.md` 中登记或更新接口分类、鉴权方式、权限码、酒店隔离和审计要求? - [ ] 模块内部是否遵守 `control` / `service` / `service.impl` / `domain` / `mapper` / `repository` / `common.dto` / `common.request` / `common.result` / `common.enums` 目录规则? - [ ] Controller 是否没有直接访问 Mapper 或外部 Adapter? - [ ] 第三方接口是否没有误用用户登录权限,前端接口是否没有直接暴露 Secret、raw payload 或外部系统调用能力? - [ ] Request、Result、DTO 是否放在 `common.request`、`common.result`、`common.dto`,而不是混在 `service` 或临时 `application` 包? - [ ] `control`、`service`、`service.impl`、`repository` 方法是否有中文注释? - [ ] Mapper 自定义语义化方法是否按复杂度合理补充中文注释,且未为了 MyBatis-Plus 自带继承方法强行加无意义注释? - [ ] 外部协议适配类是否放在 `integrations...adapter`,并避免使用 `Mapper` 命名混淆 MyBatis Mapper? - [ ] Entity 字段是否有中文注释? - [ ] 外部 DTO 是否没有进入领域模型? - [ ] 写接口是否有幂等、版本或审计设计? - [ ] Flyway SQL 是否有规范中文注释? - [ ] Java 复杂逻辑是否有必要中文注释? - [ ] 是否没有提交真实 Secret 或客户数据? - [ ] 是否没有把 Provider 输出直接当业务事实? - [ ] 是否运行了 `./mvnw test` 或说明了无法运行原因? - [ ] 是否运行了 `./mvnw verify` 或说明了无法运行原因?