测试基础设施 - 统一 SQLite 测试 DB/临时目录 helper(tests/helpers.ts),支持 Windows EBUSY 重试清理 - 测试库使用 PRAGMA journal_mode=DELETE 避免 WAL 句柄延迟 - 路由 handler 测试改用 createMigratedMemoryTestDatabase 避免 File DB 锁 - SQLite 聚焦 --rerun-each=20 全部通过(720 pass) 后端测试补强 - 新增 tests/server/app.test.ts 真实 startServer 集成测试 - 覆盖 /api/meta、项目 CRUD、错误路径、静态 fallback、安全 header - bootstrap/logger 测试捕获预期输出,消除测试噪音 前端测试补强 - 移除 .ant-* 内部类名依赖,改为角色/文本/导航/请求契约断言 - 项目页补充搜索、Tab 切换、表单、表格操作、错误反馈行为测试 - 新增 hooks(use-theme-preference、use-sidebar-collapsed、use-projects)纯逻辑测试 - 新增 ErrorBoundary 错误展示和刷新按钮测试 - 新增搜索清空行为测试 - 测试 setup 过滤 antd/rc-trigger NaN height warning 产品修复(测试暴露) - 修复 ProjectToolbar 搜索框无法输入(新增 draftKeyword 状态) - 加固 ProjectFormModal 表单字段同步(useEffect 替代不可靠的 afterOpenChange) - 清理 ProjectFormModal 冗余 afterOpenChange 同步逻辑 重构与合规 - ProjectContext 拆分为三文件满足 React Fast Refresh 规则 - use-projects.ts 导出内部 helper 函数供测试验证 - scripts/build.ts 提取纯生成函数供测试使用,修复构建步骤日志编号 - 修复 build 测试覆盖真实生成逻辑 文档同步 - 更新后端/前端/开发文档测试规范、质量门禁和 helper 使用说明
132 lines
5.6 KiB
Markdown
132 lines
5.6 KiB
Markdown
# 后端开发
|
||
|
||
本文档说明 alfred 后端的 API、配置加载、日志、版本管理和后端测试开发约定。
|
||
|
||
适用场景:修改 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/ 中添加对应测试
|
||
|
||
## 共享工具
|
||
|
||
helpers.ts 提供跨路由共用的响应工具:
|
||
|
||
- createApiError(error, status) — 构造 API 错误体
|
||
- createHeaders(mode, init) — 创建响应 Headers
|
||
- jsonResponse(body, options) — JSON 响应构造
|
||
|
||
middleware.ts 提供 API 参数校验函数:
|
||
|
||
- validateIdParam(idStr, mode) — 校验 ID 参数格式
|
||
- validatePagination(pageParam, pageSizeParam, mode) — 校验分页参数
|
||
- validateTimeRange(from, to, mode) — 校验时间范围参数
|
||
|
||
## 数据库
|
||
|
||
项目使用 SQLite 作为存储后端,通过 bun:sqlite + Drizzle ORM 实现类型安全的数据访问。
|
||
|
||
### schema 定义
|
||
|
||
`src/server/db/schema.ts` 使用 Drizzle ORM 定义表结构,列名使用 snake_case,TypeScript 类型使用 camelCase,Drizzle schema 负责映射。
|
||
|
||
### 数据库连接
|
||
|
||
`src/server/db/connection.ts` 的 `createDatabase(dataDir, logger)` 打开 `<dataDir>/alfred.db`,设置 PRAGMA(foreign_keys=ON、journal_mode=WAL、busy_timeout=5000)。
|
||
|
||
### migration 机制
|
||
|
||
- 开发期:使用 `drizzle-kit generate` 从 TS schema 生成 SQL migration 文件到 `drizzle/` 目录
|
||
- 生产期:构建时将 `drizzle/*.sql` 嵌入可执行文件,启动时自动应用 pending migrations
|
||
- 每次 migration 前自动备份现有 DB 到 `<dataDir>/backups/alfred-<timestamp>.db`
|
||
- migration 在事务中执行,失败则回滚并停止启动
|
||
|
||
### 数据访问
|
||
|
||
`src/server/db/projects.ts` 提供项目数据访问函数,输入输出使用 `src/shared/api.ts` 的类型。函数内部使用 Drizzle query builder 包装 `bun:sqlite` Database。
|
||
|
||
## 类型规范
|
||
|
||
- 共享类型以 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.\*。
|
||
|
||
| 实现 | 用途 |
|
||
| --------------------- | ------------------------ |
|
||
| PinoLoggerWrapper | 生产运行时 |
|
||
| ConsoleFallbackLogger | 配置加载失败前的降级日志 |
|
||
| NoopLogger | 静默丢弃日志 |
|
||
| MemoryLogger | 测试替身 |
|
||
|
||
敏感信息会自动 redact authorization、cookie、password 等字段。
|
||
|
||
## 版本管理
|
||
|
||
项目使用 package.json.version 作为版本号唯一来源。
|
||
|
||
版本获取方式:
|
||
|
||
- 开发模式:src/server/version.ts 运行时从 package.json 读取
|
||
- 生产模式:scripts/build.ts 在构建时将版本号烘焙为字面量注入
|
||
|
||
版本升迁命令:
|
||
|
||
```bash
|
||
bun run version:patch # 升迁 patch 版本
|
||
bun run version:minor # 升迁 minor 版本
|
||
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、共享类型、配置契约、日志模块、版本管理或后端测试规范时,必须更新本文档。
|