实现配方领域的web接口

This commit is contained in:
2025-11-21 16:02:06 +08:00
parent 9996fcfd74
commit 534891309c
14 changed files with 6638 additions and 3 deletions

View File

@@ -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() // 设置所有路由

View File

@@ -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("所有接口注册成功")

View File

@@ -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)
}

View File

@@ -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,
},
}
}

View File

@@ -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"`
}

View File

@@ -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
}