Skip to content

数据库与迁移

项目使用 PostgreSQL + Drizzle ORM 管理数据库结构与迁移。Server 工作区的 Drizzle 配置在 packages/server/drizzle.config.ts,schema 入口是 packages/server/src/db/schema.ts,迁移目录是 packages/server/drizzle/

默认连接

.env 通过 DATABASE_URL 配置数据库连接:

ini
DATABASE_URL=postgres://postgres:postgres@localhost:5432/zenith_admin

运行时连接池参数由 config.database 控制,db/index.ts 使用 postgres + drizzle-orm/postgres-js 创建单例连接。

迁移流程

修改 packages/server/src/db/schema/ 后,在仓库根目录执行:

bash
npm run db:generate
npm run db:migrate

如需初始化演示 / 内置数据:

bash
npm run db:seed

根目录脚本会转发到 @zenith/server

根目录脚本Server 脚本
npm run db:generatenpm run db:generate -w @zenith/serverdrizzle-kit generate
npm run db:migratenpm run db:migrate -w @zenith/servertsx src/db/migrate.ts
npm run db:seednpm run db:seed -w @zenith/servertsx src/db/seed.ts

迁移入口 packages/server/src/db/migrate.ts 使用 Drizzle migrator 执行 ./drizzle。开发、生产和容器启动链路都会先执行迁移再启动服务;迁移失败以非零码退出,阻断服务启动。

重要约定

迁移文件来源

结构变更先改 src/db/schema/,再由 drizzle-kit generate 生成迁移 SQL。不要手工改写已生成迁移来适配代码。仅 Drizzle schema 无法表达的 DDL 可使用 custom migration,例如扩展、表达式索引、条件 DDL。

迁移目录

packages/server/drizzle/ 包含 0000_baseline.sql0001_extensions.sql 和后续增量迁移,执行顺序由 drizzle/meta/_journal.json 管理。全新数据库执行 npm run db:migrate 会按该顺序建库。

0001_extensions.sql 收口维护 Drizzle schema 无法表达的手写 DDL,当前仅一项:

  • 条件启用 pgvector 的 ai_kb_chunks.embedding_vec 列(条件 DDL、扩展创建与无维度 vector 列均超出 Drizzle 表达范围,且该列刻意不进 schema——无 pgvector 的部署必须照常工作);运行时通过 hasPgVector() 探测,不可用时回退 JS 余弦相似度。

pg_trgm 扩展在 0000_baseline.sql 顶部创建;trigram 索引(含 async_tasks.payload/result 的「表达式 + gin_trgm_ops」形态)已全部收进 schema DSL,由 drizzle-kit generate 随基线生成。

后续新增无法表达的 DDL 时,用 drizzle-kit generate --custom 建独立迁移;重建基线时将其内容并回 0001_extensions.sql

枚举同步

枚举必须保持三端一致:

  • PostgreSQL pgEnum
  • @zenith/shared/{domain} 中的 TS union / 常量数组;
  • Zod enum。

可被其他域复用的枚举常量放在 shared/src/{domain}/constants.ts,不要放在 validation.ts 中制造 ESM 值循环。

LIKE 查询转义

使用 like() / ilike() 拼接用户输入时必须调用 escapeLike();跨列关键字搜索优先用 keywordCondition()

ts
import { escapeLike } from '../lib/where-helpers';

like(users.username, `%${escapeLike(keyword)}%`);

Schema 组织(按业务域拆分)

全库约 375 张表,schema 按业务域拆分在 packages/server/src/db/schema/src/db/schema.ts 是 barrel,业务代码导入方式保持:

ts
import { users, roles } from '../db/schema';

表间关联统一声明在 schema/relations.ts;数据库类型别名在 src/db/types.ts

Schema 文件业务域代表性表
core.ts租户 / 组织 / 权限tenantstenant_packagesdepartmentspositionsusersmenusrolesuser_rolesuser_groups
licensing.ts授权许可system_installationslicenseslicense_events
auth.ts认证与账号安全user_oauth_accountsoauth_configsuser_api_tokenspassword_reset_tokensuser_mfa_factorsuser_trusted_deviceslogin_risk_eventsrate_limit_rules
identity-providers.ts企业 SSOtenant_identity_providersuser_identity_accountsidentity_provider_sync_logs
directory-sync.ts通讯录同步directory_sync_sourcesdirectory_sync_runsdirectory_sync_run_itemsdirectory_sync_conflictsdirectory_sync_user_linksdirectory_sync_dept_links
system.ts系统配置与调度system_configscron_jobscron_job_logssystem_scheduler_*retention_policiesregionsmaintenance_modeuser_feedbacks
dicts.ts数据字典dictsdict_items
files.ts文件存储file_storage_configsmanaged_filesupload_sessionsupload_chunksbusiness_files
logs.ts审计日志login_logsoperation_logsip_access_logs
announcements.ts通知公告announcementsannouncement_readsannouncement_recipients
messaging.ts邮件 / 短信 / 站内信email_configsemail_templatesemail_send_logssms_*in_app_*、通知策略与偏好表
channels.ts消息渠道频道、订阅、消息、菜单、自动回复、客服会话等表
tasks.ts任务中心 / 导出中心async_tasksasync_task_itemsasync_task_type_configsexport_jobsexport_job_downloads
db-admin.ts数据库运维db_backupsdb_admin_query_historydb_query_favorites
monitor.ts监控告警system_metric_samplesmonitor_alert_rulesmonitor_alert_eventsssl_certificates
terminal.ts终端 / SSHterminal_sessionsterminal_recordingsssh_profiles
data-mask.ts数据脱敏data_mask_configs
tags.ts通用标签tags
workflow.ts工作流流程分类、表单、定义、版本、实例、任务、作业、事件订阅、调度、健康快照等表
payment.ts支付中心应用、订单、退款、回调、事件、对账、分账、结算、风控、合约等表
member.ts会员体系membersmember_levelsmember_tags、积分 / 钱包账户与流水、优惠券、签到、充值、登录日志
chat.ts聊天会话、成员、消息、反应、收藏、Webhook、快捷回复、定时消息、坐席等表
ai.tsAI提供方配置、会话、消息、提示词、知识库、评测、Arena、分享等表
analytics.ts埋点分析 / 前端错误事件、身份映射、会话、聚合、Tracking Plan、实验、错误组、错误事件、Source Map、告警历史
report.ts / report-platform.ts报表中心文件夹、数据源、数据集、仪表盘、订阅、投递、打印、质量规则、资产、填报等表
cms.tsCMS站点、模型、栏目、内容、素材、发布、采集、评论、页面搭建、表单、订阅、互动等表
mp.ts微信公众号账号、粉丝、标签、菜单、素材、群发、模板消息、客服、网页授权等表
open-platform.ts开放平台OAuth2 客户端、授权、Token、API Scope、限流套餐、调用日志、统计、Webhook 等表
rules.ts规则引擎决策表、版本、测试用例、执行记录、资产版本、决策流、名单库
biz.ts业务示例biz_leavesbiz_pay_demos
app-releases.ts应用发布client_appsapp_releasesapp_artifactsapp_release_events
wiki.ts知识中心空间、成员、文档、版本、模板、标签、评论、导入导出、治理表
common.ts公共枚举无表,提供 statusEnum 等跨域共享枚举
relations.ts关联关系无表,统一声明全部 xxxRelations

新增表时在对应域文件声明 pgTable,关联写进 relations.ts,新建域文件时同步 src/db/schema.ts re-export。

通用审计字段(created_by / updated_by

业务主表通过 auditColumns() 展开 created_by / updated_by。赋值由 db/index.ts 的 Proxy 统一注入:

  • runAsUser(userId, fn) 覆盖优先;
  • 其次读取请求上下文中的 currentUserOrNull()
  • 没有可用身份时写入 null
  • 拦截 db.insert(table).values(...)db.update(table).set(...)db.insert(...).onConflictDoUpdate({ set }),事务内 tx 同样生效。

Service、route、seed、cron 不手动赋值 createdBy / updatedBy。需要指定操作人时使用:

ts
import { runAsUser } from '../lib/audit-context';

await runAsUser(adminId, async () => {
  await db.insert(xxxs).values(data);
});

典型不加审计列的表:纯关联表、追加型日志、临时凭证、IM 消息、天然已有操作者语义的运行时表。

数据库备份

系统内置数据库备份功能,路由在 packages/server/src/routes/ops/db-backups.ts,服务在 services/ops/db-backups.service.tslib/db-backup.ts

菜单入口

系统设置 → 数据库备份(路由 /system/db-backups,权限 system:db-backup:list)。

操作说明

  • 立即备份:创建 pg_dump 完整 SQL 压缩备份或 Drizzle 逻辑 JSON 导出。
  • 删除备份:删除指定备份记录。
  • 文件归档:配置默认 file_storage_configs 后,备份文件保存到文件存储,并在 db_backups.file_id 记录 managed_files.id

前置条件

使用 pg_dump 类型时,服务器环境必须安装 PostgreSQL 客户端工具,并保证版本与数据库服务端兼容。

Built with VitePress for local documentation preview.