From 0637f5fb6cb50d9fcbbbcd8961f616df660cf867 Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Sat, 22 Nov 2025 17:55:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BF=AE=E6=94=B9=E7=8C=AA?= =?UTF-8?q?=E8=90=A5=E5=85=BB=E9=9C=80=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/docs.go | 110 ++++++++++++++++-- docs/swagger.json | 110 ++++++++++++++++-- docs/swagger.yaml | 67 ++++++++++- internal/app/api/router.go | 1 + .../app/controller/feed/feed_controller.go | 42 +++++++ internal/app/dto/feed_dto.go | 12 ++ .../app/service/feed_management_service.go | 40 +++++++ internal/domain/recipe/recipe_service.go | 36 ++++++ .../infra/repository/pig_type_repository.go | 29 +++++ 9 files changed, 427 insertions(+), 20 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 75329e9..1698c3e 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -2653,6 +2653,64 @@ const docTemplate = `{ } } }, + "/api/v1/feed/pig-types/{id}/nutrient-requirements": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据猪类型ID,替换其所有的营养需求信息。这是一个覆盖操作。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "饲料管理" + ], + "summary": "全量更新猪类型的营养需求", + "parameters": [ + { + "type": "integer", + "description": "猪类型ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "新的营养需求列表", + "name": "nutrientRequirements", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdatePigTypeNutrientRequirementsRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为200代表更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.PigTypeResponse" + } + } + } + ] + } + } + } + } + }, "/api/v1/feed/raw-materials": { "get": { "security": [ @@ -3144,6 +3202,7 @@ const docTemplate = `{ }, { "enum": [ + 7, -1, 0, 1, @@ -3153,12 +3212,12 @@ const docTemplate = `{ 5, -1, 5, - 6, - 7 + 6 ], "type": "integer", "format": "int32", "x-enum-varnames": [ + "_numLevels", "DebugLevel", "InfoLevel", "WarnLevel", @@ -3168,8 +3227,7 @@ const docTemplate = `{ "FatalLevel", "_minLevel", "_maxLevel", - "InvalidLevel", - "_numLevels" + "InvalidLevel" ], "name": "level", "in": "query" @@ -7764,6 +7822,28 @@ const docTemplate = `{ } } }, + "dto.PigNutrientRequirementItem": { + "type": "object", + "required": [ + "nutrient_id" + ], + "properties": { + "max_requirement": { + "description": "最高营养需求量", + "type": "number", + "minimum": 0 + }, + "min_requirement": { + "description": "最低营养需求量", + "type": "number", + "minimum": 0 + }, + "nutrient_id": { + "description": "营养素ID", + "type": "integer" + } + } + }, "dto.PigPurchaseDTO": { "type": "object", "properties": { @@ -8933,6 +9013,20 @@ const docTemplate = `{ } } }, + "dto.UpdatePigTypeNutrientRequirementsRequest": { + "type": "object", + "required": [ + "nutrient_requirements" + ], + "properties": { + "nutrient_requirements": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.PigNutrientRequirementItem" + } + } + } + }, "dto.UpdatePigTypeRequest": { "type": "object", "required": [ @@ -9705,6 +9799,7 @@ const docTemplate = `{ "type": "integer", "format": "int32", "enum": [ + 7, -1, 0, 1, @@ -9714,10 +9809,10 @@ const docTemplate = `{ 5, -1, 5, - 6, - 7 + 6 ], "x-enum-varnames": [ + "_numLevels", "DebugLevel", "InfoLevel", "WarnLevel", @@ -9727,8 +9822,7 @@ const docTemplate = `{ "FatalLevel", "_minLevel", "_maxLevel", - "InvalidLevel", - "_numLevels" + "InvalidLevel" ] } }, diff --git a/docs/swagger.json b/docs/swagger.json index bdf18ce..ceb65c1 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -2645,6 +2645,64 @@ } } }, + "/api/v1/feed/pig-types/{id}/nutrient-requirements": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "根据猪类型ID,替换其所有的营养需求信息。这是一个覆盖操作。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "饲料管理" + ], + "summary": "全量更新猪类型的营养需求", + "parameters": [ + { + "type": "integer", + "description": "猪类型ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "新的营养需求列表", + "name": "nutrientRequirements", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdatePigTypeNutrientRequirementsRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为200代表更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.PigTypeResponse" + } + } + } + ] + } + } + } + } + }, "/api/v1/feed/raw-materials": { "get": { "security": [ @@ -3136,6 +3194,7 @@ }, { "enum": [ + 7, -1, 0, 1, @@ -3145,12 +3204,12 @@ 5, -1, 5, - 6, - 7 + 6 ], "type": "integer", "format": "int32", "x-enum-varnames": [ + "_numLevels", "DebugLevel", "InfoLevel", "WarnLevel", @@ -3160,8 +3219,7 @@ "FatalLevel", "_minLevel", "_maxLevel", - "InvalidLevel", - "_numLevels" + "InvalidLevel" ], "name": "level", "in": "query" @@ -7756,6 +7814,28 @@ } } }, + "dto.PigNutrientRequirementItem": { + "type": "object", + "required": [ + "nutrient_id" + ], + "properties": { + "max_requirement": { + "description": "最高营养需求量", + "type": "number", + "minimum": 0 + }, + "min_requirement": { + "description": "最低营养需求量", + "type": "number", + "minimum": 0 + }, + "nutrient_id": { + "description": "营养素ID", + "type": "integer" + } + } + }, "dto.PigPurchaseDTO": { "type": "object", "properties": { @@ -8925,6 +9005,20 @@ } } }, + "dto.UpdatePigTypeNutrientRequirementsRequest": { + "type": "object", + "required": [ + "nutrient_requirements" + ], + "properties": { + "nutrient_requirements": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.PigNutrientRequirementItem" + } + } + } + }, "dto.UpdatePigTypeRequest": { "type": "object", "required": [ @@ -9697,6 +9791,7 @@ "type": "integer", "format": "int32", "enum": [ + 7, -1, 0, 1, @@ -9706,10 +9801,10 @@ 5, -1, 5, - 6, - 7 + 6 ], "x-enum-varnames": [ + "_numLevels", "DebugLevel", "InfoLevel", "WarnLevel", @@ -9719,8 +9814,7 @@ "FatalLevel", "_minLevel", "_maxLevel", - "InvalidLevel", - "_numLevels" + "InvalidLevel" ] } }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 0bf0acb..929eeea 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1120,6 +1120,22 @@ definitions: description: 营养素名称 type: string type: object + dto.PigNutrientRequirementItem: + properties: + max_requirement: + description: 最高营养需求量 + minimum: 0 + type: number + min_requirement: + description: 最低营养需求量 + minimum: 0 + type: number + nutrient_id: + description: 营养素ID + type: integer + required: + - nutrient_id + type: object dto.PigPurchaseDTO: properties: created_at: @@ -1913,6 +1929,15 @@ definitions: required: - name type: object + dto.UpdatePigTypeNutrientRequirementsRequest: + properties: + nutrient_requirements: + items: + $ref: '#/definitions/dto.PigNutrientRequirementItem' + type: array + required: + - nutrient_requirements + type: object dto.UpdatePigTypeRequest: properties: age_stage_id: @@ -2510,6 +2535,7 @@ definitions: - PlanTypeFilterSystem zapcore.Level: enum: + - 7 - -1 - 0 - 1 @@ -2520,10 +2546,10 @@ definitions: - -1 - 5 - 6 - - 7 format: int32 type: integer x-enum-varnames: + - _numLevels - DebugLevel - InfoLevel - WarnLevel @@ -2534,7 +2560,6 @@ definitions: - _minLevel - _maxLevel - InvalidLevel - - _numLevels info: contact: email: divano@example.com @@ -4145,6 +4170,40 @@ paths: summary: 更新猪类型 tags: - 饲料管理 + /api/v1/feed/pig-types/{id}/nutrient-requirements: + put: + consumes: + - application/json + description: 根据猪类型ID,替换其所有的营养需求信息。这是一个覆盖操作。 + parameters: + - description: 猪类型ID + in: path + name: id + required: true + type: integer + - description: 新的营养需求列表 + in: body + name: nutrientRequirements + required: true + schema: + $ref: '#/definitions/dto.UpdatePigTypeNutrientRequirementsRequest' + produces: + - application/json + responses: + "200": + description: 业务码为200代表更新成功 + schema: + allOf: + - $ref: '#/definitions/controller.Response' + - properties: + data: + $ref: '#/definitions/dto.PigTypeResponse' + type: object + security: + - BearerAuth: [] + summary: 全量更新猪类型的营养需求 + tags: + - 饲料管理 /api/v1/feed/raw-materials: get: description: 获取所有原料的列表,支持分页和过滤。 @@ -4425,6 +4484,7 @@ paths: name: end_time type: string - enum: + - 7 - -1 - 0 - 1 @@ -4435,12 +4495,12 @@ paths: - -1 - 5 - 6 - - 7 format: int32 in: query name: level type: integer x-enum-varnames: + - _numLevels - DebugLevel - InfoLevel - WarnLevel @@ -4451,7 +4511,6 @@ paths: - _minLevel - _maxLevel - InvalidLevel - - _numLevels - enum: - 邮件 - 企业微信 diff --git a/internal/app/api/router.go b/internal/app/api/router.go index 98f03b5..c1835ce 100644 --- a/internal/app/api/router.go +++ b/internal/app/api/router.go @@ -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("饲料管理相关接口注册成功 (需要认证和审计)") } diff --git a/internal/app/controller/feed/feed_controller.go b/internal/app/controller/feed/feed_controller.go index 260d63f..185e070 100644 --- a/internal/app/controller/feed/feed_controller.go +++ b/internal/app/controller/feed/feed_controller.go @@ -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) +} diff --git a/internal/app/dto/feed_dto.go b/internal/app/dto/feed_dto.go index 57d6f18..1fcb10c 100644 --- a/internal/app/dto/feed_dto.go +++ b/internal/app/dto/feed_dto.go @@ -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"` // 最高营养需求量 +} diff --git a/internal/app/service/feed_management_service.go b/internal/app/service/feed_management_service.go index 3fdb4b2..ef4d2ae 100644 --- a/internal/app/service/feed_management_service.go +++ b/internal/app/service/feed_management_service.go @@ -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 +} diff --git a/internal/domain/recipe/recipe_service.go b/internal/domain/recipe/recipe_service.go index 3a210df..3641275 100644 --- a/internal/domain/recipe/recipe_service.go +++ b/internal/domain/recipe/recipe_service.go @@ -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 +} diff --git a/internal/infra/repository/pig_type_repository.go b/internal/infra/repository/pig_type_repository.go index a975b9b..0f006e6 100644 --- a/internal/infra/repository/pig_type_repository.go +++ b/internal/infra/repository/pig_type_repository.go @@ -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 +}