Files
pig-farm-controller/internal/infra/repository/recipe_repository.go
2025-11-22 20:52:15 +08:00

183 lines
6.7 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
UpdateRecipeIngredients(ctx context.Context, recipeID uint32, ingredients []models.RecipeIngredient) error
DeleteRecipe(ctx context.Context, id uint32) 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")
return r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error {
if err := tx.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
}
// UpdateRecipeIngredients 更新配方关联的原料列表
func (r *gormRecipeRepository) UpdateRecipeIngredients(ctx context.Context, recipeID uint32, ingredients []models.RecipeIngredient) error {
repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdateRecipeIngredients")
return r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error {
// 1. 删除所有旧的关联配方原料
if err := tx.Where("recipe_id = ?", recipeID).Delete(&models.RecipeIngredient{}).Error; err != nil {
return fmt.Errorf("删除旧的配方原料失败: %w", err)
}
// 2. 批量创建新的关联配方原料
if len(ingredients) > 0 {
for i := range ingredients {
ingredients[i].RecipeID = recipeID
}
if err := tx.Create(&ingredients).Error; err != nil {
return fmt.Errorf("创建新的配方原料失败: %w", err)
}
}
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
})
}