From 21661eb7481320e698b68eeb6a062be5bca43749 Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Mon, 6 Oct 2025 23:48:31 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AE=80=E5=8C=96=E6=8E=A7=E5=88=B6=E5=99=A8?= =?UTF-8?q?=E5=B1=82=E9=87=8D=E5=A4=8D=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../management/controller_helpers.go | 294 ++++++++++++++++++ .../management/pig_batch_controller.go | 286 ++++++----------- .../management/pig_batch_health_controller.go | 215 +++---------- .../management/pig_batch_trade_controller.go | 75 +---- .../pig_batch_transfer_controller.go | 82 ++--- 5 files changed, 477 insertions(+), 475 deletions(-) create mode 100644 internal/app/controller/management/controller_helpers.go diff --git a/internal/app/controller/management/controller_helpers.go b/internal/app/controller/management/controller_helpers.go new file mode 100644 index 0000000..ee43ed9 --- /dev/null +++ b/internal/app/controller/management/controller_helpers.go @@ -0,0 +1,294 @@ +package management + +import ( + "errors" + "strconv" + + "git.huangwc.com/pig/pig-farm-controller/internal/app/controller" + "git.huangwc.com/pig/pig-farm-controller/internal/app/service" + "github.com/gin-gonic/gin" +) + +// mapAndSendError 统一映射服务层错误并发送响应。 +// 这个函数将服务层返回的错误转换为控制器层应返回的HTTP状态码和审计信息。 +func mapAndSendError(c *PigBatchController, ctx *gin.Context, action string, err error, id uint) { + if errors.Is(err, service.ErrPigBatchNotFound) || + errors.Is(err, service.ErrPenNotFound) || + errors.Is(err, service.ErrPenNotAssociatedWithBatch) { + controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), id) + } else if errors.Is(err, service.ErrInvalidOperation) || + errors.Is(err, service.ErrPigBatchActive) || + errors.Is(err, service.ErrPigBatchNotActive) || + errors.Is(err, service.ErrPenOccupiedByOtherBatch) || + errors.Is(err, service.ErrPenStatusInvalidForAllocation) || + errors.Is(err, service.ErrPenNotEmpty) { + controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), id) + } else { + c.logger.Errorf("操作[%s]业务逻辑失败: %v", action, err) + controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "操作失败", action, err.Error(), id) + } +} + +// idExtractorFunc 定义了一个函数类型,用于从gin.Context中提取主ID。 +type idExtractorFunc func(ctx *gin.Context) (uint, error) + +// handleAPIRequest 封装了控制器中处理带有请求体和路径参数的API请求的通用逻辑。 +// 它负责请求体绑定、操作员ID获取、服务层调用、错误映射和响应发送。 +// +// 参数: +// +// c: *PigBatchController - 控制器实例,用于访问其服务和日志。 +// ctx: *gin.Context - Gin上下文。 +// action: string - 当前操作的描述,用于日志和审计。 +// reqDTO: Req - 请求数据传输对象。 +// serviceExecutor: func(ctx *gin.Context, operatorID uint, primaryID uint, req Req) error - 实际执行服务层逻辑的回调函数。 +// 这个回调函数负责从ctx中解析所有必要的路径参数(除了primaryID,如果idExtractorFunc提供了), +// 获取operatorID,并调用具体的服务方法。 +// successMsg: string - 操作成功时返回给客户端的消息。 +// idExtractor: idExtractorFunc - 可选函数,用于从ctx中提取主ID。如果为nil,则尝试从":id"路径参数中提取。 +func handleAPIRequest[Req any]( // 使用泛型Req + c *PigBatchController, + ctx *gin.Context, + action string, + reqDTO Req, + serviceExecutor func(ctx *gin.Context, operatorID uint, primaryID uint, req Req) error, + successMsg string, + idExtractor idExtractorFunc, +) { + var primaryID uint // 用于审计和日志的主ID + + // 1. 绑定请求体 + if err := ctx.ShouldBindJSON(&reqDTO); err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", reqDTO) + return + } + + // 2. 获取操作员ID + operatorID, err := controller.GetOperatorIDFromContext(ctx) + if err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeUnauthorized, "未授权", action, "无法获取操作员ID", nil) + return + } + + // 3. 提取主ID + if idExtractor != nil { + primaryID, err = idExtractor(ctx) + if err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID格式", action, "ID格式错误", err.Error()) + return + } + } else { // 默认从 ":id" 路径参数提取 + idParam := ctx.Param("id") + if idParam == "" { // 有些端点可能没有 "id" 参数,例如列表或创建操作 + // 如果没有ID参数且没有自定义提取器,primaryID保持为0,这对于某些操作是可接受的 + } else { + parsedID, err := strconv.ParseUint(idParam, 10, 32) + if err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID格式", action, "ID格式错误", idParam) + return + } + primaryID = uint(parsedID) + } + } + + // 4. 执行服务层逻辑 + err = serviceExecutor(ctx, operatorID, primaryID, reqDTO) + if err != nil { + mapAndSendError(c, ctx, action, err, primaryID) + return + } + + // 5. 发送成功响应 + controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, successMsg, nil, action, successMsg, primaryID) +} + +// handleNoBodyAPIRequest 封装了处理不带请求体,但有路径参数和操作员ID的API请求的通用逻辑。 +func handleNoBodyAPIRequest( + c *PigBatchController, + ctx *gin.Context, + action string, + serviceExecutor func(ctx *gin.Context, operatorID uint, primaryID uint) error, + successMsg string, + idExtractor idExtractorFunc, +) { + var primaryID uint // 用于审计和日志的主ID + + // 1. 获取操作员ID + operatorID, err := controller.GetOperatorIDFromContext(ctx) + if err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeUnauthorized, "未授权", action, "无法获取操作员ID", nil) + return + } + + // 2. 提取主ID + if idExtractor != nil { + primaryID, err = idExtractor(ctx) + if err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID格式", action, "ID格式错误", err.Error()) + return + } + } else { // 默认从 ":id" 路径参数提取 + idParam := ctx.Param("id") + if idParam == "" { + // 如果没有ID参数且没有自定义提取器,primaryID保持为0 + } else { + parsedID, err := strconv.ParseUint(idParam, 10, 32) + if err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID格式", action, "ID格式错误", idParam) + return + } + primaryID = uint(parsedID) + } + } + + // 3. 执行服务层逻辑 + err = serviceExecutor(ctx, operatorID, primaryID) + if err != nil { + mapAndSendError(c, ctx, action, err, primaryID) + return + } + + // 4. 发送成功响应 + controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, successMsg, nil, action, successMsg, primaryID) +} + +// handleAPIRequestWithResponse 封装了控制器中处理带有请求体、路径参数并返回响应DTO的API请求的通用逻辑。 +func handleAPIRequestWithResponse[Req any, Resp any]( + c *PigBatchController, + ctx *gin.Context, + action string, + reqDTO Req, + serviceExecutor func(ctx *gin.Context, operatorID uint, primaryID uint, req Req) (Resp, error), // serviceExecutor现在返回Resp + successMsg string, + idExtractor idExtractorFunc, +) { + var primaryID uint // 用于审计和日志的主ID + + // 1. 绑定请求体 + if err := ctx.ShouldBindJSON(&reqDTO); err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", reqDTO) + return + } + + // 2. 获取操作员ID + operatorID, err := controller.GetOperatorIDFromContext(ctx) + if err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeUnauthorized, "未授权", action, "无法获取操作员ID", nil) + return + } + + // 3. 提取主ID + if idExtractor != nil { + primaryID, err = idExtractor(ctx) + if err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID格式", action, "ID格式错误", err.Error()) + return + } + } else { // 默认从 ":id" 路径参数提取 + idParam := ctx.Param("id") + if idParam == "" { // 有些端点可能没有 "id" 参数,例如列表或创建操作 + // 如果没有ID参数且没有自定义提取器,primaryID保持为0,这对于某些操作是可接受的 + } else { + parsedID, err := strconv.ParseUint(idParam, 10, 32) + if err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID格式", action, "ID格式错误", idParam) + return + } + primaryID = uint(parsedID) + } + } + + // 4. 执行服务层逻辑 + respDTO, err := serviceExecutor(ctx, operatorID, primaryID, reqDTO) + if err != nil { + mapAndSendError(c, ctx, action, err, primaryID) + return + } + + // 5. 发送成功响应 + controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, successMsg, respDTO, action, successMsg, primaryID) +} + +// handleNoBodyAPIRequestWithResponse 封装了处理不带请求体,但有路径参数和操作员ID,并返回响应DTO的API请求的通用逻辑。 +func handleNoBodyAPIRequestWithResponse[Resp any]( + c *PigBatchController, + ctx *gin.Context, + action string, + serviceExecutor func(ctx *gin.Context, operatorID uint, primaryID uint) (Resp, error), // serviceExecutor现在返回Resp + successMsg string, + idExtractor idExtractorFunc, +) { + var primaryID uint // 用于审计和日志的主ID + + // 1. 获取操作员ID + operatorID, err := controller.GetOperatorIDFromContext(ctx) + if err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeUnauthorized, "未授权", action, "无法获取操作员ID", nil) + return + } + + // 2. 提取主ID + if idExtractor != nil { + primaryID, err = idExtractor(ctx) + if err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID格式", action, "ID格式错误", err.Error()) + return + } + } else { // 默认从 ":id" 路径参数提取 + idParam := ctx.Param("id") + if idParam == "" { + // 如果没有ID参数且没有自定义提取器,primaryID保持为0 + } else { + parsedID, err := strconv.ParseUint(idParam, 10, 32) + if err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID格式", action, "ID格式错误", idParam) + return + } + primaryID = uint(parsedID) + } + } + + // 3. 执行服务层逻辑 + respDTO, err := serviceExecutor(ctx, operatorID, primaryID) + if err != nil { + mapAndSendError(c, ctx, action, err, primaryID) + return + } + + // 4. 发送成功响应 + controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, successMsg, respDTO, action, successMsg, primaryID) +} + +// handleQueryAPIRequestWithResponse 封装了处理带有查询参数并返回响应DTO的API请求的通用逻辑。 +func handleQueryAPIRequestWithResponse[Query any, Resp any]( + c *PigBatchController, + ctx *gin.Context, + action string, + queryDTO Query, + serviceExecutor func(ctx *gin.Context, operatorID uint, query Query) (Resp, error), // serviceExecutor现在接收queryDTO + successMsg string, +) { + // 1. 绑定查询参数 + if err := ctx.ShouldBindQuery(&queryDTO); err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数", action, "查询参数绑定失败", queryDTO) + return + } + + // 2. 获取操作员ID + operatorID, err := controller.GetOperatorIDFromContext(ctx) + if err != nil { + controller.SendErrorWithAudit(ctx, controller.CodeUnauthorized, "未授权", action, "无法获取操作员ID", nil) + return + } + + // 3. 执行服务层逻辑 + respDTO, err := serviceExecutor(ctx, operatorID, queryDTO) + if err != nil { + // 对于列表查询,通常没有primaryID,所以传递0 + mapAndSendError(c, ctx, action, err, 0) + return + } + + // 4. 发送成功响应 + controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, successMsg, respDTO, action, successMsg, nil) +} diff --git a/internal/app/controller/management/pig_batch_controller.go b/internal/app/controller/management/pig_batch_controller.go index 623d680..79a6bff 100644 --- a/internal/app/controller/management/pig_batch_controller.go +++ b/internal/app/controller/management/pig_batch_controller.go @@ -1,10 +1,8 @@ package management import ( - "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" @@ -38,21 +36,16 @@ func NewPigBatchController(logger *logs.Logger, service service.PigBatchService) func (c *PigBatchController) CreatePigBatch(ctx *gin.Context) { const action = "创建猪批次" var req dto.PigBatchCreateDTO - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - userID, err := controller.GetOperatorIDFromContext(ctx) - - respDTO, err := c.service.CreatePigBatch(userID, &req) - if err != nil { - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建猪批次失败", action, "业务逻辑失败", req) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeCreated, "创建成功", respDTO, action, "创建成功", respDTO) + handleAPIRequestWithResponse( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.PigBatchCreateDTO) (*dto.PigBatchResponseDTO, error) { + // 对于创建操作,primaryID通常不从路径中获取,而是由服务层生成 + return c.service.CreatePigBatch(operatorID, req) + }, + "创建成功", + nil, // 无需自定义ID提取器,primaryID将为0 + ) } // GetPigBatch godoc @@ -62,27 +55,18 @@ func (c *PigBatchController) CreatePigBatch(ctx *gin.Context) { // @Produce json // @Param id path int true "猪批次ID" // @Success 200 {object} controller.Response{data=dto.PigBatchResponseDTO} "获取成功" -// @Router /api/v1/pig-batches/{id} [get] +// @Router /api/v1/pig-batches/{id} [get]\ func (c *PigBatchController) GetPigBatch(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 - } - respDTO, err := c.service.GetPigBatch(uint(id)) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "猪批次不存在", action, "猪批次不存在", id) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取猪批次失败", action, "业务逻辑失败", id) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取成功", respDTO, action, "获取成功", respDTO) + handleNoBodyAPIRequestWithResponse( + c, ctx, action, + func(ctx *gin.Context, operatorID uint, primaryID uint) (*dto.PigBatchResponseDTO, error) { + return c.service.GetPigBatch(primaryID) + }, + "获取成功", + nil, // 默认从 ":id" 路径参数提取ID + ) } // UpdatePigBatch godoc @@ -97,30 +81,16 @@ func (c *PigBatchController) GetPigBatch(ctx *gin.Context) { // @Router /api/v1/pig-batches/{id} [put] func (c *PigBatchController) UpdatePigBatch(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.PigBatchUpdateDTO - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - respDTO, err := c.service.UpdatePigBatch(uint(id), &req) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "猪批次不存在", action, "猪批次不存在", id) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新猪批次失败", action, "业务逻辑失败", req) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "更新成功", respDTO, action, "更新成功", respDTO) + handleAPIRequestWithResponse( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.PigBatchUpdateDTO) (*dto.PigBatchResponseDTO, error) { + return c.service.UpdatePigBatch(primaryID, req) + }, + "更新成功", + nil, // 默认从 ":id" 路径参数提取ID + ) } // DeletePigBatch godoc @@ -133,23 +103,15 @@ func (c *PigBatchController) UpdatePigBatch(ctx *gin.Context) { // @Router /api/v1/pig-batches/{id} [delete] func (c *PigBatchController) DeletePigBatch(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 - } - if err := c.service.DeletePigBatch(uint(id)); err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "猪批次不存在", action, "猪批次不存在", id) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除猪批次失败", action, "业务逻辑失败", id) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "删除成功", nil, action, "删除成功", id) + handleNoBodyAPIRequest( + c, ctx, action, + func(ctx *gin.Context, operatorID uint, primaryID uint) error { + return c.service.DeletePigBatch(primaryID) + }, + "删除成功", + nil, // 默认从 ":id" 路径参数提取ID + ) } // ListPigBatches godoc @@ -163,20 +125,14 @@ func (c *PigBatchController) DeletePigBatch(ctx *gin.Context) { func (c *PigBatchController) ListPigBatches(ctx *gin.Context) { const action = "获取猪批次列表" var query dto.PigBatchQueryDTO - // ShouldBindQuery 会自动处理 URL 查询参数,例如 ?is_active=true - if err := ctx.ShouldBindQuery(&query); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数", action, "查询参数绑定失败", nil) - return - } - respDTOs, err := c.service.ListPigBatches(query.IsActive) - if err != nil { - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取猪批次列表失败", action, "业务逻辑失败", nil) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取成功", respDTOs, action, "获取成功", respDTOs) + handleQueryAPIRequestWithResponse( + c, ctx, action, &query, + func(ctx *gin.Context, operatorID uint, query *dto.PigBatchQueryDTO) ([]*dto.PigBatchResponseDTO, error) { + return c.service.ListPigBatches(query.IsActive) + }, + "获取成功", + ) } // AssignEmptyPensToBatch godoc @@ -191,35 +147,16 @@ func (c *PigBatchController) ListPigBatches(ctx *gin.Context) { // @Router /api/v1/pig-batches/{id}/assign-pens [post] func (c *PigBatchController) AssignEmptyPensToBatch(ctx *gin.Context) { const action = "为猪批次分配空栏" - batchID, 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.AssignEmptyPensToBatchRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - userID, err := controller.GetOperatorIDFromContext(ctx) - - err = c.service.AssignEmptyPensToBatch(uint(batchID), req.PenIDs, userID) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) || errors.Is(err, service.ErrPenNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), batchID) - return - } else if errors.Is(err, service.ErrPigBatchNotActive) || errors.Is(err, service.ErrPenOccupiedByOtherBatch) || errors.Is(err, service.ErrPenStatusInvalidForAllocation) { - controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), batchID) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "分配空栏失败", action, err.Error(), batchID) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "分配成功", nil, action, "分配成功", batchID) + handleAPIRequest( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.AssignEmptyPensToBatchRequest) error { + return c.service.AssignEmptyPensToBatch(primaryID, req.PenIDs, operatorID) + }, + "分配成功", + nil, // 默认从 ":id" 路径参数提取ID + ) } // ReclassifyPenToNewBatch godoc @@ -234,35 +171,24 @@ func (c *PigBatchController) AssignEmptyPensToBatch(ctx *gin.Context) { // @Router /api/v1/pig-batches/{fromBatchID}/reclassify-pen [post] func (c *PigBatchController) ReclassifyPenToNewBatch(ctx *gin.Context) { const action = "划拨猪栏到新批次" - fromBatchID, err := strconv.ParseUint(ctx.Param("fromBatchID"), 10, 32) - if err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的源猪批次ID格式", action, "ID格式错误", ctx.Param("fromBatchID")) - return - } - var req dto.ReclassifyPenToNewBatchRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - userID, err := controller.GetOperatorIDFromContext(ctx) - - err = c.service.ReclassifyPenToNewBatch(uint(fromBatchID), req.ToBatchID, req.PenID, userID, req.Remarks) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) || errors.Is(err, service.ErrPenNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), fromBatchID) - return - } else if errors.Is(err, service.ErrPigBatchNotActive) || errors.Is(err, service.ErrPenOccupiedByOtherBatch) || errors.Is(err, service.ErrPenNotAssociatedWithBatch) || errors.Is(err, service.ErrInvalidOperation) { - controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), fromBatchID) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "划拨猪栏失败", action, err.Error(), fromBatchID) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "划拨成功", nil, action, "划拨成功", fromBatchID) + handleAPIRequest( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.ReclassifyPenToNewBatchRequest) error { + // primaryID 在这里是 fromBatchID + return c.service.ReclassifyPenToNewBatch(primaryID, req.ToBatchID, req.PenID, operatorID, req.Remarks) + }, + "划拨成功", + func(ctx *gin.Context) (uint, error) { // 自定义ID提取器,从 ":fromBatchID" 路径参数提取 + idParam := ctx.Param("fromBatchID") + parsedID, err := strconv.ParseUint(idParam, 10, 32) + if err != nil { + return 0, err + } + return uint(parsedID), nil + }, + ) } // RemoveEmptyPenFromBatch godoc @@ -276,33 +202,28 @@ func (c *PigBatchController) ReclassifyPenToNewBatch(ctx *gin.Context) { // @Router /api/v1/pig-batches/{batchID}/remove-pen/{penID} [delete] func (c *PigBatchController) RemoveEmptyPenFromBatch(ctx *gin.Context) { const action = "从猪批次移除空栏" - batchID, err := strconv.ParseUint(ctx.Param("batchID"), 10, 32) - if err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的猪批次ID格式", action, "ID格式错误", ctx.Param("batchID")) - return - } - penID, err := strconv.ParseUint(ctx.Param("penID"), 10, 32) - if err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的猪栏ID格式", action, "ID格式错误", ctx.Param("penID")) - return - } - - err = c.service.RemoveEmptyPenFromBatch(uint(batchID), uint(penID)) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) || errors.Is(err, service.ErrPenNotFound) || errors.Is(err, service.ErrPenNotAssociatedWithBatch) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), batchID) - return - } else if errors.Is(err, service.ErrPigBatchNotActive) || errors.Is(err, service.ErrPenNotEmpty) { - controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), batchID) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "移除空栏失败", action, err.Error(), batchID) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "移除成功", nil, action, "移除成功", batchID) + handleNoBodyAPIRequest( + c, ctx, action, + func(ctx *gin.Context, operatorID uint, primaryID uint) error { + // primaryID 在这里是 batchID + penIDParam := ctx.Param("penID") + penID, err := strconv.ParseUint(penIDParam, 10, 32) + if err != nil { + return err // 返回错误,因为 penID 格式无效 + } + return c.service.RemoveEmptyPenFromBatch(primaryID, uint(penID)) + }, + "移除成功", + func(ctx *gin.Context) (uint, error) { // 自定义ID提取器,从 ":batchID" 路径参数提取 + idParam := ctx.Param("batchID") + parsedID, err := strconv.ParseUint(idParam, 10, 32) + if err != nil { + return 0, err + } + return uint(parsedID), nil + }, + ) } // MovePigsIntoPen godoc @@ -317,33 +238,14 @@ func (c *PigBatchController) RemoveEmptyPenFromBatch(ctx *gin.Context) { // @Router /api/v1/pig-batches/{id}/move-pigs-into-pen [post] func (c *PigBatchController) MovePigsIntoPen(ctx *gin.Context) { const action = "将猪只移入猪栏" - batchID, 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.MovePigsIntoPenRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - userID, err := controller.GetOperatorIDFromContext(ctx) - - err = c.service.MovePigsIntoPen(uint(batchID), req.ToPenID, req.Quantity, userID, req.Remarks) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) || errors.Is(err, service.ErrPenNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), batchID) - return - } else if errors.Is(err, service.ErrPigBatchNotActive) || errors.Is(err, service.ErrInvalidOperation) { - controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), batchID) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "移入猪只失败", action, err.Error(), batchID) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "移入成功", nil, action, "移入成功", batchID) + handleAPIRequest( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.MovePigsIntoPenRequest) error { + return c.service.MovePigsIntoPen(primaryID, req.ToPenID, req.Quantity, operatorID, req.Remarks) + }, + "移入成功", + nil, // 默认从 ":id" 路径参数提取ID + ) } diff --git a/internal/app/controller/management/pig_batch_health_controller.go b/internal/app/controller/management/pig_batch_health_controller.go index 3b4c09f..6afb94a 100644 --- a/internal/app/controller/management/pig_batch_health_controller.go +++ b/internal/app/controller/management/pig_batch_health_controller.go @@ -1,12 +1,7 @@ package management import ( - "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" "github.com/gin-gonic/gin" ) @@ -22,35 +17,16 @@ import ( // @Router /api/v1/pig-batches/{id}/record-sick-pigs [post] func (c *PigBatchController) RecordSickPigs(ctx *gin.Context) { const action = "记录新增病猪事件" - batchID, 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.RecordSickPigsRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - operatorID, err := controller.GetOperatorIDFromContext(ctx) - - err = c.service.RecordSickPigs(operatorID, uint(batchID), req.PenID, req.Quantity, req.TreatmentLocation, req.HappenedAt, req.Remarks) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) || errors.Is(err, service.ErrPenNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), batchID) - return - } else if errors.Is(err, service.ErrInvalidOperation) || errors.Is(err, service.ErrPigBatchNotActive) || errors.Is(err, service.ErrPenNotAssociatedWithBatch) || errors.Is(err, service.ErrPenNotEmpty) { - controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), batchID) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "记录新增病猪事件失败", action, err.Error(), batchID) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "记录成功", nil, action, "记录成功", batchID) + handleAPIRequest( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.RecordSickPigsRequest) error { + return c.service.RecordSickPigs(operatorID, primaryID, req.PenID, req.Quantity, req.TreatmentLocation, req.HappenedAt, req.Remarks) + }, + "记录成功", + nil, // 默认从 ":id" 路径参数提取ID + ) } // RecordSickPigRecovery godoc @@ -65,35 +41,16 @@ func (c *PigBatchController) RecordSickPigs(ctx *gin.Context) { // @Router /api/v1/pig-batches/{id}/record-sick-pig-recovery [post] func (c *PigBatchController) RecordSickPigRecovery(ctx *gin.Context) { const action = "记录病猪康复事件" - batchID, 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.RecordSickPigRecoveryRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - operatorID, err := controller.GetOperatorIDFromContext(ctx) - - err = c.service.RecordSickPigRecovery(operatorID, uint(batchID), req.PenID, req.Quantity, req.TreatmentLocation, req.HappenedAt, req.Remarks) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) || errors.Is(err, service.ErrPenNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), batchID) - return - } else if errors.Is(err, service.ErrInvalidOperation) || errors.Is(err, service.ErrPigBatchNotActive) || errors.Is(err, service.ErrPenNotAssociatedWithBatch) || errors.Is(err, service.ErrPenNotEmpty) { - controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), batchID) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "记录病猪康复事件失败", action, err.Error(), batchID) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "记录成功", nil, action, "记录成功", batchID) + handleAPIRequest( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.RecordSickPigRecoveryRequest) error { + return c.service.RecordSickPigRecovery(operatorID, primaryID, req.PenID, req.Quantity, req.TreatmentLocation, req.HappenedAt, req.Remarks) + }, + "记录成功", + nil, // 默认从 ":id" 路径参数提取ID + ) } // RecordSickPigDeath godoc @@ -108,35 +65,16 @@ func (c *PigBatchController) RecordSickPigRecovery(ctx *gin.Context) { // @Router /api/v1/pig-batches/{id}/record-sick-pig-death [post] func (c *PigBatchController) RecordSickPigDeath(ctx *gin.Context) { const action = "记录病猪死亡事件" - batchID, 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.RecordSickPigDeathRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - operatorID, err := controller.GetOperatorIDFromContext(ctx) - - err = c.service.RecordSickPigDeath(operatorID, uint(batchID), req.PenID, req.Quantity, req.TreatmentLocation, req.HappenedAt, req.Remarks) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) || errors.Is(err, service.ErrPenNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), batchID) - return - } else if errors.Is(err, service.ErrInvalidOperation) || errors.Is(err, service.ErrPigBatchNotActive) || errors.Is(err, service.ErrPenNotAssociatedWithBatch) || errors.Is(err, service.ErrPenNotEmpty) { - controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), batchID) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "记录病猪死亡事件失败", action, err.Error(), batchID) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "记录成功", nil, action, "记录成功", batchID) + handleAPIRequest( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.RecordSickPigDeathRequest) error { + return c.service.RecordSickPigDeath(operatorID, primaryID, req.PenID, req.Quantity, req.TreatmentLocation, req.HappenedAt, req.Remarks) + }, + "记录成功", + nil, // 默认从 ":id" 路径参数提取ID + ) } // RecordSickPigCull godoc @@ -151,35 +89,16 @@ func (c *PigBatchController) RecordSickPigDeath(ctx *gin.Context) { // @Router /api/v1/pig-batches/{id}/record-sick-pig-cull [post] func (c *PigBatchController) RecordSickPigCull(ctx *gin.Context) { const action = "记录病猪淘汰事件" - batchID, 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.RecordSickPigCullRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - operatorID, err := controller.GetOperatorIDFromContext(ctx) - - err = c.service.RecordSickPigCull(operatorID, uint(batchID), req.PenID, req.Quantity, req.TreatmentLocation, req.HappenedAt, req.Remarks) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) || errors.Is(err, service.ErrPenNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), batchID) - return - } else if errors.Is(err, service.ErrInvalidOperation) || errors.Is(err, service.ErrPigBatchNotActive) || errors.Is(err, service.ErrPenNotAssociatedWithBatch) || errors.Is(err, service.ErrPenNotEmpty) { - controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), batchID) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "记录病猪淘汰事件失败", action, err.Error(), batchID) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "记录成功", nil, action, "记录成功", batchID) + handleAPIRequest( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.RecordSickPigCullRequest) error { + return c.service.RecordSickPigCull(operatorID, primaryID, req.PenID, req.Quantity, req.TreatmentLocation, req.HappenedAt, req.Remarks) + }, + "记录成功", + nil, // 默认从 ":id" 路径参数提取ID + ) } // RecordDeath godoc @@ -194,35 +113,16 @@ func (c *PigBatchController) RecordSickPigCull(ctx *gin.Context) { // @Router /api/v1/pig-batches/{id}/record-death [post] func (c *PigBatchController) RecordDeath(ctx *gin.Context) { const action = "记录正常猪只死亡事件" - batchID, 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.RecordDeathRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - operatorID, err := controller.GetOperatorIDFromContext(ctx) - - err = c.service.RecordDeath(operatorID, uint(batchID), req.PenID, req.Quantity, req.HappenedAt, req.Remarks) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) || errors.Is(err, service.ErrPenNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), batchID) - return - } else if errors.Is(err, service.ErrInvalidOperation) || errors.Is(err, service.ErrPigBatchNotActive) || errors.Is(err, service.ErrPenNotAssociatedWithBatch) || errors.Is(err, service.ErrPenNotEmpty) { - controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), batchID) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "记录正常猪只死亡事件失败", action, err.Error(), batchID) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "记录成功", nil, action, "记录成功", batchID) + handleAPIRequest( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.RecordDeathRequest) error { + return c.service.RecordDeath(operatorID, primaryID, req.PenID, req.Quantity, req.HappenedAt, req.Remarks) + }, + "记录成功", + nil, // 默认从 ":id" 路径参数提取ID + ) } // RecordCull godoc @@ -237,33 +137,14 @@ func (c *PigBatchController) RecordDeath(ctx *gin.Context) { // @Router /api/v1/pig-batches/{id}/record-cull [post] func (c *PigBatchController) RecordCull(ctx *gin.Context) { const action = "记录正常猪只淘汰事件" - batchID, 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.RecordCullRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - operatorID, err := controller.GetOperatorIDFromContext(ctx) - - err = c.service.RecordCull(operatorID, uint(batchID), req.PenID, req.Quantity, req.HappenedAt, req.Remarks) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) || errors.Is(err, service.ErrPenNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), batchID) - return - } else if errors.Is(err, service.ErrInvalidOperation) || errors.Is(err, service.ErrPigBatchNotActive) || errors.Is(err, service.ErrPenNotAssociatedWithBatch) || errors.Is(err, service.ErrPenNotEmpty) { - controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), batchID) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "记录正常猪只淘汰事件失败", action, err.Error(), batchID) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "记录成功", nil, action, "记录成功", batchID) + handleAPIRequest( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.RecordCullRequest) error { + return c.service.RecordCull(operatorID, primaryID, req.PenID, req.Quantity, req.HappenedAt, req.Remarks) + }, + "记录成功", + nil, // 默认从 ":id" 路径参数提取ID + ) } diff --git a/internal/app/controller/management/pig_batch_trade_controller.go b/internal/app/controller/management/pig_batch_trade_controller.go index 821203c..08f2505 100644 --- a/internal/app/controller/management/pig_batch_trade_controller.go +++ b/internal/app/controller/management/pig_batch_trade_controller.go @@ -1,12 +1,7 @@ package management import ( - "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" "github.com/gin-gonic/gin" ) @@ -22,35 +17,16 @@ import ( // @Router /api/v1/pig-batches/{id}/sell-pigs [post] func (c *PigBatchController) SellPigs(ctx *gin.Context) { const action = "卖猪" - batchID, 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.SellPigsRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - operatorID, err := controller.GetOperatorIDFromContext(ctx) - - err = c.service.SellPigs(uint(batchID), req.PenID, req.Quantity, req.UnitPrice, req.TotalPrice, req.TraderName, req.TradeDate, req.Remarks, operatorID) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) || errors.Is(err, service.ErrPenNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), batchID) - return - } else if errors.Is(err, service.ErrInvalidOperation) || errors.Is(err, service.ErrPigBatchNotActive) || errors.Is(err, service.ErrPenNotAssociatedWithBatch) || errors.Is(err, service.ErrPenNotEmpty) { - controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), batchID) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "卖猪失败", action, err.Error(), batchID) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "卖猪成功", nil, action, "卖猪成功", batchID) + handleAPIRequest( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.SellPigsRequest) error { + return c.service.SellPigs(primaryID, req.PenID, req.Quantity, req.UnitPrice, req.TotalPrice, req.TraderName, req.TradeDate, req.Remarks, operatorID) + }, + "卖猪成功", + nil, // 默认从 ":id" 路径参数提取ID + ) } // BuyPigs godoc @@ -65,33 +41,14 @@ func (c *PigBatchController) SellPigs(ctx *gin.Context) { // @Router /api/v1/pig-batches/{id}/buy-pigs [post] func (c *PigBatchController) BuyPigs(ctx *gin.Context) { const action = "买猪" - batchID, 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.BuyPigsRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - operatorID, err := controller.GetOperatorIDFromContext(ctx) - - err = c.service.BuyPigs(uint(batchID), req.PenID, req.Quantity, req.UnitPrice, req.TotalPrice, req.TraderName, req.TradeDate, req.Remarks, operatorID) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) || errors.Is(err, service.ErrPenNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), batchID) - return - } else if errors.Is(err, service.ErrInvalidOperation) || errors.Is(err, service.ErrPigBatchNotActive) || errors.Is(err, service.ErrPenNotAssociatedWithBatch) { - controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), batchID) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "买猪失败", action, err.Error(), batchID) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "买猪成功", nil, action, "买猪成功", batchID) + handleAPIRequest( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.BuyPigsRequest) error { + return c.service.BuyPigs(primaryID, req.PenID, req.Quantity, req.UnitPrice, req.TotalPrice, req.TraderName, req.TradeDate, req.Remarks, operatorID) + }, + "买猪成功", + nil, // 默认从 ":id" 路径参数提取ID + ) } diff --git a/internal/app/controller/management/pig_batch_transfer_controller.go b/internal/app/controller/management/pig_batch_transfer_controller.go index 637367b..005a234 100644 --- a/internal/app/controller/management/pig_batch_transfer_controller.go +++ b/internal/app/controller/management/pig_batch_transfer_controller.go @@ -1,12 +1,9 @@ package management import ( - "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" "github.com/gin-gonic/gin" ) @@ -22,35 +19,24 @@ import ( // @Router /api/v1/pig-batches/{sourceBatchID}/transfer-across-batches [post] func (c *PigBatchController) TransferPigsAcrossBatches(ctx *gin.Context) { const action = "跨猪群调栏" - sourceBatchID, err := strconv.ParseUint(ctx.Param("sourceBatchID"), 10, 32) - if err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的源猪批次ID格式", action, "ID格式错误", ctx.Param("sourceBatchID")) - return - } - var req dto.TransferPigsAcrossBatchesRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - operatorID, err := controller.GetOperatorIDFromContext(ctx) - - err = c.service.TransferPigsAcrossBatches(uint(sourceBatchID), req.DestBatchID, req.FromPenID, req.ToPenID, req.Quantity, operatorID, req.Remarks) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) || errors.Is(err, service.ErrPenNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), sourceBatchID) - return - } else if errors.Is(err, service.ErrInvalidOperation) || errors.Is(err, service.ErrPigBatchNotActive) || errors.Is(err, service.ErrPenNotAssociatedWithBatch) { - controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), sourceBatchID) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "跨猪群调栏失败", action, err.Error(), sourceBatchID) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "调栏成功", nil, action, "调栏成功", sourceBatchID) + handleAPIRequest( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.TransferPigsAcrossBatchesRequest) error { + // primaryID 在这里是 sourceBatchID + return c.service.TransferPigsAcrossBatches(primaryID, req.DestBatchID, req.FromPenID, req.ToPenID, req.Quantity, operatorID, req.Remarks) + }, + "调栏成功", + func(ctx *gin.Context) (uint, error) { // 自定义ID提取器,从 ":sourceBatchID" 路径参数提取 + idParam := ctx.Param("sourceBatchID") + parsedID, err := strconv.ParseUint(idParam, 10, 32) + if err != nil { + return 0, err + } + return uint(parsedID), nil + }, + ) } // TransferPigsWithinBatch godoc @@ -65,33 +51,15 @@ func (c *PigBatchController) TransferPigsAcrossBatches(ctx *gin.Context) { // @Router /api/v1/pig-batches/{id}/transfer-within-batch [post] func (c *PigBatchController) TransferPigsWithinBatch(ctx *gin.Context) { const action = "群内调栏" - batchID, 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.TransferPigsWithinBatchRequest - if err := ctx.ShouldBindJSON(&req); err != nil { - controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体", action, "请求体绑定失败", req) - return - } - operatorID, err := controller.GetOperatorIDFromContext(ctx) - - err = c.service.TransferPigsWithinBatch(uint(batchID), req.FromPenID, req.ToPenID, req.Quantity, operatorID, req.Remarks) - if err != nil { - if errors.Is(err, service.ErrPigBatchNotFound) || errors.Is(err, service.ErrPenNotFound) { - controller.SendErrorWithAudit(ctx, controller.CodeNotFound, err.Error(), action, err.Error(), batchID) - return - } else if errors.Is(err, service.ErrInvalidOperation) || errors.Is(err, service.ErrPigBatchNotActive) || errors.Is(err, service.ErrPenNotAssociatedWithBatch) { - controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), action, err.Error(), batchID) - return - } - c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) - controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "群内调栏失败", action, err.Error(), batchID) - return - } - - controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "调栏成功", nil, action, "调栏成功", batchID) + handleAPIRequest( + c, ctx, action, &req, + func(ctx *gin.Context, operatorID uint, primaryID uint, req *dto.TransferPigsWithinBatchRequest) error { + // primaryID 在这里是 batchID + return c.service.TransferPigsWithinBatch(primaryID, req.FromPenID, req.ToPenID, req.Quantity, operatorID, req.Remarks) + }, + "调栏成功", + nil, // 默认从 ":id" 路径参数提取ID + ) }