diff --git a/internal/core/component_initializers.go b/internal/core/component_initializers.go index d9c6659..9fbc395 100644 --- a/internal/core/component_initializers.go +++ b/internal/core/component_initializers.go @@ -24,6 +24,7 @@ import ( "git.huangwc.com/pig/pig-farm-controller/internal/infra/utils/token" "gorm.io/gorm" + "gorm.io/gorm/schema" ) // Infrastructure 聚合了所有基础设施层的组件。 @@ -421,6 +422,8 @@ func initNotifyService( // initStorage 封装了数据库的初始化、连接和迁移逻辑。 func initStorage(ctx context.Context, cfg config.DatabaseConfig) (database.Storage, error) { + logger := logs.GetLogger(ctx) + // 创建存储实例 storage := database.NewStorage(logs.AddCompName(context.Background(), "Storage"), cfg) if err := storage.Connect(ctx); err != nil { @@ -428,8 +431,20 @@ func initStorage(ctx context.Context, cfg config.DatabaseConfig) (database.Stora return nil, fmt.Errorf("数据库连接失败: %w", err) } + // 获取所有模型 + allModels := models.GetAllModels() + + // -- 启动时检查:确保所有模型都实现了 schema.Tabler 接口 -- + // 这是一个硬性要求,用于保证代码质量和表名定义的明确性。 + // 如果一个模型没有实现 TableName() string 方法,程序将在此处 panic。 + for _, model := range allModels { + if _, ok := model.(schema.Tabler); !ok { + logger.Panicf(fmt.Sprintf("启动失败:模型 %T 未实现 schema.Tabler 接口。请为该模型添加 TableName() string 方法,以显式指定其数据库表名。", model)) + } + } + // 执行数据库迁移 - if err := storage.Migrate(ctx, models.GetAllModels()...); err != nil { + if err := storage.Migrate(ctx, allModels...); err != nil { return nil, fmt.Errorf("数据库迁移失败: %w", err) } diff --git a/internal/infra/database/postgres.go b/internal/infra/database/postgres.go index b2a513b..4d2647f 100644 --- a/internal/infra/database/postgres.go +++ b/internal/infra/database/postgres.go @@ -263,18 +263,26 @@ func (ps *PostgresStorage) creatingIndex(ctx context.Context) error { return nil } +// uniqueIndexDefinition 结构体定义了唯一索引的详细信息 +type uniqueIndexDefinition struct { + tableName string // 索引所属的表名 + columns []string // 构成唯一索引的列名 + indexName string // 唯一索引的名称 + whereClause string // 可选的 WHERE 子句,用于创建部分索引 + description string // 索引的描述,用于日志记录 +} + +// ginIndexDefinition 结构体定义了 GIN 索引的详细信息 +type ginIndexDefinition struct { + tableName string // 索引所属的表名 + columnName string // 需要创建 GIN 索引的列名 + indexName string // GIN 索引的名称 + description string // 索引的描述,用于日志记录 +} + func (ps *PostgresStorage) creatingUniqueIndex(ctx context.Context) error { storageCtx, logger := logs.Trace(ctx, ps.ctx, "creatingUniqueIndex") - // uniqueIndexDefinition 结构体定义了唯一索引的详细信息 - type uniqueIndexDefinition struct { - tableName string // 索引所属的表名 - columns []string // 构成唯一索引的列名 - indexName string // 唯一索引的名称 - whereClause string // 可选的 WHERE 子句,用于创建部分索引 - description string // 索引的描述,用于日志记录 - } - // 定义所有需要创建的唯一索引 uniqueIndexesToCreate := []uniqueIndexDefinition{ { @@ -392,14 +400,6 @@ func (ps *PostgresStorage) creatingUniqueIndex(ctx context.Context) error { func (ps *PostgresStorage) createGinIndexes(ctx context.Context) error { storageCtx, logger := logs.Trace(ctx, ps.ctx, "createGinIndexes") - // ginIndexDefinition 结构体定义了 GIN 索引的详细信息 - type ginIndexDefinition struct { - tableName string // 索引所属的表名 - columnName string // 需要创建 GIN 索引的列名 - indexName string // GIN 索引的名称 - description string // 索引的描述,用于日志记录 - } - // 定义所有需要创建的 GIN 索引 ginIndexesToCreate := []ginIndexDefinition{ {