实现修改猪营养需求

This commit is contained in:
2025-11-22 17:55:52 +08:00
parent 4405d1f3f1
commit 0637f5fb6c
9 changed files with 427 additions and 20 deletions

View File

@@ -251,6 +251,7 @@ func (a *API) setupRoutes() {
feedGroup.DELETE("/pig-types/:id", a.feedController.DeletePigType)
feedGroup.GET("/pig-types/:id", a.feedController.GetPigType)
feedGroup.GET("/pig-types", a.feedController.ListPigTypes)
feedGroup.PUT("/pig-types/:id/nutrient-requirements", a.feedController.UpdatePigTypeNutrientRequirements)
}
logger.Debug("饲料管理相关接口注册成功 (需要认证和审计)")
}

View File

@@ -915,3 +915,45 @@ func (c *Controller) ListPigTypes(ctx echo.Context) error {
logger.Infof("%s: 获取猪类型列表成功, 数量: %d", actionType, len(resp.List))
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取猪类型列表成功", resp, actionType, "获取猪类型列表成功", resp)
}
// UpdatePigTypeNutrientRequirements godoc
// @Summary 全量更新猪类型的营养需求
// @Description 根据猪类型ID替换其所有的营养需求信息。这是一个覆盖操作。
// @Tags 饲料管理
// @Security BearerAuth
// @Accept json
// @Produce json
// @Param id path int true "猪类型ID"
// @Param nutrientRequirements body dto.UpdatePigTypeNutrientRequirementsRequest true "新的营养需求列表"
// @Success 200 {object} controller.Response{data=dto.PigTypeResponse} "业务码为200代表更新成功"
// @Router /api/v1/feed/pig-types/{id}/nutrient-requirements [put]
func (c *Controller) UpdatePigTypeNutrientRequirements(ctx echo.Context) error {
reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "UpdatePigTypeNutrientRequirements")
const actionType = "更新猪类型营养需求"
idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
logger.Errorf("%s: 猪类型ID格式错误: %v, ID: %s", actionType, err, idStr)
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的猪类型ID格式", actionType, "猪类型ID格式错误", idStr)
}
var req dto.UpdatePigTypeNutrientRequirementsRequest
if err := ctx.Bind(&req); err != nil {
logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
}
resp, err := c.feedManagementService.UpdatePigTypeNutrientRequirements(reqCtx, uint32(id), &req)
if err != nil {
logger.Errorf("%s: 服务层更新猪类型营养需求失败: %v, ID: %d", actionType, err, id)
if errors.Is(err, service.ErrPigTypeNotFound) {
return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "猪类型不存在", id)
}
// 这里可以根据未来可能从服务层返回的其他特定错误进行处理
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新猪类型营养需求失败: "+err.Error(), actionType, "服务层更新失败", req)
}
logger.Infof("%s: 猪类型营养需求更新成功, ID: %d", actionType, resp.ID)
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "猪类型营养需求更新成功", resp, actionType, "猪类型营养需求更新成功", resp)
}

View File

@@ -262,3 +262,15 @@ type ListPigTypeResponse struct {
List []PigTypeResponse `json:"list"`
Pagination PaginationDTO `json:"pagination"`
}
// UpdatePigTypeNutrientRequirementsRequest 更新猪类型营养需求的请求体
type UpdatePigTypeNutrientRequirementsRequest struct {
NutrientRequirements []PigNutrientRequirementItem `json:"nutrient_requirements" validate:"required,dive"`
}
// PigNutrientRequirementItem 代表一个营养需求项
type PigNutrientRequirementItem struct {
NutrientID uint32 `json:"nutrient_id" validate:"required"` // 营养素ID
MinRequirement float32 `json:"min_requirement" validate:"gte=0"` // 最低营养需求量
MaxRequirement float32 `json:"max_requirement" validate:"gte=0"` // 最高营养需求量
}

View File

@@ -62,6 +62,7 @@ type FeedManagementService interface {
DeletePigType(ctx context.Context, id uint32) error
GetPigType(ctx context.Context, id uint32) (*dto.PigTypeResponse, error)
ListPigTypes(ctx context.Context, req *dto.ListPigTypeRequest) (*dto.ListPigTypeResponse, error)
UpdatePigTypeNutrientRequirements(ctx context.Context, id uint32, req *dto.UpdatePigTypeNutrientRequirementsRequest) (*dto.PigTypeResponse, error) // 新增
}
// feedManagementServiceImpl 是 FeedManagementService 接口的实现
@@ -576,3 +577,42 @@ func (s *feedManagementServiceImpl) ListPigTypes(ctx context.Context, req *dto.L
return dto.ConvertPigTypeListToDTO(pigTypes, total, req.Page, req.PageSize), nil
}
// UpdatePigTypeNutrientRequirements 全量更新猪类型的营养需求
func (s *feedManagementServiceImpl) UpdatePigTypeNutrientRequirements(ctx context.Context, id uint32, req *dto.UpdatePigTypeNutrientRequirementsRequest) (*dto.PigTypeResponse, error) {
serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdatePigTypeNutrientRequirements")
// 1. 将 DTO 转换为领域模型
requirements := make([]models.PigNutrientRequirement, len(req.NutrientRequirements))
for i, item := range req.NutrientRequirements {
requirements[i] = models.PigNutrientRequirement{
PigTypeID: id, // 设置所属的 PigTypeID
NutrientID: item.NutrientID,
MinRequirement: item.MinRequirement,
MaxRequirement: item.MaxRequirement,
}
}
// 2. 调用领域服务执行更新命令
err := s.recipeSvc.UpdatePigTypeNutrientRequirements(serviceCtx, id, requirements)
if err != nil {
if errors.Is(err, recipe.ErrPigTypeNotFound) {
return nil, ErrPigTypeNotFound
}
// 此处可以根据领域层可能返回的其他特定错误进行转换
return nil, fmt.Errorf("更新猪类型营养需求失败: %w", err)
}
// 3. 更新成功后,调用查询服务获取最新的猪类型信息
updatedPigType, err := s.recipeSvc.GetPigTypeByID(serviceCtx, id)
if err != nil {
if errors.Is(err, recipe.ErrPigTypeNotFound) {
// 理论上不应该发生,因为刚更新成功
return nil, ErrPigTypeNotFound
}
return nil, fmt.Errorf("更新后获取猪类型信息失败: %w", err)
}
// 4. 将领域模型转换为 DTO 并返回
return dto.ConvertPigTypeToDTO(updatedPigType), nil
}

View File

@@ -62,6 +62,7 @@ type Service interface {
UpdatePigType(ctx context.Context, pigType *models.PigType) error
DeletePigType(ctx context.Context, id uint32) error
ListPigTypes(ctx context.Context, opts repository.PigTypeListOptions, page, pageSize int) ([]models.PigType, int64, error)
UpdatePigTypeNutrientRequirements(ctx context.Context, pigTypeID uint32, requirements []models.PigNutrientRequirement) error
}
// recipeServiceImpl 是 RecipeService 的实现
@@ -526,3 +527,38 @@ func (s *recipeServiceImpl) ListPigTypes(ctx context.Context, opts repository.Pi
}
return pigTypes, total, nil
}
// UpdatePigTypeNutrientRequirements 实现了全量更新猪类型营养需求的核心业务逻辑
func (s *recipeServiceImpl) UpdatePigTypeNutrientRequirements(ctx context.Context, pigTypeID uint32, requirements []models.PigNutrientRequirement) error {
serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdatePigTypeNutrientRequirements")
// 1. 检查猪类型是否存在
if _, err := s.pigTypeRepo.GetPigTypeByID(serviceCtx, pigTypeID); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return ErrPigTypeNotFound
}
return fmt.Errorf("获取待更新营养需求的猪类型失败: %w", err)
}
// 2. 在事务中执行替换操作
err := s.uow.ExecuteInTransaction(serviceCtx, func(tx *gorm.DB) error {
// 2.1. 删除旧的关联记录
if err := s.pigTypeRepo.DeletePigNutrientRequirementsByPigTypeIDTx(serviceCtx, tx, pigTypeID); err != nil {
return err // 错误已在仓库层封装,直接返回
}
// 2.2. 创建新的关联记录
if err := s.pigTypeRepo.CreateBatchPigNutrientRequirementsTx(serviceCtx, tx, requirements); err != nil {
return err // 错误已在仓库层封装,直接返回
}
return nil
})
if err != nil {
return fmt.Errorf("更新猪类型营养需求事务执行失败: %w", err)
}
// 3. 操作成功,直接返回 nil
return nil
}

View File

@@ -2,6 +2,7 @@ package repository
import (
"context"
"fmt"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
@@ -71,6 +72,10 @@ type PigTypeRepository interface {
DeletePigType(ctx context.Context, id uint32) error
// ListPigTypes 支持分页和过滤的猪类型记录列表查询。
ListPigTypes(ctx context.Context, opts PigTypeListOptions, page, pageSize int) ([]models.PigType, int64, error)
// DeletePigNutrientRequirementsByPigTypeIDTx 在事务中软删除指定猪类型的所有营养需求记录。
DeletePigNutrientRequirementsByPigTypeIDTx(ctx context.Context, db *gorm.DB, pigTypeID uint32) error
// CreateBatchPigNutrientRequirementsTx 在事务中批量创建猪营养需求记录。
CreateBatchPigNutrientRequirementsTx(ctx context.Context, db *gorm.DB, requirements []models.PigNutrientRequirement) error
}
// gormPigTypeRepository 是 PigTypeRepository 接口的 GORM 实现。
@@ -279,3 +284,27 @@ func (r *gormPigTypeRepository) ListPigTypes(ctx context.Context, opts PigTypeLi
return results, total, err
}
// DeletePigNutrientRequirementsByPigTypeIDTx 实现了在事务中软删除指定猪类型的所有营养需求记录的逻辑。
func (r *gormPigTypeRepository) DeletePigNutrientRequirementsByPigTypeIDTx(ctx context.Context, db *gorm.DB, pigTypeID uint32) error {
repoCtx := logs.AddFuncName(ctx, r.ctx, "DeletePigNutrientRequirementsByPigTypeIDTx")
tx := db.WithContext(repoCtx)
if err := tx.Where("pig_type_id = ?", pigTypeID).Delete(&models.PigNutrientRequirement{}).Error; err != nil {
return fmt.Errorf("软删除猪营养需求失败: %w", err)
}
return nil
}
// CreateBatchPigNutrientRequirementsTx 实现了在事务中批量创建猪营养需求记录的逻辑。
func (r *gormPigTypeRepository) CreateBatchPigNutrientRequirementsTx(ctx context.Context, db *gorm.DB, requirements []models.PigNutrientRequirement) error {
// 如果没有要创建的记录直接返回成功避免执行空的Create语句
if len(requirements) == 0 {
return nil
}
repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateBatchPigNutrientRequirementsTx")
tx := db.WithContext(repoCtx)
if err := tx.Create(&requirements).Error; err != nil {
return fmt.Errorf("批量创建猪营养需求失败: %w", err)
}
return nil
}