实现修改原料营养信息

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

@@ -56,3 +56,4 @@ http://git.huangwc.com/pig/pig-farm-controller/issues/66
6. 实现从json读取猪营养需求并写入数据库 6. 实现从json读取猪营养需求并写入数据库
7. 实现配方领域关于猪模型和营养需求的增删改查 7. 实现配方领域关于猪模型和营养需求的增删改查
8. 实现配方领域的web接口 8. 实现配方领域的web接口
9. 实现修改原料营养信息

View File

@@ -2905,6 +2905,64 @@ const docTemplate = `{
} }
} }
}, },
"/api/v1/feed/raw-materials/{id}/nutrients": {
"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": "nutrients",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.UpdateRawMaterialNutrientsRequest"
}
}
],
"responses": {
"200": {
"description": "业务码为200代表更新成功",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/dto.RawMaterialResponse"
}
}
}
]
}
}
}
}
},
"/api/v1/monitor/device-command-logs": { "/api/v1/monitor/device-command-logs": {
"get": { "get": {
"security": [ "security": [
@@ -7368,9 +7426,6 @@ const docTemplate = `{
"dto.NutrientResponse": { "dto.NutrientResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"created_at": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
}, },
@@ -7386,9 +7441,6 @@ const docTemplate = `{
"items": { "items": {
"$ref": "#/definitions/dto.NutrientRawMaterialDTO" "$ref": "#/definitions/dto.NutrientRawMaterialDTO"
} }
},
"updated_at": {
"type": "string"
} }
} }
}, },
@@ -7461,9 +7513,6 @@ const docTemplate = `{
"dto.PigAgeStageResponse": { "dto.PigAgeStageResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"created_at": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
}, },
@@ -7472,9 +7521,6 @@ const docTemplate = `{
}, },
"name": { "name": {
"type": "string" "type": "string"
},
"updated_at": {
"type": "string"
} }
} }
}, },
@@ -7667,9 +7713,6 @@ const docTemplate = `{
"breed_disadvantages": { "breed_disadvantages": {
"type": "string" "type": "string"
}, },
"created_at": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
}, },
@@ -7681,9 +7724,6 @@ const docTemplate = `{
}, },
"parent_info": { "parent_info": {
"type": "string" "type": "string"
},
"updated_at": {
"type": "string"
} }
} }
}, },
@@ -7704,9 +7744,6 @@ const docTemplate = `{
"dto.PigNutrientRequirementDTO": { "dto.PigNutrientRequirementDTO": {
"type": "object", "type": "object",
"properties": { "properties": {
"created_at": {
"type": "string"
},
"id": { "id": {
"type": "integer" "type": "integer"
}, },
@@ -7724,9 +7761,6 @@ const docTemplate = `{
"nutrient_name": { "nutrient_name": {
"description": "营养素名称", "description": "营养素名称",
"type": "string" "type": "string"
},
"updated_at": {
"type": "string"
} }
} }
}, },
@@ -7905,9 +7939,6 @@ const docTemplate = `{
"description": "猪品种名称", "description": "猪品种名称",
"type": "string" "type": "string"
}, },
"created_at": {
"type": "string"
},
"daily_feed_intake": { "daily_feed_intake": {
"type": "number" "type": "number"
}, },
@@ -7938,9 +7969,6 @@ const docTemplate = `{
"items": { "items": {
"$ref": "#/definitions/dto.PigNutrientRequirementDTO" "$ref": "#/definitions/dto.PigNutrientRequirementDTO"
} }
},
"updated_at": {
"type": "string"
} }
} }
}, },
@@ -8052,9 +8080,6 @@ const docTemplate = `{
"dto.RawMaterialNutrientDTO": { "dto.RawMaterialNutrientDTO": {
"type": "object", "type": "object",
"properties": { "properties": {
"created_at": {
"type": "string"
},
"id": { "id": {
"type": "integer" "type": "integer"
}, },
@@ -8065,21 +8090,32 @@ const docTemplate = `{
"description": "营养素名称", "description": "营养素名称",
"type": "string" "type": "string"
}, },
"updated_at": {
"type": "string"
},
"value": { "value": {
"description": "营养价值含量", "description": "营养价值含量",
"type": "number" "type": "number"
} }
} }
}, },
"dto.RawMaterialNutrientItem": {
"type": "object",
"required": [
"nutrient_id"
],
"properties": {
"nutrient_id": {
"description": "营养素ID",
"type": "integer"
},
"value": {
"description": "含量值必须大于等于0",
"type": "number",
"minimum": 0
}
}
},
"dto.RawMaterialResponse": { "dto.RawMaterialResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"created_at": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
}, },
@@ -8095,9 +8131,6 @@ const docTemplate = `{
"items": { "items": {
"$ref": "#/definitions/dto.RawMaterialNutrientDTO" "$ref": "#/definitions/dto.RawMaterialNutrientDTO"
} }
},
"updated_at": {
"type": "string"
} }
} }
}, },
@@ -8991,6 +9024,20 @@ const docTemplate = `{
} }
} }
}, },
"dto.UpdateRawMaterialNutrientsRequest": {
"type": "object",
"required": [
"nutrients"
],
"properties": {
"nutrients": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.RawMaterialNutrientItem"
}
}
}
},
"dto.UpdateRawMaterialRequest": { "dto.UpdateRawMaterialRequest": {
"type": "object", "type": "object",
"required": [ "required": [

View File

@@ -2897,6 +2897,64 @@
} }
} }
}, },
"/api/v1/feed/raw-materials/{id}/nutrients": {
"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": "nutrients",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.UpdateRawMaterialNutrientsRequest"
}
}
],
"responses": {
"200": {
"description": "业务码为200代表更新成功",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/dto.RawMaterialResponse"
}
}
}
]
}
}
}
}
},
"/api/v1/monitor/device-command-logs": { "/api/v1/monitor/device-command-logs": {
"get": { "get": {
"security": [ "security": [
@@ -7360,9 +7418,6 @@
"dto.NutrientResponse": { "dto.NutrientResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"created_at": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
}, },
@@ -7378,9 +7433,6 @@
"items": { "items": {
"$ref": "#/definitions/dto.NutrientRawMaterialDTO" "$ref": "#/definitions/dto.NutrientRawMaterialDTO"
} }
},
"updated_at": {
"type": "string"
} }
} }
}, },
@@ -7453,9 +7505,6 @@
"dto.PigAgeStageResponse": { "dto.PigAgeStageResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"created_at": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
}, },
@@ -7464,9 +7513,6 @@
}, },
"name": { "name": {
"type": "string" "type": "string"
},
"updated_at": {
"type": "string"
} }
} }
}, },
@@ -7659,9 +7705,6 @@
"breed_disadvantages": { "breed_disadvantages": {
"type": "string" "type": "string"
}, },
"created_at": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
}, },
@@ -7673,9 +7716,6 @@
}, },
"parent_info": { "parent_info": {
"type": "string" "type": "string"
},
"updated_at": {
"type": "string"
} }
} }
}, },
@@ -7696,9 +7736,6 @@
"dto.PigNutrientRequirementDTO": { "dto.PigNutrientRequirementDTO": {
"type": "object", "type": "object",
"properties": { "properties": {
"created_at": {
"type": "string"
},
"id": { "id": {
"type": "integer" "type": "integer"
}, },
@@ -7716,9 +7753,6 @@
"nutrient_name": { "nutrient_name": {
"description": "营养素名称", "description": "营养素名称",
"type": "string" "type": "string"
},
"updated_at": {
"type": "string"
} }
} }
}, },
@@ -7897,9 +7931,6 @@
"description": "猪品种名称", "description": "猪品种名称",
"type": "string" "type": "string"
}, },
"created_at": {
"type": "string"
},
"daily_feed_intake": { "daily_feed_intake": {
"type": "number" "type": "number"
}, },
@@ -7930,9 +7961,6 @@
"items": { "items": {
"$ref": "#/definitions/dto.PigNutrientRequirementDTO" "$ref": "#/definitions/dto.PigNutrientRequirementDTO"
} }
},
"updated_at": {
"type": "string"
} }
} }
}, },
@@ -8044,9 +8072,6 @@
"dto.RawMaterialNutrientDTO": { "dto.RawMaterialNutrientDTO": {
"type": "object", "type": "object",
"properties": { "properties": {
"created_at": {
"type": "string"
},
"id": { "id": {
"type": "integer" "type": "integer"
}, },
@@ -8057,21 +8082,32 @@
"description": "营养素名称", "description": "营养素名称",
"type": "string" "type": "string"
}, },
"updated_at": {
"type": "string"
},
"value": { "value": {
"description": "营养价值含量", "description": "营养价值含量",
"type": "number" "type": "number"
} }
} }
}, },
"dto.RawMaterialNutrientItem": {
"type": "object",
"required": [
"nutrient_id"
],
"properties": {
"nutrient_id": {
"description": "营养素ID",
"type": "integer"
},
"value": {
"description": "含量值必须大于等于0",
"type": "number",
"minimum": 0
}
}
},
"dto.RawMaterialResponse": { "dto.RawMaterialResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"created_at": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
}, },
@@ -8087,9 +8123,6 @@
"items": { "items": {
"$ref": "#/definitions/dto.RawMaterialNutrientDTO" "$ref": "#/definitions/dto.RawMaterialNutrientDTO"
} }
},
"updated_at": {
"type": "string"
} }
} }
}, },
@@ -8983,6 +9016,20 @@
} }
} }
}, },
"dto.UpdateRawMaterialNutrientsRequest": {
"type": "object",
"required": [
"nutrients"
],
"properties": {
"nutrients": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.RawMaterialNutrientItem"
}
}
}
},
"dto.UpdateRawMaterialRequest": { "dto.UpdateRawMaterialRequest": {
"type": "object", "type": "object",
"required": [ "required": [

View File

@@ -898,8 +898,6 @@ definitions:
type: object type: object
dto.NutrientResponse: dto.NutrientResponse:
properties: properties:
created_at:
type: string
description: description:
type: string type: string
id: id:
@@ -911,8 +909,6 @@ definitions:
items: items:
$ref: '#/definitions/dto.NutrientRawMaterialDTO' $ref: '#/definitions/dto.NutrientRawMaterialDTO'
type: array type: array
updated_at:
type: string
type: object type: object
dto.PaginationDTO: dto.PaginationDTO:
properties: properties:
@@ -959,16 +955,12 @@ definitions:
type: object type: object
dto.PigAgeStageResponse: dto.PigAgeStageResponse:
properties: properties:
created_at:
type: string
description: description:
type: string type: string
id: id:
type: integer type: integer
name: name:
type: string type: string
updated_at:
type: string
type: object type: object
dto.PigBatchCreateDTO: dto.PigBatchCreateDTO:
properties: properties:
@@ -1094,8 +1086,6 @@ definitions:
type: string type: string
breed_disadvantages: breed_disadvantages:
type: string type: string
created_at:
type: string
description: description:
type: string type: string
id: id:
@@ -1104,8 +1094,6 @@ definitions:
type: string type: string
parent_info: parent_info:
type: string type: string
updated_at:
type: string
type: object type: object
dto.PigHouseResponse: dto.PigHouseResponse:
properties: properties:
@@ -1118,8 +1106,6 @@ definitions:
type: object type: object
dto.PigNutrientRequirementDTO: dto.PigNutrientRequirementDTO:
properties: properties:
created_at:
type: string
id: id:
type: integer type: integer
max_requirement: max_requirement:
@@ -1133,8 +1119,6 @@ definitions:
nutrient_name: nutrient_name:
description: 营养素名称 description: 营养素名称
type: string type: string
updated_at:
type: string
type: object type: object
dto.PigPurchaseDTO: dto.PigPurchaseDTO:
properties: properties:
@@ -1252,8 +1236,6 @@ definitions:
breed_name: breed_name:
description: 猪品种名称 description: 猪品种名称
type: string type: string
created_at:
type: string
daily_feed_intake: daily_feed_intake:
type: number type: number
daily_gain_weight: daily_gain_weight:
@@ -1275,8 +1257,6 @@ definitions:
items: items:
$ref: '#/definitions/dto.PigNutrientRequirementDTO' $ref: '#/definitions/dto.PigNutrientRequirementDTO'
type: array type: array
updated_at:
type: string
type: object type: object
dto.PlanExecutionLogDTO: dto.PlanExecutionLogDTO:
properties: properties:
@@ -1346,8 +1326,6 @@ definitions:
type: object type: object
dto.RawMaterialNutrientDTO: dto.RawMaterialNutrientDTO:
properties: properties:
created_at:
type: string
id: id:
type: integer type: integer
nutrient_id: nutrient_id:
@@ -1355,16 +1333,24 @@ definitions:
nutrient_name: nutrient_name:
description: 营养素名称 description: 营养素名称
type: string type: string
updated_at:
type: string
value: value:
description: 营养价值含量 description: 营养价值含量
type: number type: number
type: object type: object
dto.RawMaterialNutrientItem:
properties:
nutrient_id:
description: 营养素ID
type: integer
value:
description: 含量值必须大于等于0
minimum: 0
type: number
required:
- nutrient_id
type: object
dto.RawMaterialResponse: dto.RawMaterialResponse:
properties: properties:
created_at:
type: string
description: description:
type: string type: string
id: id:
@@ -1376,8 +1362,6 @@ definitions:
items: items:
$ref: '#/definitions/dto.RawMaterialNutrientDTO' $ref: '#/definitions/dto.RawMaterialNutrientDTO'
type: array type: array
updated_at:
type: string
type: object type: object
dto.ReclassifyPenToNewBatchRequest: dto.ReclassifyPenToNewBatchRequest:
properties: properties:
@@ -1993,6 +1977,15 @@ definitions:
required: required:
- execution_type - execution_type
type: object type: object
dto.UpdateRawMaterialNutrientsRequest:
properties:
nutrients:
items:
$ref: '#/definitions/dto.RawMaterialNutrientItem'
type: array
required:
- nutrients
type: object
dto.UpdateRawMaterialRequest: dto.UpdateRawMaterialRequest:
properties: properties:
description: description:
@@ -4300,6 +4293,40 @@ paths:
summary: 更新原料 summary: 更新原料
tags: tags:
- 饲料管理 - 饲料管理
/api/v1/feed/raw-materials/{id}/nutrients:
put:
consumes:
- application/json
description: 根据原料ID替换其所有的营养成分信息。这是一个覆盖操作。
parameters:
- description: 原料ID
in: path
name: id
required: true
type: integer
- description: 新的营养成分列表
in: body
name: nutrients
required: true
schema:
$ref: '#/definitions/dto.UpdateRawMaterialNutrientsRequest'
produces:
- application/json
responses:
"200":
description: 业务码为200代表更新成功
schema:
allOf:
- $ref: '#/definitions/controller.Response'
- properties:
data:
$ref: '#/definitions/dto.RawMaterialResponse'
type: object
security:
- BearerAuth: []
summary: 全量更新原料的营养成分
tags:
- 饲料管理
/api/v1/monitor/device-command-logs: /api/v1/monitor/device-command-logs:
get: get:
description: 根据提供的过滤条件,分页获取设备命令日志 description: 根据提供的过滤条件,分页获取设备命令日志

View File

@@ -365,6 +365,48 @@ func (c *Controller) ListRawMaterials(ctx echo.Context) error {
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取原料列表成功", resp, actionType, "获取原料列表成功", resp) 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) 接口方法实现 --- // --- 猪品种 (PigBreed) 接口方法实现 ---
// CreatePigBreed godoc // CreatePigBreed godoc

View File

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

View File

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

View File

@@ -40,6 +40,7 @@ type FeedManagementService interface {
DeleteRawMaterial(ctx context.Context, id uint32) error DeleteRawMaterial(ctx context.Context, id uint32) error
GetRawMaterial(ctx context.Context, id uint32) (*dto.RawMaterialResponse, error) GetRawMaterial(ctx context.Context, id uint32) (*dto.RawMaterialResponse, error)
ListRawMaterials(ctx context.Context, req *dto.ListRawMaterialRequest) (*dto.ListRawMaterialResponse, 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) 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 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) 实现 // 猪品种 (PigBreed) 实现
// ===================================================================================================================== // =====================================================================================================================

View File

@@ -217,6 +217,7 @@ func initDomainServices(ctx context.Context, cfg *config.Config, infra *Infrastr
// 配方管理服务 // 配方管理服务
recipeService := recipe.NewRecipeService( recipeService := recipe.NewRecipeService(
logs.AddCompName(baseCtx, "RecipeService"), logs.AddCompName(baseCtx, "RecipeService"),
infra.repos.unitOfWork,
infra.repos.nutrientRepo, infra.repos.nutrientRepo,
infra.repos.rawMaterialRepo, infra.repos.rawMaterialRepo,
infra.repos.pigTypeRepo, infra.repos.pigTypeRepo,

View File

@@ -40,6 +40,7 @@ type Service interface {
DeleteRawMaterial(ctx context.Context, id uint32) error DeleteRawMaterial(ctx context.Context, id uint32) error
GetRawMaterial(ctx context.Context, id uint32) (*models.RawMaterial, error) GetRawMaterial(ctx context.Context, id uint32) (*models.RawMaterial, error)
ListRawMaterials(ctx context.Context, opts repository.RawMaterialListOptions, page, pageSize int) ([]models.RawMaterial, int64, error) ListRawMaterials(ctx context.Context, opts repository.RawMaterialListOptions, page, pageSize int) ([]models.RawMaterial, int64, error)
UpdateRawMaterialNutrients(ctx context.Context, rawMaterialID uint32, nutrients []models.RawMaterialNutrient) error
// 猪品种相关接口 // 猪品种相关接口
CreatePigBreed(ctx context.Context, breed *models.PigBreed) error CreatePigBreed(ctx context.Context, breed *models.PigBreed) error
@@ -66,15 +67,17 @@ type Service interface {
// recipeServiceImpl 是 RecipeService 的实现 // recipeServiceImpl 是 RecipeService 的实现
type recipeServiceImpl struct { type recipeServiceImpl struct {
ctx context.Context ctx context.Context
uow repository.UnitOfWork
nutrientRepo repository.NutrientRepository nutrientRepo repository.NutrientRepository
rawMaterialRepo repository.RawMaterialRepository rawMaterialRepo repository.RawMaterialRepository
pigTypeRepo repository.PigTypeRepository pigTypeRepo repository.PigTypeRepository
} }
// NewRecipeService 创建一个新的 RecipeService 实例 // NewRecipeService 创建一个新的 RecipeService 实例
func NewRecipeService(ctx context.Context, nutrientRepo repository.NutrientRepository, rawMaterialRepo repository.RawMaterialRepository, pigTypeRepo repository.PigTypeRepository) Service { func NewRecipeService(ctx context.Context, uow repository.UnitOfWork, nutrientRepo repository.NutrientRepository, rawMaterialRepo repository.RawMaterialRepository, pigTypeRepo repository.PigTypeRepository) Service {
return &recipeServiceImpl{ return &recipeServiceImpl{
ctx: ctx, ctx: ctx,
uow: uow,
nutrientRepo: nutrientRepo, nutrientRepo: nutrientRepo,
rawMaterialRepo: rawMaterialRepo, rawMaterialRepo: rawMaterialRepo,
pigTypeRepo: pigTypeRepo, pigTypeRepo: pigTypeRepo,
@@ -289,6 +292,41 @@ func (s *recipeServiceImpl) ListRawMaterials(ctx context.Context, opts repositor
return rawMaterials, total, nil return rawMaterials, total, nil
} }
// UpdateRawMaterialNutrients 实现了全量更新原料营养成分的业务逻辑
func (s *recipeServiceImpl) UpdateRawMaterialNutrients(ctx context.Context, rawMaterialID uint32, nutrients []models.RawMaterialNutrient) error {
serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateRawMaterialNutrients")
// 1. 检查原料是否存在
if _, err := s.rawMaterialRepo.GetRawMaterialByID(serviceCtx, rawMaterialID); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return ErrRawMaterialNotFound
}
return fmt.Errorf("获取待更新的原料失败: %w", err)
}
// 2. 在事务中执行替换操作
err := s.uow.ExecuteInTransaction(serviceCtx, func(tx *gorm.DB) error {
// 2.1. 删除旧的关联记录
if err := s.rawMaterialRepo.DeleteNutrientsByRawMaterialIDTx(serviceCtx, tx, rawMaterialID); err != nil {
return err // 错误已在仓库层封装,直接返回
}
// 2.2. 创建新的关联记录
if err := s.rawMaterialRepo.CreateBatchRawMaterialNutrientsTx(serviceCtx, tx, nutrients); err != nil {
return err // 错误已在仓库层封装,直接返回
}
return nil
})
if err != nil {
return fmt.Errorf("更新原料营养成分事务执行失败: %w", err)
}
// 3. 操作成功,直接返回 nil
return nil
}
// CreatePigBreed 实现了创建猪品种的核心业务逻辑 // CreatePigBreed 实现了创建猪品种的核心业务逻辑
func (s *recipeServiceImpl) CreatePigBreed(ctx context.Context, breed *models.PigBreed) error { func (s *recipeServiceImpl) CreatePigBreed(ctx context.Context, breed *models.PigBreed) error {
serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreatePigBreed") serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreatePigBreed")

View File

@@ -26,6 +26,8 @@ type RawMaterialRepository interface {
ListRawMaterials(ctx context.Context, opts RawMaterialListOptions, page, pageSize int) ([]models.RawMaterial, int64, error) ListRawMaterials(ctx context.Context, opts RawMaterialListOptions, page, pageSize int) ([]models.RawMaterial, int64, error)
UpdateRawMaterial(ctx context.Context, rawMaterial *models.RawMaterial) error UpdateRawMaterial(ctx context.Context, rawMaterial *models.RawMaterial) error
DeleteRawMaterial(ctx context.Context, id uint32) error DeleteRawMaterial(ctx context.Context, id uint32) error
DeleteNutrientsByRawMaterialIDTx(ctx context.Context, db *gorm.DB, rawMaterialID uint32) error
CreateBatchRawMaterialNutrientsTx(ctx context.Context, db *gorm.DB, nutrients []models.RawMaterialNutrient) error
// 库存日志相关方法 // 库存日志相关方法
CreateRawMaterialStockLog(ctx context.Context, log *models.RawMaterialStockLog) error CreateRawMaterialStockLog(ctx context.Context, log *models.RawMaterialStockLog) error
@@ -162,6 +164,30 @@ func (r *gormRawMaterialRepository) DeleteRawMaterial(ctx context.Context, id ui
}) })
} }
// DeleteNutrientsByRawMaterialIDTx 在事务中软删除指定原料的所有营养成分
func (r *gormRawMaterialRepository) DeleteNutrientsByRawMaterialIDTx(ctx context.Context, db *gorm.DB, rawMaterialID uint32) error {
repoCtx := logs.AddFuncName(ctx, r.ctx, "DeleteNutrientsByRawMaterialIDTx")
tx := db.WithContext(repoCtx)
if err := tx.Where("raw_material_id = ?", rawMaterialID).Delete(&models.RawMaterialNutrient{}).Error; err != nil {
return fmt.Errorf("软删除原料营养成分失败: %w", err)
}
return nil
}
// CreateBatchRawMaterialNutrientsTx 在事务中批量创建原料营养成分
func (r *gormRawMaterialRepository) CreateBatchRawMaterialNutrientsTx(ctx context.Context, db *gorm.DB, nutrients []models.RawMaterialNutrient) error {
// 如果没有要创建的记录直接返回成功避免执行空的Create语句
if len(nutrients) == 0 {
return nil
}
repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateBatchRawMaterialNutrientsTx")
tx := db.WithContext(repoCtx)
if err := tx.Create(&nutrients).Error; err != nil {
return fmt.Errorf("批量创建原料营养成分失败: %w", err)
}
return nil
}
// CreateRawMaterialStockLog 创建一条新的原料库存日志 // CreateRawMaterialStockLog 创建一条新的原料库存日志
func (r *gormRawMaterialRepository) CreateRawMaterialStockLog(ctx context.Context, log *models.RawMaterialStockLog) error { func (r *gormRawMaterialRepository) CreateRawMaterialStockLog(ctx context.Context, log *models.RawMaterialStockLog) error {
repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateRawMaterialStockLog") repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateRawMaterialStockLog")