定义猪的模型和营养需求模型

This commit is contained in:
2025-11-20 14:38:36 +08:00
parent c697e668e3
commit 6ca101727a
6 changed files with 121 additions and 4 deletions

View File

@@ -250,9 +250,20 @@ func (ps *PostgresStorage) applyCompressionPolicies(ctx context.Context) error {
// creatingIndex 用于创建gorm无法处理的索引, 如gin索引
func (ps *PostgresStorage) creatingIndex(ctx context.Context) error {
storageCtx, logger := logs.Trace(ctx, ps.ctx, "creatingIndex")
storageCtx := logs.AddFuncName(ctx, ps.ctx, "creatingIndex")
// 使用 IF NOT EXISTS 保证幂等性
// 如果索引已存在,此命令不会报错
if err := ps.creatingUniqueIndex(storageCtx); err != nil {
return err
}
if err := ps.createGinIndexes(storageCtx); err != nil {
return err
}
return nil
}
func (ps *PostgresStorage) creatingUniqueIndex(ctx context.Context) error {
storageCtx, logger := logs.Trace(ctx, ps.ctx, "creatingUniqueIndex")
// 为 raw_material_nutrients 表创建部分唯一索引,以兼容软删除
logger.Debug("正在为 raw_material_nutrients 表创建部分唯一索引")
@@ -263,6 +274,47 @@ func (ps *PostgresStorage) creatingIndex(ctx context.Context) error {
}
logger.Debug("成功为 raw_material_nutrients 创建部分唯一索引 (或已存在)")
// 为 pig_breeds 表创建部分唯一索引,以兼容软删除 (name 唯一)
logger.Debug("正在为 pig_breeds 表创建部分唯一索引")
partialIndexSQL = "CREATE UNIQUE INDEX IF NOT EXISTS idx_pig_breeds_unique_name_when_not_deleted ON pig_breeds (name) WHERE deleted_at IS NULL;"
if err := ps.db.WithContext(storageCtx).Exec(partialIndexSQL).Error; err != nil {
logger.Errorw("为 pig_breeds 创建部分唯一索引失败", "error", err)
return fmt.Errorf("为 pig_breeds 创建部分唯一索引失败: %w", err)
}
logger.Debug("成功为 pig_breeds 创建部分唯一索引 (或已存在)")
// 为 pig_age_stages 表创建部分唯一索引,以兼容软删除 (name 唯一)
logger.Debug("正在为 pig_age_stages 表创建部分唯一索引")
partialIndexSQL = "CREATE UNIQUE INDEX IF NOT EXISTS idx_pig_age_stages_unique_name_when_not_deleted ON pig_age_stages (name) WHERE deleted_at IS NULL;"
if err := ps.db.WithContext(storageCtx).Exec(partialIndexSQL).Error; err != nil {
logger.Errorw("为 pig_age_stages 创建部分唯一索引失败", "error", err)
return fmt.Errorf("为 pig_age_stages 创建部分唯一索引失败: %w", err)
}
logger.Debug("成功为 pig_age_stages 创建部分唯一索引 (或已存在)")
// 为 pig_types 表创建部分唯一索引,以兼容软删除 (breed_id, age_stage_id 组合唯一)
logger.Debug("正在为 pig_types 表创建部分唯一索引")
partialIndexSQL = "CREATE UNIQUE INDEX IF NOT EXISTS idx_pig_types_unique_breed_age_stage_when_not_deleted ON pig_types (breed_id, age_stage_id) WHERE deleted_at IS NULL;"
if err := ps.db.WithContext(storageCtx).Exec(partialIndexSQL).Error; err != nil {
logger.Errorw("为 pig_types 创建部分唯一索引失败", "error", err)
return fmt.Errorf("为 pig_types 创建部分唯一索引失败: %w", err)
}
logger.Debug("成功为 pig_types 创建部分唯一索引 (或已存在)")
// 为 pig_nutrient_requirements 表创建部分唯一索引,以兼容软删除 (pig_type_id, nutrient_id 组合唯一)
logger.Debug("正在为 pig_nutrient_requirements 表创建部分唯一索引")
partialIndexSQL = "CREATE UNIQUE INDEX IF NOT EXISTS idx_pig_nutrient_requirements_unique_type_nutrient_when_not_deleted ON pig_nutrient_requirements (pig_type_id, nutrient_id) WHERE deleted_at IS NULL;"
if err := ps.db.WithContext(storageCtx).Exec(partialIndexSQL).Error; err != nil {
logger.Errorw("为 pig_nutrient_requirements 创建部分唯一索引失败", "error", err)
return fmt.Errorf("为 pig_nutrient_requirements 创建部分唯一索引失败: %w", err)
}
logger.Debug("成功为 pig_nutrient_requirements 创建部分唯一索引 (或已存在)")
return nil
}
func (ps *PostgresStorage) createGinIndexes(ctx context.Context) error {
storageCtx, logger := logs.Trace(ctx, ps.ctx, "createGinIndexes")
// 为 sensor_data 表的 data 字段创建 GIN 索引
logger.Debug("正在为 sensor_data 表的 data 字段创建 GIN 索引")
ginSensorDataIndexSQL := "CREATE INDEX IF NOT EXISTS idx_sensor_data_data_gin ON sensor_data USING GIN (data);"
@@ -280,6 +332,5 @@ func (ps *PostgresStorage) creatingIndex(ctx context.Context) error {
return fmt.Errorf("为 tasks 的 parameters 字段创建 GIN 索引失败: %w", err)
}
logger.Debug("成功为 tasks 的 parameters 字段创建 GIN 索引 (或已存在)")
return nil
}

View File

@@ -56,6 +56,10 @@ func GetAllModels() []interface{} {
&WeighingRecord{},
&PigTransferLog{},
&PigSickLog{},
&PigBreed{},
&PigAgeStage{},
&PigType{},
&PigNutrientRequirement{},
// Pig Buy & Sell
&PigPurchase{},
@@ -119,7 +123,7 @@ func (a *UintArray) Scan(src interface{}) error {
case string:
srcStr = v
default:
return errors.New("无法扫描非字符串或字节类型的源到 UintArray")
return errors.New("无法将值 %v (类型 %T) 扫描为 UintArray")
}
// 去掉花括号

View File

@@ -0,0 +1,43 @@
package models
// PigBreed 猪品种模型
type PigBreed struct {
Model
Name string `gorm:"size:50;not null;comment:品种名称"`
Description string `gorm:"size:255;comment:品种描述"`
}
func (PigBreed) TableName() string {
return "pig_breeds"
}
// PigAgeStage 猪年龄阶段模型
type PigAgeStage struct {
Model
Name string `gorm:"size:50;not null;comment:年龄阶段名称 (如: 仔猪, 生长猪, 育肥猪)"`
Description string `gorm:"size:255;comment:阶段描述"`
}
func (PigAgeStage) TableName() string {
return "pig_age_stages"
}
// PigType 猪类型模型,代表特定品种和年龄阶段的组合
type PigType struct {
Model
BreedID uint32 `gorm:"not null;index;comment:关联的猪品种ID"`
Breed PigBreed `gorm:"foreignKey:BreedID"`
AgeStageID uint32 `gorm:"not null;index;comment:关联的猪年龄阶段ID"`
AgeStage PigAgeStage `gorm:"foreignKey:AgeStageID"`
Description string `gorm:"size:255;comment:该猪类型的描述或特点"`
DailyFeedIntake float32 `gorm:"comment:理论日均食量 (g/天)"`
DailyGainWeight float32 `gorm:"comment:理论日增重 (g/天)"`
MinDays uint32 `gorm:"comment:该猪类型在该年龄阶段的最小日龄"`
MaxDays uint32 `gorm:"comment:该猪类型在该年龄阶段的最大日龄"`
MinWeight float32 `gorm:"comment:该猪类型在该年龄阶段的最小体重 (g)"`
MaxWeight float32 `gorm:"comment:该猪类型在该年龄阶段的最大体重 (g)"`
}
func (PigType) TableName() string {
return "pig_types"
}

View File

@@ -0,0 +1,16 @@
package models
// PigNutrientRequirement 猪营养需求模型
type PigNutrientRequirement struct {
Model
PigTypeID uint32 `gorm:"not null;index;comment:关联的猪类型ID"`
PigType PigType `gorm:"foreignKey:PigTypeID"`
NutrientID uint32 `gorm:"not null;index;comment:关联的营养素ID"`
Nutrient Nutrient `gorm:"foreignKey:NutrientID"`
MinRequirement float32 `gorm:"not null;comment:最低营养需求量"`
MaxRequirement float32 `gorm:"not null;comment:最高营养需求量"`
}
func (PigNutrientRequirement) TableName() string {
return "pig_nutrient_requirements"
}