实现修改原料营养信息

This commit is contained in:
2025-11-22 16:44:22 +08:00
parent f81635f997
commit 9aea487537
11 changed files with 411 additions and 165 deletions

View File

@@ -365,6 +365,48 @@ func (c *Controller) ListRawMaterials(ctx echo.Context) error {
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取原料列表成功", resp, actionType, "获取原料列表成功", resp)
}
// UpdateRawMaterialNutrients godoc
// @Summary 全量更新原料的营养成分
// @Description 根据原料ID替换其所有的营养成分信息。这是一个覆盖操作。
// @Tags 饲料管理
// @Security BearerAuth
// @Accept json
// @Produce json
// @Param id path int true "原料ID"
// @Param nutrients body dto.UpdateRawMaterialNutrientsRequest true "新的营养成分列表"
// @Success 200 {object} controller.Response{data=dto.RawMaterialResponse} "业务码为200代表更新成功"
// @Router /api/v1/feed/raw-materials/{id}/nutrients [put]
func (c *Controller) UpdateRawMaterialNutrients(ctx echo.Context) error {
reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "UpdateRawMaterialNutrients")
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.UpdateRawMaterialNutrientsRequest
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.UpdateRawMaterialNutrients(reqCtx, uint32(id), &req)
if err != nil {
logger.Errorf("%s: 服务层更新原料营养成分失败: %v, ID: %d", actionType, err, id)
if errors.Is(err, service.ErrRawMaterialNotFound) {
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)
}
// --- 猪品种 (PigBreed) 接口方法实现 ---
// CreatePigBreed godoc

View File

@@ -22,8 +22,6 @@ func ConvertNutrientToDTO(nutrient *models.Nutrient) *NutrientResponse {
return &NutrientResponse{
ID: nutrient.ID,
CreatedAt: nutrient.CreatedAt,
UpdatedAt: nutrient.UpdatedAt,
Name: nutrient.Name,
Description: nutrient.Description,
RawMaterials: rawMaterials,
@@ -57,8 +55,6 @@ func ConvertRawMaterialToDTO(rm *models.RawMaterial) *RawMaterialResponse {
for i, rmn := range rm.RawMaterialNutrients {
rawMaterialNutrientDTOs[i] = RawMaterialNutrientDTO{
ID: rmn.ID,
CreatedAt: rmn.CreatedAt,
UpdatedAt: rmn.UpdatedAt,
NutrientID: rmn.NutrientID,
Nutrient: rmn.Nutrient.Name, // 假设 Nutrient 已经被预加载
Value: rmn.Value,
@@ -67,8 +63,6 @@ func ConvertRawMaterialToDTO(rm *models.RawMaterial) *RawMaterialResponse {
return &RawMaterialResponse{
ID: rm.ID,
CreatedAt: rm.CreatedAt,
UpdatedAt: rm.UpdatedAt,
Name: rm.Name,
Description: rm.Description,
RawMaterialNutrients: rawMaterialNutrientDTOs,
@@ -99,8 +93,6 @@ func ConvertPigBreedToDTO(breed *models.PigBreed) *PigBreedResponse {
}
return &PigBreedResponse{
ID: breed.ID,
CreatedAt: breed.CreatedAt,
UpdatedAt: breed.UpdatedAt,
Name: breed.Name,
Description: breed.Description,
ParentInfo: breed.ParentInfo,
@@ -134,8 +126,6 @@ func ConvertPigAgeStageToDTO(ageStage *models.PigAgeStage) *PigAgeStageResponse
}
return &PigAgeStageResponse{
ID: ageStage.ID,
CreatedAt: ageStage.CreatedAt,
UpdatedAt: ageStage.UpdatedAt,
Name: ageStage.Name,
Description: ageStage.Description,
}
@@ -168,8 +158,6 @@ func ConvertPigTypeToDTO(pt *models.PigType) *PigTypeResponse {
for i, pnr := range pt.PigNutrientRequirements {
pigNutrientRequirementDTOs[i] = PigNutrientRequirementDTO{
ID: pnr.ID,
CreatedAt: pnr.CreatedAt,
UpdatedAt: pnr.UpdatedAt,
NutrientID: pnr.NutrientID,
NutrientName: pnr.Nutrient.Name, // 假设 Nutrient 已经被预加载
MinRequirement: pnr.MinRequirement,
@@ -179,8 +167,6 @@ func ConvertPigTypeToDTO(pt *models.PigType) *PigTypeResponse {
return &PigTypeResponse{
ID: pt.ID,
CreatedAt: pt.CreatedAt,
UpdatedAt: pt.UpdatedAt,
BreedID: pt.BreedID,
BreedName: pt.Breed.Name, // 假设 Breed 已经被预加载
AgeStageID: pt.AgeStageID,

View File

@@ -1,9 +1,5 @@
package dto
import (
"time"
)
// =============================================================================================================
// 营养种类 (Nutrient) 相关 DTO
// =============================================================================================================
@@ -30,8 +26,6 @@ type NutrientRawMaterialDTO struct {
// NutrientResponse 营养种类响应体
type NutrientResponse struct {
ID uint32 `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Name string `json:"name"`
Description string `json:"description"`
RawMaterials []NutrientRawMaterialDTO `json:"raw_materials"` // 包含此营养的原料列表
@@ -70,19 +64,15 @@ type UpdateRawMaterialRequest struct {
// RawMaterialNutrientDTO 原料营养素响应体
type RawMaterialNutrientDTO struct {
ID uint32 `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
NutrientID uint32 `json:"nutrient_id"`
Nutrient string `json:"nutrient_name"` // 营养素名称
Value float32 `json:"value"` // 营养价值含量
ID uint32 `json:"id"`
NutrientID uint32 `json:"nutrient_id"`
Nutrient string `json:"nutrient_name"` // 营养素名称
Value float32 `json:"value"` // 营养价值含量
}
// RawMaterialResponse 原料响应体
type RawMaterialResponse struct {
ID uint32 `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Name string `json:"name"`
Description string `json:"description"`
RawMaterialNutrients []RawMaterialNutrientDTO `json:"raw_material_nutrients"` // 关联的营养素信息
@@ -103,6 +93,17 @@ type ListRawMaterialResponse struct {
Pagination PaginationDTO `json:"pagination"`
}
// UpdateRawMaterialNutrientsRequest 更新原料营养成分的请求体
type UpdateRawMaterialNutrientsRequest struct {
Nutrients []RawMaterialNutrientItem `json:"nutrients" validate:"required,dive"`
}
// RawMaterialNutrientItem 代表一个营养成分及其含量
type RawMaterialNutrientItem struct {
NutrientID uint32 `json:"nutrient_id" validate:"required"` // 营养素ID
Value float32 `json:"value" validate:"gte=0"` // 含量值必须大于等于0
}
// =============================================================================================================
// 猪品种 (PigBreed) 相关 DTO
// =============================================================================================================
@@ -129,15 +130,13 @@ type UpdatePigBreedRequest struct {
// PigBreedResponse 猪品种响应体
type PigBreedResponse struct {
ID uint32 `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Name string `json:"name"`
Description string `json:"description"`
ParentInfo string `json:"parent_info"`
AppearanceFeatures string `json:"appearance_features"`
BreedAdvantages string `json:"breed_advantages"`
BreedDisadvantages string `json:"breed_disadvantages"`
ID uint32 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
ParentInfo string `json:"parent_info"`
AppearanceFeatures string `json:"appearance_features"`
BreedAdvantages string `json:"breed_advantages"`
BreedDisadvantages string `json:"breed_disadvantages"`
}
// ListPigBreedRequest 定义了获取猪品种列表的请求参数
@@ -172,11 +171,9 @@ type UpdatePigAgeStageRequest struct {
// PigAgeStageResponse 猪年龄阶段响应体
type PigAgeStageResponse struct {
ID uint32 `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Name string `json:"name"`
Description string `json:"description"`
ID uint32 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
}
// ListPigAgeStageRequest 定义了获取猪年龄阶段列表的请求参数
@@ -225,20 +222,16 @@ type UpdatePigTypeRequest struct {
// PigNutrientRequirementDTO 猪营养需求响应体
type PigNutrientRequirementDTO struct {
ID uint32 `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
NutrientID uint32 `json:"nutrient_id"`
NutrientName string `json:"nutrient_name"` // 营养素名称
MinRequirement float32 `json:"min_requirement"` // 最低营养需求量
MaxRequirement float32 `json:"max_requirement"` // 最高营养需求量
ID uint32 `json:"id"`
NutrientID uint32 `json:"nutrient_id"`
NutrientName string `json:"nutrient_name"` // 营养素名称
MinRequirement float32 `json:"min_requirement"` // 最低营养需求量
MaxRequirement float32 `json:"max_requirement"` // 最高营养需求量
}
// PigTypeResponse 猪类型响应体
type PigTypeResponse struct {
ID uint32 `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
BreedID uint32 `json:"breed_id"`
BreedName string `json:"breed_name"` // 猪品种名称
AgeStageID uint32 `json:"age_stage_id"`

View File

@@ -40,6 +40,7 @@ type FeedManagementService interface {
DeleteRawMaterial(ctx context.Context, id uint32) error
GetRawMaterial(ctx context.Context, id uint32) (*dto.RawMaterialResponse, error)
ListRawMaterials(ctx context.Context, req *dto.ListRawMaterialRequest) (*dto.ListRawMaterialResponse, error)
UpdateRawMaterialNutrients(ctx context.Context, id uint32, req *dto.UpdateRawMaterialNutrientsRequest) (*dto.RawMaterialResponse, error) // 新增
// 猪品种相关
CreatePigBreed(ctx context.Context, req *dto.CreatePigBreedRequest) (*dto.PigBreedResponse, error)
@@ -236,6 +237,43 @@ func (s *feedManagementServiceImpl) ListRawMaterials(ctx context.Context, req *d
return dto.ConvertRawMaterialListToDTO(rawMaterials, total, req.Page, req.PageSize), nil
}
// UpdateRawMaterialNutrients 全量更新原料的营养成分
func (s *feedManagementServiceImpl) UpdateRawMaterialNutrients(ctx context.Context, id uint32, req *dto.UpdateRawMaterialNutrientsRequest) (*dto.RawMaterialResponse, error) {
serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateRawMaterialNutrients")
// 1. 将 DTO 转换为领域模型
nutrients := make([]models.RawMaterialNutrient, len(req.Nutrients))
for i, item := range req.Nutrients {
nutrients[i] = models.RawMaterialNutrient{
NutrientID: item.NutrientID,
Value: item.Value,
}
}
// 2. 调用领域服务执行更新命令
err := s.recipeSvc.UpdateRawMaterialNutrients(serviceCtx, id, nutrients)
if err != nil {
if errors.Is(err, recipe.ErrRawMaterialNotFound) {
return nil, ErrRawMaterialNotFound
}
// 此处可以根据领域层可能返回的其他特定错误进行转换
return nil, fmt.Errorf("更新原料营养成分失败: %w", err)
}
// 3. 更新成功后,调用查询服务获取最新的原料信息
updatedRawMaterial, err := s.recipeSvc.GetRawMaterial(serviceCtx, id)
if err != nil {
if errors.Is(err, recipe.ErrRawMaterialNotFound) {
// 理论上不应该发生,因为刚更新成功
return nil, ErrRawMaterialNotFound
}
return nil, fmt.Errorf("更新后获取原料信息失败: %w", err)
}
// 4. 将领域模型转换为 DTO 并返回
return dto.ConvertRawMaterialToDTO(updatedRawMaterial), nil
}
// =====================================================================================================================
// 猪品种 (PigBreed) 实现
// =====================================================================================================================