docs: 文档全面审查更新与规范集中化重构

This commit is contained in:
2026-06-01 13:13:52 +08:00
parent 897fad95eb
commit d765f86b65
8 changed files with 324 additions and 331 deletions

View File

@@ -1,45 +1,31 @@
# 后端开发
本文档说明 alfred 后端的 API、配置加载、日志、版本管理和后端测试开发约定
本文档说明 alfred 后端的模块实现细节、API 索引和开发方式。开发规范库优先级、类型规范、配置契约、API 路由规范、测试约定等)见 [开发规范文档](README.md#开发规范)
适用场景:修改 src/server/、src/shared/api.ts、后端测试、配置契约、API 响应或日志模块
## 库使用优先级
| 优先级 | 来源 | 典型用途 |
| ------ | ------------ | ---------------------------------------------------- |
| 1 | Bun 内置 API | Bun.serve、Bun.file、Bun.YAML、Bun.spawn、bun:sqlite |
| 2 | es-toolkit | 类型判断、深度比较、并发控制 |
| 3 | 标准 Web API | Headers、fetch、AbortController |
| 4 | 主流三方库 | pino、@sinclair/typebox、ajv、drizzle-orm |
| 5 | 自行实现 | 仅在以上都无法满足时 |
新增依赖前必须先检查上述每一层是否已有可用方案。
## API 路由开发
路由文件位于 src/server/routes/,每个端点一个文件。路由通过 server.ts 的 Bun.serve({ routes }) 声明式注册。
新增路由步骤:
1. 在 src/server/routes/ 下创建 <name>.ts
2. 实现 handler 函数并 export
3. 在 server.ts 的 routes 对象中注册路径和 method handler
4. 在 tests/server/ 中添加对应测试
适用场景:修改 `src/server/`、了解现有模块 API、查找可供复用的工具函数和数据访问接口
## 共享工具
helpers.ts 提供跨路由共用的响应工具:
`src/server/helpers/` 提供跨路由共用的工具模块
- createApiError(error, status) — 构造 API 错误体
- createHeaders(mode, init) — 创建响应 Headers
- jsonResponse(body, options) — JSON 响应构造
- `response.ts` — 响应格式化工具:
- `createApiError(error, status)` — 构造 API 错误体
- `createHeaders(mode, init)` — 创建响应 Headers生产模式附加安全 header
- `createMetaResponse(version)` — 构造应用元信息响应
- `formatDuration(ms)` — 格式化毫秒时长为可读字符串
- `jsonResponse(body, options)` — JSON 响应构造
- `url.ts` — URL 工具:
- `parseIdFromUrl(url)` — 从 URL 解析路径中的 ID
middleware.ts 提供 API 参数校验函数
`src/server/middleware/` 提供 API 参数校验和错误处理中间件
- validateIdParam(idStr, mode) — 校验 ID 参数格式
- validatePagination(pageParam, pageSizeParam, mode) — 校验分页参数
- validateTimeRange(from, to, mode) — 校验时间范围参数
- `validate.ts` — 参数校验函数:
- `validateIdParam(idStr, mode)` — 校验 ID 参数格式(字母数字 + `_` `-`
- `validatePagination(pageParam, pageSizeParam, mode)` — 校验分页参数page 最小 1pageSize 最大 200
- `validateTimeRange(from, to, mode)` — 校验时间范围参数
- `error-handler.ts` — 错误处理中间件:
- `AppError` — 业务异常类(含 statusCode
- `withErrorHandler(fn, mode, logger?)` — 包裹路由 handler捕获 AppError 和未知异常
## 数据库
@@ -62,7 +48,7 @@ middleware.ts 提供 API 参数校验函数:
### 数据访问
`src/server/db/projects.ts` 提供项目数据访问函数`src/server/db/providers.ts` 提供供应商数据访问函数,`src/server/db/models.ts` 提供模型数据访问函数,`src/server/db/conversations.ts` 提供会话和消息数据访问函数。输入输出使用 `src/shared/api.ts` 的类型。函数内部使用 Drizzle query builder 包装 `bun:sqlite` Database。
`src/server/db/projects.ts` 提供项目数据访问函数`createProject``getProject``listProjects``updateProject``deleteProject``archiveProject``restoreProject`)。`src/server/db/providers.ts` 提供供应商数据访问函数(`createProvider``getProvider``listProviders``listProviderOptions``updateProvider``deleteProvider`)。`src/server/db/models.ts` 提供模型数据访问函数(`createModel``getModel``listModels``getModelsByProviderId``updateModel``deleteModel`)。`src/server/db/conversations.ts` 提供会话和消息数据访问函数(`createConversation``getConversation``listConversations``updateConversation``updateConversationTimestamp``deleteConversation``createMessage``createMessages``listMessages`)。`src/server/db/connection.ts` 还提供 `paginateQuery``wrap` 工具函数。输入输出使用 `src/shared/api.ts` 的类型。函数内部使用 Drizzle query builder 包装 `bun:sqlite` Database。
## AI 服务层
@@ -82,6 +68,8 @@ middleware.ts 提供 API 参数校验函数:
- `buildProviderRegistry(db)` — 从 DB 查询所有供应商,构建 Vercel AI SDK Provider Registry
- `testProviderConnection(config)` — 先测试 Base URL 可达性,再请求 `/models` 验证 API Key 和模型列表接口
- `testModelConnection(config)` — 先通过 providerId 查询供应商(含 apiKey再以 modelId 请求对应模型的 API 连通性
- `countModels(db)` — 统计 DB 中已配置的模型总数,用于 Agent 调用前的可用性检查
每次 AI 调用时从 DB 查询 providers构建 registry 后通过 `registry.languageModel('providerId:modelId')` 获取模型实例。不使用缓存层。模型是否存在以及业务能力标签由调用方基于 models 表先行校验registry 只负责将 providerId/modelId 映射到 AI SDK 模型实例。
@@ -115,31 +103,16 @@ middleware.ts 提供 API 参数校验函数:
| GET | `/api/projects/:id/conversations` | 列出项目下所有会话 |
| POST | `/api/projects/:id/conversations` | 创建新会话 |
| GET | `/api/projects/:id/conversations/:cid` | 获取会话详情 |
| PATCH | `/api/projects/:id/conversations/:cid` | 更新会话标题或模型 |
| DELETE | `/api/projects/:id/conversations/:cid` | 删除会话及其消息 |
| GET | `/api/projects/:id/conversations/:cid/messages` | 获取会话消息列表 |
| POST | `/api/projects/:id/chat` | 发送消息并流式回复 |
聊天路由处理器位于 `src/server/routes/chat/`,遵循统一的 handler 模式。`send.ts` 处理发送消息:验证会话归属后只保存最后一条用户消息到 DB通过 `createAlfredAgent` 创建 agent调用 `createAgentUIStreamResponse` 返回 SSE UI 消息流,流结束通过 `onFinish` 回调保存 AI 回复到 DB。
## 类型规范
- 共享类型以 src/shared/api.ts 为唯一源头
- 应用常量以 src/shared/app.ts 为唯一源头
- 版本号以 package.json.version 为唯一源头
- 前端不得 import src/server/ 下的任何文件
- 严格联合类型优先于宽类型
## 配置契约
配置加载流程固定为unknown -> AuthoringConfig -> NormalizedConfig -> ValidatedConfig -> ServerConfig。
Ajv 保持严格拒绝模式allErrors: true、不启用类型强制转换、不注入默认值、不自动删除未知字段。
新增或修改配置字段时必须同步更新 TypeBox schema fragments、config.schema.json、测试和对应用户文档。
## 日志模块
后端运行时代码统一通过 Logger 接口输出日志,禁止直接使用 console.\*
后端运行时代码统一通过 Logger 接口输出日志。
| 实现 | 用途 |
| --------------------- | ------------------------ |
@@ -148,16 +121,14 @@ Ajv 保持严格拒绝模式allErrors: true、不启用类型强制转换、
| NoopLogger | 静默丢弃日志 |
| MemoryLogger | 测试替身 |
敏感信息会自动 redact authorization、cookie、password 等字段。
## 版本管理
项目使用 package.json.version 作为版本号唯一来源。
版本号以 `package.json.version`唯一来源。
版本获取方式:
- 开发模式src/server/version.ts 运行时从 package.json 读取
- 生产模式scripts/build.ts 在构建时将版本号烘焙为字面量注入
- 开发模式:`src/server/version.ts` 运行时从 package.json 读取
- 生产模式:`scripts/build.ts` 在构建时将版本号烘焙为字面量注入
版本升迁命令:
@@ -168,21 +139,6 @@ bun run version:major # 升迁 major 版本
bun run version:set # 显式设置版本号
```
## 后端测试
| 变更类型 | 测试重点 |
| ------------------ | --------------------------------- |
| API 路由 | tests/server/app.test.ts 集成行为 |
| 配置 schema | schema 导出、合法/非法配置 |
| helpers/middleware | 单元测试 |
后端测试约定:
- API 路由集成测试必须通过真实 `startServer` 覆盖路由注册、HTTP method、fallback、响应 header 和核心错误路径。
- SQLite 相关测试必须复用 `tests/helpers.ts` 中的测试数据库 helper不要在测试文件内分散实现临时目录清理或直接裸用 `rmSync(dir, { recursive: true })`
- DAO 和路由边界测试应优先使用真实 migration 初始化测试库,只有 migration 执行器单测可以使用最小 fake migration。
- logger/bootstrap 中预期的 fallback 输出必须在测试中捕获并断言,正常通过的测试不应污染 stdout/stderr。
## 更新触发条件
修改后端 API、共享类型、配置契约、日志模块、版本管理或后端测试规范时,必须更新本文档。
修改后端模块 API、共享工具、数据库 schema、AI 服务层或聊天 API 时,必须更新本文档。