From 8d9e4286b0197cf4c113f7f8551090409ecf54aa Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Sat, 4 Oct 2025 01:28:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=95=E7=8B=AC=E4=BF=AE=E6=94=B9=E7=8C=AA?= =?UTF-8?q?=E5=9C=88=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/app/api/api.go | 1 + .../management/pig_farm_controller.go | 78 +++++++++++++++++-- internal/app/dto/pig_farm_dto.go | 14 ++-- internal/app/service/pig_batch_service.go | 1 - internal/app/service/pig_farm_service.go | 70 ++++++++++++++++- 5 files changed, 147 insertions(+), 17 deletions(-) diff --git a/internal/app/api/api.go b/internal/app/api/api.go index fc70039..3533ecc 100644 --- a/internal/app/api/api.go +++ b/internal/app/api/api.go @@ -222,6 +222,7 @@ func (a *API) setupRoutes() { penGroup.GET("/:id", a.pigFarmController.GetPen) penGroup.PUT("/:id", a.pigFarmController.UpdatePen) penGroup.DELETE("/:id", a.pigFarmController.DeletePen) + penGroup.PUT("/:id/status", a.pigFarmController.UpdatePenStatus) } a.logger.Info("猪圈相关接口注册成功 (需要认证和审计)") diff --git a/internal/app/controller/management/pig_farm_controller.go b/internal/app/controller/management/pig_farm_controller.go index fc0defa..84a6a05 100644 --- a/internal/app/controller/management/pig_farm_controller.go +++ b/internal/app/controller/management/pig_farm_controller.go @@ -9,7 +9,6 @@ import ( "git.huangwc.com/pig/pig-farm-controller/internal/app/service" "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "github.com/gin-gonic/gin" - "gorm.io/gorm" ) // --- 控制器定义 --- @@ -80,7 +79,7 @@ func (c *PigFarmController) GetPigHouse(ctx *gin.Context) { house, err := c.service.GetPigHouseByID(uint(id)) if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { + if errors.Is(err, service.ErrHouseNotFound) { controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "猪舍不存在", action, "猪舍不存在", id) return } @@ -151,7 +150,7 @@ func (c *PigFarmController) UpdatePigHouse(ctx *gin.Context) { house, err := c.service.UpdatePigHouse(uint(id), req.Name, req.Description) if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { + if errors.Is(err, service.ErrHouseNotFound) { controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "猪舍不存在", action, "猪舍不存在", id) return } @@ -185,10 +184,15 @@ func (c *PigFarmController) DeletePigHouse(ctx *gin.Context) { } if err := c.service.DeletePigHouse(uint(id)); err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { + if errors.Is(err, service.ErrHouseNotFound) { controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "猪舍不存在", action, "猪舍不存在", id) return } + // 检查是否是业务逻辑错误 + if errors.Is(err, service.ErrHouseContainsPens) { + controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), id) + return + } c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除失败", action, "业务逻辑失败", id) return @@ -218,6 +222,11 @@ func (c *PigFarmController) CreatePen(ctx *gin.Context) { pen, err := c.service.CreatePen(req.PenNumber, req.HouseID, req.Capacity) if err != nil { + // 检查是否是业务逻辑错误 + if errors.Is(err, service.ErrHouseNotFound) { + controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), req) + return + } c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建猪栏失败", action, "业务逻辑失败", req) return @@ -252,7 +261,7 @@ func (c *PigFarmController) GetPen(ctx *gin.Context) { pen, err := c.service.GetPenByID(uint(id)) if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { + if errors.Is(err, service.ErrPenNotFound) { controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "猪栏不存在", action, "猪栏不存在", id) return } @@ -329,10 +338,11 @@ func (c *PigFarmController) UpdatePen(ctx *gin.Context) { pen, err := c.service.UpdatePen(uint(id), req.PenNumber, req.HouseID, req.Capacity, req.Status) if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { + if errors.Is(err, service.ErrPenNotFound) { controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "猪栏不存在", action, "猪栏不存在", id) return } + // 其他业务逻辑错误可以在这里添加处理 c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新失败", action, "业务逻辑失败", req) return @@ -366,10 +376,15 @@ func (c *PigFarmController) DeletePen(ctx *gin.Context) { } if err := c.service.DeletePen(uint(id)); err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { + if errors.Is(err, service.ErrPenNotFound) { controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "猪栏不存在", action, "猪栏不存在", id) return } + // 检查是否是业务逻辑错误 + if errors.Is(err, service.ErrPenInUse) { + controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), id) + return + } c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除失败", action, "业务逻辑失败", id) return @@ -377,3 +392,52 @@ func (c *PigFarmController) DeletePen(ctx *gin.Context) { controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "删除成功", nil, action, "删除成功", id) } + +// UpdatePenStatus godoc +// @Summary 更新猪栏状态 +// @Description 更新指定猪栏的当前状态 +// @Tags 猪场管理 +// @Accept json +// @Produce json +// @Param id path int true "猪栏ID" +// @Param body body dto.UpdatePenStatusRequest true "新的猪栏状态" +// @Success 200 {object} controller.Response{data=dto.PenResponse} "更新成功" +// @Router /api/v1/pens/{id}/status [put] +func (c *PigFarmController) UpdatePenStatus(ctx *gin.Context) { + const action = "更新猪栏状态" + id, err := strconv.ParseUint(ctx.Param("id"), 10, 32) + if err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID格式", action, "ID格式错误", ctx.Param("id")) + return + } + + var req dto.UpdatePenStatusRequest + if err := ctx.ShouldBindJSON(&req); err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) + return + } + + pen, err := c.service.UpdatePenStatus(uint(id), req.Status) + if err != nil { + if errors.Is(err, service.ErrPenNotFound) { + controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), id) + return + } else if errors.Is(err, service.ErrPenStatusInvalidForOccupiedPen) || errors.Is(err, service.ErrPenStatusInvalidForUnoccupiedPen) { + controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), id) + return + } + c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) + controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新猪栏状态失败", action, err.Error(), id) + return + } + + resp := dto.PenResponse{ + ID: pen.ID, + PenNumber: pen.PenNumber, + HouseID: pen.HouseID, + Capacity: pen.Capacity, + Status: pen.Status, + PigBatchID: *pen.PigBatchID, + } + controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "更新成功", resp, action, "更新成功", resp) +} diff --git a/internal/app/dto/pig_farm_dto.go b/internal/app/dto/pig_farm_dto.go index 2b5b29b..b10e274 100644 --- a/internal/app/dto/pig_farm_dto.go +++ b/internal/app/dto/pig_farm_dto.go @@ -33,10 +33,9 @@ type UpdatePigHouseRequest struct { // CreatePenRequest 定义了创建猪栏的请求结构 type CreatePenRequest struct { - PenNumber string `json:"pen_number" binding:"required"` - HouseID uint `json:"house_id" binding:"required"` - Capacity int `json:"capacity" binding:"required"` - Status models.PenStatus `json:"status" binding:"required"` + PenNumber string `json:"pen_number" binding:"required"` + HouseID uint `json:"house_id" binding:"required"` + Capacity int `json:"capacity" binding:"required"` } // UpdatePenRequest 定义了更新猪栏的请求结构 @@ -44,5 +43,10 @@ type UpdatePenRequest struct { PenNumber string `json:"pen_number" binding:"required"` HouseID uint `json:"house_id" binding:"required"` Capacity int `json:"capacity" binding:"required"` - Status models.PenStatus `json:"status" binding:"required"` + Status models.PenStatus `json:"status" binding:"required,oneof=空闲 占用 病猪栏 康复栏 清洗消毒 维修中"` // 添加oneof校验 +} + +// UpdatePenStatusRequest 定义了更新猪栏状态的请求结构 +type UpdatePenStatusRequest struct { + Status models.PenStatus `json:"status" binding:"required,oneof=空闲 占用 病猪栏 康复栏 清洗消毒 维修中" example:"病猪栏"` } diff --git a/internal/app/service/pig_batch_service.go b/internal/app/service/pig_batch_service.go index 5d94f77..3879c0d 100644 --- a/internal/app/service/pig_batch_service.go +++ b/internal/app/service/pig_batch_service.go @@ -16,7 +16,6 @@ var ( ErrPigBatchNotFound = errors.New("指定的猪批次不存在") ErrPigBatchActive = errors.New("活跃的猪批次不能被删除") ErrPigBatchNotActive = errors.New("猪批次不处于活跃状态,无法修改关联猪栏") - ErrPenNotFound = errors.New("指定的猪栏不存在") ErrPenOccupiedByOtherBatch = errors.New("猪栏已被其他批次占用") ErrPenStatusInvalidForAllocation = errors.New("猪栏状态不允许分配") ErrPenNotAssociatedWithBatch = errors.New("猪栏未与该批次关联") diff --git a/internal/app/service/pig_farm_service.go b/internal/app/service/pig_farm_service.go index f342520..d59347c 100644 --- a/internal/app/service/pig_farm_service.go +++ b/internal/app/service/pig_farm_service.go @@ -2,6 +2,7 @@ package service import ( "errors" + "fmt" "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" @@ -11,9 +12,12 @@ import ( ) var ( - ErrHouseContainsPens = errors.New("无法删除包含猪栏的猪舍") - ErrHouseNotFound = errors.New("指定的猪舍不存在") - ErrPenInUse = errors.New("猪栏正在被活跃批次使用,无法删除") + ErrHouseContainsPens = errors.New("无法删除包含猪栏的猪舍") + ErrHouseNotFound = errors.New("指定的猪舍不存在") + ErrPenInUse = errors.New("猪栏正在被活跃批次使用,无法删除") + ErrPenNotFound = errors.New("指定的猪栏不存在") + ErrPenStatusInvalidForOccupiedPen = errors.New("猪栏已被批次占用,无法设置为非占用状态") + ErrPenStatusInvalidForUnoccupiedPen = errors.New("猪栏未被批次占用,无法设置为占用状态") ) // PigFarmService 提供了猪场资产管理的业务逻辑 @@ -31,18 +35,22 @@ type PigFarmService interface { ListPens() ([]models.Pen, error) UpdatePen(id uint, penNumber string, houseID uint, capacity int, status models.PenStatus) (*models.Pen, error) DeletePen(id uint) error + // UpdatePenStatus 更新猪栏状态 + UpdatePenStatus(id uint, newStatus models.PenStatus) (*models.Pen, error) } type pigFarmService struct { logger *logs.Logger repo repository.PigFarmRepository + uow repository.UnitOfWork // 工作单元,用于事务管理 } // NewPigFarmService 创建一个新的 PigFarmService 实例 -func NewPigFarmService(repo repository.PigFarmRepository, logger *logs.Logger) PigFarmService { +func NewPigFarmService(repo repository.PigFarmRepository, uow repository.UnitOfWork, logger *logs.Logger) PigFarmService { return &pigFarmService{ logger: logger, repo: repo, + uow: uow, } } @@ -194,3 +202,57 @@ func (s *pigFarmService) DeletePen(id uint) error { } return nil } + +// UpdatePenStatus 更新猪栏状态 +func (s *pigFarmService) UpdatePenStatus(id uint, newStatus models.PenStatus) (*models.Pen, error) { + var updatedPen *models.Pen + err := s.uow.ExecuteInTransaction(func(tx *gorm.DB) error { + pen, err := s.repo.GetPenByIDTx(tx, id) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return ErrPenNotFound + } + s.logger.Errorf("更新猪栏状态失败: 获取猪栏 %d 信息错误: %v", id, err) + return fmt.Errorf("获取猪栏 %d 信息失败: %w", id, err) + } + + // 业务逻辑:根据猪栏的 PigBatchID 和当前状态,判断是否允许设置为 newStatus + if pen.PigBatchID != nil && *pen.PigBatchID != 0 { // 猪栏已被批次占用 + if newStatus == models.PenStatusEmpty { // 猪栏已被批次占用,不能直接设置为空闲 + return ErrPenStatusInvalidForOccupiedPen + } + } else { // 猪栏未被批次占用 (PigBatchID == nil) + if newStatus == models.PenStatusOccupied { // 猪栏未被批次占用,不能设置为占用 + return ErrPenStatusInvalidForUnoccupiedPen + } + } + + // 如果新状态与旧状态相同,则无需更新 + if pen.Status == newStatus { + updatedPen = pen // 返回原始猪栏,因为没有实际更新 + return nil + } + + updates := map[string]interface{}{ + "status": newStatus, + } + + if err := s.repo.UpdatePenFields(tx, id, updates); err != nil { + s.logger.Errorf("更新猪栏 %d 状态失败: %v", id, err) + return fmt.Errorf("更新猪栏 %d 状态失败: %w", id, err) + } + + // 获取更新后的猪栏信息 + updatedPen, err = s.repo.GetPenByIDTx(tx, id) + if err != nil { + s.logger.Errorf("更新猪栏状态后获取猪栏 %d 信息失败: %v", id, err) + return fmt.Errorf("更新猪栏状态后获取猪栏 %d 信息失败: %w", id, err) + } + return nil + }) + + if err != nil { + return nil, err + } + return updatedPen, nil +}