From 534891309c5d314b4a8e619c90f541646784b594 Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Fri, 21 Nov 2025 16:02:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E9=85=8D=E6=96=B9=E9=A2=86?= =?UTF-8?q?=E5=9F=9F=E7=9A=84web=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- design/archive/recipe-management/index.md | 3 +- docs/docs.go | 1793 +++++++++++++++++ docs/swagger.json | 1793 +++++++++++++++++ docs/swagger.yaml | 1111 ++++++++++ internal/app/api/api.go | 5 + internal/app/api/router.go | 40 + .../app/controller/feed/feed_controller.go | 875 ++++++++ internal/app/dto/feed_converter.go | 202 ++ internal/app/dto/feed_dto.go | 261 +++ .../app/service/feed_management_service.go | 529 +++++ internal/core/application.go | 1 + internal/core/component_initializers.go | 20 + .../infra/repository/pig_type_repository.go | 4 +- project_structure.txt | 4 + 14 files changed, 6638 insertions(+), 3 deletions(-) create mode 100644 internal/app/controller/feed/feed_controller.go create mode 100644 internal/app/dto/feed_converter.go create mode 100644 internal/app/dto/feed_dto.go create mode 100644 internal/app/service/feed_management_service.go diff --git a/design/archive/recipe-management/index.md b/design/archive/recipe-management/index.md index 9c25944..379a678 100644 --- a/design/archive/recipe-management/index.md +++ b/design/archive/recipe-management/index.md @@ -54,4 +54,5 @@ http://git.huangwc.com/pig/pig-farm-controller/issues/66 4. 实现原材料的增删改查和仓库层的原料库存记录表增查 5. 定义猪的模型和营养需求模型 6. 实现从json读取猪营养需求并写入数据库 -7. 实现配方领域关于猪模型和营养需求的增删改查 \ No newline at end of file +7. 实现配方领域关于猪模型和营养需求的增删改查 +8. 实现配方领域的web接口 \ No newline at end of file diff --git a/docs/docs.go b/docs/docs.go index a8352fe..6493ec4 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -1645,6 +1645,1254 @@ const docTemplate = `{ } } }, + "/api/v1/feed/nutrients": { + "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.ListNutrientResponse" + } + } + } + ] + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建一个新的营养种类。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "饲料管理" + ], + "summary": "创建营养种类", + "parameters": [ + { + "description": "营养种类信息", + "name": "nutrient", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CreateNutrientRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为201代表创建成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.NutrientResponse" + } + } + } + ] + } + } + } + } + }, + "/api/v1/feed/nutrients/{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.NutrientResponse" + } + } + } + ] + } + } + } + }, + "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": "nutrient", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdateNutrientRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为200代表更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.NutrientResponse" + } + } + } + ] + } + } + } + }, + "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/feed/pig-age-stages": { + "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.ListPigAgeStageResponse" + } + } + } + ] + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建一个新的猪年龄阶段。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "饲料管理" + ], + "summary": "创建猪年龄阶段", + "parameters": [ + { + "description": "猪年龄阶段信息", + "name": "pigAgeStage", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CreatePigAgeStageRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为201代表创建成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.PigAgeStageResponse" + } + } + } + ] + } + } + } + } + }, + "/api/v1/feed/pig-age-stages/{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.PigAgeStageResponse" + } + } + } + ] + } + } + } + }, + "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": "pigAgeStage", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdatePigAgeStageRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为200代表更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.PigAgeStageResponse" + } + } + } + ] + } + } + } + }, + "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/feed/pig-breeds": { + "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.ListPigBreedResponse" + } + } + } + ] + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建一个新的猪品种。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "饲料管理" + ], + "summary": "创建猪品种", + "parameters": [ + { + "description": "猪品种信息", + "name": "pigBreed", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CreatePigBreedRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为201代表创建成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.PigBreedResponse" + } + } + } + ] + } + } + } + } + }, + "/api/v1/feed/pig-breeds/{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.PigBreedResponse" + } + } + } + ] + } + } + } + }, + "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": "pigBreed", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdatePigBreedRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为200代表更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.PigBreedResponse" + } + } + } + ] + } + } + } + }, + "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/feed/pig-types": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "获取所有猪类型的列表,支持分页和过滤。", + "produces": [ + "application/json" + ], + "tags": [ + "饲料管理" + ], + "summary": "获取猪类型列表", + "parameters": [ + { + "type": "integer", + "description": "关联的猪年龄阶段ID", + "name": "age_stage_id", + "in": "query" + }, + { + "type": "string", + "description": "关联的猪年龄阶段名称 (用于模糊查询)", + "name": "age_stage_name", + "in": "query" + }, + { + "type": "integer", + "description": "关联的猪品种ID", + "name": "breed_id", + "in": "query" + }, + { + "type": "string", + "description": "关联的猪品种名称 (用于模糊查询)", + "name": "breed_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.ListPigTypeResponse" + } + } + } + ] + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建一个新的猪类型。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "饲料管理" + ], + "summary": "创建猪类型", + "parameters": [ + { + "description": "猪类型信息", + "name": "pigType", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CreatePigTypeRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为201代表创建成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.PigTypeResponse" + } + } + } + ] + } + } + } + } + }, + "/api/v1/feed/pig-types/{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.PigTypeResponse" + } + } + } + ] + } + } + } + }, + "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": "pigType", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdatePigTypeRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为200代表更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.PigTypeResponse" + } + } + } + ] + } + } + } + }, + "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/feed/raw-materials": { + "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.ListRawMaterialResponse" + } + } + } + ] + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建一个新的原料。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "饲料管理" + ], + "summary": "创建原料", + "parameters": [ + { + "description": "原料信息", + "name": "rawMaterial", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CreateRawMaterialRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为201代表创建成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.RawMaterialResponse" + } + } + } + ] + } + } + } + } + }, + "/api/v1/feed/raw-materials/{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.RawMaterialResponse" + } + } + } + ] + } + } + } + }, + "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": "rawMaterial", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdateRawMaterialRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为200代表更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.RawMaterialResponse" + } + } + } + ] + } + } + } + }, + "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": [ @@ -5173,6 +6421,24 @@ const docTemplate = `{ } } }, + "dto.CreateNutrientRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "描述", + "type": "string", + "maxLength": 255 + }, + "name": { + "description": "营养素名称", + "type": "string", + "maxLength": 100 + } + } + }, "dto.CreatePenRequest": { "type": "object", "required": [ @@ -5192,6 +6458,57 @@ const docTemplate = `{ } } }, + "dto.CreatePigAgeStageRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "阶段描述", + "type": "string", + "maxLength": 255 + }, + "name": { + "description": "年龄阶段名称", + "type": "string", + "maxLength": 50 + } + } + }, + "dto.CreatePigBreedRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "appearance_features": { + "description": "外貌特征", + "type": "string" + }, + "breed_advantages": { + "description": "品种优点", + "type": "string" + }, + "breed_disadvantages": { + "description": "品种缺点", + "type": "string" + }, + "description": { + "description": "其他描述", + "type": "string" + }, + "name": { + "description": "品种名称", + "type": "string", + "maxLength": 50 + }, + "parent_info": { + "description": "父母信息", + "type": "string" + } + } + }, "dto.CreatePigHouseRequest": { "type": "object", "required": [ @@ -5206,6 +6523,52 @@ const docTemplate = `{ } } }, + "dto.CreatePigTypeRequest": { + "type": "object", + "required": [ + "age_stage_id", + "breed_id" + ], + "properties": { + "age_stage_id": { + "description": "关联的猪年龄阶段ID", + "type": "integer" + }, + "breed_id": { + "description": "关联的猪品种ID", + "type": "integer" + }, + "daily_feed_intake": { + "description": "理论日均食量 (g/天)", + "type": "number" + }, + "daily_gain_weight": { + "description": "理论日增重 (g/天)", + "type": "number" + }, + "description": { + "description": "该猪类型的描述或特点", + "type": "string", + "maxLength": 255 + }, + "max_days": { + "description": "该猪类型在该年龄阶段的最大日龄", + "type": "integer" + }, + "max_weight": { + "description": "该猪类型在该年龄阶段的最大体重 (g)", + "type": "number" + }, + "min_days": { + "description": "该猪类型在该年龄阶段的最小日龄", + "type": "integer" + }, + "min_weight": { + "description": "该猪类型在该年龄阶段的最小体重 (g)", + "type": "number" + } + } + }, "dto.CreatePlanRequest": { "type": "object", "required": [ @@ -5252,6 +6615,24 @@ const docTemplate = `{ } } }, + "dto.CreateRawMaterialRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "描述", + "type": "string", + "maxLength": 255 + }, + "name": { + "description": "原料名称", + "type": "string", + "maxLength": 100 + } + } + }, "dto.CreateUserRequest": { "type": "object", "required": [ @@ -5549,6 +6930,20 @@ const docTemplate = `{ } } }, + "dto.ListNutrientResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.NutrientResponse" + } + }, + "pagination": { + "$ref": "#/definitions/dto.PaginationDTO" + } + } + }, "dto.ListPendingCollectionResponse": { "type": "object", "properties": { @@ -5563,6 +6958,20 @@ const docTemplate = `{ } } }, + "dto.ListPigAgeStageResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.PigAgeStageResponse" + } + }, + "pagination": { + "$ref": "#/definitions/dto.PaginationDTO" + } + } + }, "dto.ListPigBatchLogResponse": { "type": "object", "properties": { @@ -5577,6 +6986,20 @@ const docTemplate = `{ } } }, + "dto.ListPigBreedResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.PigBreedResponse" + } + }, + "pagination": { + "$ref": "#/definitions/dto.PaginationDTO" + } + } + }, "dto.ListPigPurchaseResponse": { "type": "object", "properties": { @@ -5633,6 +7056,20 @@ const docTemplate = `{ } } }, + "dto.ListPigTypeResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.PigTypeResponse" + } + }, + "pagination": { + "$ref": "#/definitions/dto.PaginationDTO" + } + } + }, "dto.ListPlanExecutionLogResponse": { "type": "object", "properties": { @@ -5662,6 +7099,20 @@ const docTemplate = `{ } } }, + "dto.ListRawMaterialResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.RawMaterialResponse" + } + }, + "pagination": { + "$ref": "#/definitions/dto.PaginationDTO" + } + } + }, "dto.ListSensorDataResponse": { "type": "object", "properties": { @@ -5885,6 +7336,26 @@ const docTemplate = `{ } } }, + "dto.NutrientResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, "dto.PaginationDTO": { "type": "object", "properties": { @@ -5951,6 +7422,26 @@ const docTemplate = `{ } } }, + "dto.PigAgeStageResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, "dto.PigBatchCreateDTO": { "type": "object", "required": [ @@ -6128,6 +7619,38 @@ const docTemplate = `{ } } }, + "dto.PigBreedResponse": { + "type": "object", + "properties": { + "appearance_features": { + "type": "string" + }, + "breed_advantages": { + "type": "string" + }, + "breed_disadvantages": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "parent_info": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, "dto.PigHouseResponse": { "type": "object", "properties": { @@ -6142,6 +7665,35 @@ const docTemplate = `{ } } }, + "dto.PigNutrientRequirementDTO": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "max_requirement": { + "description": "最高营养需求量", + "type": "number" + }, + "min_requirement": { + "description": "最低营养需求量", + "type": "number" + }, + "nutrient_id": { + "type": "integer" + }, + "nutrient_name": { + "description": "营养素名称", + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, "dto.PigPurchaseDTO": { "type": "object", "properties": { @@ -6300,6 +7852,62 @@ const docTemplate = `{ } } }, + "dto.PigTypeResponse": { + "type": "object", + "properties": { + "age_stage_id": { + "type": "integer" + }, + "age_stage_name": { + "description": "猪年龄阶段名称", + "type": "string" + }, + "breed_id": { + "type": "integer" + }, + "breed_name": { + "description": "猪品种名称", + "type": "string" + }, + "created_at": { + "type": "string" + }, + "daily_feed_intake": { + "type": "number" + }, + "daily_gain_weight": { + "type": "number" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "max_days": { + "type": "integer" + }, + "max_weight": { + "type": "number" + }, + "min_days": { + "type": "integer" + }, + "min_weight": { + "type": "number" + }, + "pig_nutrient_requirements": { + "description": "关联的营养需求", + "type": "array", + "items": { + "$ref": "#/definitions/dto.PigNutrientRequirementDTO" + } + }, + "updated_at": { + "type": "string" + } + } + }, "dto.PlanExecutionLogDTO": { "type": "object", "properties": { @@ -6405,6 +8013,58 @@ const docTemplate = `{ } } }, + "dto.RawMaterialNutrientDTO": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "nutrient_id": { + "type": "integer" + }, + "nutrient_name": { + "description": "营养素名称", + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "value": { + "description": "营养价值含量", + "type": "number" + } + } + }, + "dto.RawMaterialResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "raw_material_nutrients": { + "description": "关联的营养素信息", + "type": "array", + "items": { + "$ref": "#/definitions/dto.RawMaterialNutrientDTO" + } + }, + "updated_at": { + "type": "string" + } + } + }, "dto.ReclassifyPenToNewBatchRequest": { "type": "object", "required": [ @@ -7061,6 +8721,24 @@ const docTemplate = `{ } } }, + "dto.UpdateNutrientRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "描述", + "type": "string", + "maxLength": 255 + }, + "name": { + "description": "营养素名称", + "type": "string", + "maxLength": 100 + } + } + }, "dto.UpdatePenRequest": { "type": "object", "required": [ @@ -7121,6 +8799,57 @@ const docTemplate = `{ } } }, + "dto.UpdatePigAgeStageRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "阶段描述", + "type": "string", + "maxLength": 255 + }, + "name": { + "description": "年龄阶段名称", + "type": "string", + "maxLength": 50 + } + } + }, + "dto.UpdatePigBreedRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "appearance_features": { + "description": "外貌特征", + "type": "string" + }, + "breed_advantages": { + "description": "品种优点", + "type": "string" + }, + "breed_disadvantages": { + "description": "品种缺点", + "type": "string" + }, + "description": { + "description": "其他描述", + "type": "string" + }, + "name": { + "description": "品种名称", + "type": "string", + "maxLength": 50 + }, + "parent_info": { + "description": "父母信息", + "type": "string" + } + } + }, "dto.UpdatePigHouseRequest": { "type": "object", "required": [ @@ -7135,6 +8864,52 @@ const docTemplate = `{ } } }, + "dto.UpdatePigTypeRequest": { + "type": "object", + "required": [ + "age_stage_id", + "breed_id" + ], + "properties": { + "age_stage_id": { + "description": "关联的猪年龄阶段ID", + "type": "integer" + }, + "breed_id": { + "description": "关联的猪品种ID", + "type": "integer" + }, + "daily_feed_intake": { + "description": "理论日均食量 (g/天)", + "type": "number" + }, + "daily_gain_weight": { + "description": "理论日增重 (g/天)", + "type": "number" + }, + "description": { + "description": "该猪类型的描述或特点", + "type": "string", + "maxLength": 255 + }, + "max_days": { + "description": "该猪类型在该年龄阶段的最大日龄", + "type": "integer" + }, + "max_weight": { + "description": "该猪类型在该年龄阶段的最大体重 (g)", + "type": "number" + }, + "min_days": { + "description": "该猪类型在该年龄阶段的最小日龄", + "type": "integer" + }, + "min_weight": { + "description": "该猪类型在该年龄阶段的最小体重 (g)", + "type": "number" + } + } + }, "dto.UpdatePlanRequest": { "type": "object", "required": [ @@ -7180,6 +8955,24 @@ const docTemplate = `{ } } }, + "dto.UpdateRawMaterialRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "描述", + "type": "string", + "maxLength": 255 + }, + "name": { + "description": "原料名称", + "type": "string", + "maxLength": 100 + } + } + }, "dto.UserActionLogDTO": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 01c16a5..6dd2ea1 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1637,6 +1637,1254 @@ } } }, + "/api/v1/feed/nutrients": { + "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.ListNutrientResponse" + } + } + } + ] + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建一个新的营养种类。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "饲料管理" + ], + "summary": "创建营养种类", + "parameters": [ + { + "description": "营养种类信息", + "name": "nutrient", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CreateNutrientRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为201代表创建成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.NutrientResponse" + } + } + } + ] + } + } + } + } + }, + "/api/v1/feed/nutrients/{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.NutrientResponse" + } + } + } + ] + } + } + } + }, + "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": "nutrient", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdateNutrientRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为200代表更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.NutrientResponse" + } + } + } + ] + } + } + } + }, + "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/feed/pig-age-stages": { + "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.ListPigAgeStageResponse" + } + } + } + ] + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建一个新的猪年龄阶段。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "饲料管理" + ], + "summary": "创建猪年龄阶段", + "parameters": [ + { + "description": "猪年龄阶段信息", + "name": "pigAgeStage", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CreatePigAgeStageRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为201代表创建成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.PigAgeStageResponse" + } + } + } + ] + } + } + } + } + }, + "/api/v1/feed/pig-age-stages/{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.PigAgeStageResponse" + } + } + } + ] + } + } + } + }, + "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": "pigAgeStage", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdatePigAgeStageRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为200代表更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.PigAgeStageResponse" + } + } + } + ] + } + } + } + }, + "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/feed/pig-breeds": { + "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.ListPigBreedResponse" + } + } + } + ] + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建一个新的猪品种。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "饲料管理" + ], + "summary": "创建猪品种", + "parameters": [ + { + "description": "猪品种信息", + "name": "pigBreed", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CreatePigBreedRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为201代表创建成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.PigBreedResponse" + } + } + } + ] + } + } + } + } + }, + "/api/v1/feed/pig-breeds/{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.PigBreedResponse" + } + } + } + ] + } + } + } + }, + "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": "pigBreed", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdatePigBreedRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为200代表更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.PigBreedResponse" + } + } + } + ] + } + } + } + }, + "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/feed/pig-types": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "获取所有猪类型的列表,支持分页和过滤。", + "produces": [ + "application/json" + ], + "tags": [ + "饲料管理" + ], + "summary": "获取猪类型列表", + "parameters": [ + { + "type": "integer", + "description": "关联的猪年龄阶段ID", + "name": "age_stage_id", + "in": "query" + }, + { + "type": "string", + "description": "关联的猪年龄阶段名称 (用于模糊查询)", + "name": "age_stage_name", + "in": "query" + }, + { + "type": "integer", + "description": "关联的猪品种ID", + "name": "breed_id", + "in": "query" + }, + { + "type": "string", + "description": "关联的猪品种名称 (用于模糊查询)", + "name": "breed_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.ListPigTypeResponse" + } + } + } + ] + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建一个新的猪类型。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "饲料管理" + ], + "summary": "创建猪类型", + "parameters": [ + { + "description": "猪类型信息", + "name": "pigType", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CreatePigTypeRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为201代表创建成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.PigTypeResponse" + } + } + } + ] + } + } + } + } + }, + "/api/v1/feed/pig-types/{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.PigTypeResponse" + } + } + } + ] + } + } + } + }, + "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": "pigType", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdatePigTypeRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为200代表更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.PigTypeResponse" + } + } + } + ] + } + } + } + }, + "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/feed/raw-materials": { + "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.ListRawMaterialResponse" + } + } + } + ] + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "创建一个新的原料。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "饲料管理" + ], + "summary": "创建原料", + "parameters": [ + { + "description": "原料信息", + "name": "rawMaterial", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.CreateRawMaterialRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为201代表创建成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.RawMaterialResponse" + } + } + } + ] + } + } + } + } + }, + "/api/v1/feed/raw-materials/{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.RawMaterialResponse" + } + } + } + ] + } + } + } + }, + "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": "rawMaterial", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.UpdateRawMaterialRequest" + } + } + ], + "responses": { + "200": { + "description": "业务码为200代表更新成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controller.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/dto.RawMaterialResponse" + } + } + } + ] + } + } + } + }, + "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": [ @@ -5165,6 +6413,24 @@ } } }, + "dto.CreateNutrientRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "描述", + "type": "string", + "maxLength": 255 + }, + "name": { + "description": "营养素名称", + "type": "string", + "maxLength": 100 + } + } + }, "dto.CreatePenRequest": { "type": "object", "required": [ @@ -5184,6 +6450,57 @@ } } }, + "dto.CreatePigAgeStageRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "阶段描述", + "type": "string", + "maxLength": 255 + }, + "name": { + "description": "年龄阶段名称", + "type": "string", + "maxLength": 50 + } + } + }, + "dto.CreatePigBreedRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "appearance_features": { + "description": "外貌特征", + "type": "string" + }, + "breed_advantages": { + "description": "品种优点", + "type": "string" + }, + "breed_disadvantages": { + "description": "品种缺点", + "type": "string" + }, + "description": { + "description": "其他描述", + "type": "string" + }, + "name": { + "description": "品种名称", + "type": "string", + "maxLength": 50 + }, + "parent_info": { + "description": "父母信息", + "type": "string" + } + } + }, "dto.CreatePigHouseRequest": { "type": "object", "required": [ @@ -5198,6 +6515,52 @@ } } }, + "dto.CreatePigTypeRequest": { + "type": "object", + "required": [ + "age_stage_id", + "breed_id" + ], + "properties": { + "age_stage_id": { + "description": "关联的猪年龄阶段ID", + "type": "integer" + }, + "breed_id": { + "description": "关联的猪品种ID", + "type": "integer" + }, + "daily_feed_intake": { + "description": "理论日均食量 (g/天)", + "type": "number" + }, + "daily_gain_weight": { + "description": "理论日增重 (g/天)", + "type": "number" + }, + "description": { + "description": "该猪类型的描述或特点", + "type": "string", + "maxLength": 255 + }, + "max_days": { + "description": "该猪类型在该年龄阶段的最大日龄", + "type": "integer" + }, + "max_weight": { + "description": "该猪类型在该年龄阶段的最大体重 (g)", + "type": "number" + }, + "min_days": { + "description": "该猪类型在该年龄阶段的最小日龄", + "type": "integer" + }, + "min_weight": { + "description": "该猪类型在该年龄阶段的最小体重 (g)", + "type": "number" + } + } + }, "dto.CreatePlanRequest": { "type": "object", "required": [ @@ -5244,6 +6607,24 @@ } } }, + "dto.CreateRawMaterialRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "描述", + "type": "string", + "maxLength": 255 + }, + "name": { + "description": "原料名称", + "type": "string", + "maxLength": 100 + } + } + }, "dto.CreateUserRequest": { "type": "object", "required": [ @@ -5541,6 +6922,20 @@ } } }, + "dto.ListNutrientResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.NutrientResponse" + } + }, + "pagination": { + "$ref": "#/definitions/dto.PaginationDTO" + } + } + }, "dto.ListPendingCollectionResponse": { "type": "object", "properties": { @@ -5555,6 +6950,20 @@ } } }, + "dto.ListPigAgeStageResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.PigAgeStageResponse" + } + }, + "pagination": { + "$ref": "#/definitions/dto.PaginationDTO" + } + } + }, "dto.ListPigBatchLogResponse": { "type": "object", "properties": { @@ -5569,6 +6978,20 @@ } } }, + "dto.ListPigBreedResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.PigBreedResponse" + } + }, + "pagination": { + "$ref": "#/definitions/dto.PaginationDTO" + } + } + }, "dto.ListPigPurchaseResponse": { "type": "object", "properties": { @@ -5625,6 +7048,20 @@ } } }, + "dto.ListPigTypeResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.PigTypeResponse" + } + }, + "pagination": { + "$ref": "#/definitions/dto.PaginationDTO" + } + } + }, "dto.ListPlanExecutionLogResponse": { "type": "object", "properties": { @@ -5654,6 +7091,20 @@ } } }, + "dto.ListRawMaterialResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/dto.RawMaterialResponse" + } + }, + "pagination": { + "$ref": "#/definitions/dto.PaginationDTO" + } + } + }, "dto.ListSensorDataResponse": { "type": "object", "properties": { @@ -5877,6 +7328,26 @@ } } }, + "dto.NutrientResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, "dto.PaginationDTO": { "type": "object", "properties": { @@ -5943,6 +7414,26 @@ } } }, + "dto.PigAgeStageResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, "dto.PigBatchCreateDTO": { "type": "object", "required": [ @@ -6120,6 +7611,38 @@ } } }, + "dto.PigBreedResponse": { + "type": "object", + "properties": { + "appearance_features": { + "type": "string" + }, + "breed_advantages": { + "type": "string" + }, + "breed_disadvantages": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "parent_info": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, "dto.PigHouseResponse": { "type": "object", "properties": { @@ -6134,6 +7657,35 @@ } } }, + "dto.PigNutrientRequirementDTO": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "max_requirement": { + "description": "最高营养需求量", + "type": "number" + }, + "min_requirement": { + "description": "最低营养需求量", + "type": "number" + }, + "nutrient_id": { + "type": "integer" + }, + "nutrient_name": { + "description": "营养素名称", + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, "dto.PigPurchaseDTO": { "type": "object", "properties": { @@ -6292,6 +7844,62 @@ } } }, + "dto.PigTypeResponse": { + "type": "object", + "properties": { + "age_stage_id": { + "type": "integer" + }, + "age_stage_name": { + "description": "猪年龄阶段名称", + "type": "string" + }, + "breed_id": { + "type": "integer" + }, + "breed_name": { + "description": "猪品种名称", + "type": "string" + }, + "created_at": { + "type": "string" + }, + "daily_feed_intake": { + "type": "number" + }, + "daily_gain_weight": { + "type": "number" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "max_days": { + "type": "integer" + }, + "max_weight": { + "type": "number" + }, + "min_days": { + "type": "integer" + }, + "min_weight": { + "type": "number" + }, + "pig_nutrient_requirements": { + "description": "关联的营养需求", + "type": "array", + "items": { + "$ref": "#/definitions/dto.PigNutrientRequirementDTO" + } + }, + "updated_at": { + "type": "string" + } + } + }, "dto.PlanExecutionLogDTO": { "type": "object", "properties": { @@ -6397,6 +8005,58 @@ } } }, + "dto.RawMaterialNutrientDTO": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "nutrient_id": { + "type": "integer" + }, + "nutrient_name": { + "description": "营养素名称", + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "value": { + "description": "营养价值含量", + "type": "number" + } + } + }, + "dto.RawMaterialResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "raw_material_nutrients": { + "description": "关联的营养素信息", + "type": "array", + "items": { + "$ref": "#/definitions/dto.RawMaterialNutrientDTO" + } + }, + "updated_at": { + "type": "string" + } + } + }, "dto.ReclassifyPenToNewBatchRequest": { "type": "object", "required": [ @@ -7053,6 +8713,24 @@ } } }, + "dto.UpdateNutrientRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "描述", + "type": "string", + "maxLength": 255 + }, + "name": { + "description": "营养素名称", + "type": "string", + "maxLength": 100 + } + } + }, "dto.UpdatePenRequest": { "type": "object", "required": [ @@ -7113,6 +8791,57 @@ } } }, + "dto.UpdatePigAgeStageRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "阶段描述", + "type": "string", + "maxLength": 255 + }, + "name": { + "description": "年龄阶段名称", + "type": "string", + "maxLength": 50 + } + } + }, + "dto.UpdatePigBreedRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "appearance_features": { + "description": "外貌特征", + "type": "string" + }, + "breed_advantages": { + "description": "品种优点", + "type": "string" + }, + "breed_disadvantages": { + "description": "品种缺点", + "type": "string" + }, + "description": { + "description": "其他描述", + "type": "string" + }, + "name": { + "description": "品种名称", + "type": "string", + "maxLength": 50 + }, + "parent_info": { + "description": "父母信息", + "type": "string" + } + } + }, "dto.UpdatePigHouseRequest": { "type": "object", "required": [ @@ -7127,6 +8856,52 @@ } } }, + "dto.UpdatePigTypeRequest": { + "type": "object", + "required": [ + "age_stage_id", + "breed_id" + ], + "properties": { + "age_stage_id": { + "description": "关联的猪年龄阶段ID", + "type": "integer" + }, + "breed_id": { + "description": "关联的猪品种ID", + "type": "integer" + }, + "daily_feed_intake": { + "description": "理论日均食量 (g/天)", + "type": "number" + }, + "daily_gain_weight": { + "description": "理论日增重 (g/天)", + "type": "number" + }, + "description": { + "description": "该猪类型的描述或特点", + "type": "string", + "maxLength": 255 + }, + "max_days": { + "description": "该猪类型在该年龄阶段的最大日龄", + "type": "integer" + }, + "max_weight": { + "description": "该猪类型在该年龄阶段的最大体重 (g)", + "type": "number" + }, + "min_days": { + "description": "该猪类型在该年龄阶段的最小日龄", + "type": "integer" + }, + "min_weight": { + "description": "该猪类型在该年龄阶段的最小体重 (g)", + "type": "number" + } + } + }, "dto.UpdatePlanRequest": { "type": "object", "required": [ @@ -7172,6 +8947,24 @@ } } }, + "dto.UpdateRawMaterialRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "description": "描述", + "type": "string", + "maxLength": 255 + }, + "name": { + "description": "原料名称", + "type": "string", + "maxLength": 100 + } + } + }, "dto.UserActionLogDTO": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index d9aff30..01fb86b 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -273,6 +273,19 @@ definitions: - sensor_type - thresholds type: object + dto.CreateNutrientRequest: + properties: + description: + description: 描述 + maxLength: 255 + type: string + name: + description: 营养素名称 + maxLength: 100 + type: string + required: + - name + type: object dto.CreatePenRequest: properties: capacity: @@ -286,6 +299,43 @@ definitions: - house_id - pen_number type: object + dto.CreatePigAgeStageRequest: + properties: + description: + description: 阶段描述 + maxLength: 255 + type: string + name: + description: 年龄阶段名称 + maxLength: 50 + type: string + required: + - name + type: object + dto.CreatePigBreedRequest: + properties: + appearance_features: + description: 外貌特征 + type: string + breed_advantages: + description: 品种优点 + type: string + breed_disadvantages: + description: 品种缺点 + type: string + description: + description: 其他描述 + type: string + name: + description: 品种名称 + maxLength: 50 + type: string + parent_info: + description: 父母信息 + type: string + required: + - name + type: object dto.CreatePigHouseRequest: properties: description: @@ -295,6 +345,40 @@ definitions: required: - name type: object + dto.CreatePigTypeRequest: + properties: + age_stage_id: + description: 关联的猪年龄阶段ID + type: integer + breed_id: + description: 关联的猪品种ID + type: integer + daily_feed_intake: + description: 理论日均食量 (g/天) + type: number + daily_gain_weight: + description: 理论日增重 (g/天) + type: number + description: + description: 该猪类型的描述或特点 + maxLength: 255 + type: string + max_days: + description: 该猪类型在该年龄阶段的最大日龄 + type: integer + max_weight: + description: 该猪类型在该年龄阶段的最大体重 (g) + type: number + min_days: + description: 该猪类型在该年龄阶段的最小日龄 + type: integer + min_weight: + description: 该猪类型在该年龄阶段的最小体重 (g) + type: number + required: + - age_stage_id + - breed_id + type: object dto.CreatePlanRequest: properties: cron_expression: @@ -326,6 +410,19 @@ definitions: - execution_type - name type: object + dto.CreateRawMaterialRequest: + properties: + description: + description: 描述 + maxLength: 255 + type: string + name: + description: 原料名称 + maxLength: 100 + type: string + required: + - name + type: object dto.CreateUserRequest: properties: password: @@ -520,6 +617,15 @@ definitions: pagination: $ref: '#/definitions/dto.PaginationDTO' type: object + dto.ListNutrientResponse: + properties: + list: + items: + $ref: '#/definitions/dto.NutrientResponse' + type: array + pagination: + $ref: '#/definitions/dto.PaginationDTO' + type: object dto.ListPendingCollectionResponse: properties: list: @@ -529,6 +635,15 @@ definitions: pagination: $ref: '#/definitions/dto.PaginationDTO' type: object + dto.ListPigAgeStageResponse: + properties: + list: + items: + $ref: '#/definitions/dto.PigAgeStageResponse' + type: array + pagination: + $ref: '#/definitions/dto.PaginationDTO' + type: object dto.ListPigBatchLogResponse: properties: list: @@ -538,6 +653,15 @@ definitions: pagination: $ref: '#/definitions/dto.PaginationDTO' type: object + dto.ListPigBreedResponse: + properties: + list: + items: + $ref: '#/definitions/dto.PigBreedResponse' + type: array + pagination: + $ref: '#/definitions/dto.PaginationDTO' + type: object dto.ListPigPurchaseResponse: properties: list: @@ -574,6 +698,15 @@ definitions: pagination: $ref: '#/definitions/dto.PaginationDTO' type: object + dto.ListPigTypeResponse: + properties: + list: + items: + $ref: '#/definitions/dto.PigTypeResponse' + type: array + pagination: + $ref: '#/definitions/dto.PaginationDTO' + type: object dto.ListPlanExecutionLogResponse: properties: list: @@ -593,6 +726,15 @@ definitions: example: 100 type: integer type: object + dto.ListRawMaterialResponse: + properties: + list: + items: + $ref: '#/definitions/dto.RawMaterialResponse' + type: array + pagination: + $ref: '#/definitions/dto.PaginationDTO' + type: object dto.ListSensorDataResponse: properties: list: @@ -742,6 +884,19 @@ definitions: user_id: type: integer type: object + dto.NutrientResponse: + properties: + created_at: + type: string + description: + type: string + id: + type: integer + name: + type: string + updated_at: + type: string + type: object dto.PaginationDTO: properties: page: @@ -785,6 +940,19 @@ definitions: status: $ref: '#/definitions/models.PendingCollectionStatus' type: object + dto.PigAgeStageResponse: + properties: + created_at: + type: string + description: + type: string + id: + type: integer + name: + type: string + updated_at: + type: string + type: object dto.PigBatchCreateDTO: properties: batch_number: @@ -901,6 +1069,27 @@ definitions: - $ref: '#/definitions/models.PigBatchStatus' description: 批次状态,可选 type: object + dto.PigBreedResponse: + properties: + appearance_features: + type: string + breed_advantages: + type: string + breed_disadvantages: + type: string + created_at: + type: string + description: + type: string + id: + type: integer + name: + type: string + parent_info: + type: string + updated_at: + type: string + type: object dto.PigHouseResponse: properties: description: @@ -910,6 +1099,26 @@ definitions: name: type: string type: object + dto.PigNutrientRequirementDTO: + properties: + created_at: + type: string + id: + type: integer + max_requirement: + description: 最高营养需求量 + type: number + min_requirement: + description: 最低营养需求量 + type: number + nutrient_id: + type: integer + nutrient_name: + description: 营养素名称 + type: string + updated_at: + type: string + type: object dto.PigPurchaseDTO: properties: created_at: @@ -1014,6 +1223,44 @@ definitions: updated_at: type: string type: object + dto.PigTypeResponse: + properties: + age_stage_id: + type: integer + age_stage_name: + description: 猪年龄阶段名称 + type: string + breed_id: + type: integer + breed_name: + description: 猪品种名称 + type: string + created_at: + type: string + daily_feed_intake: + type: number + daily_gain_weight: + type: number + description: + type: string + id: + type: integer + max_days: + type: integer + max_weight: + type: number + min_days: + type: integer + min_weight: + type: number + pig_nutrient_requirements: + description: 关联的营养需求 + items: + $ref: '#/definitions/dto.PigNutrientRequirementDTO' + type: array + updated_at: + type: string + type: object dto.PlanExecutionLogDTO: properties: created_at: @@ -1080,6 +1327,41 @@ definitions: $ref: '#/definitions/dto.TaskResponse' type: array type: object + dto.RawMaterialNutrientDTO: + properties: + created_at: + type: string + id: + type: integer + nutrient_id: + type: integer + nutrient_name: + description: 营养素名称 + type: string + updated_at: + type: string + value: + description: 营养价值含量 + type: number + type: object + dto.RawMaterialResponse: + properties: + created_at: + type: string + description: + type: string + id: + type: integer + name: + type: string + raw_material_nutrients: + description: 关联的营养素信息 + items: + $ref: '#/definitions/dto.RawMaterialNutrientDTO' + type: array + updated_at: + type: string + type: object dto.ReclassifyPenToNewBatchRequest: properties: pen_id: @@ -1530,6 +1812,19 @@ definitions: - operator - thresholds type: object + dto.UpdateNutrientRequest: + properties: + description: + description: 描述 + maxLength: 255 + type: string + name: + description: 营养素名称 + maxLength: 100 + type: string + required: + - name + type: object dto.UpdatePenRequest: properties: capacity: @@ -1571,6 +1866,43 @@ definitions: required: - status type: object + dto.UpdatePigAgeStageRequest: + properties: + description: + description: 阶段描述 + maxLength: 255 + type: string + name: + description: 年龄阶段名称 + maxLength: 50 + type: string + required: + - name + type: object + dto.UpdatePigBreedRequest: + properties: + appearance_features: + description: 外貌特征 + type: string + breed_advantages: + description: 品种优点 + type: string + breed_disadvantages: + description: 品种缺点 + type: string + description: + description: 其他描述 + type: string + name: + description: 品种名称 + maxLength: 50 + type: string + parent_info: + description: 父母信息 + type: string + required: + - name + type: object dto.UpdatePigHouseRequest: properties: description: @@ -1580,6 +1912,40 @@ definitions: required: - name type: object + dto.UpdatePigTypeRequest: + properties: + age_stage_id: + description: 关联的猪年龄阶段ID + type: integer + breed_id: + description: 关联的猪品种ID + type: integer + daily_feed_intake: + description: 理论日均食量 (g/天) + type: number + daily_gain_weight: + description: 理论日增重 (g/天) + type: number + description: + description: 该猪类型的描述或特点 + maxLength: 255 + type: string + max_days: + description: 该猪类型在该年龄阶段的最大日龄 + type: integer + max_weight: + description: 该猪类型在该年龄阶段的最大体重 (g) + type: number + min_days: + description: 该猪类型在该年龄阶段的最小日龄 + type: integer + min_weight: + description: 该猪类型在该年龄阶段的最小体重 (g) + type: number + required: + - age_stage_id + - breed_id + type: object dto.UpdatePlanRequest: properties: cron_expression: @@ -1610,6 +1976,19 @@ definitions: required: - execution_type type: object + dto.UpdateRawMaterialRequest: + properties: + description: + description: 描述 + maxLength: 255 + type: string + name: + description: 原料名称 + maxLength: 100 + type: string + required: + - name + type: object dto.UserActionLogDTO: properties: action_type: @@ -3164,6 +3543,738 @@ paths: summary: 手动控制设备 tags: - 设备管理 + /api/v1/feed/nutrients: + 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.ListNutrientResponse' + type: object + security: + - BearerAuth: [] + summary: 获取营养种类列表 + tags: + - 饲料管理 + post: + consumes: + - application/json + description: 创建一个新的营养种类。 + parameters: + - description: 营养种类信息 + in: body + name: nutrient + required: true + schema: + $ref: '#/definitions/dto.CreateNutrientRequest' + produces: + - application/json + responses: + "200": + description: 业务码为201代表创建成功 + schema: + allOf: + - $ref: '#/definitions/controller.Response' + - properties: + data: + $ref: '#/definitions/dto.NutrientResponse' + type: object + security: + - BearerAuth: [] + summary: 创建营养种类 + tags: + - 饲料管理 + /api/v1/feed/nutrients/{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.NutrientResponse' + 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: nutrient + required: true + schema: + $ref: '#/definitions/dto.UpdateNutrientRequest' + produces: + - application/json + responses: + "200": + description: 业务码为200代表更新成功 + schema: + allOf: + - $ref: '#/definitions/controller.Response' + - properties: + data: + $ref: '#/definitions/dto.NutrientResponse' + type: object + security: + - BearerAuth: [] + summary: 更新营养种类 + tags: + - 饲料管理 + /api/v1/feed/pig-age-stages: + 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.ListPigAgeStageResponse' + type: object + security: + - BearerAuth: [] + summary: 获取猪年龄阶段列表 + tags: + - 饲料管理 + post: + consumes: + - application/json + description: 创建一个新的猪年龄阶段。 + parameters: + - description: 猪年龄阶段信息 + in: body + name: pigAgeStage + required: true + schema: + $ref: '#/definitions/dto.CreatePigAgeStageRequest' + produces: + - application/json + responses: + "200": + description: 业务码为201代表创建成功 + schema: + allOf: + - $ref: '#/definitions/controller.Response' + - properties: + data: + $ref: '#/definitions/dto.PigAgeStageResponse' + type: object + security: + - BearerAuth: [] + summary: 创建猪年龄阶段 + tags: + - 饲料管理 + /api/v1/feed/pig-age-stages/{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.PigAgeStageResponse' + 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: pigAgeStage + required: true + schema: + $ref: '#/definitions/dto.UpdatePigAgeStageRequest' + produces: + - application/json + responses: + "200": + description: 业务码为200代表更新成功 + schema: + allOf: + - $ref: '#/definitions/controller.Response' + - properties: + data: + $ref: '#/definitions/dto.PigAgeStageResponse' + type: object + security: + - BearerAuth: [] + summary: 更新猪年龄阶段 + tags: + - 饲料管理 + /api/v1/feed/pig-breeds: + 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.ListPigBreedResponse' + type: object + security: + - BearerAuth: [] + summary: 获取猪品种列表 + tags: + - 饲料管理 + post: + consumes: + - application/json + description: 创建一个新的猪品种。 + parameters: + - description: 猪品种信息 + in: body + name: pigBreed + required: true + schema: + $ref: '#/definitions/dto.CreatePigBreedRequest' + produces: + - application/json + responses: + "200": + description: 业务码为201代表创建成功 + schema: + allOf: + - $ref: '#/definitions/controller.Response' + - properties: + data: + $ref: '#/definitions/dto.PigBreedResponse' + type: object + security: + - BearerAuth: [] + summary: 创建猪品种 + tags: + - 饲料管理 + /api/v1/feed/pig-breeds/{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.PigBreedResponse' + 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: pigBreed + required: true + schema: + $ref: '#/definitions/dto.UpdatePigBreedRequest' + produces: + - application/json + responses: + "200": + description: 业务码为200代表更新成功 + schema: + allOf: + - $ref: '#/definitions/controller.Response' + - properties: + data: + $ref: '#/definitions/dto.PigBreedResponse' + type: object + security: + - BearerAuth: [] + summary: 更新猪品种 + tags: + - 饲料管理 + /api/v1/feed/pig-types: + get: + description: 获取所有猪类型的列表,支持分页和过滤。 + parameters: + - description: 关联的猪年龄阶段ID + in: query + name: age_stage_id + type: integer + - description: 关联的猪年龄阶段名称 (用于模糊查询) + in: query + name: age_stage_name + type: string + - description: 关联的猪品种ID + in: query + name: breed_id + type: integer + - description: 关联的猪品种名称 (用于模糊查询) + in: query + name: breed_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.ListPigTypeResponse' + type: object + security: + - BearerAuth: [] + summary: 获取猪类型列表 + tags: + - 饲料管理 + post: + consumes: + - application/json + description: 创建一个新的猪类型。 + parameters: + - description: 猪类型信息 + in: body + name: pigType + required: true + schema: + $ref: '#/definitions/dto.CreatePigTypeRequest' + produces: + - application/json + responses: + "200": + description: 业务码为201代表创建成功 + schema: + allOf: + - $ref: '#/definitions/controller.Response' + - properties: + data: + $ref: '#/definitions/dto.PigTypeResponse' + type: object + security: + - BearerAuth: [] + summary: 创建猪类型 + tags: + - 饲料管理 + /api/v1/feed/pig-types/{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.PigTypeResponse' + 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: pigType + required: true + schema: + $ref: '#/definitions/dto.UpdatePigTypeRequest' + 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: 获取所有原料的列表,支持分页和过滤。 + 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.ListRawMaterialResponse' + type: object + security: + - BearerAuth: [] + summary: 获取原料列表 + tags: + - 饲料管理 + post: + consumes: + - application/json + description: 创建一个新的原料。 + parameters: + - description: 原料信息 + in: body + name: rawMaterial + required: true + schema: + $ref: '#/definitions/dto.CreateRawMaterialRequest' + produces: + - application/json + responses: + "200": + description: 业务码为201代表创建成功 + schema: + allOf: + - $ref: '#/definitions/controller.Response' + - properties: + data: + $ref: '#/definitions/dto.RawMaterialResponse' + type: object + security: + - BearerAuth: [] + summary: 创建原料 + tags: + - 饲料管理 + /api/v1/feed/raw-materials/{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.RawMaterialResponse' + 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: rawMaterial + required: true + schema: + $ref: '#/definitions/dto.UpdateRawMaterialRequest' + 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: get: description: 根据提供的过滤条件,分页获取设备命令日志 diff --git a/internal/app/api/api.go b/internal/app/api/api.go index 43804b4..627ac5d 100644 --- a/internal/app/api/api.go +++ b/internal/app/api/api.go @@ -21,6 +21,7 @@ import ( _ "git.huangwc.com/pig/pig-farm-controller/docs" // 引入 swag 生成的 docs "git.huangwc.com/pig/pig-farm-controller/internal/app/controller/alarm" "git.huangwc.com/pig/pig-farm-controller/internal/app/controller/device" + "git.huangwc.com/pig/pig-farm-controller/internal/app/controller/feed" "git.huangwc.com/pig/pig-farm-controller/internal/app/controller/health" "git.huangwc.com/pig/pig-farm-controller/internal/app/controller/management" "git.huangwc.com/pig/pig-farm-controller/internal/app/controller/monitor" @@ -55,6 +56,7 @@ type API struct { monitorController *monitor.Controller // 数据监控控制器实例 healthController *health.Controller // 健康检查控制器实例 alarmController *alarm.ThresholdAlarmController // 阈值告警控制器 + feedController *feed.Controller // 饲料管理控制器实例 listenHandler webhook.ListenHandler // 设备上行事件监听器 analysisTaskManager *domain_plan.AnalysisPlanTaskManager // 计划触发器管理器实例 } @@ -72,6 +74,7 @@ func NewAPI(cfg config.ServerConfig, userService service.UserService, auditService service.AuditService, alarmService service.ThresholdAlarmService, + feedManagementService service.FeedManagementService, tokenGenerator token.Generator, listenHandler webhook.ListenHandler, ) *API { @@ -111,6 +114,8 @@ func NewAPI(cfg config.ServerConfig, healthController: health.NewController(logs.AddCompName(baseCtx, "HealthController")), // 在 NewAPI 中初始化阈 alarmController: alarm.NewThresholdAlarmController(logs.AddCompName(baseCtx, "ThresholdAlarmController"), alarmService), + // 在 NewAPI 中初始化饲料管理控制器 + feedController: feed.NewController(logs.AddCompName(baseCtx, "FeedController"), feedManagementService), } api.setupRoutes() // 设置所有路由 diff --git a/internal/app/api/router.go b/internal/app/api/router.go index 6560d5b..800aeaa 100644 --- a/internal/app/api/router.go +++ b/internal/app/api/router.go @@ -212,6 +212,46 @@ func (a *API) setupRoutes() { } } logger.Debug("告警相关接口注册成功 (需要认证和审计)") + + // 饲料管理相关路由组 + feedGroup := authGroup.Group("/feed") + { + // 营养种类 (Nutrient) 路由 + feedGroup.POST("/nutrients", a.feedController.CreateNutrient) + feedGroup.PUT("/nutrients/:id", a.feedController.UpdateNutrient) + feedGroup.DELETE("/nutrients/:id", a.feedController.DeleteNutrient) + feedGroup.GET("/nutrients/:id", a.feedController.GetNutrient) + feedGroup.GET("/nutrients", a.feedController.ListNutrients) + + // 原料 (RawMaterial) 路由 + feedGroup.POST("/raw-materials", a.feedController.CreateRawMaterial) + feedGroup.PUT("/raw-materials/:id", a.feedController.UpdateRawMaterial) + feedGroup.DELETE("/raw-materials/:id", a.feedController.DeleteRawMaterial) + feedGroup.GET("/raw-materials/:id", a.feedController.GetRawMaterial) + feedGroup.GET("/raw-materials", a.feedController.ListRawMaterials) + + // 猪品种 (PigBreed) 路由 + feedGroup.POST("/pig-breeds", a.feedController.CreatePigBreed) + feedGroup.PUT("/pig-breeds/:id", a.feedController.UpdatePigBreed) + feedGroup.DELETE("/pig-breeds/:id", a.feedController.DeletePigBreed) + feedGroup.GET("/pig-breeds/:id", a.feedController.GetPigBreed) + feedGroup.GET("/pig-breeds", a.feedController.ListPigBreeds) + + // 猪年龄阶段 (PigAgeStage) 路由 + feedGroup.POST("/pig-age-stages", a.feedController.CreatePigAgeStage) + feedGroup.PUT("/pig-age-stages/:id", a.feedController.UpdatePigAgeStage) + feedGroup.DELETE("/pig-age-stages/:id", a.feedController.DeletePigAgeStage) + feedGroup.GET("/pig-age-stages/:id", a.feedController.GetPigAgeStage) + feedGroup.GET("/pig-age-stages", a.feedController.ListPigAgeStages) + + // 猪类型 (PigType) 路由 + feedGroup.POST("/pig-types", a.feedController.CreatePigType) + feedGroup.PUT("/pig-types/:id", a.feedController.UpdatePigType) + feedGroup.DELETE("/pig-types/:id", a.feedController.DeletePigType) + feedGroup.GET("/pig-types/:id", a.feedController.GetPigType) + feedGroup.GET("/pig-types", a.feedController.ListPigTypes) + } + logger.Debug("饲料管理相关接口注册成功 (需要认证和审计)") } logger.Debug("所有接口注册成功") diff --git a/internal/app/controller/feed/feed_controller.go b/internal/app/controller/feed/feed_controller.go new file mode 100644 index 0000000..6a5e4f1 --- /dev/null +++ b/internal/app/controller/feed/feed_controller.go @@ -0,0 +1,875 @@ +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/infra/logs" + + "github.com/labstack/echo/v4" +) + +// Controller 定义了饲料管理相关的控制器 +type Controller struct { + ctx context.Context + feedManagementService service.FeedManagementService +} + +// NewController 创建一个新的 Controller 实例 +func NewController(ctx context.Context, feedManagementService service.FeedManagementService) *Controller { + return &Controller{ + ctx: ctx, + feedManagementService: feedManagementService, + } +} + +// --- 营养种类 (Nutrient) 接口方法实现 --- + +// CreateNutrient godoc +// @Summary 创建营养种类 +// @Description 创建一个新的营养种类。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param nutrient body dto.CreateNutrientRequest true "营养种类信息" +// @Success 200 {object} controller.Response{data=dto.NutrientResponse} "业务码为201代表创建成功" +// @Router /api/v1/feed/nutrients [post] +func (c *Controller) CreateNutrient(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "CreateNutrient") + var req dto.CreateNutrientRequest + 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.feedManagementService.CreateNutrient(reqCtx, &req) + if err != nil { + logger.Errorf("%s: 服务层创建营养种类失败: %v", actionType, err) + if errors.Is(err, service.ErrNutrientNameConflict) { + 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) +} + +// UpdateNutrient godoc +// @Summary 更新营养种类 +// @Description 根据ID更新营养种类信息。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param id path int true "营养种类ID" +// @Param nutrient body dto.UpdateNutrientRequest true "更新后的营养种类信息" +// @Success 200 {object} controller.Response{data=dto.NutrientResponse} "业务码为200代表更新成功" +// @Router /api/v1/feed/nutrients/{id} [put] +func (c *Controller) UpdateNutrient(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "UpdateNutrient") + 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.UpdateNutrientRequest + 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.UpdateNutrient(reqCtx, uint32(id), &req) + if err != nil { + logger.Errorf("%s: 服务层更新营养种类失败: %v, ID: %d", actionType, err, id) + if errors.Is(err, service.ErrNutrientNotFound) { + return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "营养种类不存在", id) + } + if errors.Is(err, service.ErrNutrientNameConflict) { + 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) +} + +// DeleteNutrient 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/nutrients/{id} [delete] +func (c *Controller) DeleteNutrient(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "DeleteNutrient") + 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) + } + + err = c.feedManagementService.DeleteNutrient(reqCtx, uint32(id)) + if err != nil { + logger.Errorf("%s: 服务层删除营养种类失败: %v, ID: %d", actionType, err, id) + if errors.Is(err, service.ErrNutrientNotFound) { + 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) +} + +// GetNutrient godoc +// @Summary 获取营养种类详情 +// @Description 根据ID获取单个营养种类的详细信息。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Produce json +// @Param id path int true "营养种类ID" +// @Success 200 {object} controller.Response{data=dto.NutrientResponse} "业务码为200代表成功获取" +// @Router /api/v1/feed/nutrients/{id} [get] +func (c *Controller) GetNutrient(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "GetNutrient") + 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) + } + + resp, err := c.feedManagementService.GetNutrient(reqCtx, uint32(id)) + if err != nil { + logger.Errorf("%s: 服务层获取营养种类详情失败: %v, ID: %d", actionType, err, id) + if errors.Is(err, service.ErrNutrientNotFound) { + 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) +} + +// ListNutrients godoc +// @Summary 获取营养种类列表 +// @Description 获取所有营养种类的列表,支持分页和过滤。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Produce json +// @Param query query dto.ListNutrientRequest false "查询参数" +// @Success 200 {object} controller.Response{data=dto.ListNutrientResponse} "业务码为200代表成功获取列表" +// @Router /api/v1/feed/nutrients [get] +func (c *Controller) ListNutrients(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListNutrients") + const actionType = "获取营养种类列表" + var req dto.ListNutrientRequest + 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.ListNutrients(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) +} + +// --- 原料 (RawMaterial) 接口方法实现 --- + +// CreateRawMaterial godoc +// @Summary 创建原料 +// @Description 创建一个新的原料。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param rawMaterial body dto.CreateRawMaterialRequest true "原料信息" +// @Success 200 {object} controller.Response{data=dto.RawMaterialResponse} "业务码为201代表创建成功" +// @Router /api/v1/feed/raw-materials [post] +func (c *Controller) CreateRawMaterial(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "CreateRawMaterial") + var req dto.CreateRawMaterialRequest + 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.feedManagementService.CreateRawMaterial(reqCtx, &req) + if err != nil { + logger.Errorf("%s: 服务层创建原料失败: %v", actionType, err) + if errors.Is(err, service.ErrRawMaterialNameConflict) { + 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) +} + +// UpdateRawMaterial godoc +// @Summary 更新原料 +// @Description 根据ID更新原料信息。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param id path int true "原料ID" +// @Param rawMaterial body dto.UpdateRawMaterialRequest true "更新后的原料信息" +// @Success 200 {object} controller.Response{data=dto.RawMaterialResponse} "业务码为200代表更新成功" +// @Router /api/v1/feed/raw-materials/{id} [put] +func (c *Controller) UpdateRawMaterial(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "UpdateRawMaterial") + 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.UpdateRawMaterialRequest + 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.UpdateRawMaterial(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) + } + if errors.Is(err, service.ErrRawMaterialNameConflict) { + 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) +} + +// DeleteRawMaterial 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/raw-materials/{id} [delete] +func (c *Controller) DeleteRawMaterial(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "DeleteRawMaterial") + 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) + } + + err = c.feedManagementService.DeleteRawMaterial(reqCtx, uint32(id)) + 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, "服务层删除原料失败", id) + } + + logger.Infof("%s: 原料删除成功, ID: %d", actionType, id) + return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "原料删除成功", nil, actionType, "原料删除成功", id) +} + +// GetRawMaterial godoc +// @Summary 获取原料详情 +// @Description 根据ID获取单个原料的详细信息。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Produce json +// @Param id path int true "原料ID" +// @Success 200 {object} controller.Response{data=dto.RawMaterialResponse} "业务码为200代表成功获取" +// @Router /api/v1/feed/raw-materials/{id} [get] +func (c *Controller) GetRawMaterial(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "GetRawMaterial") + 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) + } + + resp, err := c.feedManagementService.GetRawMaterial(reqCtx, uint32(id)) + 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, "服务层获取原料详情失败", id) + } + + logger.Infof("%s: 获取原料详情成功, ID: %d", actionType, id) + return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取原料详情成功", resp, actionType, "获取原料详情成功", resp) +} + +// ListRawMaterials godoc +// @Summary 获取原料列表 +// @Description 获取所有原料的列表,支持分页和过滤。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Produce json +// @Param query query dto.ListRawMaterialRequest false "查询参数" +// @Success 200 {object} controller.Response{data=dto.ListRawMaterialResponse} "业务码为200代表成功获取列表" +// @Router /api/v1/feed/raw-materials [get] +func (c *Controller) ListRawMaterials(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListRawMaterials") + const actionType = "获取原料列表" + var req dto.ListRawMaterialRequest + 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.ListRawMaterials(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) +} + +// --- 猪品种 (PigBreed) 接口方法实现 --- + +// CreatePigBreed godoc +// @Summary 创建猪品种 +// @Description 创建一个新的猪品种。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param pigBreed body dto.CreatePigBreedRequest true "猪品种信息" +// @Success 200 {object} controller.Response{data=dto.PigBreedResponse} "业务码为201代表创建成功" +// @Router /api/v1/feed/pig-breeds [post] +func (c *Controller) CreatePigBreed(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "CreatePigBreed") + var req dto.CreatePigBreedRequest + 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.feedManagementService.CreatePigBreed(reqCtx, &req) + if err != nil { + logger.Errorf("%s: 服务层创建猪品种失败: %v", actionType, err) + // 猪品种没有名称冲突的领域错误,这里直接返回内部错误 + 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) +} + +// UpdatePigBreed godoc +// @Summary 更新猪品种 +// @Description 根据ID更新猪品种信息。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param id path int true "猪品种ID" +// @Param pigBreed body dto.UpdatePigBreedRequest true "更新后的猪品种信息" +// @Success 200 {object} controller.Response{data=dto.PigBreedResponse} "业务码为200代表更新成功" +// @Router /api/v1/feed/pig-breeds/{id} [put] +func (c *Controller) UpdatePigBreed(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "UpdatePigBreed") + 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.UpdatePigBreedRequest + 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.UpdatePigBreed(reqCtx, uint32(id), &req) + if err != nil { + logger.Errorf("%s: 服务层更新猪品种失败: %v, ID: %d", actionType, err, id) + if errors.Is(err, service.ErrPigBreedNotFound) { + 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) +} + +// DeletePigBreed 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/pig-breeds/{id} [delete] +func (c *Controller) DeletePigBreed(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "DeletePigBreed") + 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) + } + + err = c.feedManagementService.DeletePigBreed(reqCtx, uint32(id)) + if err != nil { + logger.Errorf("%s: 服务层删除猪品种失败: %v, ID: %d", actionType, err, id) + if errors.Is(err, service.ErrPigBreedNotFound) { + return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "猪品种不存在", id) + } + if errors.Is(err, service.ErrPigBreedInUse) { + return controller.SendErrorWithAudit(ctx, controller.CodeConflict, 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) +} + +// GetPigBreed godoc +// @Summary 获取猪品种详情 +// @Description 根据ID获取单个猪品种的详细信息。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Produce json +// @Param id path int true "猪品种ID" +// @Success 200 {object} controller.Response{data=dto.PigBreedResponse} "业务码为200代表成功获取" +// @Router /api/v1/feed/pig-breeds/{id} [get] +func (c *Controller) GetPigBreed(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "GetPigBreed") + 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) + } + + resp, err := c.feedManagementService.GetPigBreed(reqCtx, uint32(id)) + if err != nil { + logger.Errorf("%s: 服务层获取猪品种详情失败: %v, ID: %d", actionType, err, id) + if errors.Is(err, service.ErrPigBreedNotFound) { + 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) +} + +// ListPigBreeds godoc +// @Summary 获取猪品种列表 +// @Description 获取所有猪品种的列表,支持分页和过滤。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Produce json +// @Param query query dto.ListPigBreedRequest false "查询参数" +// @Success 200 {object} controller.Response{data=dto.ListPigBreedResponse} "业务码为200代表成功获取列表" +// @Router /api/v1/feed/pig-breeds [get] +func (c *Controller) ListPigBreeds(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListPigBreeds") + const actionType = "获取猪品种列表" + var req dto.ListPigBreedRequest + 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.ListPigBreeds(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) +} + +// --- 猪年龄阶段 (PigAgeStage) 接口方法实现 --- + +// CreatePigAgeStage godoc +// @Summary 创建猪年龄阶段 +// @Description 创建一个新的猪年龄阶段。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param pigAgeStage body dto.CreatePigAgeStageRequest true "猪年龄阶段信息" +// @Success 200 {object} controller.Response{data=dto.PigAgeStageResponse} "业务码为201代表创建成功" +// @Router /api/v1/feed/pig-age-stages [post] +func (c *Controller) CreatePigAgeStage(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "CreatePigAgeStage") + var req dto.CreatePigAgeStageRequest + 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.feedManagementService.CreatePigAgeStage(reqCtx, &req) + if err != nil { + logger.Errorf("%s: 服务层创建猪年龄阶段失败: %v", actionType, err) + // 猪年龄阶段没有名称冲突的领域错误,这里直接返回内部错误 + 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) +} + +// UpdatePigAgeStage godoc +// @Summary 更新猪年龄阶段 +// @Description 根据ID更新猪年龄阶段信息。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param id path int true "猪年龄阶段ID" +// @Param pigAgeStage body dto.UpdatePigAgeStageRequest true "更新后的猪年龄阶段信息" +// @Success 200 {object} controller.Response{data=dto.PigAgeStageResponse} "业务码为200代表更新成功" +// @Router /api/v1/feed/pig-age-stages/{id} [put] +func (c *Controller) UpdatePigAgeStage(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "UpdatePigAgeStage") + 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.UpdatePigAgeStageRequest + 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.UpdatePigAgeStage(reqCtx, uint32(id), &req) + if err != nil { + logger.Errorf("%s: 服务层更新猪年龄阶段失败: %v, ID: %d", actionType, err, id) + if errors.Is(err, service.ErrPigAgeStageNotFound) { + 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) +} + +// DeletePigAgeStage 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/pig-age-stages/{id} [delete] +func (c *Controller) DeletePigAgeStage(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "DeletePigAgeStage") + 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) + } + + err = c.feedManagementService.DeletePigAgeStage(reqCtx, uint32(id)) + if err != nil { + logger.Errorf("%s: 服务层删除猪年龄阶段失败: %v, ID: %d", actionType, err, id) + if errors.Is(err, service.ErrPigAgeStageNotFound) { + return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "猪年龄阶段不存在", id) + } + if errors.Is(err, service.ErrPigAgeStageInUse) { + return controller.SendErrorWithAudit(ctx, controller.CodeConflict, 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) +} + +// GetPigAgeStage godoc +// @Summary 获取猪年龄阶段详情 +// @Description 根据ID获取单个猪年龄阶段的详细信息。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Produce json +// @Param id path int true "猪年龄阶段ID" +// @Success 200 {object} controller.Response{data=dto.PigAgeStageResponse} "业务码为200代表成功获取" +// @Router /api/v1/feed/pig-age-stages/{id} [get] +func (c *Controller) GetPigAgeStage(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "GetPigAgeStage") + 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) + } + + resp, err := c.feedManagementService.GetPigAgeStage(reqCtx, uint32(id)) + if err != nil { + logger.Errorf("%s: 服务层获取猪年龄阶段详情失败: %v, ID: %d", actionType, err, id) + if errors.Is(err, service.ErrPigAgeStageNotFound) { + 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) +} + +// ListPigAgeStages godoc +// @Summary 获取猪年龄阶段列表 +// @Description 获取所有猪年龄阶段的列表,支持分页和过滤。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Produce json +// @Param query query dto.ListPigAgeStageRequest false "查询参数" +// @Success 200 {object} controller.Response{data=dto.ListPigAgeStageResponse} "业务码为200代表成功获取列表" +// @Router /api/v1/feed/pig-age-stages [get] +func (c *Controller) ListPigAgeStages(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListPigAgeStages") + const actionType = "获取猪年龄阶段列表" + var req dto.ListPigAgeStageRequest + 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.ListPigAgeStages(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) +} + +// --- 猪类型 (PigType) 接口方法实现 --- + +// CreatePigType godoc +// @Summary 创建猪类型 +// @Description 创建一个新的猪类型。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param pigType body dto.CreatePigTypeRequest true "猪类型信息" +// @Success 200 {object} controller.Response{data=dto.PigTypeResponse} "业务码为201代表创建成功" +// @Router /api/v1/feed/pig-types [post] +func (c *Controller) CreatePigType(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "CreatePigType") + var req dto.CreatePigTypeRequest + 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.feedManagementService.CreatePigType(reqCtx, &req) + if err != nil { + logger.Errorf("%s: 服务层创建猪类型失败: %v", actionType, err) + if errors.Is(err, service.ErrPigBreedNotFound) { + return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "关联猪品种不存在", req) + } + if errors.Is(err, service.ErrPigAgeStageNotFound) { + return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, 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) +} + +// UpdatePigType godoc +// @Summary 更新猪类型 +// @Description 根据ID更新猪类型信息。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param id path int true "猪类型ID" +// @Param pigType body dto.UpdatePigTypeRequest true "更新后的猪类型信息" +// @Success 200 {object} controller.Response{data=dto.PigTypeResponse} "业务码为200代表更新成功" +// @Router /api/v1/feed/pig-types/{id} [put] +func (c *Controller) UpdatePigType(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "UpdatePigType") + 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.UpdatePigTypeRequest + 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.UpdatePigType(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) + } + if errors.Is(err, service.ErrPigBreedNotFound) { + return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), actionType, "关联猪品种不存在", req) + } + if errors.Is(err, service.ErrPigAgeStageNotFound) { + return controller.SendErrorWithAudit(ctx, controller.CodeNotFound, 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) +} + +// DeletePigType 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/pig-types/{id} [delete] +func (c *Controller) DeletePigType(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "DeletePigType") + 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) + } + + err = c.feedManagementService.DeletePigType(reqCtx, uint32(id)) + 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, "服务层删除猪类型失败", id) + } + + logger.Infof("%s: 猪类型删除成功, ID: %d", actionType, id) + return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "猪类型删除成功", nil, actionType, "猪类型删除成功", id) +} + +// GetPigType godoc +// @Summary 获取猪类型详情 +// @Description 根据ID获取单个猪类型的详细信息。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Produce json +// @Param id path int true "猪类型ID" +// @Success 200 {object} controller.Response{data=dto.PigTypeResponse} "业务码为200代表成功获取" +// @Router /api/v1/feed/pig-types/{id} [get] +func (c *Controller) GetPigType(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "GetPigType") + 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) + } + + resp, err := c.feedManagementService.GetPigType(reqCtx, uint32(id)) + 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, "服务层获取猪类型详情失败", id) + } + + logger.Infof("%s: 获取猪类型详情成功, ID: %d", actionType, id) + return controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取猪类型详情成功", resp, actionType, "获取猪类型详情成功", resp) +} + +// ListPigTypes godoc +// @Summary 获取猪类型列表 +// @Description 获取所有猪类型的列表,支持分页和过滤。 +// @Tags 饲料管理 +// @Security BearerAuth +// @Produce json +// @Param query query dto.ListPigTypeRequest false "查询参数" +// @Success 200 {object} controller.Response{data=dto.ListPigTypeResponse} "业务码为200代表成功获取列表" +// @Router /api/v1/feed/pig-types [get] +func (c *Controller) ListPigTypes(ctx echo.Context) error { + reqCtx, logger := logs.Trace(ctx.Request().Context(), c.ctx, "ListPigTypes") + const actionType = "获取猪类型列表" + var req dto.ListPigTypeRequest + 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.ListPigTypes(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) +} diff --git a/internal/app/dto/feed_converter.go b/internal/app/dto/feed_converter.go new file mode 100644 index 0000000..43eda01 --- /dev/null +++ b/internal/app/dto/feed_converter.go @@ -0,0 +1,202 @@ +package dto + +import ( + "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" +) + +// ConvertNutrientToDTO 将 models.Nutrient 转换为 NutrientResponse DTO +func ConvertNutrientToDTO(nutrient *models.Nutrient) *NutrientResponse { + if nutrient == nil { + return nil + } + return &NutrientResponse{ + ID: nutrient.ID, + CreatedAt: nutrient.CreatedAt, + UpdatedAt: nutrient.UpdatedAt, + Name: nutrient.Name, + Description: nutrient.Description, + } +} + +// ConvertNutrientListToDTO 将 []models.Nutrient 转换为 ListNutrientResponse DTO +func ConvertNutrientListToDTO(nutrients []models.Nutrient, total int64, page, pageSize int) *ListNutrientResponse { + nutrientDTOs := make([]NutrientResponse, len(nutrients)) + for i, n := range nutrients { + nutrientDTOs[i] = *ConvertNutrientToDTO(&n) + } + + return &ListNutrientResponse{ + List: nutrientDTOs, + Pagination: PaginationDTO{ + Page: page, + PageSize: pageSize, + Total: total, + }, + } +} + +// ConvertRawMaterialToDTO 将 models.RawMaterial 转换为 RawMaterialResponse DTO +func ConvertRawMaterialToDTO(rm *models.RawMaterial) *RawMaterialResponse { + if rm == nil { + return nil + } + + rawMaterialNutrientDTOs := make([]RawMaterialNutrientDTO, len(rm.RawMaterialNutrients)) + for i, rmn := range rm.RawMaterialNutrients { + rawMaterialNutrientDTOs[i] = RawMaterialNutrientDTO{ + ID: rmn.ID, + CreatedAt: rmn.CreatedAt, + UpdatedAt: rmn.UpdatedAt, + NutrientID: rmn.NutrientID, + Nutrient: rmn.Nutrient.Name, // 假设 Nutrient 已经被预加载 + Value: rmn.Value, + } + } + + return &RawMaterialResponse{ + ID: rm.ID, + CreatedAt: rm.CreatedAt, + UpdatedAt: rm.UpdatedAt, + Name: rm.Name, + Description: rm.Description, + RawMaterialNutrients: rawMaterialNutrientDTOs, + } +} + +// ConvertRawMaterialListToDTO 将 []models.RawMaterial 转换为 ListRawMaterialResponse DTO +func ConvertRawMaterialListToDTO(rawMaterials []models.RawMaterial, total int64, page, pageSize int) *ListRawMaterialResponse { + rawMaterialDTOs := make([]RawMaterialResponse, len(rawMaterials)) + for i, rm := range rawMaterials { + rawMaterialDTOs[i] = *ConvertRawMaterialToDTO(&rm) + } + + return &ListRawMaterialResponse{ + List: rawMaterialDTOs, + Pagination: PaginationDTO{ + Page: page, + PageSize: pageSize, + Total: total, + }, + } +} + +// ConvertPigBreedToDTO 将 models.PigBreed 转换为 PigBreedResponse DTO +func ConvertPigBreedToDTO(breed *models.PigBreed) *PigBreedResponse { + if breed == nil { + return nil + } + return &PigBreedResponse{ + ID: breed.ID, + CreatedAt: breed.CreatedAt, + UpdatedAt: breed.UpdatedAt, + Name: breed.Name, + Description: breed.Description, + ParentInfo: breed.ParentInfo, + AppearanceFeatures: breed.AppearanceFeatures, + BreedAdvantages: breed.BreedAdvantages, + BreedDisadvantages: breed.BreedDisadvantages, + } +} + +// ConvertPigBreedListToDTO 将 []models.PigBreed 转换为 ListPigBreedResponse DTO +func ConvertPigBreedListToDTO(breeds []models.PigBreed, total int64, page, pageSize int) *ListPigBreedResponse { + breedDTOs := make([]PigBreedResponse, len(breeds)) + for i, b := range breeds { + breedDTOs[i] = *ConvertPigBreedToDTO(&b) + } + + return &ListPigBreedResponse{ + List: breedDTOs, + Pagination: PaginationDTO{ + Page: page, + PageSize: pageSize, + Total: total, + }, + } +} + +// ConvertPigAgeStageToDTO 将 models.PigAgeStage 转换为 PigAgeStageResponse DTO +func ConvertPigAgeStageToDTO(ageStage *models.PigAgeStage) *PigAgeStageResponse { + if ageStage == nil { + return nil + } + return &PigAgeStageResponse{ + ID: ageStage.ID, + CreatedAt: ageStage.CreatedAt, + UpdatedAt: ageStage.UpdatedAt, + Name: ageStage.Name, + Description: ageStage.Description, + } +} + +// ConvertPigAgeStageListToDTO 将 []models.PigAgeStage 转换为 ListPigAgeStageResponse DTO +func ConvertPigAgeStageListToDTO(ageStages []models.PigAgeStage, total int64, page, pageSize int) *ListPigAgeStageResponse { + ageStageDTOs := make([]PigAgeStageResponse, len(ageStages)) + for i, as := range ageStages { + ageStageDTOs[i] = *ConvertPigAgeStageToDTO(&as) + } + + return &ListPigAgeStageResponse{ + List: ageStageDTOs, + Pagination: PaginationDTO{ + Page: page, + PageSize: pageSize, + Total: total, + }, + } +} + +// ConvertPigTypeToDTO 将 models.PigType 转换为 PigTypeResponse DTO +func ConvertPigTypeToDTO(pt *models.PigType) *PigTypeResponse { + if pt == nil { + return nil + } + + pigNutrientRequirementDTOs := make([]PigNutrientRequirementDTO, len(pt.PigNutrientRequirements)) + for i, pnr := range pt.PigNutrientRequirements { + pigNutrientRequirementDTOs[i] = PigNutrientRequirementDTO{ + ID: pnr.ID, + CreatedAt: pnr.CreatedAt, + UpdatedAt: pnr.UpdatedAt, + NutrientID: pnr.NutrientID, + NutrientName: pnr.Nutrient.Name, // 假设 Nutrient 已经被预加载 + MinRequirement: pnr.MinRequirement, + MaxRequirement: pnr.MaxRequirement, + } + } + + return &PigTypeResponse{ + ID: pt.ID, + CreatedAt: pt.CreatedAt, + UpdatedAt: pt.UpdatedAt, + BreedID: pt.BreedID, + BreedName: pt.Breed.Name, // 假设 Breed 已经被预加载 + AgeStageID: pt.AgeStageID, + AgeStageName: pt.AgeStage.Name, // 假设 AgeStage 已经被预加载 + Description: pt.Description, + DailyFeedIntake: pt.DailyFeedIntake, + DailyGainWeight: pt.DailyGainWeight, + MinDays: pt.MinDays, + MaxDays: pt.MaxDays, + MinWeight: pt.MinWeight, + MaxWeight: pt.MaxWeight, + PigNutrientRequirements: pigNutrientRequirementDTOs, + } +} + +// ConvertPigTypeListToDTO 将 []models.PigType 转换为 ListPigTypeResponse DTO +func ConvertPigTypeListToDTO(pigTypes []models.PigType, total int64, page, pageSize int) *ListPigTypeResponse { + pigTypeDTOs := make([]PigTypeResponse, len(pigTypes)) + for i, pt := range pigTypes { + pigTypeDTOs[i] = *ConvertPigTypeToDTO(&pt) + } + + return &ListPigTypeResponse{ + List: pigTypeDTOs, + Pagination: PaginationDTO{ + Page: page, + PageSize: pageSize, + Total: total, + }, + } +} diff --git a/internal/app/dto/feed_dto.go b/internal/app/dto/feed_dto.go new file mode 100644 index 0000000..00afdd0 --- /dev/null +++ b/internal/app/dto/feed_dto.go @@ -0,0 +1,261 @@ +package dto + +import ( + "time" +) + +// ============================================================================================================= +// 营养种类 (Nutrient) 相关 DTO +// ============================================================================================================= + +// CreateNutrientRequest 创建营养种类的请求体 +type CreateNutrientRequest struct { + Name string `json:"name" validate:"required,max=100"` // 营养素名称 + Description string `json:"description" validate:"max=255"` // 描述 +} + +// UpdateNutrientRequest 更新营养种类的请求体 +type UpdateNutrientRequest struct { + Name string `json:"name" validate:"required,max=100"` // 营养素名称 + Description string `json:"description" validate:"max=255"` // 描述 +} + +// NutrientResponse 营养种类响应体 +type NutrientResponse struct { + ID uint32 `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Name string `json:"name"` + Description string `json:"description"` +} + +// ListNutrientRequest 定义了获取营养种类列表的请求参数 +type ListNutrientRequest 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" +} + +// ListNutrientResponse 是获取营养种类列表的响应结构 +type ListNutrientResponse struct { + List []NutrientResponse `json:"list"` + Pagination PaginationDTO `json:"pagination"` +} + +// ============================================================================================================= +// 原料 (RawMaterial) 相关 DTO +// ============================================================================================================= + +// CreateRawMaterialRequest 创建原料的请求体 +type CreateRawMaterialRequest struct { + Name string `json:"name" validate:"required,max=100"` // 原料名称 + Description string `json:"description" validate:"max=255"` // 描述 +} + +// UpdateRawMaterialRequest 更新原料的请求体 +type UpdateRawMaterialRequest struct { + Name string `json:"name" validate:"required,max=100"` // 原料名称 + Description string `json:"description" validate:"max=255"` // 描述 +} + +// RawMaterialNutrientDTO 原料营养素响应体 +type RawMaterialNutrientDTO struct { + ID uint32 `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + NutrientID uint32 `json:"nutrient_id"` + Nutrient string `json:"nutrient_name"` // 营养素名称 + Value float32 `json:"value"` // 营养价值含量 +} + +// RawMaterialResponse 原料响应体 +type RawMaterialResponse struct { + ID uint32 `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Name string `json:"name"` + Description string `json:"description"` + RawMaterialNutrients []RawMaterialNutrientDTO `json:"raw_material_nutrients"` // 关联的营养素信息 +} + +// ListRawMaterialRequest 定义了获取原料列表的请求参数 +type ListRawMaterialRequest 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" +} + +// ListRawMaterialResponse 是获取原料列表的响应结构 +type ListRawMaterialResponse struct { + List []RawMaterialResponse `json:"list"` + Pagination PaginationDTO `json:"pagination"` +} + +// ============================================================================================================= +// 猪品种 (PigBreed) 相关 DTO +// ============================================================================================================= + +// CreatePigBreedRequest 创建猪品种的请求体 +type CreatePigBreedRequest struct { + Name string `json:"name" validate:"required,max=50"` // 品种名称 + Description string `json:"description"` // 其他描述 + ParentInfo string `json:"parent_info"` // 父母信息 + AppearanceFeatures string `json:"appearance_features"` // 外貌特征 + BreedAdvantages string `json:"breed_advantages"` // 品种优点 + BreedDisadvantages string `json:"breed_disadvantages"` // 品种缺点 +} + +// UpdatePigBreedRequest 更新猪品种的请求体 +type UpdatePigBreedRequest struct { + Name string `json:"name" validate:"required,max=50"` // 品种名称 + Description string `json:"description"` // 其他描述 + ParentInfo string `json:"parent_info"` // 父母信息 + AppearanceFeatures string `json:"appearance_features"` // 外貌特征 + BreedAdvantages string `json:"breed_advantages"` // 品种优点 + BreedDisadvantages string `json:"breed_disadvantages"` // 品种缺点 +} + +// PigBreedResponse 猪品种响应体 +type PigBreedResponse struct { + ID uint32 `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Name string `json:"name"` + Description string `json:"description"` + ParentInfo string `json:"parent_info"` + AppearanceFeatures string `json:"appearance_features"` + BreedAdvantages string `json:"breed_advantages"` + BreedDisadvantages string `json:"breed_disadvantages"` +} + +// ListPigBreedRequest 定义了获取猪品种列表的请求参数 +type ListPigBreedRequest 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" +} + +// ListPigBreedResponse 是获取猪品种列表的响应结构 +type ListPigBreedResponse struct { + List []PigBreedResponse `json:"list"` + Pagination PaginationDTO `json:"pagination"` +} + +// ============================================================================================================= +// 猪年龄阶段 (PigAgeStage) 相关 DTO +// ============================================================================================================= + +// CreatePigAgeStageRequest 创建猪年龄阶段的请求体 +type CreatePigAgeStageRequest struct { + Name string `json:"name" validate:"required,max=50"` // 年龄阶段名称 + Description string `json:"description" validate:"max=255"` // 阶段描述 +} + +// UpdatePigAgeStageRequest 更新猪年龄阶段的请求体 +type UpdatePigAgeStageRequest struct { + Name string `json:"name" validate:"required,max=50"` // 年龄阶段名称 + Description string `json:"description" validate:"max=255"` // 阶段描述 +} + +// PigAgeStageResponse 猪年龄阶段响应体 +type PigAgeStageResponse struct { + ID uint32 `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Name string `json:"name"` + Description string `json:"description"` +} + +// ListPigAgeStageRequest 定义了获取猪年龄阶段列表的请求参数 +type ListPigAgeStageRequest 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" +} + +// ListPigAgeStageResponse 是获取猪年龄阶段列表的响应结构 +type ListPigAgeStageResponse struct { + List []PigAgeStageResponse `json:"list"` + Pagination PaginationDTO `json:"pagination"` +} + +// ============================================================================================================= +// 猪类型 (PigType) 相关 DTO +// ============================================================================================================= + +// CreatePigTypeRequest 创建猪类型的请求体 +type CreatePigTypeRequest struct { + BreedID uint32 `json:"breed_id" validate:"required"` // 关联的猪品种ID + AgeStageID uint32 `json:"age_stage_id" validate:"required"` // 关联的猪年龄阶段ID + Description string `json:"description" validate:"max=255"` // 该猪类型的描述或特点 + DailyFeedIntake float32 `json:"daily_feed_intake"` // 理论日均食量 (g/天) + DailyGainWeight float32 `json:"daily_gain_weight"` // 理论日增重 (g/天) + MinDays uint32 `json:"min_days"` // 该猪类型在该年龄阶段的最小日龄 + MaxDays uint32 `json:"max_days"` // 该猪类型在该年龄阶段的最大日龄 + MinWeight float32 `json:"min_weight"` // 该猪类型在该年龄阶段的最小体重 (g) + MaxWeight float32 `json:"max_weight"` // 该猪类型在该年龄阶段的最大体重 (g) +} + +// UpdatePigTypeRequest 更新猪类型的请求体 +type UpdatePigTypeRequest struct { + BreedID uint32 `json:"breed_id" validate:"required"` // 关联的猪品种ID + AgeStageID uint32 `json:"age_stage_id" validate:"required"` // 关联的猪年龄阶段ID + Description string `json:"description" validate:"max=255"` // 该猪类型的描述或特点 + DailyFeedIntake float32 `json:"daily_feed_intake"` // 理论日均食量 (g/天) + DailyGainWeight float32 `json:"daily_gain_weight"` // 理论日增重 (g/天) + MinDays uint32 `json:"min_days"` // 该猪类型在该年龄阶段的最小日龄 + MaxDays uint32 `json:"max_days"` // 该猪类型在该年龄阶段的最大日龄 + MinWeight float32 `json:"min_weight"` // 该猪类型在该年龄阶段的最小体重 (g) + MaxWeight float32 `json:"max_weight"` // 该猪类型在该年龄阶段的最大体重 (g) +} + +// PigNutrientRequirementDTO 猪营养需求响应体 +type PigNutrientRequirementDTO struct { + ID uint32 `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + NutrientID uint32 `json:"nutrient_id"` + NutrientName string `json:"nutrient_name"` // 营养素名称 + MinRequirement float32 `json:"min_requirement"` // 最低营养需求量 + MaxRequirement float32 `json:"max_requirement"` // 最高营养需求量 +} + +// PigTypeResponse 猪类型响应体 +type PigTypeResponse struct { + ID uint32 `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + BreedID uint32 `json:"breed_id"` + BreedName string `json:"breed_name"` // 猪品种名称 + AgeStageID uint32 `json:"age_stage_id"` + AgeStageName string `json:"age_stage_name"` // 猪年龄阶段名称 + Description string `json:"description"` + DailyFeedIntake float32 `json:"daily_feed_intake"` + DailyGainWeight float32 `json:"daily_gain_weight"` + MinDays uint32 `json:"min_days"` + MaxDays uint32 `json:"max_days"` + MinWeight float32 `json:"min_weight"` + MaxWeight float32 `json:"max_weight"` + PigNutrientRequirements []PigNutrientRequirementDTO `json:"pig_nutrient_requirements"` // 关联的营养需求 +} + +// ListPigTypeRequest 定义了获取猪类型列表的请求参数 +type ListPigTypeRequest struct { + Page int `json:"page" query:"page"` // 页码 + PageSize int `json:"page_size" query:"page_size"` // 每页数量 + BreedID *uint32 `json:"breed_id" query:"breed_id"` // 关联的猪品种ID + AgeStageID *uint32 `json:"age_stage_id" query:"age_stage_id"` // 关联的猪年龄阶段ID + BreedName *string `json:"breed_name" query:"breed_name"` // 关联的猪品种名称 (用于模糊查询) + AgeStageName *string `json:"age_stage_name" query:"age_stage_name"` // 关联的猪年龄阶段名称 (用于模糊查询) + OrderBy string `json:"order_by" query:"order_by"` // 排序字段,例如 "id DESC" +} + +// ListPigTypeResponse 是获取猪类型列表的响应结构 +type ListPigTypeResponse struct { + List []PigTypeResponse `json:"list"` + Pagination PaginationDTO `json:"pagination"` +} diff --git a/internal/app/service/feed_management_service.go b/internal/app/service/feed_management_service.go new file mode 100644 index 0000000..2c7fabc --- /dev/null +++ b/internal/app/service/feed_management_service.go @@ -0,0 +1,529 @@ +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 ( + ErrNutrientNameConflict = errors.New("营养种类名称已存在") + ErrNutrientNotFound = errors.New("营养种类不存在") + ErrRawMaterialNameConflict = errors.New("原料名称已存在") + ErrRawMaterialNotFound = errors.New("原料不存在") + ErrPigBreedInUse = errors.New("猪品种正在被猪类型使用,无法删除") + ErrPigBreedNotFound = errors.New("猪品种不存在") + ErrPigAgeStageInUse = errors.New("猪年龄阶段正在被猪类型使用,无法删除") + ErrPigAgeStageNotFound = errors.New("猪年龄阶段不存在") + ErrPigTypeNotFound = errors.New("猪类型不存在") +) + +// FeedManagementService 定义了饲料管理的应用服务接口 +type FeedManagementService interface { + // 营养种类相关 + CreateNutrient(ctx context.Context, req *dto.CreateNutrientRequest) (*dto.NutrientResponse, error) + UpdateNutrient(ctx context.Context, id uint32, req *dto.UpdateNutrientRequest) (*dto.NutrientResponse, error) + DeleteNutrient(ctx context.Context, id uint32) error + GetNutrient(ctx context.Context, id uint32) (*dto.NutrientResponse, error) + ListNutrients(ctx context.Context, req *dto.ListNutrientRequest) (*dto.ListNutrientResponse, error) + + // 原料相关 + CreateRawMaterial(ctx context.Context, req *dto.CreateRawMaterialRequest) (*dto.RawMaterialResponse, error) + UpdateRawMaterial(ctx context.Context, id uint32, req *dto.UpdateRawMaterialRequest) (*dto.RawMaterialResponse, error) + DeleteRawMaterial(ctx context.Context, id uint32) error + GetRawMaterial(ctx context.Context, id uint32) (*dto.RawMaterialResponse, error) + ListRawMaterials(ctx context.Context, req *dto.ListRawMaterialRequest) (*dto.ListRawMaterialResponse, error) + + // 猪品种相关 + CreatePigBreed(ctx context.Context, req *dto.CreatePigBreedRequest) (*dto.PigBreedResponse, error) + UpdatePigBreed(ctx context.Context, id uint32, req *dto.UpdatePigBreedRequest) (*dto.PigBreedResponse, error) + DeletePigBreed(ctx context.Context, id uint32) error + GetPigBreed(ctx context.Context, id uint32) (*dto.PigBreedResponse, error) + ListPigBreeds(ctx context.Context, req *dto.ListPigBreedRequest) (*dto.ListPigBreedResponse, error) + + // 猪年龄阶段相关 + CreatePigAgeStage(ctx context.Context, req *dto.CreatePigAgeStageRequest) (*dto.PigAgeStageResponse, error) + UpdatePigAgeStage(ctx context.Context, id uint32, req *dto.UpdatePigAgeStageRequest) (*dto.PigAgeStageResponse, error) + DeletePigAgeStage(ctx context.Context, id uint32) error + GetPigAgeStage(ctx context.Context, id uint32) (*dto.PigAgeStageResponse, error) + ListPigAgeStages(ctx context.Context, req *dto.ListPigAgeStageRequest) (*dto.ListPigAgeStageResponse, error) + + // 猪类型相关 + CreatePigType(ctx context.Context, req *dto.CreatePigTypeRequest) (*dto.PigTypeResponse, error) + UpdatePigType(ctx context.Context, id uint32, req *dto.UpdatePigTypeRequest) (*dto.PigTypeResponse, error) + 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) +} + +// feedManagementServiceImpl 是 FeedManagementService 接口的实现 +type feedManagementServiceImpl struct { + ctx context.Context + recipeSvc recipe.Service +} + +// NewFeedManagementService 创建一个新的 FeedManagementService 实例 +func NewFeedManagementService(ctx context.Context, recipeSvc recipe.Service) FeedManagementService { + return &feedManagementServiceImpl{ + ctx: ctx, + recipeSvc: recipeSvc, + } +} + +// ===================================================================================================================== +// 营养种类 (Nutrient) 实现 +// ===================================================================================================================== + +// CreateNutrient 创建营养种类 +func (s *feedManagementServiceImpl) CreateNutrient(ctx context.Context, req *dto.CreateNutrientRequest) (*dto.NutrientResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateNutrient") + + nutrient, err := s.recipeSvc.CreateNutrient(serviceCtx, req.Name, req.Description) + if err != nil { + if errors.Is(err, recipe.ErrNutrientNameConflict) { + return nil, ErrNutrientNameConflict + } + return nil, fmt.Errorf("创建营养种类失败: %w", err) + } + return dto.ConvertNutrientToDTO(nutrient), nil +} + +// UpdateNutrient 更新营养种类 +func (s *feedManagementServiceImpl) UpdateNutrient(ctx context.Context, id uint32, req *dto.UpdateNutrientRequest) (*dto.NutrientResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateNutrient") + + nutrient, err := s.recipeSvc.UpdateNutrient(serviceCtx, id, req.Name, req.Description) + if err != nil { + if errors.Is(err, recipe.ErrNutrientNotFound) { + return nil, ErrNutrientNotFound + } + if errors.Is(err, recipe.ErrNutrientNameConflict) { + return nil, ErrNutrientNameConflict + } + return nil, fmt.Errorf("更新营养种类失败: %w", err) + } + return dto.ConvertNutrientToDTO(nutrient), nil +} + +// DeleteNutrient 删除营养种类 +func (s *feedManagementServiceImpl) DeleteNutrient(ctx context.Context, id uint32) error { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteNutrient") + err := s.recipeSvc.DeleteNutrient(serviceCtx, id) + if err != nil { + if errors.Is(err, recipe.ErrNutrientNotFound) { + return ErrNutrientNotFound + } + return fmt.Errorf("删除营养种类失败: %w", err) + } + return nil +} + +// GetNutrient 获取单个营养种类 +func (s *feedManagementServiceImpl) GetNutrient(ctx context.Context, id uint32) (*dto.NutrientResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetNutrient") + + nutrient, err := s.recipeSvc.GetNutrient(serviceCtx, id) + if err != nil { + if errors.Is(err, recipe.ErrNutrientNotFound) { + return nil, ErrNutrientNotFound + } + return nil, fmt.Errorf("获取营养种类失败: %w", err) + } + return dto.ConvertNutrientToDTO(nutrient), nil +} + +// ListNutrients 列出营养种类 +func (s *feedManagementServiceImpl) ListNutrients(ctx context.Context, req *dto.ListNutrientRequest) (*dto.ListNutrientResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListNutrients") + + nutrients, total, err := s.recipeSvc.ListNutrients(serviceCtx, req.Page, req.PageSize) + if err != nil { + return nil, fmt.Errorf("获取营养种类列表失败: %w", err) + } + + return dto.ConvertNutrientListToDTO(nutrients, total, req.Page, req.PageSize), nil +} + +// ===================================================================================================================== +// 原料 (RawMaterial) 实现 +// ===================================================================================================================== + +// CreateRawMaterial 创建原料 +func (s *feedManagementServiceImpl) CreateRawMaterial(ctx context.Context, req *dto.CreateRawMaterialRequest) (*dto.RawMaterialResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateRawMaterial") + + rawMaterial, err := s.recipeSvc.CreateRawMaterial(serviceCtx, req.Name, req.Description) + if err != nil { + if errors.Is(err, recipe.ErrRawMaterialNameConflict) { + return nil, ErrRawMaterialNameConflict + } + return nil, fmt.Errorf("创建原料失败: %w", err) + } + + return dto.ConvertRawMaterialToDTO(rawMaterial), nil +} + +// UpdateRawMaterial 更新原料 +func (s *feedManagementServiceImpl) UpdateRawMaterial(ctx context.Context, id uint32, req *dto.UpdateRawMaterialRequest) (*dto.RawMaterialResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateRawMaterial") + + rawMaterial, err := s.recipeSvc.UpdateRawMaterial(serviceCtx, id, req.Name, req.Description) + if err != nil { + if errors.Is(err, recipe.ErrRawMaterialNotFound) { + return nil, ErrRawMaterialNotFound + } + if errors.Is(err, recipe.ErrRawMaterialNameConflict) { + return nil, ErrRawMaterialNameConflict + } + return nil, fmt.Errorf("更新原料失败: %w", err) + } + return dto.ConvertRawMaterialToDTO(rawMaterial), nil +} + +// DeleteRawMaterial 删除原料 +func (s *feedManagementServiceImpl) DeleteRawMaterial(ctx context.Context, id uint32) error { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteRawMaterial") + err := s.recipeSvc.DeleteRawMaterial(serviceCtx, id) + if err != nil { + if errors.Is(err, recipe.ErrRawMaterialNotFound) { + return ErrRawMaterialNotFound + } + return fmt.Errorf("删除原料失败: %w", err) + } + return nil +} + +// GetRawMaterial 获取单个原料 +func (s *feedManagementServiceImpl) GetRawMaterial(ctx context.Context, id uint32) (*dto.RawMaterialResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetRawMaterial") + + rawMaterial, err := s.recipeSvc.GetRawMaterial(serviceCtx, id) + if err != nil { + if errors.Is(err, recipe.ErrRawMaterialNotFound) { + return nil, ErrRawMaterialNotFound + } + return nil, fmt.Errorf("获取原料失败: %w", err) + } + return dto.ConvertRawMaterialToDTO(rawMaterial), nil +} + +// ListRawMaterials 列出原料 +func (s *feedManagementServiceImpl) ListRawMaterials(ctx context.Context, req *dto.ListRawMaterialRequest) (*dto.ListRawMaterialResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListRawMaterials") + + rawMaterials, total, err := s.recipeSvc.ListRawMaterials(serviceCtx, req.Page, req.PageSize) + if err != nil { + return nil, fmt.Errorf("获取原料列表失败: %w", err) + } + + return dto.ConvertRawMaterialListToDTO(rawMaterials, total, req.Page, req.PageSize), nil +} + +// ===================================================================================================================== +// 猪品种 (PigBreed) 实现 +// ===================================================================================================================== + +// CreatePigBreed 创建猪品种 +func (s *feedManagementServiceImpl) CreatePigBreed(ctx context.Context, req *dto.CreatePigBreedRequest) (*dto.PigBreedResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreatePigBreed") + + breed := &models.PigBreed{ + Name: req.Name, + Description: req.Description, + ParentInfo: req.ParentInfo, + AppearanceFeatures: req.AppearanceFeatures, + BreedAdvantages: req.BreedAdvantages, + BreedDisadvantages: req.BreedDisadvantages, + } + + if err := s.recipeSvc.CreatePigBreed(serviceCtx, breed); err != nil { + return nil, fmt.Errorf("创建猪品种失败: %w", err) + } + return dto.ConvertPigBreedToDTO(breed), nil +} + +// UpdatePigBreed 更新猪品种 +func (s *feedManagementServiceImpl) UpdatePigBreed(ctx context.Context, id uint32, req *dto.UpdatePigBreedRequest) (*dto.PigBreedResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdatePigBreed") + + breed := &models.PigBreed{ + Model: models.Model{ID: id}, + Name: req.Name, + Description: req.Description, + ParentInfo: req.ParentInfo, + AppearanceFeatures: req.AppearanceFeatures, + BreedAdvantages: req.BreedAdvantages, + BreedDisadvantages: req.BreedDisadvantages, + } + + if err := s.recipeSvc.UpdatePigBreed(serviceCtx, breed); err != nil { + if errors.Is(err, recipe.ErrPigBreedNotFound) { + return nil, ErrPigBreedNotFound + } + return nil, fmt.Errorf("更新猪品种失败: %w", err) + } + return dto.ConvertPigBreedToDTO(breed), nil +} + +// DeletePigBreed 删除猪品种 +func (s *feedManagementServiceImpl) DeletePigBreed(ctx context.Context, id uint32) error { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeletePigBreed") + err := s.recipeSvc.DeletePigBreed(serviceCtx, id) + if err != nil { + if errors.Is(err, recipe.ErrPigBreedNotFound) { + return ErrPigBreedNotFound + } + if errors.Is(err, recipe.ErrPigBreedInUse) { + return ErrPigBreedInUse + } + return fmt.Errorf("删除猪品种失败: %w", err) + } + return nil +} + +// GetPigBreed 获取单个猪品种 +func (s *feedManagementServiceImpl) GetPigBreed(ctx context.Context, id uint32) (*dto.PigBreedResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetPigBreed") + + breed, err := s.recipeSvc.GetPigBreedByID(serviceCtx, id) + if err != nil { + if errors.Is(err, recipe.ErrPigBreedNotFound) { + return nil, ErrPigBreedNotFound + } + return nil, fmt.Errorf("获取猪品种失败: %w", err) + } + return dto.ConvertPigBreedToDTO(breed), nil +} + +// ListPigBreeds 列出猪品种 +func (s *feedManagementServiceImpl) ListPigBreeds(ctx context.Context, req *dto.ListPigBreedRequest) (*dto.ListPigBreedResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListPigBreeds") + + opts := repository.PigBreedListOptions{ + Name: req.Name, + OrderBy: req.OrderBy, + } + breeds, total, err := s.recipeSvc.ListPigBreeds(serviceCtx, opts, req.Page, req.PageSize) + if err != nil { + return nil, fmt.Errorf("获取猪品种列表失败: %w", err) + } + + return dto.ConvertPigBreedListToDTO(breeds, total, req.Page, req.PageSize), nil +} + +// ===================================================================================================================== +// 猪年龄阶段 (PigAgeStage) 实现 +// ===================================================================================================================== + +// CreatePigAgeStage 创建猪年龄阶段 +func (s *feedManagementServiceImpl) CreatePigAgeStage(ctx context.Context, req *dto.CreatePigAgeStageRequest) (*dto.PigAgeStageResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreatePigAgeStage") + + ageStage := &models.PigAgeStage{ + Name: req.Name, + Description: req.Description, + } + + if err := s.recipeSvc.CreatePigAgeStage(serviceCtx, ageStage); err != nil { + return nil, fmt.Errorf("创建猪年龄阶段失败: %w", err) + } + return dto.ConvertPigAgeStageToDTO(ageStage), nil +} + +// UpdatePigAgeStage 更新猪年龄阶段 +func (s *feedManagementServiceImpl) UpdatePigAgeStage(ctx context.Context, id uint32, req *dto.UpdatePigAgeStageRequest) (*dto.PigAgeStageResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdatePigAgeStage") + + ageStage := &models.PigAgeStage{ + Model: models.Model{ID: id}, + Name: req.Name, + Description: req.Description, + } + + if err := s.recipeSvc.UpdatePigAgeStage(serviceCtx, ageStage); err != nil { + if errors.Is(err, recipe.ErrPigAgeStageNotFound) { + return nil, ErrPigAgeStageNotFound + } + return nil, fmt.Errorf("更新猪年龄阶段失败: %w", err) + } + return dto.ConvertPigAgeStageToDTO(ageStage), nil +} + +// DeletePigAgeStage 删除猪年龄阶段 +func (s *feedManagementServiceImpl) DeletePigAgeStage(ctx context.Context, id uint32) error { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeletePigAgeStage") + err := s.recipeSvc.DeletePigAgeStage(serviceCtx, id) + if err != nil { + if errors.Is(err, recipe.ErrPigAgeStageNotFound) { + return ErrPigAgeStageNotFound + } + if errors.Is(err, recipe.ErrPigAgeStageInUse) { + return ErrPigAgeStageInUse + } + return fmt.Errorf("删除猪年龄阶段失败: %w", err) + } + return nil +} + +// GetPigAgeStage 获取单个猪年龄阶段 +func (s *feedManagementServiceImpl) GetPigAgeStage(ctx context.Context, id uint32) (*dto.PigAgeStageResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetPigAgeStage") + + ageStage, err := s.recipeSvc.GetPigAgeStageByID(serviceCtx, id) + if err != nil { + if errors.Is(err, recipe.ErrPigAgeStageNotFound) { + return nil, ErrPigAgeStageNotFound + } + return nil, fmt.Errorf("获取猪年龄阶段失败: %w", err) + } + return dto.ConvertPigAgeStageToDTO(ageStage), nil +} + +// ListPigAgeStages 列出猪年龄阶段 +func (s *feedManagementServiceImpl) ListPigAgeStages(ctx context.Context, req *dto.ListPigAgeStageRequest) (*dto.ListPigAgeStageResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListPigAgeStages") + + opts := repository.PigAgeStageListOptions{ + Name: req.Name, + OrderBy: req.OrderBy, + } + ageStages, total, err := s.recipeSvc.ListPigAgeStages(serviceCtx, opts, req.Page, req.PageSize) + if err != nil { + return nil, fmt.Errorf("获取猪年龄阶段列表失败: %w", err) + } + + return dto.ConvertPigAgeStageListToDTO(ageStages, total, req.Page, req.PageSize), nil +} + +// ===================================================================================================================== +// 猪类型 (PigType) 实现 +// ===================================================================================================================== + +// CreatePigType 创建猪类型 +func (s *feedManagementServiceImpl) CreatePigType(ctx context.Context, req *dto.CreatePigTypeRequest) (*dto.PigTypeResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreatePigType") + + pigType := &models.PigType{ + BreedID: req.BreedID, + AgeStageID: req.AgeStageID, + Description: req.Description, + DailyFeedIntake: req.DailyFeedIntake, + DailyGainWeight: req.DailyGainWeight, + MinDays: req.MinDays, + MaxDays: req.MaxDays, + MinWeight: req.MinWeight, + MaxWeight: req.MaxWeight, + } + + if err := s.recipeSvc.CreatePigType(serviceCtx, pigType); err != nil { + if errors.Is(err, recipe.ErrPigBreedNotFound) { + return nil, ErrPigBreedNotFound + } + if errors.Is(err, recipe.ErrPigAgeStageNotFound) { + return nil, ErrPigAgeStageNotFound + } + return nil, fmt.Errorf("创建猪类型失败: %w", err) + } + // 创建后需要重新获取,以包含关联数据 + createdPigType, err := s.recipeSvc.GetPigTypeByID(serviceCtx, pigType.ID) + if err != nil { + if errors.Is(err, recipe.ErrPigTypeNotFound) { // 理论上不应该发生,因为刚创建 + return nil, ErrPigTypeNotFound + } + return nil, fmt.Errorf("创建猪类型后获取详情失败: %w", err) + } + return dto.ConvertPigTypeToDTO(createdPigType), nil +} + +// UpdatePigType 更新猪类型 +func (s *feedManagementServiceImpl) UpdatePigType(ctx context.Context, id uint32, req *dto.UpdatePigTypeRequest) (*dto.PigTypeResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdatePigType") + + pigType := &models.PigType{ + Model: models.Model{ID: id}, + BreedID: req.BreedID, + AgeStageID: req.AgeStageID, + Description: req.Description, + DailyFeedIntake: req.DailyFeedIntake, + DailyGainWeight: req.DailyGainWeight, + MinDays: req.MinDays, + MaxDays: req.MaxDays, + MinWeight: req.MinWeight, + MaxWeight: req.MaxWeight, + } + + if err := s.recipeSvc.UpdatePigType(serviceCtx, pigType); err != nil { + if errors.Is(err, recipe.ErrPigTypeNotFound) { + return nil, ErrPigTypeNotFound + } + if errors.Is(err, recipe.ErrPigBreedNotFound) { + return nil, ErrPigBreedNotFound + } + if errors.Is(err, recipe.ErrPigAgeStageNotFound) { + return nil, ErrPigAgeStageNotFound + } + return nil, fmt.Errorf("更新猪类型失败: %w", err) + } + // 更新后需要重新获取,以包含关联数据 + 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) + } + return dto.ConvertPigTypeToDTO(updatedPigType), nil +} + +// DeletePigType 删除猪类型 +func (s *feedManagementServiceImpl) DeletePigType(ctx context.Context, id uint32) error { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeletePigType") + err := s.recipeSvc.DeletePigType(serviceCtx, id) + if err != nil { + if errors.Is(err, recipe.ErrPigTypeNotFound) { + return ErrPigTypeNotFound + } + return fmt.Errorf("删除猪类型失败: %w", err) + } + return nil +} + +// GetPigType 获取单个猪类型 +func (s *feedManagementServiceImpl) GetPigType(ctx context.Context, id uint32) (*dto.PigTypeResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetPigType") + + pigType, err := s.recipeSvc.GetPigTypeByID(serviceCtx, id) + if err != nil { + if errors.Is(err, recipe.ErrPigTypeNotFound) { + return nil, ErrPigTypeNotFound + } + return nil, fmt.Errorf("获取猪类型失败: %w", err) + } + return dto.ConvertPigTypeToDTO(pigType), nil +} + +// ListPigTypes 列出猪类型 +func (s *feedManagementServiceImpl) ListPigTypes(ctx context.Context, req *dto.ListPigTypeRequest) (*dto.ListPigTypeResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListPigTypes") + + opts := repository.PigTypeListOptions{ + BreedID: req.BreedID, + AgeStageID: req.AgeStageID, + BreedName: req.BreedName, + AgeStageName: req.AgeStageName, + OrderBy: req.OrderBy, + } + pigTypes, total, err := s.recipeSvc.ListPigTypes(serviceCtx, opts, req.Page, req.PageSize) + if err != nil { + return nil, fmt.Errorf("获取猪类型列表失败: %w", err) + } + + return dto.ConvertPigTypeListToDTO(pigTypes, total, req.Page, req.PageSize), nil +} diff --git a/internal/core/application.go b/internal/core/application.go index 5270b0a..377fbff 100644 --- a/internal/core/application.go +++ b/internal/core/application.go @@ -63,6 +63,7 @@ func NewApplication(configPath string) (*Application, error) { appServices.userService, appServices.auditService, appServices.thresholdAlarmService, + appServices.feedManagementService, infra.tokenGenerator, infra.lora.listenHandler, ) diff --git a/internal/core/component_initializers.go b/internal/core/component_initializers.go index 9fbc395..10b213d 100644 --- a/internal/core/component_initializers.go +++ b/internal/core/component_initializers.go @@ -12,6 +12,7 @@ import ( domain_notify "git.huangwc.com/pig/pig-farm-controller/internal/domain/notify" "git.huangwc.com/pig/pig-farm-controller/internal/domain/pig" "git.huangwc.com/pig/pig-farm-controller/internal/domain/plan" + "git.huangwc.com/pig/pig-farm-controller/internal/domain/recipe" "git.huangwc.com/pig/pig-farm-controller/internal/domain/task" "git.huangwc.com/pig/pig-farm-controller/internal/infra/config" "git.huangwc.com/pig/pig-farm-controller/internal/infra/database" @@ -82,6 +83,9 @@ type Repositories struct { medicationLogRepo repository.MedicationLogRepository notificationRepo repository.NotificationRepository alarmRepo repository.AlarmRepository + pigTypeRepo repository.PigTypeRepository + rawMaterialRepo repository.RawMaterialRepository + nutrientRepo repository.NutrientRepository unitOfWork repository.UnitOfWork } @@ -110,6 +114,9 @@ func initRepositories(ctx context.Context, db *gorm.DB) *Repositories { medicationLogRepo: repository.NewGormMedicationLogRepository(logs.AddCompName(baseCtx, "MedicationLogRepo"), db), notificationRepo: repository.NewGormNotificationRepository(logs.AddCompName(baseCtx, "NotificationRepo"), db), alarmRepo: repository.NewGormAlarmRepository(logs.AddCompName(baseCtx, "AlarmRepo"), db), + pigTypeRepo: repository.NewGormPigTypeRepository(logs.AddCompName(baseCtx, "PigTypeRepo"), db), + rawMaterialRepo: repository.NewGormRawMaterialRepository(logs.AddCompName(baseCtx, "RawMaterialRepo"), db), + nutrientRepo: repository.NewGormNutrientRepository(logs.AddCompName(baseCtx, "NutrientRepo"), db), unitOfWork: repository.NewGormUnitOfWork(logs.AddCompName(baseCtx, "UnitOfWork"), db), } } @@ -127,6 +134,7 @@ type DomainServices struct { planService plan.Service notifyService domain_notify.Service alarmService alarm.AlarmService + recipeService recipe.Service } // initDomainServices 初始化所有的领域服务。 @@ -206,6 +214,14 @@ func initDomainServices(ctx context.Context, cfg *config.Config, infra *Infrastr taskFactory, ) + // 配方管理服务 + recipeService := recipe.NewRecipeService( + logs.AddCompName(baseCtx, "RecipeService"), + infra.repos.nutrientRepo, + infra.repos.rawMaterialRepo, + infra.repos.pigTypeRepo, + ) + return &DomainServices{ pigPenTransferManager: pigPenTransferManager, pigTradeManager: pigTradeManager, @@ -218,6 +234,7 @@ func initDomainServices(ctx context.Context, cfg *config.Config, infra *Infrastr planService: planService, notifyService: notifyService, alarmService: alarmService, + recipeService: recipeService, }, nil } @@ -231,6 +248,7 @@ type AppServices struct { userService service.UserService auditService service.AuditService thresholdAlarmService service.ThresholdAlarmService + feedManagementService service.FeedManagementService } // initAppServices 初始化所有的应用服务。 @@ -278,6 +296,7 @@ func initAppServices(ctx context.Context, infra *Infrastructure, domainServices auditService := service.NewAuditService(logs.AddCompName(baseCtx, "AuditService"), infra.repos.userActionLogRepo) planService := service.NewPlanService(logs.AddCompName(baseCtx, "AppPlanService"), domainServices.planService) userService := service.NewUserService(logs.AddCompName(baseCtx, "UserService"), infra.repos.userRepo, infra.tokenGenerator, domainServices.notifyService) + feedManagementService := service.NewFeedManagementService(logs.AddCompName(baseCtx, "FeedManagementService"), domainServices.recipeService) return &AppServices{ pigFarmService: pigFarmService, @@ -288,6 +307,7 @@ func initAppServices(ctx context.Context, infra *Infrastructure, domainServices planService: planService, userService: userService, thresholdAlarmService: thresholdAlarmService, + feedManagementService: feedManagementService, } } diff --git a/internal/infra/repository/pig_type_repository.go b/internal/infra/repository/pig_type_repository.go index 05a9e65..a975b9b 100644 --- a/internal/infra/repository/pig_type_repository.go +++ b/internal/infra/repository/pig_type_repository.go @@ -216,7 +216,7 @@ func (r *gormPigTypeRepository) CreatePigType(ctx context.Context, pigType *mode func (r *gormPigTypeRepository) GetPigTypeByID(ctx context.Context, id uint32) (*models.PigType, error) { repoCtx := logs.AddFuncName(ctx, r.ctx, "GetPigTypeByID") var pigType models.PigType - err := r.db.WithContext(repoCtx).Preload("Breed").Preload("AgeStage").First(&pigType, id).Error + err := r.db.WithContext(repoCtx).Preload("Breed").Preload("AgeStage").Preload("PigNutrientRequirements.Nutrient").First(&pigType, id).Error if err != nil { return nil, err } @@ -275,7 +275,7 @@ func (r *gormPigTypeRepository) ListPigTypes(ctx context.Context, opts PigTypeLi query = query.Order(orderBy) offset := (page - 1) * pageSize - err := query.Limit(pageSize).Offset(offset).Preload("Breed").Preload("AgeStage").Find(&results).Error + err := query.Limit(pageSize).Offset(offset).Preload("Breed").Preload("AgeStage").Preload("PigNutrientRequirements.Nutrient").Find(&results).Error return results, total, err } diff --git a/project_structure.txt b/project_structure.txt index ab57131..d1c4654 100644 --- a/project_structure.txt +++ b/project_structure.txt @@ -48,6 +48,7 @@ internal/app/api/router.go internal/app/controller/alarm/threshold_alarm_controller.go internal/app/controller/auth_utils.go internal/app/controller/device/device_controller.go +internal/app/controller/feed/feed_controller.go internal/app/controller/health/health_controller.go internal/app/controller/management/controller_helpers.go internal/app/controller/management/pig_batch_controller.go @@ -64,6 +65,8 @@ internal/app/dto/alarm_dto.go internal/app/dto/device_converter.go internal/app/dto/device_dto.go internal/app/dto/dto.go +internal/app/dto/feed_converter.go +internal/app/dto/feed_dto.go internal/app/dto/monitor_converter.go internal/app/dto/monitor_dto.go internal/app/dto/notification_converter.go @@ -77,6 +80,7 @@ internal/app/middleware/audit.go internal/app/middleware/auth.go internal/app/service/audit_service.go internal/app/service/device_service.go +internal/app/service/feed_management_service.go internal/app/service/monitor_service.go internal/app/service/pig_batch_service.go internal/app/service/pig_farm_service.go