配方增删改查服务层和控制器
This commit is contained in:
@@ -61,3 +61,4 @@ http://git.huangwc.com/pig/pig-farm-controller/issues/66
|
||||
11. 配方模型定义和仓库层增删改查方法
|
||||
12. 配方领域层方法
|
||||
13. 重构配方领域
|
||||
14. 配方增删改查服务层和控制器
|
||||
348
docs/docs.go
348
docs/docs.go
@@ -3021,6 +3021,252 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/feed/recipes": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取所有配方的列表,支持分页和过滤。",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"饲料管理-配方"
|
||||
],
|
||||
"summary": "获取配方列表",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "按名称模糊查询",
|
||||
"name": "name",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "排序字段,例如 \"id DESC\"",
|
||||
"name": "order_by",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "页码",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "每页数量",
|
||||
"name": "page_size",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "业务码为200代表成功获取列表",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/controller.Response"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/dto.ListRecipeResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "创建一个新的配方,包含其原料组成。",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"饲料管理-配方"
|
||||
],
|
||||
"summary": "创建配方",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "配方信息",
|
||||
"name": "recipe",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.CreateRecipeRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "业务码为201代表创建成功",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/controller.Response"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/dto.RecipeResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/feed/recipes/{id}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "根据ID获取单个配方的详细信息。",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"饲料管理-配方"
|
||||
],
|
||||
"summary": "获取配方详情",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "配方ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "业务码为200代表成功获取",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/controller.Response"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/dto.RecipeResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": "recipe",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.UpdateRecipeRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "业务码为200代表更新成功",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/controller.Response"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/dto.RecipeResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "根据ID删除配方。",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"饲料管理-配方"
|
||||
],
|
||||
"summary": "删除配方",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "配方ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "业务码为200代表删除成功",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controller.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/monitor/device-command-logs": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -6761,6 +7007,31 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.CreateRecipeRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"description": "配方描述",
|
||||
"type": "string",
|
||||
"maxLength": 255
|
||||
},
|
||||
"name": {
|
||||
"description": "配方名称",
|
||||
"type": "string",
|
||||
"maxLength": 100
|
||||
},
|
||||
"recipe_ingredients": {
|
||||
"description": "配方原料组成",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.RecipeIngredientDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.CreateUserRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -7241,6 +7512,20 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.ListRecipeResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"list": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.RecipeResponse"
|
||||
}
|
||||
},
|
||||
"pagination": {
|
||||
"$ref": "#/definitions/dto.PaginationDTO"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.ListSensorDataResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -8214,6 +8499,44 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.RecipeIngredientDto": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"raw_material_id"
|
||||
],
|
||||
"properties": {
|
||||
"percentage": {
|
||||
"description": "原料在配方中的百分比 (0-1之间)",
|
||||
"type": "number",
|
||||
"maximum": 1,
|
||||
"minimum": 0
|
||||
},
|
||||
"raw_material_id": {
|
||||
"description": "原料ID",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.RecipeResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"recipe_ingredients": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.RecipeIngredientDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.ReclassifyPenToNewBatchRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -9150,6 +9473,31 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.UpdateRecipeRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"description": "配方描述",
|
||||
"type": "string",
|
||||
"maxLength": 255
|
||||
},
|
||||
"name": {
|
||||
"description": "配方名称",
|
||||
"type": "string",
|
||||
"maxLength": 100
|
||||
},
|
||||
"recipe_ingredients": {
|
||||
"description": "配方原料组成",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.RecipeIngredientDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.UserActionLogDTO": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -3013,6 +3013,252 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/feed/recipes": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取所有配方的列表,支持分页和过滤。",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"饲料管理-配方"
|
||||
],
|
||||
"summary": "获取配方列表",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "按名称模糊查询",
|
||||
"name": "name",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "排序字段,例如 \"id DESC\"",
|
||||
"name": "order_by",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "页码",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "每页数量",
|
||||
"name": "page_size",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "业务码为200代表成功获取列表",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/controller.Response"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/dto.ListRecipeResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "创建一个新的配方,包含其原料组成。",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"饲料管理-配方"
|
||||
],
|
||||
"summary": "创建配方",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "配方信息",
|
||||
"name": "recipe",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.CreateRecipeRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "业务码为201代表创建成功",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/controller.Response"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/dto.RecipeResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/feed/recipes/{id}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "根据ID获取单个配方的详细信息。",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"饲料管理-配方"
|
||||
],
|
||||
"summary": "获取配方详情",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "配方ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "业务码为200代表成功获取",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/controller.Response"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/dto.RecipeResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": "recipe",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.UpdateRecipeRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "业务码为200代表更新成功",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/controller.Response"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/dto.RecipeResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "根据ID删除配方。",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"饲料管理-配方"
|
||||
],
|
||||
"summary": "删除配方",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "配方ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "业务码为200代表删除成功",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/controller.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/monitor/device-command-logs": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -6753,6 +6999,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.CreateRecipeRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"description": "配方描述",
|
||||
"type": "string",
|
||||
"maxLength": 255
|
||||
},
|
||||
"name": {
|
||||
"description": "配方名称",
|
||||
"type": "string",
|
||||
"maxLength": 100
|
||||
},
|
||||
"recipe_ingredients": {
|
||||
"description": "配方原料组成",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.RecipeIngredientDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.CreateUserRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -7233,6 +7504,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.ListRecipeResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"list": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.RecipeResponse"
|
||||
}
|
||||
},
|
||||
"pagination": {
|
||||
"$ref": "#/definitions/dto.PaginationDTO"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.ListSensorDataResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -8206,6 +8491,44 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.RecipeIngredientDto": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"raw_material_id"
|
||||
],
|
||||
"properties": {
|
||||
"percentage": {
|
||||
"description": "原料在配方中的百分比 (0-1之间)",
|
||||
"type": "number",
|
||||
"maximum": 1,
|
||||
"minimum": 0
|
||||
},
|
||||
"raw_material_id": {
|
||||
"description": "原料ID",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.RecipeResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"recipe_ingredients": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.RecipeIngredientDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.ReclassifyPenToNewBatchRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -9142,6 +9465,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.UpdateRecipeRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"description": "配方描述",
|
||||
"type": "string",
|
||||
"maxLength": 255
|
||||
},
|
||||
"name": {
|
||||
"description": "配方名称",
|
||||
"type": "string",
|
||||
"maxLength": 100
|
||||
},
|
||||
"recipe_ingredients": {
|
||||
"description": "配方原料组成",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.RecipeIngredientDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.UserActionLogDTO": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -423,6 +423,24 @@ definitions:
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
dto.CreateRecipeRequest:
|
||||
properties:
|
||||
description:
|
||||
description: 配方描述
|
||||
maxLength: 255
|
||||
type: string
|
||||
name:
|
||||
description: 配方名称
|
||||
maxLength: 100
|
||||
type: string
|
||||
recipe_ingredients:
|
||||
description: 配方原料组成
|
||||
items:
|
||||
$ref: '#/definitions/dto.RecipeIngredientDto'
|
||||
type: array
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
dto.CreateUserRequest:
|
||||
properties:
|
||||
password:
|
||||
@@ -735,6 +753,15 @@ definitions:
|
||||
pagination:
|
||||
$ref: '#/definitions/dto.PaginationDTO'
|
||||
type: object
|
||||
dto.ListRecipeResponse:
|
||||
properties:
|
||||
list:
|
||||
items:
|
||||
$ref: '#/definitions/dto.RecipeResponse'
|
||||
type: array
|
||||
pagination:
|
||||
$ref: '#/definitions/dto.PaginationDTO'
|
||||
type: object
|
||||
dto.ListSensorDataResponse:
|
||||
properties:
|
||||
list:
|
||||
@@ -1379,6 +1406,32 @@ definitions:
|
||||
$ref: '#/definitions/dto.RawMaterialNutrientDTO'
|
||||
type: array
|
||||
type: object
|
||||
dto.RecipeIngredientDto:
|
||||
properties:
|
||||
percentage:
|
||||
description: 原料在配方中的百分比 (0-1之间)
|
||||
maximum: 1
|
||||
minimum: 0
|
||||
type: number
|
||||
raw_material_id:
|
||||
description: 原料ID
|
||||
type: integer
|
||||
required:
|
||||
- raw_material_id
|
||||
type: object
|
||||
dto.RecipeResponse:
|
||||
properties:
|
||||
description:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
recipe_ingredients:
|
||||
items:
|
||||
$ref: '#/definitions/dto.RecipeIngredientDto'
|
||||
type: array
|
||||
type: object
|
||||
dto.ReclassifyPenToNewBatchRequest:
|
||||
properties:
|
||||
pen_id:
|
||||
@@ -2024,6 +2077,24 @@ definitions:
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
dto.UpdateRecipeRequest:
|
||||
properties:
|
||||
description:
|
||||
description: 配方描述
|
||||
maxLength: 255
|
||||
type: string
|
||||
name:
|
||||
description: 配方名称
|
||||
maxLength: 100
|
||||
type: string
|
||||
recipe_ingredients:
|
||||
description: 配方原料组成
|
||||
items:
|
||||
$ref: '#/definitions/dto.RecipeIngredientDto'
|
||||
type: array
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
dto.UserActionLogDTO:
|
||||
properties:
|
||||
action_type:
|
||||
@@ -4386,6 +4457,150 @@ paths:
|
||||
summary: 全量更新原料的营养成分
|
||||
tags:
|
||||
- 饲料管理-原料
|
||||
/api/v1/feed/recipes:
|
||||
get:
|
||||
description: 获取所有配方的列表,支持分页和过滤。
|
||||
parameters:
|
||||
- description: 按名称模糊查询
|
||||
in: query
|
||||
name: name
|
||||
type: string
|
||||
- description: 排序字段,例如 "id DESC"
|
||||
in: query
|
||||
name: order_by
|
||||
type: string
|
||||
- description: 页码
|
||||
in: query
|
||||
name: page
|
||||
type: integer
|
||||
- description: 每页数量
|
||||
in: query
|
||||
name: page_size
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 业务码为200代表成功获取列表
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/controller.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/dto.ListRecipeResponse'
|
||||
type: object
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 获取配方列表
|
||||
tags:
|
||||
- 饲料管理-配方
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 创建一个新的配方,包含其原料组成。
|
||||
parameters:
|
||||
- description: 配方信息
|
||||
in: body
|
||||
name: recipe
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.CreateRecipeRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 业务码为201代表创建成功
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/controller.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/dto.RecipeResponse'
|
||||
type: object
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 创建配方
|
||||
tags:
|
||||
- 饲料管理-配方
|
||||
/api/v1/feed/recipes/{id}:
|
||||
delete:
|
||||
description: 根据ID删除配方。
|
||||
parameters:
|
||||
- description: 配方ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 业务码为200代表删除成功
|
||||
schema:
|
||||
$ref: '#/definitions/controller.Response'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 删除配方
|
||||
tags:
|
||||
- 饲料管理-配方
|
||||
get:
|
||||
description: 根据ID获取单个配方的详细信息。
|
||||
parameters:
|
||||
- description: 配方ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 业务码为200代表成功获取
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/controller.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/dto.RecipeResponse'
|
||||
type: object
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 获取配方详情
|
||||
tags:
|
||||
- 饲料管理-配方
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 根据ID更新配方信息及其原料组成。
|
||||
parameters:
|
||||
- description: 配方ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
- description: 更新后的配方信息
|
||||
in: body
|
||||
name: recipe
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.UpdateRecipeRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 业务码为200代表更新成功
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/controller.Response'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/dto.RecipeResponse'
|
||||
type: object
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: 更新配方
|
||||
tags:
|
||||
- 饲料管理-配方
|
||||
/api/v1/monitor/device-command-logs:
|
||||
get:
|
||||
description: 根据提供的过滤条件,分页获取设备命令日志
|
||||
|
||||
@@ -61,6 +61,7 @@ type API struct {
|
||||
pigBreedController *feed.PigBreedController // 猪品种控制器实例
|
||||
pigTypeController *feed.PigTypeController // 猪种类控制器实例
|
||||
rawMaterialController *feed.RawMaterialController // 原料控制器实例
|
||||
recipeController *feed.RecipeController // 配方控制器实例
|
||||
listenHandler webhook.ListenHandler // 设备上行事件监听器
|
||||
analysisTaskManager *domain_plan.AnalysisPlanTaskManager // 计划触发器管理器实例
|
||||
}
|
||||
@@ -83,6 +84,7 @@ func NewAPI(cfg config.ServerConfig,
|
||||
pigBreedService service.PigBreedService,
|
||||
pigAgeStageService service.PigAgeStageService,
|
||||
pigTypeService service.PigTypeService,
|
||||
recipeService service.RecipeService,
|
||||
tokenGenerator token.Generator,
|
||||
listenHandler webhook.ListenHandler,
|
||||
) *API {
|
||||
@@ -119,6 +121,7 @@ func NewAPI(cfg config.ServerConfig,
|
||||
pigBreedController: feed.NewPigBreedController(logs.AddCompName(baseCtx, "PigBreedController"), pigBreedService),
|
||||
pigTypeController: feed.NewPigTypeController(logs.AddCompName(baseCtx, "PigTypeController"), pigTypeService),
|
||||
rawMaterialController: feed.NewRawMaterialController(logs.AddCompName(baseCtx, "RawMaterialController"), rawMaterialService),
|
||||
recipeController: feed.NewRecipeController(logs.AddCompName(baseCtx, "RecipeController"), recipeService),
|
||||
}
|
||||
|
||||
api.setupRoutes() // 设置所有路由
|
||||
|
||||
@@ -252,6 +252,13 @@ func (a *API) setupRoutes() {
|
||||
feedGroup.GET("/pig-types/:id", a.pigTypeController.GetPigType)
|
||||
feedGroup.GET("/pig-types", a.pigTypeController.ListPigTypes)
|
||||
feedGroup.PUT("/pig-types/:id/nutrient-requirements", a.pigTypeController.UpdatePigTypeNutrientRequirements)
|
||||
|
||||
// 配方 (Recipe) 路由
|
||||
feedGroup.POST("/recipes", a.recipeController.CreateRecipe)
|
||||
feedGroup.PUT("/recipes/:id", a.recipeController.UpdateRecipe)
|
||||
feedGroup.DELETE("/recipes/:id", a.recipeController.DeleteRecipe)
|
||||
feedGroup.GET("/recipes/:id", a.recipeController.GetRecipe)
|
||||
feedGroup.GET("/recipes", a.recipeController.ListRecipes)
|
||||
}
|
||||
logger.Debug("饲料管理相关接口注册成功 (需要认证和审计)")
|
||||
}
|
||||
|
||||
196
internal/app/controller/feed/recipe_controller.go
Normal file
196
internal/app/controller/feed/recipe_controller.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package feed
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/service"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/domain/recipe"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// RecipeController 包含配方相关的处理器
|
||||
type RecipeController struct {
|
||||
ctx context.Context
|
||||
recipeService service.RecipeService
|
||||
}
|
||||
|
||||
// NewRecipeController 创建一个新的 RecipeController
|
||||
func NewRecipeController(ctx context.Context, recipeService service.RecipeService) *RecipeController {
|
||||
return &RecipeController{
|
||||
ctx: ctx,
|
||||
recipeService: recipeService,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateRecipe godoc
|
||||
// @Summary 创建配方
|
||||
// @Description 创建一个新的配方,包含其原料组成。
|
||||
// @Tags 饲料管理-配方
|
||||
// @Security BearerAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param recipe body dto.CreateRecipeRequest true "配方信息"
|
||||
// @Success 200 {object} controller.Response{data=dto.RecipeResponse} "业务码为201代表创建成功"
|
||||
// @Router /api/v1/feed/recipes [post]
|
||||
func (c *RecipeController) CreateRecipe(ctx echo.Context) error {
|
||||
reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "CreateRecipe")
|
||||
var req dto.CreateRecipeRequest
|
||||
const actionType = "创建配方"
|
||||
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.recipeService.CreateRecipe(reqCtx, &req)
|
||||
if err != nil {
|
||||
logger.Errorf("%s: 服务层创建配方失败: %v", actionType, err)
|
||||
if errors.Is(err, recipe.ErrRecipeNameConflict) {
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), actionType, "配方名称已存在", req)
|
||||
}
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建配方失败: "+err.Error(), actionType, "服务层创建配方失败", req)
|
||||
}
|
||||
|
||||
logger.Infof("%s: 配方创建成功, ID: %d", actionType, resp.ID)
|
||||
return controller.SendSuccessWithAudit(ctx, controller.CodeCreated, "配方创建成功", resp, actionType, "配方创建成功", resp)
|
||||
}
|
||||
|
||||
// UpdateRecipe godoc
|
||||
// @Summary 更新配方
|
||||
// @Description 根据ID更新配方信息及其原料组成。
|
||||
// @Tags 饲料管理-配方
|
||||
// @Security BearerAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "配方ID"
|
||||
// @Param recipe body dto.UpdateRecipeRequest true "更新后的配方信息"
|
||||
// @Success 200 {object} controller.Response{data=dto.RecipeResponse} "业务码为200代表更新成功"
|
||||
// @Router /api/v1/feed/recipes/{id} [put]
|
||||
func (c *RecipeController) UpdateRecipe(ctx echo.Context) error {
|
||||
reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "UpdateRecipe")
|
||||
const actionType = "更新配方"
|
||||
idStr := ctx.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
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.UpdateRecipeRequest
|
||||
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.recipeService.UpdateRecipe(reqCtx, uint32(id), &req)
|
||||
if err != nil {
|
||||
logger.Errorf("%s: 服务层更新配方失败: %v, ID: %d", actionType, err, id)
|
||||
if errors.Is(err, recipe.ErrRecipeNotFound) {
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "配方不存在", id)
|
||||
}
|
||||
if errors.Is(err, recipe.ErrRecipeNameConflict) {
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), actionType, "配方名称已存在", req)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
// DeleteRecipe godoc
|
||||
// @Summary 删除配方
|
||||
// @Description 根据ID删除配方。
|
||||
// @Tags 饲料管理-配方
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Param id path int true "配方ID"
|
||||
// @Success 200 {object} controller.Response "业务码为200代表删除成功"
|
||||
// @Router /api/v1/feed/recipes/{id} [delete]
|
||||
func (c *RecipeController) DeleteRecipe(ctx echo.Context) error {
|
||||
reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "DeleteRecipe")
|
||||
const actionType = "删除配方"
|
||||
idStr := ctx.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
logger.Errorf("%s: 配方ID格式错误: %v, ID: %s", actionType, err, idStr)
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的配方ID格式", actionType, "配方ID格式错误", idStr)
|
||||
}
|
||||
|
||||
err = c.recipeService.DeleteRecipe(reqCtx, uint32(id))
|
||||
if err != nil {
|
||||
logger.Errorf("%s: 服务层删除配方失败: %v, ID: %d", actionType, err, id)
|
||||
if errors.Is(err, recipe.ErrRecipeNotFound) {
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "配方不存在", id)
|
||||
}
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除配方失败: "+err.Error(), actionType, "服务层删除配方失败", id)
|
||||
}
|
||||
|
||||
logger.Infof("%s: 配方删除成功, ID: %d", actionType, id)
|
||||
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "配方删除成功", nil, actionType, "配方删除成功", id)
|
||||
}
|
||||
|
||||
// GetRecipe godoc
|
||||
// @Summary 获取配方详情
|
||||
// @Description 根据ID获取单个配方的详细信息。
|
||||
// @Tags 饲料管理-配方
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Param id path int true "配方ID"
|
||||
// @Success 200 {object} controller.Response{data=dto.RecipeResponse} "业务码为200代表成功获取"
|
||||
// @Router /api/v1/feed/recipes/{id} [get]
|
||||
func (c *RecipeController) GetRecipe(ctx echo.Context) error {
|
||||
reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "GetRecipe")
|
||||
const actionType = "获取配方详情"
|
||||
idStr := ctx.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
logger.Errorf("%s: 配方ID格式错误: %v, ID: %s", actionType, err, idStr)
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的配方ID格式", actionType, "配方ID格式错误", idStr)
|
||||
}
|
||||
|
||||
resp, err := c.recipeService.GetRecipeByID(reqCtx, uint32(id))
|
||||
if err != nil {
|
||||
logger.Errorf("%s: 服务层获取配方详情失败: %v, ID: %d", actionType, err, id)
|
||||
if errors.Is(err, recipe.ErrRecipeNotFound) {
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "配方不存在", id)
|
||||
}
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取配方详情失败: "+err.Error(), actionType, "服务层获取配方详情失败", id)
|
||||
}
|
||||
|
||||
logger.Infof("%s: 获取配方详情成功, ID: %d", actionType, id)
|
||||
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取配方详情成功", resp, actionType, "获取配方详情成功", resp)
|
||||
}
|
||||
|
||||
// ListRecipes godoc
|
||||
// @Summary 获取配方列表
|
||||
// @Description 获取所有配方的列表,支持分页和过滤。
|
||||
// @Tags 饲料管理-配方
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Param query query dto.ListRecipeRequest false "查询参数"
|
||||
// @Success 200 {object} controller.Response{data=dto.ListRecipeResponse} "业务码为200代表成功获取列表"
|
||||
// @Router /api/v1/feed/recipes [get]
|
||||
func (c *RecipeController) ListRecipes(ctx echo.Context) error {
|
||||
reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListRecipes")
|
||||
const actionType = "获取配方列表"
|
||||
var req dto.ListRecipeRequest
|
||||
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.recipeService.ListRecipes(reqCtx, &req)
|
||||
if err != nil {
|
||||
logger.Errorf("%s: 服务层获取配方列表失败: %v", actionType, err)
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取配方列表失败: "+err.Error(), actionType, "服务层获取配方列表失败", nil)
|
||||
}
|
||||
|
||||
logger.Infof("%s: 获取配方列表成功, 数量: %d", actionType, len(resp.List))
|
||||
return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取配方列表成功", resp, actionType, "获取配方列表成功", resp)
|
||||
}
|
||||
@@ -198,3 +198,84 @@ func ConvertPigTypeListToDTO(pigTypes []models.PigType, total int64, page, pageS
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertRecipeToDto 将 models.Recipe 转换为 RecipeResponse DTO
|
||||
func ConvertRecipeToDto(recipe *models.Recipe) *RecipeResponse {
|
||||
if recipe == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ingredients := make([]RecipeIngredientDto, len(recipe.RecipeIngredients))
|
||||
for i, ri := range recipe.RecipeIngredients {
|
||||
ingredients[i] = RecipeIngredientDto{
|
||||
RawMaterialID: ri.RawMaterialID,
|
||||
Percentage: ri.Percentage,
|
||||
}
|
||||
}
|
||||
|
||||
return &RecipeResponse{
|
||||
ID: recipe.ID,
|
||||
Name: recipe.Name,
|
||||
Description: recipe.Description,
|
||||
RecipeIngredients: ingredients,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertRecipeListToDTO 将 []models.Recipe 转换为 ListRecipeResponse DTO
|
||||
func ConvertRecipeListToDTO(recipes []models.Recipe, total int64, page, pageSize int) *ListRecipeResponse {
|
||||
recipeDTOs := make([]RecipeResponse, len(recipes))
|
||||
for i, r := range recipes {
|
||||
recipeDTOs[i] = *ConvertRecipeToDto(&r)
|
||||
}
|
||||
|
||||
return &ListRecipeResponse{
|
||||
List: recipeDTOs,
|
||||
Pagination: PaginationDTO{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
Total: total,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertCreateRecipeRequestToModel 将 CreateRecipeRequest DTO 转换为 models.Recipe 模型
|
||||
func ConvertCreateRecipeRequestToModel(req *CreateRecipeRequest) *models.Recipe {
|
||||
if req == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ingredients := make([]models.RecipeIngredient, len(req.RecipeIngredients))
|
||||
for i, ri := range req.RecipeIngredients {
|
||||
ingredients[i] = models.RecipeIngredient{
|
||||
RawMaterialID: ri.RawMaterialID,
|
||||
Percentage: ri.Percentage,
|
||||
}
|
||||
}
|
||||
|
||||
return &models.Recipe{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
RecipeIngredients: ingredients,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertUpdateRecipeRequestToModel 将 UpdateRecipeRequest DTO 转换为 models.Recipe 模型
|
||||
func ConvertUpdateRecipeRequestToModel(req *UpdateRecipeRequest) *models.Recipe {
|
||||
if req == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ingredients := make([]models.RecipeIngredient, len(req.RecipeIngredients))
|
||||
for i, ri := range req.RecipeIngredients {
|
||||
ingredients[i] = models.RecipeIngredient{
|
||||
RawMaterialID: ri.RawMaterialID,
|
||||
Percentage: ri.Percentage,
|
||||
}
|
||||
}
|
||||
|
||||
return &models.Recipe{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
RecipeIngredients: ingredients,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,3 +274,49 @@ type PigNutrientRequirementItem struct {
|
||||
MinRequirement float32 `json:"min_requirement" validate:"gte=0"` // 最低营养需求量
|
||||
MaxRequirement float32 `json:"max_requirement" validate:"gte=0"` // 最高营养需求量
|
||||
}
|
||||
|
||||
// =============================================================================================================
|
||||
// 配方 (Recipe) 相关 DTO
|
||||
// =============================================================================================================
|
||||
|
||||
// RecipeIngredientDto 代表配方中的一个原料及其百分比
|
||||
type RecipeIngredientDto struct {
|
||||
RawMaterialID uint32 `json:"raw_material_id" validate:"required"` // 原料ID
|
||||
Percentage float32 `json:"percentage" validate:"gte=0,lte=1"` // 原料在配方中的百分比 (0-1之间)
|
||||
}
|
||||
|
||||
// CreateRecipeRequest 创建配方的请求体
|
||||
type CreateRecipeRequest struct {
|
||||
Name string `json:"name" validate:"required,max=100"` // 配方名称
|
||||
Description string `json:"description" validate:"max=255"` // 配方描述
|
||||
RecipeIngredients []RecipeIngredientDto `json:"recipe_ingredients" validate:"dive"` // 配方原料组成
|
||||
}
|
||||
|
||||
// UpdateRecipeRequest 更新配方的请求体
|
||||
type UpdateRecipeRequest struct {
|
||||
Name string `json:"name" validate:"required,max=100"` // 配方名称
|
||||
Description string `json:"description" validate:"max=255"` // 配方描述
|
||||
RecipeIngredients []RecipeIngredientDto `json:"recipe_ingredients" validate:"dive"` // 配方原料组成
|
||||
}
|
||||
|
||||
// RecipeResponse 配方响应体
|
||||
type RecipeResponse struct {
|
||||
ID uint32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
RecipeIngredients []RecipeIngredientDto `json:"recipe_ingredients"`
|
||||
}
|
||||
|
||||
// ListRecipeRequest 定义了获取配方列表的请求参数
|
||||
type ListRecipeRequest struct {
|
||||
Page int `json:"page" query:"page"` // 页码
|
||||
PageSize int `json:"page_size" query:"page_size"` // 每页数量
|
||||
Name *string `json:"name" query:"name"` // 按名称模糊查询
|
||||
OrderBy string `json:"order_by" query:"order_by"` // 排序字段,例如 "id DESC"
|
||||
}
|
||||
|
||||
// ListRecipeResponse 是获取配方列表的响应结构
|
||||
type ListRecipeResponse struct {
|
||||
List []RecipeResponse `json:"list"`
|
||||
Pagination PaginationDTO `json:"pagination"`
|
||||
}
|
||||
|
||||
159
internal/app/service/recipe_service.go
Normal file
159
internal/app/service/recipe_service.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/domain/recipe"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
||||
)
|
||||
|
||||
// 定义配方服务特定的错误
|
||||
var (
|
||||
ErrRecipeNameConflict = errors.New("配方名称已存在")
|
||||
ErrRecipeNotFound = errors.New("配方不存在")
|
||||
)
|
||||
|
||||
// RecipeService 定义了配方相关的应用服务接口
|
||||
type RecipeService interface {
|
||||
CreateRecipe(ctx context.Context, req *dto.CreateRecipeRequest) (*dto.RecipeResponse, error)
|
||||
UpdateRecipe(ctx context.Context, id uint32, req *dto.UpdateRecipeRequest) (*dto.RecipeResponse, error)
|
||||
DeleteRecipe(ctx context.Context, id uint32) error
|
||||
GetRecipeByID(ctx context.Context, id uint32) (*dto.RecipeResponse, error)
|
||||
ListRecipes(ctx context.Context, req *dto.ListRecipeRequest) (*dto.ListRecipeResponse, error)
|
||||
}
|
||||
|
||||
// recipeServiceImpl 是 RecipeService 接口的实现
|
||||
type recipeServiceImpl struct {
|
||||
ctx context.Context
|
||||
recipeSvc recipe.RecipeCoreService
|
||||
}
|
||||
|
||||
// NewRecipeService 创建一个新的 RecipeService 实例
|
||||
func NewRecipeService(ctx context.Context, recipeSvc recipe.RecipeCoreService) RecipeService {
|
||||
return &recipeServiceImpl{
|
||||
ctx: ctx,
|
||||
recipeSvc: recipeSvc,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateRecipe 创建配方
|
||||
func (s *recipeServiceImpl) CreateRecipe(ctx context.Context, req *dto.CreateRecipeRequest) (*dto.RecipeResponse, error) {
|
||||
serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateRecipe")
|
||||
|
||||
recipeModel := dto.ConvertCreateRecipeRequestToModel(req)
|
||||
|
||||
createdRecipe, err := s.recipeSvc.CreateRecipe(serviceCtx, recipeModel)
|
||||
if err != nil {
|
||||
if errors.Is(err, recipe.ErrRecipeNameConflict) {
|
||||
return nil, ErrRecipeNameConflict
|
||||
}
|
||||
return nil, fmt.Errorf("创建配方失败: %w", err)
|
||||
}
|
||||
|
||||
// 创建成功后,获取包含完整信息的配方
|
||||
fullRecipe, err := s.recipeSvc.GetRecipeByID(serviceCtx, createdRecipe.ID)
|
||||
if err != nil {
|
||||
// 理论上不应该发生,因为刚创建成功
|
||||
return nil, fmt.Errorf("创建后获取配方信息失败: %w", err)
|
||||
}
|
||||
|
||||
return dto.ConvertRecipeToDto(fullRecipe), nil
|
||||
}
|
||||
|
||||
// UpdateRecipe 更新配方
|
||||
func (s *recipeServiceImpl) UpdateRecipe(ctx context.Context, id uint32, req *dto.UpdateRecipeRequest) (*dto.RecipeResponse, error) {
|
||||
serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateRecipe")
|
||||
|
||||
// 1. 转换 DTO 为模型
|
||||
recipeModel := dto.ConvertUpdateRecipeRequestToModel(req)
|
||||
recipeModel.ID = id
|
||||
|
||||
// 2. 更新配方基础信息
|
||||
_, err := s.recipeSvc.UpdateRecipe(serviceCtx, recipeModel)
|
||||
if err != nil {
|
||||
if errors.Is(err, recipe.ErrRecipeNotFound) {
|
||||
return nil, ErrRecipeNotFound
|
||||
}
|
||||
if errors.Is(err, recipe.ErrRecipeNameConflict) {
|
||||
return nil, ErrRecipeNameConflict
|
||||
}
|
||||
return nil, fmt.Errorf("更新配方基础信息失败: %w", err)
|
||||
}
|
||||
|
||||
// 3. 更新配方原料
|
||||
ingredients := make([]models.RecipeIngredient, len(req.RecipeIngredients))
|
||||
for i, item := range req.RecipeIngredients {
|
||||
ingredients[i] = models.RecipeIngredient{
|
||||
RecipeID: id,
|
||||
RawMaterialID: item.RawMaterialID,
|
||||
Percentage: item.Percentage,
|
||||
}
|
||||
}
|
||||
err = s.recipeSvc.UpdateRecipeIngredients(serviceCtx, id, ingredients)
|
||||
if err != nil {
|
||||
if errors.Is(err, recipe.ErrRecipeNotFound) {
|
||||
return nil, ErrRecipeNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("更新配方原料失败: %w", err)
|
||||
}
|
||||
|
||||
// 4. 更新成功后,获取最新的完整配方信息并返回
|
||||
updatedRecipe, err := s.recipeSvc.GetRecipeByID(serviceCtx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, recipe.ErrRecipeNotFound) {
|
||||
// 理论上不应该发生,因为刚更新成功
|
||||
return nil, ErrRecipeNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("更新后获取配方信息失败: %w", err)
|
||||
}
|
||||
|
||||
return dto.ConvertRecipeToDto(updatedRecipe), nil
|
||||
}
|
||||
|
||||
// DeleteRecipe 删除配方
|
||||
func (s *recipeServiceImpl) DeleteRecipe(ctx context.Context, id uint32) error {
|
||||
serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteRecipe")
|
||||
err := s.recipeSvc.DeleteRecipe(serviceCtx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, recipe.ErrRecipeNotFound) {
|
||||
return ErrRecipeNotFound
|
||||
}
|
||||
return fmt.Errorf("删除配方失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRecipeByID 获取单个配方
|
||||
func (s *recipeServiceImpl) GetRecipeByID(ctx context.Context, id uint32) (*dto.RecipeResponse, error) {
|
||||
serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetRecipeByID")
|
||||
|
||||
recipeModel, err := s.recipeSvc.GetRecipeByID(serviceCtx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, recipe.ErrRecipeNotFound) {
|
||||
return nil, ErrRecipeNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("获取配方失败: %w", err)
|
||||
}
|
||||
return dto.ConvertRecipeToDto(recipeModel), nil
|
||||
}
|
||||
|
||||
// ListRecipes 列出配方
|
||||
func (s *recipeServiceImpl) ListRecipes(ctx context.Context, req *dto.ListRecipeRequest) (*dto.ListRecipeResponse, error) {
|
||||
serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListRecipes")
|
||||
|
||||
opts := repository.RecipeListOptions{
|
||||
Name: req.Name,
|
||||
OrderBy: req.OrderBy,
|
||||
}
|
||||
recipes, total, err := s.recipeSvc.ListRecipes(serviceCtx, opts, req.Page, req.PageSize)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取配方列表失败: %w", err)
|
||||
}
|
||||
|
||||
return dto.ConvertRecipeListToDTO(recipes, total, req.Page, req.PageSize), nil
|
||||
}
|
||||
@@ -68,6 +68,7 @@ func NewApplication(configPath string) (*Application, error) {
|
||||
appServices.pigBreedService,
|
||||
appServices.pigAgeStageService,
|
||||
appServices.pigTypeService,
|
||||
appServices.recipeService,
|
||||
infra.tokenGenerator,
|
||||
infra.lora.listenHandler,
|
||||
)
|
||||
|
||||
@@ -264,6 +264,7 @@ type AppServices struct {
|
||||
pigBreedService service.PigBreedService
|
||||
pigTypeService service.PigTypeService
|
||||
rawMaterialService service.RawMaterialService
|
||||
recipeService service.RecipeService
|
||||
}
|
||||
|
||||
// initAppServices 初始化所有的应用服务。
|
||||
@@ -316,6 +317,7 @@ func initAppServices(ctx context.Context, infra *Infrastructure, domainServices
|
||||
pigBreedService := service.NewPigBreedService(logs.AddCompName(baseCtx, "PigBreedService"), domainServices.recipeService)
|
||||
pigTypeService := service.NewPigTypeService(logs.AddCompName(baseCtx, "PigTypeService"), domainServices.recipeService)
|
||||
rawMaterialService := service.NewRawMaterialService(logs.AddCompName(baseCtx, "RawMaterialService"), domainServices.recipeService)
|
||||
recipeService := service.NewRecipeService(logs.AddCompName(baseCtx, "RecipeService"), domainServices.recipeService)
|
||||
|
||||
return &AppServices{
|
||||
pigFarmService: pigFarmService,
|
||||
@@ -331,6 +333,7 @@ func initAppServices(ctx context.Context, infra *Infrastructure, domainServices
|
||||
pigBreedService: pigBreedService,
|
||||
pigTypeService: pigTypeService,
|
||||
rawMaterialService: rawMaterialService,
|
||||
recipeService: recipeService,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ internal/app/controller/feed/pig_age_stage_controller.go
|
||||
internal/app/controller/feed/pig_breed_controller.go
|
||||
internal/app/controller/feed/pig_type_controller.go
|
||||
internal/app/controller/feed/raw_material_controller.go
|
||||
internal/app/controller/feed/recipe_controller.go
|
||||
internal/app/controller/health/health_controller.go
|
||||
internal/app/controller/management/controller_helpers.go
|
||||
internal/app/controller/management/pig_batch_controller.go
|
||||
@@ -94,6 +95,7 @@ internal/app/service/pig_service.go
|
||||
internal/app/service/pig_type_service.go
|
||||
internal/app/service/plan_service.go
|
||||
internal/app/service/raw_material_service.go
|
||||
internal/app/service/recipe_service.go
|
||||
internal/app/service/threshold_alarm_service.go
|
||||
internal/app/service/user_service.go
|
||||
internal/app/webhook/chirp_stack.go
|
||||
|
||||
Reference in New Issue
Block a user