Files
pig-farm-controller/internal/infra/repository/recipe_repository.go

191 lines
7.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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"
)
// RecipeListOptions 定义了查询配方列表时的筛选条件
type RecipeListOptions struct {
Name *string
RawMaterialName *string
OrderBy string
}
// RecipeRepository 定义了与配方相关的数据库操作接口
type RecipeRepository interface {
CreateRecipe(ctx context.Context, recipe *models.Recipe) error
GetRecipeByID(ctx context.Context, id uint32) (*models.Recipe, error)
GetRecipeByName(ctx context.Context, name string) (*models.Recipe, error)
ListRecipes(ctx context.Context, opts RecipeListOptions, page, pageSize int) ([]models.Recipe, int64, error)
UpdateRecipe(ctx context.Context, recipe *models.Recipe) error
DeleteRecipe(ctx context.Context, id uint32) error
// 在事务中删除配方原料
DeleteRecipeIngredientsByRecipeIDTx(ctx context.Context, tx *gorm.DB, recipeID uint32) error
// 在事务中批量创建配方原料
CreateBatchRecipeIngredientsTx(ctx context.Context, tx *gorm.DB, ingredients []models.RecipeIngredient) error
}
// gormRecipeRepository 是 RecipeRepository 的 GORM 实现
type gormRecipeRepository struct {
ctx context.Context
db *gorm.DB
}
// NewGormRecipeRepository 创建一个新的 RecipeRepository GORM 实现实例
func NewGormRecipeRepository(ctx context.Context, db *gorm.DB) RecipeRepository {
return &gormRecipeRepository{ctx: ctx, db: db}
}
// CreateRecipe 创建一个新的配方,并处理其关联的配方原料
func (r *gormRecipeRepository) CreateRecipe(ctx context.Context, recipe *models.Recipe) error {
repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateRecipe")
// 注意:这里的事务只针对 Recipe 本身,如果 RecipeIngredient 也在同一个 Create 中GORM 会自动处理。
// 但如果 RecipeIngredient 是单独操作,则需要外部事务。
if err := r.db.WithContext(repoCtx).Create(recipe).Error; err != nil {
return fmt.Errorf("创建配方失败: %w", err)
}
return nil
}
// GetRecipeByID 根据ID获取单个配方并预加载其关联的配方原料和原料信息
func (r *gormRecipeRepository) GetRecipeByID(ctx context.Context, id uint32) (*models.Recipe, error) {
repoCtx := logs.AddFuncName(ctx, r.ctx, "GetRecipeByID")
var recipe models.Recipe
// 如果记录未找到GORM 会返回 gorm.ErrRecordNotFound 错误
if err := r.db.WithContext(repoCtx).Preload("RecipeIngredients.RawMaterial.RawMaterialNutrients.Nutrient").First(&recipe, id).Error; err != nil {
return nil, err
}
return &recipe, nil
}
// GetRecipeByName 根据名称获取单个配方,并预加载其关联的配方原料和原料信息
func (r *gormRecipeRepository) GetRecipeByName(ctx context.Context, name string) (*models.Recipe, error) {
repoCtx := logs.AddFuncName(ctx, r.ctx, "GetRecipeByName")
var recipe models.Recipe
// 如果记录未找到GORM 会返回 gorm.ErrRecordNotFound 错误
if err := r.db.WithContext(repoCtx).Preload("RecipeIngredients.RawMaterial.RawMaterialNutrients.Nutrient").Where("name = ?", name).First(&recipe).Error; err != nil {
return nil, err
}
return &recipe, nil
}
// ListRecipes 列出所有配方(分页),并预加载其关联的配方原料和原料信息
func (r *gormRecipeRepository) ListRecipes(ctx context.Context, opts RecipeListOptions, page, pageSize int) ([]models.Recipe, int64, error) {
repoCtx := logs.AddFuncName(ctx, r.ctx, "ListRecipes")
var recipes []models.Recipe
var total int64
db := r.db.WithContext(repoCtx).Model(&models.Recipe{})
// 应用筛选条件
if opts.Name != nil && *opts.Name != "" {
db = db.Where("name LIKE ?", "%"+*opts.Name+"%")
}
// 如果传入了原料名称,则使用子查询进行筛选
if opts.RawMaterialName != nil && *opts.RawMaterialName != "" {
subQuery := r.db.Model(&models.RecipeIngredient{}).
Select("recipe_id").
Joins("JOIN raw_materials ON raw_materials.id = recipe_ingredients.raw_material_id").
Where("raw_materials.name LIKE ?", "%"+*opts.RawMaterialName+"%")
db = db.Where("id IN (?)", subQuery)
}
// 首先计算总数
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
// 然后应用排序、分页并获取数据
if opts.OrderBy != "" {
db = db.Order(opts.OrderBy)
}
offset := (page - 1) * pageSize
if err := db.Preload("RecipeIngredients.RawMaterial.RawMaterialNutrients.Nutrient").Offset(offset).Limit(pageSize).Find(&recipes).Error; err != nil {
return nil, 0, err
}
return recipes, total, nil
}
// UpdateRecipe 更新一个配方的主体信息(名称和描述)
func (r *gormRecipeRepository) UpdateRecipe(ctx context.Context, recipe *models.Recipe) error {
repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdateRecipe")
updateData := map[string]interface{}{
"name": recipe.Name,
"description": recipe.Description,
}
result := r.db.WithContext(repoCtx).Model(&models.Recipe{}).Where("id = ?", recipe.ID).Updates(updateData)
if result.Error != nil {
return fmt.Errorf("更新配方主体信息失败: %w", result.Error)
}
if result.RowsAffected == 0 {
return fmt.Errorf("未找到要更新的配方ID: %d", recipe.ID)
}
return nil
}
// DeleteRecipe 根据ID删除一个配方并级联软删除关联的 RecipeIngredient 记录
func (r *gormRecipeRepository) DeleteRecipe(ctx context.Context, id uint32) error {
repoCtx := logs.AddFuncName(ctx, r.ctx, "DeleteRecipe")
return r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error {
// 1. 查找 Recipe 记录,确保其存在
var recipe models.Recipe
if err := tx.First(&recipe, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("未找到要删除的配方ID: %d", id)
}
return fmt.Errorf("查询配方失败: %w", err)
}
// 2. 软删除所有关联的 RecipeIngredient 记录
if err := tx.Where("recipe_id = ?", id).Delete(&models.RecipeIngredient{}).Error; err != nil {
return fmt.Errorf("软删除关联的配方原料记录失败: %w", err)
}
// 3. 软删除 Recipe 记录本身
if err := tx.Delete(&recipe).Error; err != nil {
return fmt.Errorf("软删除配方失败: %w", err)
}
return nil
})
}
// DeleteRecipeIngredientsByRecipeIDTx 在给定事务中删除配方原料
func (r *gormRecipeRepository) DeleteRecipeIngredientsByRecipeIDTx(ctx context.Context, tx *gorm.DB, recipeID uint32) error {
repoCtx := logs.AddFuncName(ctx, r.ctx, "DeleteRecipeIngredientsByRecipeIDTx")
if err := tx.WithContext(repoCtx).Where("recipe_id = ?", recipeID).Delete(&models.RecipeIngredient{}).Error; err != nil {
return fmt.Errorf("删除配方 %d 的原料失败: %w", recipeID, err)
}
return nil
}
// CreateBatchRecipeIngredientsTx 在给定事务中批量创建配方原料
func (r *gormRecipeRepository) CreateBatchRecipeIngredientsTx(ctx context.Context, tx *gorm.DB, ingredients []models.RecipeIngredient) error {
repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateBatchRecipeIngredientsTx")
if len(ingredients) == 0 {
return nil // 没有原料需要创建
}
// 确保每个原料都关联到正确的配方ID
// 注意:这里假设传入的 ingredients 已经设置了正确的 RecipeID
for i := range ingredients {
if ingredients[i].RecipeID == 0 {
return fmt.Errorf("创建配方原料时 RecipeID 不能为空")
}
}
if err := tx.WithContext(repoCtx).Create(&ingredients).Error; err != nil {
return fmt.Errorf("批量创建配方原料失败: %w", err)
}
return nil
}