diff --git a/internal/app/api/api.go b/internal/app/api/api.go index 405e944..1a2cea4 100644 --- a/internal/app/api/api.go +++ b/internal/app/api/api.go @@ -56,7 +56,6 @@ type API struct { monitorController *monitor.Controller // 数据监控控制器实例 healthController *health.Controller // 健康检查控制器实例 alarmController *alarm.ThresholdAlarmController // 阈值告警控制器 - feedController *feed.Controller // 饲料管理控制器实例 nutrientController *feed.NutrientController // 营养控制器实例 pigAgeStageController *feed.PigAgeStageController // 猪龄阶段控制器实例 pigBreedController *feed.PigBreedController // 猪品种控制器实例 @@ -79,7 +78,11 @@ func NewAPI(cfg config.ServerConfig, userService service.UserService, auditService service.AuditService, alarmService service.ThresholdAlarmService, - feedManagementService service.FeedManagementService, + nutrientService service.NutrientService, + rawMaterialService service.RawMaterialService, + pigBreedService service.PigBreedService, + pigAgeStageService service.PigAgeStageService, + pigTypeService service.PigTypeService, tokenGenerator token.Generator, listenHandler webhook.ListenHandler, ) *API { @@ -111,12 +114,11 @@ func NewAPI(cfg config.ServerConfig, monitorController: monitor.NewController(logs.AddCompName(baseCtx, "MonitorController"), monitorService), healthController: health.NewController(logs.AddCompName(baseCtx, "HealthController")), alarmController: alarm.NewThresholdAlarmController(logs.AddCompName(baseCtx, "ThresholdAlarmController"), alarmService), - feedController: feed.NewController(logs.AddCompName(baseCtx, "FeedController"), feedManagementService), - nutrientController: feed.NewNutrientController(logs.AddCompName(baseCtx, "NutrientController"), feedManagementService), - pigAgeStageController: feed.NewPigAgeStageController(logs.AddCompName(baseCtx, "PigAgeStageController"), feedManagementService), - pigBreedController: feed.NewPigBreedController(logs.AddCompName(baseCtx, "PigBreedController"), feedManagementService), - pigTypeController: feed.NewPigTypeController(logs.AddCompName(baseCtx, "PigTypeController"), feedManagementService), - rawMaterialController: feed.NewRawMaterialController(logs.AddCompName(baseCtx, "RawMaterialController"), feedManagementService), + nutrientController: feed.NewNutrientController(logs.AddCompName(baseCtx, "NutrientController"), nutrientService), + pigAgeStageController: feed.NewPigAgeStageController(logs.AddCompName(baseCtx, "PigAgeStageController"), pigAgeStageService), + pigBreedController: feed.NewPigBreedController(logs.AddCompName(baseCtx, "PigBreedController"), pigBreedService), + pigTypeController: feed.NewPigTypeController(logs.AddCompName(baseCtx, "PigTypeController"), pigTypeService), + rawMaterialController: feed.NewRawMaterialController(logs.AddCompName(baseCtx, "RawMaterialController"), rawMaterialService), } api.setupRoutes() // 设置所有路由 diff --git a/internal/app/controller/feed/feed_controller.go b/internal/app/controller/feed/feed_controller.go deleted file mode 100644 index 559f853..0000000 --- a/internal/app/controller/feed/feed_controller.go +++ /dev/null @@ -1,21 +0,0 @@ -package feed - -import ( - "context" - - "git.huangwc.com/pig/pig-farm-controller/internal/app/service" -) - -// 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, - } -} diff --git a/internal/app/controller/feed/nutrient_controller.go b/internal/app/controller/feed/nutrient_controller.go index 97d16eb..f7469c0 100644 --- a/internal/app/controller/feed/nutrient_controller.go +++ b/internal/app/controller/feed/nutrient_controller.go @@ -15,15 +15,15 @@ import ( // NutrientController 定义了营养种类相关的控制器 type NutrientController struct { - ctx context.Context - feedManagementService service.FeedManagementService + ctx context.Context + nutrientService service.NutrientService } // NewNutrientController 创建一个新的 NutrientController 实例 -func NewNutrientController(ctx context.Context, feedManagementService service.FeedManagementService) *NutrientController { +func NewNutrientController(ctx context.Context, feedManagementService service.NutrientService) *NutrientController { return &NutrientController{ - ctx: ctx, - feedManagementService: feedManagementService, + ctx: ctx, + nutrientService: feedManagementService, } } @@ -46,7 +46,7 @@ func (c *NutrientController) CreateNutrient(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) } - resp, err := c.feedManagementService.CreateNutrient(reqCtx, &req) + resp, err := c.nutrientService.CreateNutrient(reqCtx, &req) if err != nil { logger.Errorf("%s: 服务层创建营养种类失败: %v", actionType, err) if errors.Is(err, service.ErrNutrientNameConflict) { @@ -86,7 +86,7 @@ func (c *NutrientController) UpdateNutrient(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) } - resp, err := c.feedManagementService.UpdateNutrient(reqCtx, uint32(id), &req) + resp, err := c.nutrientService.UpdateNutrient(reqCtx, uint32(id), &req) if err != nil { logger.Errorf("%s: 服务层更新营养种类失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrNutrientNotFound) { @@ -121,7 +121,7 @@ func (c *NutrientController) DeleteNutrient(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的营养种类ID格式", actionType, "营养种类ID格式错误", idStr) } - err = c.feedManagementService.DeleteNutrient(reqCtx, uint32(id)) + err = c.nutrientService.DeleteNutrient(reqCtx, uint32(id)) if err != nil { logger.Errorf("%s: 服务层删除营养种类失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrNutrientNotFound) { @@ -153,7 +153,7 @@ func (c *NutrientController) GetNutrient(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的营养种类ID格式", actionType, "营养种类ID格式错误", idStr) } - resp, err := c.feedManagementService.GetNutrient(reqCtx, uint32(id)) + resp, err := c.nutrientService.GetNutrient(reqCtx, uint32(id)) if err != nil { logger.Errorf("%s: 服务层获取营养种类详情失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrNutrientNotFound) { @@ -184,7 +184,7 @@ func (c *NutrientController) ListNutrients(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "查询参数绑定失败", req) } - resp, err := c.feedManagementService.ListNutrients(reqCtx, &req) + resp, err := c.nutrientService.ListNutrients(reqCtx, &req) if err != nil { logger.Errorf("%s: 服务层获取营养种类列表失败: %v", actionType, err) return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取营养种类列表失败: "+err.Error(), actionType, "服务层获取营养种类列表失败", nil) diff --git a/internal/app/controller/feed/pig_age_stage_controller.go b/internal/app/controller/feed/pig_age_stage_controller.go index dffc4e1..d8261a7 100644 --- a/internal/app/controller/feed/pig_age_stage_controller.go +++ b/internal/app/controller/feed/pig_age_stage_controller.go @@ -15,15 +15,15 @@ import ( // PigAgeStageController 定义了猪年龄阶段相关的控制器 type PigAgeStageController struct { - ctx context.Context - feedManagementService service.FeedManagementService + ctx context.Context + pigAgeStageService service.PigAgeStageService } // NewPigAgeStageController 创建一个新的 PigAgeStageController 实例 -func NewPigAgeStageController(ctx context.Context, feedManagementService service.FeedManagementService) *PigAgeStageController { +func NewPigAgeStageController(ctx context.Context, feedManagementService service.PigAgeStageService) *PigAgeStageController { return &PigAgeStageController{ - ctx: ctx, - feedManagementService: feedManagementService, + ctx: ctx, + pigAgeStageService: feedManagementService, } } @@ -46,7 +46,7 @@ func (c *PigAgeStageController) CreatePigAgeStage(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) } - resp, err := c.feedManagementService.CreatePigAgeStage(reqCtx, &req) + resp, err := c.pigAgeStageService.CreatePigAgeStage(reqCtx, &req) if err != nil { logger.Errorf("%s: 服务层创建猪年龄阶段失败: %v", actionType, err) // 猪年龄阶段没有名称冲突的领域错误,这里直接返回内部错误 @@ -84,7 +84,7 @@ func (c *PigAgeStageController) UpdatePigAgeStage(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) } - resp, err := c.feedManagementService.UpdatePigAgeStage(reqCtx, uint32(id), &req) + resp, err := c.pigAgeStageService.UpdatePigAgeStage(reqCtx, uint32(id), &req) if err != nil { logger.Errorf("%s: 服务层更新猪年龄阶段失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrPigAgeStageNotFound) { @@ -116,7 +116,7 @@ func (c *PigAgeStageController) DeletePigAgeStage(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的猪年龄阶段ID格式", actionType, "猪年龄阶段ID格式错误", idStr) } - err = c.feedManagementService.DeletePigAgeStage(reqCtx, uint32(id)) + err = c.pigAgeStageService.DeletePigAgeStage(reqCtx, uint32(id)) if err != nil { logger.Errorf("%s: 服务层删除猪年龄阶段失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrPigAgeStageNotFound) { @@ -151,7 +151,7 @@ func (c *PigAgeStageController) GetPigAgeStage(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的猪年龄阶段ID格式", actionType, "猪年龄阶段ID格式错误", idStr) } - resp, err := c.feedManagementService.GetPigAgeStage(reqCtx, uint32(id)) + resp, err := c.pigAgeStageService.GetPigAgeStage(reqCtx, uint32(id)) if err != nil { logger.Errorf("%s: 服务层获取猪年龄阶段详情失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrPigAgeStageNotFound) { @@ -182,7 +182,7 @@ func (c *PigAgeStageController) ListPigAgeStages(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "查询参数绑定失败", req) } - resp, err := c.feedManagementService.ListPigAgeStages(reqCtx, &req) + resp, err := c.pigAgeStageService.ListPigAgeStages(reqCtx, &req) if err != nil { logger.Errorf("%s: 服务层获取猪年龄阶段列表失败: %v", actionType, err) return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取猪年龄阶段列表失败: "+err.Error(), actionType, "服务层获取猪年龄阶段列表失败", nil) diff --git a/internal/app/controller/feed/pig_breed_controller.go b/internal/app/controller/feed/pig_breed_controller.go index 26b54ea..a0867d8 100644 --- a/internal/app/controller/feed/pig_breed_controller.go +++ b/internal/app/controller/feed/pig_breed_controller.go @@ -15,15 +15,15 @@ import ( // PigBreedController 定义了猪品种相关的控制器 type PigBreedController struct { - ctx context.Context - feedManagementService service.FeedManagementService + ctx context.Context + pigBreedService service.PigBreedService } // NewPigBreedController 创建一个新的 PigBreedController 实例 -func NewPigBreedController(ctx context.Context, feedManagementService service.FeedManagementService) *PigBreedController { +func NewPigBreedController(ctx context.Context, feedManagementService service.PigBreedService) *PigBreedController { return &PigBreedController{ - ctx: ctx, - feedManagementService: feedManagementService, + ctx: ctx, + pigBreedService: feedManagementService, } } @@ -46,7 +46,7 @@ func (c *PigBreedController) CreatePigBreed(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) } - resp, err := c.feedManagementService.CreatePigBreed(reqCtx, &req) + resp, err := c.pigBreedService.CreatePigBreed(reqCtx, &req) if err != nil { logger.Errorf("%s: 服务层创建猪品种失败: %v", actionType, err) // 猪品种没有名称冲突的领域错误,这里直接返回内部错误 @@ -84,7 +84,7 @@ func (c *PigBreedController) UpdatePigBreed(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) } - resp, err := c.feedManagementService.UpdatePigBreed(reqCtx, uint32(id), &req) + resp, err := c.pigBreedService.UpdatePigBreed(reqCtx, uint32(id), &req) if err != nil { logger.Errorf("%s: 服务层更新猪品种失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrPigBreedNotFound) { @@ -116,7 +116,7 @@ func (c *PigBreedController) DeletePigBreed(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的猪品种ID格式", actionType, "猪品种ID格式错误", idStr) } - err = c.feedManagementService.DeletePigBreed(reqCtx, uint32(id)) + err = c.pigBreedService.DeletePigBreed(reqCtx, uint32(id)) if err != nil { logger.Errorf("%s: 服务层删除猪品种失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrPigBreedNotFound) { @@ -151,7 +151,7 @@ func (c *PigBreedController) GetPigBreed(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的猪品种ID格式", actionType, "猪品种ID格式错误", idStr) } - resp, err := c.feedManagementService.GetPigBreed(reqCtx, uint32(id)) + resp, err := c.pigBreedService.GetPigBreed(reqCtx, uint32(id)) if err != nil { logger.Errorf("%s: 服务层获取猪品种详情失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrPigBreedNotFound) { @@ -182,7 +182,7 @@ func (c *PigBreedController) ListPigBreeds(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "查询参数绑定失败", req) } - resp, err := c.feedManagementService.ListPigBreeds(reqCtx, &req) + resp, err := c.pigBreedService.ListPigBreeds(reqCtx, &req) if err != nil { logger.Errorf("%s: 服务层获取猪品种列表失败: %v", actionType, err) return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取猪品种列表失败: "+err.Error(), actionType, "服务层获取猪品种列表失败", nil) diff --git a/internal/app/controller/feed/pig_type_controller.go b/internal/app/controller/feed/pig_type_controller.go index f9d0147..e3c3e06 100644 --- a/internal/app/controller/feed/pig_type_controller.go +++ b/internal/app/controller/feed/pig_type_controller.go @@ -15,15 +15,15 @@ import ( // PigTypeController 定义了猪类型相关的控制器 type PigTypeController struct { - ctx context.Context - feedManagementService service.FeedManagementService + ctx context.Context + typeService service.PigTypeService } // NewPigTypeController 创建一个新的 PigTypeController 实例 -func NewPigTypeController(ctx context.Context, feedManagementService service.FeedManagementService) *PigTypeController { +func NewPigTypeController(ctx context.Context, feedManagementService service.PigTypeService) *PigTypeController { return &PigTypeController{ - ctx: ctx, - feedManagementService: feedManagementService, + ctx: ctx, + typeService: feedManagementService, } } @@ -46,7 +46,7 @@ func (c *PigTypeController) CreatePigType(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) } - resp, err := c.feedManagementService.CreatePigType(reqCtx, &req) + resp, err := c.typeService.CreatePigType(reqCtx, &req) if err != nil { logger.Errorf("%s: 服务层创建猪类型失败: %v", actionType, err) if errors.Is(err, service.ErrPigBreedNotFound) { @@ -89,7 +89,7 @@ func (c *PigTypeController) UpdatePigType(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) } - resp, err := c.feedManagementService.UpdatePigType(reqCtx, uint32(id), &req) + resp, err := c.typeService.UpdatePigType(reqCtx, uint32(id), &req) if err != nil { logger.Errorf("%s: 服务层更新猪类型失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrPigTypeNotFound) { @@ -127,7 +127,7 @@ func (c *PigTypeController) DeletePigType(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的猪类型ID格式", actionType, "猪类型ID格式错误", idStr) } - err = c.feedManagementService.DeletePigType(reqCtx, uint32(id)) + err = c.typeService.DeletePigType(reqCtx, uint32(id)) if err != nil { logger.Errorf("%s: 服务层删除猪类型失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrPigTypeNotFound) { @@ -159,7 +159,7 @@ func (c *PigTypeController) GetPigType(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的猪类型ID格式", actionType, "猪类型ID格式错误", idStr) } - resp, err := c.feedManagementService.GetPigType(reqCtx, uint32(id)) + resp, err := c.typeService.GetPigType(reqCtx, uint32(id)) if err != nil { logger.Errorf("%s: 服务层获取猪类型详情失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrPigTypeNotFound) { @@ -190,7 +190,7 @@ func (c *PigTypeController) ListPigTypes(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "查询参数绑定失败", req) } - resp, err := c.feedManagementService.ListPigTypes(reqCtx, &req) + resp, err := c.typeService.ListPigTypes(reqCtx, &req) if err != nil { logger.Errorf("%s: 服务层获取猪类型列表失败: %v", actionType, err) return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取猪类型列表失败: "+err.Error(), actionType, "服务层获取猪类型列表失败", nil) @@ -228,7 +228,7 @@ func (c *PigTypeController) UpdatePigTypeNutrientRequirements(ctx echo.Context) return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) } - resp, err := c.feedManagementService.UpdatePigTypeNutrientRequirements(reqCtx, uint32(id), &req) + resp, err := c.typeService.UpdatePigTypeNutrientRequirements(reqCtx, uint32(id), &req) if err != nil { logger.Errorf("%s: 服务层更新猪类型营养需求失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrPigTypeNotFound) { diff --git a/internal/app/controller/feed/raw_material_controller.go b/internal/app/controller/feed/raw_material_controller.go index dc776b7..66f67e9 100644 --- a/internal/app/controller/feed/raw_material_controller.go +++ b/internal/app/controller/feed/raw_material_controller.go @@ -15,15 +15,15 @@ import ( // RawMaterialController 定义了原料相关的控制器 type RawMaterialController struct { - ctx context.Context - feedManagementService service.FeedManagementService + ctx context.Context + rawMaterialService service.RawMaterialService } // NewRawMaterialController 创建一个新的 RawMaterialController 实例 -func NewRawMaterialController(ctx context.Context, feedManagementService service.FeedManagementService) *RawMaterialController { +func NewRawMaterialController(ctx context.Context, feedManagementService service.RawMaterialService) *RawMaterialController { return &RawMaterialController{ - ctx: ctx, - feedManagementService: feedManagementService, + ctx: ctx, + rawMaterialService: feedManagementService, } } @@ -46,7 +46,7 @@ func (c *RawMaterialController) CreateRawMaterial(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) } - resp, err := c.feedManagementService.CreateRawMaterial(reqCtx, &req) + resp, err := c.rawMaterialService.CreateRawMaterial(reqCtx, &req) if err != nil { logger.Errorf("%s: 服务层创建原料失败: %v", actionType, err) if errors.Is(err, service.ErrRawMaterialNameConflict) { @@ -86,7 +86,7 @@ func (c *RawMaterialController) UpdateRawMaterial(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) } - resp, err := c.feedManagementService.UpdateRawMaterial(reqCtx, uint32(id), &req) + resp, err := c.rawMaterialService.UpdateRawMaterial(reqCtx, uint32(id), &req) if err != nil { logger.Errorf("%s: 服务层更新原料失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrRawMaterialNotFound) { @@ -121,7 +121,7 @@ func (c *RawMaterialController) DeleteRawMaterial(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的原料ID格式", actionType, "原料ID格式错误", idStr) } - err = c.feedManagementService.DeleteRawMaterial(reqCtx, uint32(id)) + err = c.rawMaterialService.DeleteRawMaterial(reqCtx, uint32(id)) if err != nil { logger.Errorf("%s: 服务层删除原料失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrRawMaterialNotFound) { @@ -153,7 +153,7 @@ func (c *RawMaterialController) GetRawMaterial(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的原料ID格式", actionType, "原料ID格式错误", idStr) } - resp, err := c.feedManagementService.GetRawMaterial(reqCtx, uint32(id)) + resp, err := c.rawMaterialService.GetRawMaterial(reqCtx, uint32(id)) if err != nil { logger.Errorf("%s: 服务层获取原料详情失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrRawMaterialNotFound) { @@ -184,7 +184,7 @@ func (c *RawMaterialController) ListRawMaterials(ctx echo.Context) error { return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "查询参数绑定失败", req) } - resp, err := c.feedManagementService.ListRawMaterials(reqCtx, &req) + resp, err := c.rawMaterialService.ListRawMaterials(reqCtx, &req) if err != nil { logger.Errorf("%s: 服务层获取原料列表失败: %v", actionType, err) return controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取原料列表失败: "+err.Error(), actionType, "服务层获取原料列表失败", nil) @@ -222,7 +222,7 @@ func (c *RawMaterialController) UpdateRawMaterialNutrients(ctx echo.Context) err return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) } - resp, err := c.feedManagementService.UpdateRawMaterialNutrients(reqCtx, uint32(id), &req) + resp, err := c.rawMaterialService.UpdateRawMaterialNutrients(reqCtx, uint32(id), &req) if err != nil { logger.Errorf("%s: 服务层更新原料营养成分失败: %v, ID: %d", actionType, err, id) if errors.Is(err, service.ErrRawMaterialNotFound) { diff --git a/internal/app/service/feed_management_service.go b/internal/app/service/feed_management_service.go deleted file mode 100644 index ef4d2ae..0000000 --- a/internal/app/service/feed_management_service.go +++ /dev/null @@ -1,618 +0,0 @@ -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) - UpdateRawMaterialNutrients(ctx context.Context, id uint32, req *dto.UpdateRawMaterialNutrientsRequest) (*dto.RawMaterialResponse, 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) - UpdatePigTypeNutrientRequirements(ctx context.Context, id uint32, req *dto.UpdatePigTypeNutrientRequirementsRequest) (*dto.PigTypeResponse, 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") - - opts := repository.NutrientListOptions{ - Name: req.Name, - RawMaterialName: req.RawMaterialName, - OrderBy: req.OrderBy, - } - nutrients, total, err := s.recipeSvc.ListNutrients(serviceCtx, opts, 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") - - opts := repository.RawMaterialListOptions{ - Name: req.Name, - NutrientName: req.NutrientName, - OrderBy: req.OrderBy, - } - rawMaterials, total, err := s.recipeSvc.ListRawMaterials(serviceCtx, opts, req.Page, req.PageSize) - if err != nil { - return nil, fmt.Errorf("获取原料列表失败: %w", err) - } - - return dto.ConvertRawMaterialListToDTO(rawMaterials, total, req.Page, req.PageSize), nil -} - -// UpdateRawMaterialNutrients 全量更新原料的营养成分 -func (s *feedManagementServiceImpl) UpdateRawMaterialNutrients(ctx context.Context, id uint32, req *dto.UpdateRawMaterialNutrientsRequest) (*dto.RawMaterialResponse, error) { - serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateRawMaterialNutrients") - - // 1. 将 DTO 转换为领域模型 - nutrients := make([]models.RawMaterialNutrient, len(req.Nutrients)) - for i, item := range req.Nutrients { - nutrients[i] = models.RawMaterialNutrient{ - RawMaterialID: id, - NutrientID: item.NutrientID, - Value: item.Value, - } - } - - // 2. 调用领域服务执行更新命令 - err := s.recipeSvc.UpdateRawMaterialNutrients(serviceCtx, id, nutrients) - if err != nil { - if errors.Is(err, recipe.ErrRawMaterialNotFound) { - return nil, ErrRawMaterialNotFound - } - // 此处可以根据领域层可能返回的其他特定错误进行转换 - return nil, fmt.Errorf("更新原料营养成分失败: %w", err) - } - - // 3. 更新成功后,调用查询服务获取最新的原料信息 - updatedRawMaterial, err := s.recipeSvc.GetRawMaterial(serviceCtx, id) - if err != nil { - if errors.Is(err, recipe.ErrRawMaterialNotFound) { - // 理论上不应该发生,因为刚更新成功 - return nil, ErrRawMaterialNotFound - } - return nil, fmt.Errorf("更新后获取原料信息失败: %w", err) - } - - // 4. 将领域模型转换为 DTO 并返回 - return dto.ConvertRawMaterialToDTO(updatedRawMaterial), nil -} - -// ===================================================================================================================== -// 猪品种 (PigBreed) 实现 -// ===================================================================================================================== - -// 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 -} - -// UpdatePigTypeNutrientRequirements 全量更新猪类型的营养需求 -func (s *feedManagementServiceImpl) UpdatePigTypeNutrientRequirements(ctx context.Context, id uint32, req *dto.UpdatePigTypeNutrientRequirementsRequest) (*dto.PigTypeResponse, error) { - serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdatePigTypeNutrientRequirements") - - // 1. 将 DTO 转换为领域模型 - requirements := make([]models.PigNutrientRequirement, len(req.NutrientRequirements)) - for i, item := range req.NutrientRequirements { - requirements[i] = models.PigNutrientRequirement{ - PigTypeID: id, // 设置所属的 PigTypeID - NutrientID: item.NutrientID, - MinRequirement: item.MinRequirement, - MaxRequirement: item.MaxRequirement, - } - } - - // 2. 调用领域服务执行更新命令 - err := s.recipeSvc.UpdatePigTypeNutrientRequirements(serviceCtx, id, requirements) - if err != nil { - if errors.Is(err, recipe.ErrPigTypeNotFound) { - return nil, ErrPigTypeNotFound - } - // 此处可以根据领域层可能返回的其他特定错误进行转换 - return nil, fmt.Errorf("更新猪类型营养需求失败: %w", err) - } - - // 3. 更新成功后,调用查询服务获取最新的猪类型信息 - updatedPigType, err := s.recipeSvc.GetPigTypeByID(serviceCtx, id) - if err != nil { - if errors.Is(err, recipe.ErrPigTypeNotFound) { - // 理论上不应该发生,因为刚更新成功 - return nil, ErrPigTypeNotFound - } - return nil, fmt.Errorf("更新后获取猪类型信息失败: %w", err) - } - - // 4. 将领域模型转换为 DTO 并返回 - return dto.ConvertPigTypeToDTO(updatedPigType), nil -} diff --git a/internal/app/service/nutrient_service.go b/internal/app/service/nutrient_service.go new file mode 100644 index 0000000..744925e --- /dev/null +++ b/internal/app/service/nutrient_service.go @@ -0,0 +1,116 @@ +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/repository" +) + +// 定义营养种类服务特定的错误 +var ( + ErrNutrientNameConflict = errors.New("营养种类名称已存在") + ErrNutrientNotFound = errors.New("营养种类不存在") +) + +// NutrientService 定义了营养种类相关的应用服务接口 +type NutrientService 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) +} + +// nutrientServiceImpl 是 NutrientService 接口的实现 +type nutrientServiceImpl struct { + ctx context.Context + recipeSvc recipe.Service +} + +// NewNutrientService 创建一个新的 NutrientService 实例 +func NewNutrientService(ctx context.Context, recipeSvc recipe.Service) NutrientService { + return &nutrientServiceImpl{ + ctx: ctx, + recipeSvc: recipeSvc, + } +} + +// CreateNutrient 创建营养种类 +func (s *nutrientServiceImpl) 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 *nutrientServiceImpl) 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 *nutrientServiceImpl) 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 *nutrientServiceImpl) 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 *nutrientServiceImpl) ListNutrients(ctx context.Context, req *dto.ListNutrientRequest) (*dto.ListNutrientResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListNutrients") + + opts := repository.NutrientListOptions{ + Name: req.Name, + RawMaterialName: req.RawMaterialName, + OrderBy: req.OrderBy, + } + nutrients, total, err := s.recipeSvc.ListNutrients(serviceCtx, opts, req.Page, req.PageSize) + if err != nil { + return nil, fmt.Errorf("获取营养种类列表失败: %w", err) + } + + return dto.ConvertNutrientListToDTO(nutrients, total, req.Page, req.PageSize), nil +} diff --git a/internal/app/service/pig_age_stage_service.go b/internal/app/service/pig_age_stage_service.go new file mode 100644 index 0000000..d416ddd --- /dev/null +++ b/internal/app/service/pig_age_stage_service.go @@ -0,0 +1,122 @@ +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 ( + ErrPigAgeStageInUse = errors.New("猪年龄阶段正在被猪类型使用,无法删除") + ErrPigAgeStageNotFound = errors.New("猪年龄阶段不存在") +) + +// PigAgeStageService 定义了猪年龄阶段相关的应用服务接口 +type PigAgeStageService interface { + 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) +} + +// pigAgeStageServiceImpl 是 PigAgeStageService 接口的实现 +type pigAgeStageServiceImpl struct { + ctx context.Context + recipeSvc recipe.Service +} + +// NewPigAgeStageService 创建一个新的 PigAgeStageService 实例 +func NewPigAgeStageService(ctx context.Context, recipeSvc recipe.Service) PigAgeStageService { + return &pigAgeStageServiceImpl{ + ctx: ctx, + recipeSvc: recipeSvc, + } +} + +// CreatePigAgeStage 创建猪年龄阶段 +func (s *pigAgeStageServiceImpl) 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 *pigAgeStageServiceImpl) 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 *pigAgeStageServiceImpl) 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 *pigAgeStageServiceImpl) 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 *pigAgeStageServiceImpl) 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 +} diff --git a/internal/app/service/pig_breed_service.go b/internal/app/service/pig_breed_service.go new file mode 100644 index 0000000..b1b7180 --- /dev/null +++ b/internal/app/service/pig_breed_service.go @@ -0,0 +1,130 @@ +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 ( + ErrPigBreedInUse = errors.New("猪品种正在被猪类型使用,无法删除") + ErrPigBreedNotFound = errors.New("猪品种不存在") +) + +// PigBreedService 定义了猪品种相关的应用服务接口 +type PigBreedService interface { + 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) +} + +// pigBreedServiceImpl 是 PigBreedService 接口的实现 +type pigBreedServiceImpl struct { + ctx context.Context + recipeSvc recipe.Service +} + +// NewPigBreedService 创建一个新的 PigBreedService 实例 +func NewPigBreedService(ctx context.Context, recipeSvc recipe.Service) PigBreedService { + return &pigBreedServiceImpl{ + ctx: ctx, + recipeSvc: recipeSvc, + } +} + +// CreatePigBreed 创建猪品种 +func (s *pigBreedServiceImpl) 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 *pigBreedServiceImpl) 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 *pigBreedServiceImpl) 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 *pigBreedServiceImpl) 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 *pigBreedServiceImpl) 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 +} diff --git a/internal/app/service/pig_type_service.go b/internal/app/service/pig_type_service.go new file mode 100644 index 0000000..f1d9c11 --- /dev/null +++ b/internal/app/service/pig_type_service.go @@ -0,0 +1,203 @@ +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 ( + ErrPigTypeNotFound = errors.New("猪类型不存在") +) + +// PigTypeService 定义了猪类型相关的应用服务接口 +type PigTypeService interface { + 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) + UpdatePigTypeNutrientRequirements(ctx context.Context, id uint32, req *dto.UpdatePigTypeNutrientRequirementsRequest) (*dto.PigTypeResponse, error) +} + +// pigTypeServiceImpl 是 PigTypeService 接口的实现 +type pigTypeServiceImpl struct { + ctx context.Context + recipeSvc recipe.Service +} + +// NewPigTypeService 创建一个新的 PigTypeService 实例 +func NewPigTypeService(ctx context.Context, recipeSvc recipe.Service) PigTypeService { + return &pigTypeServiceImpl{ + ctx: ctx, + recipeSvc: recipeSvc, + } +} + +// CreatePigType 创建猪类型 +func (s *pigTypeServiceImpl) 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 *pigTypeServiceImpl) 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 *pigTypeServiceImpl) 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 *pigTypeServiceImpl) 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 *pigTypeServiceImpl) 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 +} + +// UpdatePigTypeNutrientRequirements 全量更新猪类型的营养需求 +func (s *pigTypeServiceImpl) UpdatePigTypeNutrientRequirements(ctx context.Context, id uint32, req *dto.UpdatePigTypeNutrientRequirementsRequest) (*dto.PigTypeResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdatePigTypeNutrientRequirements") + + // 1. 将 DTO 转换为领域模型 + requirements := make([]models.PigNutrientRequirement, len(req.NutrientRequirements)) + for i, item := range req.NutrientRequirements { + requirements[i] = models.PigNutrientRequirement{ + PigTypeID: id, // 设置所属的 PigTypeID + NutrientID: item.NutrientID, + MinRequirement: item.MinRequirement, + MaxRequirement: item.MaxRequirement, + } + } + + // 2. 调用领域服务执行更新命令 + err := s.recipeSvc.UpdatePigTypeNutrientRequirements(serviceCtx, id, requirements) + if err != nil { + if errors.Is(err, recipe.ErrPigTypeNotFound) { + return nil, ErrPigTypeNotFound + } + // 此处可以根据领域层可能返回的其他特定错误进行转换 + return nil, fmt.Errorf("更新猪类型营养需求失败: %w", err) + } + + // 3. 更新成功后,调用查询服务获取最新的猪类型信息 + updatedPigType, err := s.recipeSvc.GetPigTypeByID(serviceCtx, id) + if err != nil { + if errors.Is(err, recipe.ErrPigTypeNotFound) { + // 理论上不应该发生,因为刚更新成功 + return nil, ErrPigTypeNotFound + } + return nil, fmt.Errorf("更新后获取猪类型信息失败: %w", err) + } + + // 4. 将领域模型转换为 DTO 并返回 + return dto.ConvertPigTypeToDTO(updatedPigType), nil +} diff --git a/internal/app/service/raw_material_service.go b/internal/app/service/raw_material_service.go new file mode 100644 index 0000000..3cd5298 --- /dev/null +++ b/internal/app/service/raw_material_service.go @@ -0,0 +1,157 @@ +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 ( + ErrRawMaterialNameConflict = errors.New("原料名称已存在") + ErrRawMaterialNotFound = errors.New("原料不存在") +) + +// RawMaterialService 定义了原料相关的应用服务接口 +type RawMaterialService interface { + 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) + UpdateRawMaterialNutrients(ctx context.Context, id uint32, req *dto.UpdateRawMaterialNutrientsRequest) (*dto.RawMaterialResponse, error) +} + +// rawMaterialServiceImpl 是 RawMaterialService 接口的实现 +type rawMaterialServiceImpl struct { + ctx context.Context + recipeSvc recipe.Service +} + +// NewRawMaterialService 创建一个新的 RawMaterialService 实例 +func NewRawMaterialService(ctx context.Context, recipeSvc recipe.Service) RawMaterialService { + return &rawMaterialServiceImpl{ + ctx: ctx, + recipeSvc: recipeSvc, + } +} + +// CreateRawMaterial 创建原料 +func (s *rawMaterialServiceImpl) 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 *rawMaterialServiceImpl) 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 *rawMaterialServiceImpl) 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 *rawMaterialServiceImpl) 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 *rawMaterialServiceImpl) ListRawMaterials(ctx context.Context, req *dto.ListRawMaterialRequest) (*dto.ListRawMaterialResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListRawMaterials") + + opts := repository.RawMaterialListOptions{ + Name: req.Name, + NutrientName: req.NutrientName, + OrderBy: req.OrderBy, + } + rawMaterials, total, err := s.recipeSvc.ListRawMaterials(serviceCtx, opts, req.Page, req.PageSize) + if err != nil { + return nil, fmt.Errorf("获取原料列表失败: %w", err) + } + + return dto.ConvertRawMaterialListToDTO(rawMaterials, total, req.Page, req.PageSize), nil +} + +// UpdateRawMaterialNutrients 全量更新原料的营养成分 +func (s *rawMaterialServiceImpl) UpdateRawMaterialNutrients(ctx context.Context, id uint32, req *dto.UpdateRawMaterialNutrientsRequest) (*dto.RawMaterialResponse, error) { + serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateRawMaterialNutrients") + + // 1. 将 DTO 转换为领域模型 + nutrients := make([]models.RawMaterialNutrient, len(req.Nutrients)) + for i, item := range req.Nutrients { + nutrients[i] = models.RawMaterialNutrient{ + RawMaterialID: id, + NutrientID: item.NutrientID, + Value: item.Value, + } + } + + // 2. 调用领域服务执行更新命令 + err := s.recipeSvc.UpdateRawMaterialNutrients(serviceCtx, id, nutrients) + if err != nil { + if errors.Is(err, recipe.ErrRawMaterialNotFound) { + return nil, ErrRawMaterialNotFound + } + // 此处可以根据领域层可能返回的其他特定错误进行转换 + return nil, fmt.Errorf("更新原料营养成分失败: %w", err) + } + + // 3. 更新成功后,调用查询服务获取最新的原料信息 + updatedRawMaterial, err := s.recipeSvc.GetRawMaterial(serviceCtx, id) + if err != nil { + if errors.Is(err, recipe.ErrRawMaterialNotFound) { + // 理论上不应该发生,因为刚更新成功 + return nil, ErrRawMaterialNotFound + } + return nil, fmt.Errorf("更新后获取原料信息失败: %w", err) + } + + // 4. 将领域模型转换为 DTO 并返回 + return dto.ConvertRawMaterialToDTO(updatedRawMaterial), nil +} diff --git a/internal/core/application.go b/internal/core/application.go index 377fbff..2511771 100644 --- a/internal/core/application.go +++ b/internal/core/application.go @@ -63,7 +63,11 @@ func NewApplication(configPath string) (*Application, error) { appServices.userService, appServices.auditService, appServices.thresholdAlarmService, - appServices.feedManagementService, + appServices.nutrientService, + appServices.rawMaterialService, + appServices.pigBreedService, + appServices.pigAgeStageService, + appServices.pigTypeService, infra.tokenGenerator, infra.lora.listenHandler, ) diff --git a/internal/core/component_initializers.go b/internal/core/component_initializers.go index 97ed6f0..bc5339d 100644 --- a/internal/core/component_initializers.go +++ b/internal/core/component_initializers.go @@ -259,7 +259,11 @@ type AppServices struct { userService service.UserService auditService service.AuditService thresholdAlarmService service.ThresholdAlarmService - feedManagementService service.FeedManagementService + nutrientService service.NutrientService + pigAgeStageService service.PigAgeStageService + pigBreedService service.PigBreedService + pigTypeService service.PigTypeService + rawMaterialService service.RawMaterialService } // initAppServices 初始化所有的应用服务。 @@ -307,7 +311,11 @@ 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) + nutrientService := service.NewNutrientService(logs.AddCompName(baseCtx, "NutrientService"), domainServices.recipeService) + pigAgeStageService := service.NewPigAgeStageService(logs.AddCompName(baseCtx, "PigAgeStageService"), domainServices.recipeService) + pigBreedService := service.NewPigBreedService(logs.AddCompName(baseCtx, "PigBreedService"), domainServices.recipeService) + pigTypeService := service.NewPigTypeService(logs.AddCompName(baseCtx, "PigTypeService"), domainServices.recipeService) + rawMaterialService := service.NewRawMaterialService(logs.AddCompName(baseCtx, "RawMaterialService"), domainServices.recipeService) return &AppServices{ pigFarmService: pigFarmService, @@ -318,7 +326,11 @@ func initAppServices(ctx context.Context, infra *Infrastructure, domainServices planService: planService, userService: userService, thresholdAlarmService: thresholdAlarmService, - feedManagementService: feedManagementService, + nutrientService: nutrientService, + pigAgeStageService: pigAgeStageService, + pigBreedService: pigBreedService, + pigTypeService: pigTypeService, + rawMaterialService: rawMaterialService, } } diff --git a/project_structure.txt b/project_structure.txt index 57dc0a0..ed9a36d 100644 --- a/project_structure.txt +++ b/project_structure.txt @@ -48,7 +48,6 @@ 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/feed/nutrient_controller.go internal/app/controller/feed/pig_age_stage_controller.go internal/app/controller/feed/pig_breed_controller.go @@ -85,12 +84,16 @@ 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/nutrient_service.go +internal/app/service/pig_age_stage_service.go internal/app/service/pig_batch_service.go +internal/app/service/pig_breed_service.go internal/app/service/pig_farm_service.go internal/app/service/pig_service.go +internal/app/service/pig_type_service.go internal/app/service/plan_service.go +internal/app/service/raw_material_service.go internal/app/service/threshold_alarm_service.go internal/app/service/user_service.go internal/app/webhook/chirp_stack.go