diff --git a/internal/infra/database/postgres.go b/internal/infra/database/postgres.go index 6837f62..0ce5090 100644 --- a/internal/infra/database/postgres.go +++ b/internal/infra/database/postgres.go @@ -120,16 +120,9 @@ func (ps *PostgresStorage) Migrate(models ...interface{}) error { ps.logger.Info("数据库表结构迁移完成") // -- 处理gorm做不到的初始化逻辑 -- - // 创建GIN索引(用于优化JSONB查询) - ps.logger.Info("正在为 sensor_data 表的 data 字段创建 GIN 索引") - // 使用 IF NOT EXISTS 保证幂等性 - // 如果索引已存在,此命令不会报错 - ginIndexSQL := "CREATE INDEX IF NOT EXISTS idx_sensor_data_data_gin ON sensor_data USING GIN (data);" - if err := ps.db.Exec(ginIndexSQL).Error; err != nil { - ps.logger.Errorw("为 sensor_data 的 data 字段创建 GIN 索引失败", "error", err) - return fmt.Errorf("为 sensor_data 的 data 字段创建 GIN 索引失败: %w", err) + if err := ps.creatingIndex(); err != nil { + return err } - ps.logger.Info("成功为 sensor_data 的 data 字段创建 GIN 索引 (或已存在)") // 如果是 TimescaleDB, 则将 sensor_data 转换为 hypertable if ps.isTimescaleDB { @@ -146,3 +139,27 @@ func (ps *PostgresStorage) Migrate(models ...interface{}) error { } return nil } + +// creatingIndex 用于创建gorm无法处理的索引, 如gin索引 +func (ps *PostgresStorage) creatingIndex() error { + // 创建GIN索引(用于优化JSONB查询) + ps.logger.Info("正在为 sensor_data 表的 data 字段创建 GIN 索引") + // 使用 IF NOT EXISTS 保证幂等性 + // 如果索引已存在,此命令不会报错 + ginIndexSQL := "CREATE INDEX IF NOT EXISTS idx_sensor_data_data_gin ON sensor_data USING GIN (data);" + if err := ps.db.Exec(ginIndexSQL).Error; err != nil { + ps.logger.Errorw("为 sensor_data 的 data 字段创建 GIN 索引失败", "error", err) + return fmt.Errorf("为 sensor_data 的 data 字段创建 GIN 索引失败: %w", err) + } + ps.logger.Info("成功为 sensor_data 的 data 字段创建 GIN 索引 (或已存在)") + + // 为 tasks.parameters 创建 GIN 索引 + ps.logger.Info("正在为 tasks 表的 parameters 字段创建 GIN 索引") + taskGinIndexSQL := "CREATE INDEX IF NOT EXISTS idx_tasks_parameters_gin ON tasks USING GIN (parameters);" + if err := ps.db.Exec(taskGinIndexSQL).Error; err != nil { + ps.logger.Errorw("为 tasks 的 parameters 字段创建 GIN 索引失败", "error", err) + return fmt.Errorf("为 tasks 的 parameters 字段创建 GIN 索引失败: %w", err) + } + ps.logger.Info("成功为 tasks 的 parameters 字段创建 GIN 索引 (或已存在)") + return nil +} diff --git a/internal/infra/models/plan.go b/internal/infra/models/plan.go index 1d46c4c..516f907 100644 --- a/internal/infra/models/plan.go +++ b/internal/infra/models/plan.go @@ -53,8 +53,8 @@ type Plan struct { Name string `gorm:"not null" json:"name"` Description string `json:"description"` - ExecutionType PlanExecutionType `gorm:"not null" json:"execution_type"` - Status PlanStatus `gorm:"default:0" json:"status"` // 计划是否被启动 + ExecutionType PlanExecutionType `gorm:"not null;index" json:"execution_type"` + Status PlanStatus `gorm:"default:0;index" json:"status"` // 计划是否被启动 ExecuteNum uint `gorm:"default:0" json:"execute_num"` // 计划预期执行次数 ExecuteCount uint `gorm:"default:0" json:"execute_count"` // 执行计数器 diff --git a/internal/infra/models/schedule.go b/internal/infra/models/schedule.go index 40e43b1..12a6384 100644 --- a/internal/infra/models/schedule.go +++ b/internal/infra/models/schedule.go @@ -20,8 +20,8 @@ type PendingTask struct { // GORM 会根据 TaskID 字段自动填充此关联 Task *Task `gorm:"foreignKey:TaskID"` - ExecuteAt time.Time `gorm:"index"` // 任务执行时间 - TaskExecutionLogID uint `gorm:"unique;not null"` // 对应的执行历史记录ID + ExecuteAt time.Time `gorm:"index"` // 任务执行时间 + TaskExecutionLogID uint `gorm:"unique;not null;index"` // 对应的执行历史记录ID // 通过 TaskExecutionLogID 关联到唯一的 TaskExecutionLog 记录 // ON DELETE CASCADE 确保如果日志被删除,这个待办任务也会被自动清理