Files
Alfred/docs/development/backend.md
lanyuanxiaoyao db40d04dc5 refactor(db): 统一数据库 schema — 软删除、命名规范、约束标准化
- 全表新增 deleted_at 列,统一软删除替代硬删除+archived_at
- models.model_id 重命名为 external_id,消除语义混淆
- conversations.model_id 改为可空(模型为建议而非绑定)
- messages 新增 updated_at,移除 CASCADE 改为 DAO 层级联
- 移除 DB 层 UNIQUE 约束,改为应用层检查(配合软删除)
- 新增 helpers.ts(baseColumns + 构造层防御)、ESLint 规则、契约测试
- 迁移 0004 补全 CHECK 约束(providers.type/materials.status/messages.role)
- DAO 层全面重写:级联软删除、应用层唯一、provider 删除保护
- 路由/前端/测试全量适配 externalId 重命名及类型变更
2026-06-05 01:02:23 +08:00

7.6 KiB
Raw Blame History

后端开发

开发规范见 开发规范文档

共享工具

src/server/helpers/:

  • response.tscreateApiError(error, status)createHeaders(mode, init)createMetaResponse(version)formatDuration(ms)jsonResponse(body, options)
  • url.tsparseIdFromUrl(url)
  • list-params.tsparseListParams(url, mode, options?) — 统一校验分页/排序参数,替代 validatePagination
  • pagination.tspaginateQuery() — Drizzle 分页查询封装

src/server/middleware/:

  • validate.tsvalidateIdParam(idStr, mode) — 校验 ID 格式(字母数字 + _-validatePagination(pageParam, pageSizeParam, mode) — page≥1, pageSize≤200validateTimeRange(from, to, mode)
  • error-handler.tsAppError — 业务异常类(含 statusCodewithErrorHandler(fn, mode, logger?) — 包裹 handler 捕获异常

数据库

SQLite + bun:sqlite + Drizzle ORM。

  • src/server/db/schema.tsDrizzle 表结构,列名 snake_caseTS 类型 camelCase。所有业务表通过 helpers.tsbaseColumns 获取 id/created_at/updated_at/deleted_at。
  • src/server/db/helpers.tsbaseColumns 常量id、createdAt、updatedAt、deletedAt+ Drizzle 构建器再导出。src/server/db/ 内禁止直接从 drizzle-orm/sqlite-core 导入 sqliteTableESLint 强制)。
  • src/server/db/connection.tscreateDatabase(dataDir, logger) 打开 alfred.dbPRAGMAforeign_keys=ON、journal_mode=WAL、busy_timeout=5000。wrap(db) 转 Drizzle 实例(DrizzleDB 类型)。工具函数:timestamp()notDeleted(table)softDeleteRecord(db, table, id)paginateQuery()(支持 softDelete 参数自动过滤已删除行)。
  • Migration开发期 drizzle-kit generate 产出到 drizzle/;生产期嵌入可执行文件,启动时自动应用。备份到 <dataDir>/backups/,事务中执行(迁移期间临时关闭外键检查),失败回滚。

软删除

所有业务表projects、providers、models、conversations、materials、messages使用 deleted_at 列实现软删除,不暴露给 API 层。DAO 查询通过 notDeleted(table)paginateQuery({ softDelete }) 自动过滤已删除行。唯一性校验在应用层完成(同名 + deleted_at IS NULL),无数据库级 UNIQUE 约束。级联软删除:删除项目 → 级联软删除会话(→ 消息)+ 素材;删除会话 → 级联软删除消息;删除供应商 → 需无未删除模型。

数据访问函数

文件 函数
projects.ts createProject、getProject、listProjects、updateProject、deleteProject、archiveProject、restoreProject
providers.ts createProvider、getProvider、listProviders、listProviderOptions、updateProvider、deleteProvider
models.ts createModel、getModel、listModels、getModelWithProvider、getModelsByProviderId、updateModel、deleteModel
conversations.ts createConversation、getConversation、listConversations、updateConversation、updateConversationTimestamp、deleteConversation、createMessage、createMessages、listMessages
materials.ts createMaterial、getMaterial、listMaterials、deleteMaterial

输入输出类型来自 src/shared/api.ts

AI 服务层

  • src/server/ai/types.tsAIProviderConfigname、type、baseUrl、apiKeyAIModelConfigproviderId、modelId、capabilities。注AI 层 modelId 对应 DB 层 Model.externalId
  • src/server/ai/registry.ts
    • buildProviderRegistry(db) — 从 DB 查询供应商构建 AI SDK Provider Registry每次调用重建不缓存。通过 registry.languageModel('providerId:modelId') 获取模型实例。
    • testProviderConnection(config, logger) — 测试 Base URL 可达性 + /models 接口
    • testModelConnection(config, logger) — 测试模型连通性(需传入含 modelId 的合并配置)
  • src/server/ai/agents/alfred-agent.tscreateAlfredAgent(model) — ToolLoopAgent + stepCountIs(20) + getCurrentTime 工具。
  • src/server/ai/tools/AI 工具定义。

供应商类型

type AI SDK factory
openai createOpenAI({ apiKey, baseURL })
anthropic createAnthropic({ apiKey, baseURL })
openai-compatible createOpenAICompatible({ name, apiKey, baseURL })

连通性测试

  • POST /api/providers/test — 用未保存配置测试,不写入 DB不阻止保存。Base URL 不可达或 API Key 无效返回 ok: false/models 不支持返回 ok: true + 提示。
  • POST /api/models/test — 用模型关联供应商 + externalId 测试。

素材 API

方法 路径 说明
GET /api/projects/:id/materials 列出项目下素材(分页)
POST /api/projects/:id/materials 创建素材
GET /api/projects/:id/materials/:mid 获取素材详情
DELETE /api/projects/:id/materials/:mid 删除素材(软删除)

校验description 必填非空associatedDate 必填 YYYY-MM-DD项目须存在且 active 且未删除,素材归属校验不匹配返回 403。

聊天 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 发送消息SSE 回复

send.ts:验证会话归属 → 保存用户消息 → createAlfredAgentcreateAgentUIStreamResponseonFinish 持久化 AI 回复。

日志

实现 用途
PinoLoggerWrapper 生产运行时
ConsoleFallbackLogger 配置加载前降级
NoopLogger 静默丢弃
MemoryLogger 测试替身

版本管理

唯一来源:package.json。开发模式从 package.json 运行时读取;生产模式构建时烘焙为字面量。

更新触发条件

修改后端模块 API、共享工具、数据库 schema、AI 服务层、聊天 API 或列表查询参数解析时,必须更新本文档。管理页面 CRUD 通用模式的详细约定见 crud.md