From 47b8c5bc651f27ce4763d8ea86fc3c4326b37c42 Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Wed, 24 Sep 2025 16:34:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20timescaledb=20=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91=E5=92=8Cgin=E7=B4=A2=E5=BC=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/infra/database/postgres.go | 26 ++++++++++++++++++++++++++ internal/infra/models/SensorData.go | 8 ++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/internal/infra/database/postgres.go b/internal/infra/database/postgres.go index 5d77cf9..6837f62 100644 --- a/internal/infra/database/postgres.go +++ b/internal/infra/database/postgres.go @@ -118,5 +118,31 @@ func (ps *PostgresStorage) Migrate(models ...interface{}) error { return fmt.Errorf("数据库表结构迁移失败: %w", err) } 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) + } + ps.logger.Info("成功为 sensor_data 的 data 字段创建 GIN 索引 (或已存在)") + + // 如果是 TimescaleDB, 则将 sensor_data 转换为 hypertable + if ps.isTimescaleDB { + ps.logger.Info("检测到 TimescaleDB, 准备转换 sensor_data 为超表") + // 使用 if_not_exists => TRUE 保证幂等性 + // 如果 sensor_data 已经是超表,此命令不会报错 + // 'time' 是 SensorData 模型中定义的时间列 + sql := "SELECT create_hypertable('sensor_data', 'time', if_not_exists => TRUE);" + if err := ps.db.Exec(sql).Error; err != nil { + ps.logger.Errorw("将 sensor_data 转换为超表失败", "error", err) + return fmt.Errorf("将 sensor_data 转换为超表失败: %w", err) + } + ps.logger.Info("成功将 sensor_data 转换为超表 (或已转换)") + } return nil } diff --git a/internal/infra/models/SensorData.go b/internal/infra/models/SensorData.go index 1fdb548..0697553 100644 --- a/internal/infra/models/SensorData.go +++ b/internal/infra/models/SensorData.go @@ -10,19 +10,19 @@ import ( type SensorData struct { // Time 是数据记录的时间戳,作为复合主键的一部分。 // GORM 会将其映射到 'time' TIMESTAMPTZ 列。 - Time time.Time `gorm:"primaryKey"` + Time time.Time `gorm:"primaryKey" json:"time"` // DeviceID 是传感器的唯一标识符,作为复合主键的另一部分。 // GORM 会将其映射到 'device_id' VARCHAR(50) 列。 - DeviceID string `gorm:"primaryKey;size:50"` + DeviceID uint `gorm:"primaryKey" json:"device_id"` // RegionalControllerID 是上报此数据的区域主控的ID。 // 我们为其添加了数据库索引以优化按区域查询的性能。 - RegionalControllerID string `gorm:"size:50;index"` + RegionalControllerID uint `json:"regional_controller_id"` // Data 存储一个或多个传感器读数,格式为 JSON。 // GORM 会使用 'jsonb' 类型来创建此列。 - Data datatypes.JSON `gorm:"type:jsonb"` + Data datatypes.JSON `gorm:"type:jsonb" json:"data"` } func (SensorData) TableName() string {