演示

添加新的数据库源

多数表应使用共享默认 DataSource。需要隔离时,在根模块上用 @Database 打开额外的 TypeORM 连接,将实体注册到对应的 plugin + name,并用 @InjectDatabaseSource 注入。

目标与何时使用

多数插件表应留在共享核心 Postgres。仅在需要单独主机、库、报表库或副本时添加自定义源。

  • 优先默认 — 用 plugin: "default" 注册实体(无需 @Database)
  • 自定义 — 仅在根 @Module 上使用 @Database({ name?, options })
  • 复用 — 其他插件注入所有者的连接,不要重复声明 @Database

1. 前置条件

需要可用的插件后端模块,以及通过环境变量(PluginEnv / base .env)提供的凭证。

  • 已有根 @Module({ name: metadata.name, … })
  • 主机、端口、用户名、密码、库名来自环境变量 — 不要硬编码密钥
  • 自定义源设置 synchronize: false,并用迁移交付 schema
  • 若注入其他插件的数据库,在 pluginDependencies 中列出所有者

2. 优先默认 DataSource

除非需要隔离,否则合并到共享连接。用 DataSourceManager.DEFAULT_PLUGIN(或字符串 "default")注入。

  • 表名仍必须以 metadata.name 开头
  • 扩展 BaseEntity 以获得创建 / 更新 / 软删除列
  • 无需 @Database — 核心已拥有 "default"
TSXentities on default
1@Module({ 2 name: metadata.name, 3 providers: [OrderService], 4 controllers: [OrderController], 5 entities: [ 6 { 7 plugin: "default", 8 entities: [OrderEntity], 9 }, 10 ], 11}) 12export class MyPluginModule {}
TSXinject default
1import { DataSource } from "typeorm"; 2import { 3 InjectDatabaseSource, 4 DataSourceManager, 5 Service, 6} from "@quan-erp/shared-backend-core"; 7 8@Service() 9export class OrderService { 10 @InjectDatabaseSource(DataSourceManager.DEFAULT_PLUGIN) 11 source: DataSource; 12}

3. 添加自定义 @Database

在根 @Module 上声明连接。用 plugin: metadata.name 与相同的可选 name 注册实体。用 @InjectDatabaseSource(metadata.name, name?) 注入。

  • @Database({ name?, options }) — 所有者由正在加载的插件填充
  • name — 同一插件打开多个连接时需要
  • options — TypeORM DataSourceOptions;凭证来自环境变量
  • 预发 / 生产自定义源使用 synchronize: false
  • 实体 — 匹配的 plugin + name;不要依赖 options.entities
TSXroot module — custom source
1import { 2 Database, 3 Module, 4} from "@quan-erp/shared-backend-core"; 5import metadata from "../../module.metadata.json" with { type: "json" }; 6import { AnalyticsEntity } from "./analytics.entity.js"; 7import { AnalyticsService } from "./analytics.service.js"; 8 9@Database({ 10 name: "analytics", 11 options: { 12 type: "postgres", 13 host: process.env.ANALYTICS_DB_HOST, 14 port: Number(process.env.ANALYTICS_DB_PORT ?? 5432), 15 username: process.env.ANALYTICS_DB_USERNAME, 16 password: process.env.ANALYTICS_DB_PASSWORD, 17 database: process.env.ANALYTICS_DB_SCHEMA, 18 synchronize: false, 19 }, 20}) 21@Module({ 22 name: metadata.name, 23 providers: [AnalyticsService], 24 controllers: [], 25 entities: [ 26 { 27 plugin: metadata.name, 28 name: "analytics", 29 entities: [AnalyticsEntity], 30 }, 31 ], 32}) 33export class MyPluginModule {}
TSinject custom
@InjectDatabaseSource(metadata.name, "analytics") analyticsSource: DataSource;

4. 跨插件注入

连接按所有者插件(+ 可选 name)登记在进程级注册表中。消费方注入所有者的 DataSource,并可按相同的 plugin + name 注册自己的实体。

  • 所有者 — 声明 @Database 并先启动(写入 pluginDependencies)
  • 消费方 — @InjectDatabaseSource("owner-plugin", "connection-name?")
  • 外部实体 — entities 使用 plugin: "owner-plugin" 与 name
  • 不要在每个消费方重复声明相同的 @Database
TSconsumer service
1@Service() 2export class ReportService { 3 // "warehouse" plugin declared @Database({ name: "replica", ... }) 4 @InjectDatabaseSource("warehouse", "replica") 5 warehouseReplica: DataSource; 6}

5. 验证

  • 插件启动无 DI / DataSource 错误
  • 注入的 DataSource 已定义且 getRepository(Entity) 可用
  • 实体出现在目标连接上(default vs 自定义 name)
  • 环境凭证在 Docker / 预发与本地一致

常见错误

  • 把 @Database 加在 service 或嵌套模块上 — 必须是根 @Module
  • 把自定义库实体注册为 plugin: "default"(或错误的 name)
  • 硬编码密码而不是 PluginEnv / base .env
  • 生产环境自定义源使用 synchronize: true
  • 在消费方重复声明 @Database 而不是用 @InjectDatabaseSource
  • 忘记 pluginDependencies,导致所有者在消费方之后加载