diff --git a/README.md b/README.md new file mode 100644 index 0000000..59fb8d9 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# Patbond Doc + +Patbond 的契约、架构决策与开发过程文档。用 [MkDocs](https://www.mkdocs.org/) 构建成站点。 + +**本仓是四仓中唯一的「正典」所在**:契约、数据模型、架构决策以此为准。其余仓库中的同类内容都是副本或实现,冲突时以本仓为准。 + +## 权威文档在哪 + +| 文档 | 位置 | 说明 | +| --- | --- | --- | +| 对外契约 | `docs/api/openapi.yaml` | 客户端可见的全部端点。升版须与 `patbond-api` 内的四份快照同步 | +| 数据模型正典 | `docs/database/patbond_postgresql.sql` | 已评审的目标模型。Flyway 迁移从中逐列摘取,任何偏离都必须在迁移文件头写明原因 | +| 架构决策 | `docs/architecture/decisions.md` | ADR-001 起累积。**改动实现前先读相关 ADR** | +| 后端模块结构 | `docs/architecture/backend-modules.md` | 各模块职责与边界 | +| 发布记录 | `docs/development/releases.md` | 每次发布追加一条,含门禁结果与生效的流程纪律 | +| 功能清单 | `docs/development/feature-checklist.md` | 逐条功能的完成状态 | +| Git 与门禁 | `docs/development/git-workflow.md` | 分支、提交、CI 门禁与凭证防泄漏规范 | +| 真机验证清单 | `docs/development/device-verification.md` | 桌面不可替代的验证项,含逐项步骤与通过标准 | +| 服务器暴露面 | `docs/development/server-exposure.md` | 生产主机的端口、常驻服务与处置记录 | +| 迭代报告 | `docs/development/iterations/` | 按迭代归档。**存档不回改**,结论失效时追加带日期的修正注记 | + +## 本地构建 + +```bash +pip install mkdocs +mkdocs serve # http://127.0.0.1:8000 +``` + +## 本地门禁 + +与 CI 同一套命令(`.gitea/workflows/ci.yml`): + +```bash +sh scripts/check-secrets.sh --all # 凭证防泄漏(ADR-021) +mkdocs build --strict # 必须 exit 0 且零 warning +``` + +`--strict` 会把死链和未挂导航的孤立文档判为失败,所以**新增文档必须同时挂进 `mkdocs.yml` 的 `nav`**。 + +**每人每仓启用一次** pre-commit 钩子: + +```bash +git config core.hooksPath scripts/hooks +``` + +`scripts/check-secrets.sh` 是四仓同构副本,调整规则时四仓同步提交。 + +## 写作纪律 + +- **常设文档不写死本机绝对路径**,用 `<你的工作区>/patbond-xxx` 之类的参数化写法;迭代报告存档豁免。 +- **迭代报告是时点存档,不回改**。结论后来被推翻时,在原文下方追加带日期的修正注记并指向新依据——改掉原文等于销毁历史记录,而追加注记同样能掐断转述链。 +- **不要引用别的报告的转述结论**。本项目已多次因此误判:某报告写「无 nickname 字段」实指「契约未暴露」,被下游当成「缺列需迁移」,规模预估高估一整档。凡「XX 不存在 / XX 缺失」的判断,请打开原始 SQL、原始契约、原始源码确认,并注明核实的文件与行号。 +- `site/` 是构建产物,已被 `.gitignore` 排除,不要提交。 + +## 相关仓库 + +| 仓库 | 内容 | +| --- | --- | +| `patbond-api` | Spring Boot 多模块后端 | +| `patbond-flutter` | Flutter 客户端 | +| `patbond-ai` | AI 生成服务(Python),只调服务商 API | diff --git a/docs/architecture/backend-modules.md b/docs/architecture/backend-modules.md index fd39e85..99f4391 100644 --- a/docs/architecture/backend-modules.md +++ b/docs/architecture/backend-modules.md @@ -12,10 +12,13 @@ patbond-api(Maven 多模块,Spring Boot 3.5 + JDK 17) ├── patbond-auth 认证服务 :8081 ├── patbond-user 用户服务 + 埋点 :8082 ← Flyway 迁移链唯一持有者 ├── patbond-pet 宠物健康档案服务 :8083 ← M2 新增(ADR-009) -└── patbond-community 社区服务 :8084 ← M3 新增(ADR-017) +├── patbond-community 社区服务 :8084 ← M3 新增(ADR-017) +└── patbond-creation AI 生成编排服务 :8085 ← M4 新增(ADR-023) + +patbond-ai(独立仓库,Python) ← M4 新增(ADR-023),只调 AI 服务商 API ``` -部署形态:docker compose 六容器(postgres:18 + **MinIO 对象存储**(ADR-016,自托管、私有桶)+ auth + user + pet + community),应用容器无状态(ADR-007)。 +部署形态:docker compose 六容器(postgres:18 + **MinIO 对象存储**(ADR-016,自托管、私有桶)+ auth + user + pet + community),应用容器无状态(ADR-007)。**`patbond-creation` 尚未入 compose**——其 Worker 依赖 `patbond-ai` 与对象存储方案,两者均未落地。 ## 模块职责 @@ -57,11 +60,28 @@ patbond-api(Maven 多模块,Spring Boot 3.5 + JDK 17) - media 上传流程不在本模块(ADR-017:实现在 patbond-user,社区侧只做 asset 只读校验) - **当前状态**:第一波骨架(T3-02)已落地;业务端点随 M3 后续波次按契约实现 +### patbond-creation(AI 生成编排域,:8085,M4 新增) + +- **编排生成任务,不调用 AI 服务商**:上游调用在独立的 `patbond-ai`(Python)仓库,经 REST 到达(ADR-023 §2) +- 只读写 `creation` schema(数据表由 V6 建,含两处刻意偏离目标模型:`input_asset_id` 改可空以支持文生图、新增 `first_attempt_at` 使排队时长与执行耗时可分离) +- `/api/v1/creation/**` 客户端端点 + **数据库租约队列 Worker**(`FOR UPDATE SKIP LOCKED` + 租约列,**不引入 Redis/MQ**,ADR-023 §3) +- **不直连对象存储**:产物上传凭证来自 patbond-user 的内部媒体接口(预签名 PUT),由生成方直传——ADR-017 的边界不动,且 `patbond-ai` 也因此零对象存储凭证 +- **调用 patbond-ai 用服务身份 JWT,独立密钥对**(不转发用户 token)。选独立密钥对的依据:现有 `JwtVerifier` 只验签名、不校验 `aud`/`iss`,而用户 token 无 `aud` claim,复用密钥对会让四个模块把服务票当合法用户 token 接受(ADR-023 §5) +- **当前状态**:骨架已落地(端点、队列仓储、Worker 三通道、租约回收器、provider 适配层接口)。**刻意未实现且会响亮失败**:provider 两个实现位(待服务商文档确认五项)、预签名上传目标获取与产物入 `media.assets`(取决于**挂起中的对象存储决策**,ADR-023 §7) + +### patbond-ai(AI 生成服务,独立仓库,Python,M4 新增) + +- 不属于 `patbond-api` 多模块,是**第四个仓库**,已纳入发布语义(ADR-023 §12) +- 只负责调用 AI 服务商 API(视频 Seedance / 图片 gpt-image-2);**不访问数据库**,任务状态的唯一事实来源是 Java 侧的 `creation.generation_jobs` +- **对上游同步或异步一律对外呈现为异步**:`submit` 立即返回任务 id,同步型服务商在其内部以后台任务承载长连接。这样 Java 侧无长连接占线程,且 `provider_request_id` 立即可得——超时不再产生「是否已扣费」的歧义 +- 内部契约为该仓内独立的 `openapi-internal.yaml`,**不进四模块快照锁**,也不写入对外 `openapi.yaml` +- **是四仓中唯一持有 AI 服务商凭证的仓库**,故 `check-secrets.sh` 已补 `sk-ant-` / `sk-` / `api_key` 等形态 + ## 关键纪律 1. **契约先行**:所有对外端点以 `patbond-doc/docs/api/openapi.yaml` 为唯一事实源,新接口先冻结契约再实现(M2 起)。 -2. **单迁移链**:数据库迁移只进 patbond-user,新表按域用 schema 前缀区分(identity / platform / pet_health / …)。 -3. **服务间调用**:MVP 阶段 Feign 静态 URL 直连、无注册中心(ADR-002,Nacos 已移除);微服务化阶段再引入。 +2. **单迁移链**:数据库迁移只进 patbond-user,新表按域用 schema 前缀区分(identity / platform / pet_health / community / creation)。注意「谁拥有 schema」与「迁移文件放哪」是分开的——pet/community/creation 的业务代码各在自己模块,迁移文件全在 patbond-user,`flyway_schema_history` 只有一份。 +3. **服务间调用**:MVP 阶段 Feign 静态 URL 直连、无注册中心(ADR-002,Nacos 已移除);微服务化阶段再引入。Java↔Java 用 `InternalAuthFilter` 的共享 token,Java↔Python 用服务身份 JWT——两套并存是有意选择,见 ADR-023 §5。 4. **配置**:敏感配置走 `.env` / `application.yml`(gitignore)+ `.sample` 模式,环境变量注入(`PATBOND_DB_URL` 等)。 5. **测试**:集成测试一律 Testcontainers(postgres:18,ADR-006/008),每模块交付 `./mvnw clean test` 必绿。