package repository import ( "context" "errors" "fmt" "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" "gorm.io/gorm" ) // NutrientRepository 定义了与营养种类相关的数据库操作接口 type NutrientRepository interface { CreateNutrient(ctx context.Context, nutrient *models.Nutrient) error GetNutrientByID(ctx context.Context, id uint32) (*models.Nutrient, error) GetNutrientByName(ctx context.Context, name string) (*models.Nutrient, error) ListNutrients(ctx context.Context, page, pageSize int) ([]models.Nutrient, int64, error) UpdateNutrient(ctx context.Context, nutrient *models.Nutrient) error DeleteNutrient(ctx context.Context, id uint32) error } // gormNutrientRepository 是 NutrientRepository 的 GORM 实现 type gormNutrientRepository struct { ctx context.Context db *gorm.DB } // NewGormNutrientRepository 创建一个新的 NutrientRepository GORM 实现实例 func NewGormNutrientRepository(ctx context.Context, db *gorm.DB) NutrientRepository { return &gormNutrientRepository{ctx: ctx, db: db} } // CreateNutrient 创建一个新的营养种类 func (r *gormNutrientRepository) CreateNutrient(ctx context.Context, nutrient *models.Nutrient) error { repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateNutrient") return r.db.WithContext(repoCtx).Create(nutrient).Error } // GetNutrientByID 根据ID获取单个营养种类,并预加载关联的原料信息 func (r *gormNutrientRepository) GetNutrientByID(ctx context.Context, id uint32) (*models.Nutrient, error) { repoCtx := logs.AddFuncName(ctx, r.ctx, "GetNutrientByID") var nutrient models.Nutrient // 如果记录未找到,GORM 会返回 gorm.ErrRecordNotFound 错误 if err := r.db.WithContext(repoCtx).Preload("RawMaterialNutrients.RawMaterial").First(&nutrient, id).Error; err != nil { return nil, err } return &nutrient, nil } // GetNutrientByName 根据名称获取单个营养种类,并预加载关联的原料信息 func (r *gormNutrientRepository) GetNutrientByName(ctx context.Context, name string) (*models.Nutrient, error) { repoCtx := logs.AddFuncName(ctx, r.ctx, "GetNutrientByName") var nutrient models.Nutrient // 如果记录未找到,GORM 会返回 gorm.ErrRecordNotFound 错误 if err := r.db.WithContext(repoCtx).Preload("RawMaterialNutrients.RawMaterial").Where("name = ?", name).First(&nutrient).Error; err != nil { return nil, err } return &nutrient, nil } // ListNutrients 列出所有营养种类(分页),并预加载关联的原料信息 func (r *gormNutrientRepository) ListNutrients(ctx context.Context, page, pageSize int) ([]models.Nutrient, int64, error) { repoCtx := logs.AddFuncName(ctx, r.ctx, "ListNutrients") var nutrients []models.Nutrient var total int64 db := r.db.WithContext(repoCtx).Model(&models.Nutrient{}) // 首先计算总数 if err := db.Count(&total).Error; err != nil { return nil, 0, err } // 然后应用分页并获取数据 offset := (page - 1) * pageSize if err := db.Preload("RawMaterialNutrients.RawMaterial").Offset(offset).Limit(pageSize).Find(&nutrients).Error; err != nil { return nil, 0, err } return nutrients, total, nil } // UpdateNutrient 更新一个营养种类 func (r *gormNutrientRepository) UpdateNutrient(ctx context.Context, nutrient *models.Nutrient) error { repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdateNutrient") // 使用 map 更新以避免 GORM 的零值问题,并确保只更新指定字段 updateData := map[string]interface{}{ "name": nutrient.Name, "description": nutrient.Description, } result := r.db.WithContext(repoCtx).Model(&models.Nutrient{}).Where("id = ?", nutrient.ID).Updates(updateData) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return fmt.Errorf("未找到要更新的营养种类,ID: %d", nutrient.ID) } return nil } // DeleteNutrient 根据ID删除一个营养种类,并级联软删除关联的 RawMaterialNutrient 记录 func (r *gormNutrientRepository) DeleteNutrient(ctx context.Context, id uint32) error { repoCtx := logs.AddFuncName(ctx, r.ctx, "DeleteNutrient") return r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error { // 1. 查找 Nutrient 记录,确保其存在 var nutrient models.Nutrient if err := tx.First(&nutrient, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("未找到要删除的营养种类,ID: %d", id) } return fmt.Errorf("查询营养种类失败: %w", err) } // 2. 软删除所有关联的 RawMaterialNutrient 记录 if err := tx.Where("nutrient_id = ?", id).Delete(&models.RawMaterialNutrient{}).Error; err != nil { return fmt.Errorf("软删除关联的原料营养素记录失败: %w", err) } // 3. 软删除 Nutrient 记录本身 if err := tx.Delete(&nutrient).Error; err != nil { return fmt.Errorf("软删除营养种类失败: %w", err) } return nil }) }