猪群领域其他方法映射到api

This commit is contained in:
2025-10-06 23:22:47 +08:00
parent 632bd20e7d
commit e142405bb3
10 changed files with 2783 additions and 35 deletions

View File

@@ -226,7 +226,7 @@ func (a *API) setupRoutes() {
}
a.logger.Info("猪圈相关接口注册成功 (需要认证和审计)")
// 猪批次相关路由组
// 猪相关路由组
pigBatchGroup := authGroup.Group("/pig-batches")
{
pigBatchGroup.POST("", a.pigBatchController.CreatePigBatch)
@@ -238,6 +238,16 @@ func (a *API) setupRoutes() {
pigBatchGroup.POST("/:fromBatchID/reclassify-pen", a.pigBatchController.ReclassifyPenToNewBatch)
pigBatchGroup.DELETE("/:batchID/remove-pen/:penID", a.pigBatchController.RemoveEmptyPenFromBatch)
pigBatchGroup.POST("/:id/move-pigs-into-pen", a.pigBatchController.MovePigsIntoPen)
pigBatchGroup.POST("/:id/sell-pigs", a.pigBatchController.SellPigs)
pigBatchGroup.POST("/:id/buy-pigs", a.pigBatchController.BuyPigs)
pigBatchGroup.POST("/:sourceBatchID/transfer-across-batches", a.pigBatchController.TransferPigsAcrossBatches)
pigBatchGroup.POST("/:id/transfer-within-batch", a.pigBatchController.TransferPigsWithinBatch)
pigBatchGroup.POST("/:id/record-sick-pigs", a.pigBatchController.RecordSickPigs)
pigBatchGroup.POST("/:id/record-sick-pig-recovery", a.pigBatchController.RecordSickPigRecovery)
pigBatchGroup.POST("/:id/record-sick-pig-death", a.pigBatchController.RecordSickPigDeath)
pigBatchGroup.POST("/:id/record-sick-pig-cull", a.pigBatchController.RecordSickPigCull)
pigBatchGroup.POST("/:id/record-death", a.pigBatchController.RecordDeath)
pigBatchGroup.POST("/:id/record-cull", a.pigBatchController.RecordCull)
}
a.logger.Info("猪批次相关接口注册成功 (需要认证和审计)")

View File

@@ -29,7 +29,7 @@ func NewPigBatchController(logger *logs.Logger, service service.PigBatchService)
// CreatePigBatch godoc
// @Summary 创建猪批次
// @Description 创建一个新的猪批次
// @Tags 猪批次管理
// @Tags 猪管理
// @Accept json
// @Produce json
// @Param body body dto.PigBatchCreateDTO true "猪批次信息"
@@ -58,7 +58,7 @@ func (c *PigBatchController) CreatePigBatch(ctx *gin.Context) {
// GetPigBatch godoc
// @Summary 获取单个猪批次
// @Description 根据ID获取单个猪批次信息
// @Tags 猪批次管理
// @Tags 猪管理
// @Produce json
// @Param id path int true "猪批次ID"
// @Success 200 {object} controller.Response{data=dto.PigBatchResponseDTO} "获取成功"
@@ -88,7 +88,7 @@ func (c *PigBatchController) GetPigBatch(ctx *gin.Context) {
// UpdatePigBatch godoc
// @Summary 更新猪批次
// @Description 更新一个已存在的猪批次信息
// @Tags 猪批次管理
// @Tags 猪管理
// @Accept json
// @Produce json
// @Param id path int true "猪批次ID"
@@ -126,7 +126,7 @@ func (c *PigBatchController) UpdatePigBatch(ctx *gin.Context) {
// DeletePigBatch godoc
// @Summary 删除猪批次
// @Description 根据ID删除一个猪批次
// @Tags 猪批次管理
// @Tags 猪管理
// @Produce json
// @Param id path int true "猪批次ID"
// @Success 200 {object} controller.Response "删除成功"
@@ -155,7 +155,7 @@ func (c *PigBatchController) DeletePigBatch(ctx *gin.Context) {
// ListPigBatches godoc
// @Summary 获取猪批次列表
// @Description 获取所有猪批次的列表,支持按活跃状态筛选
// @Tags 猪批次管理
// @Tags 猪管理
// @Produce json
// @Param is_active query bool false "是否活跃 (true/false)"
// @Success 200 {object} controller.Response{data=[]dto.PigBatchResponseDTO} "获取成功"
@@ -182,7 +182,7 @@ func (c *PigBatchController) ListPigBatches(ctx *gin.Context) {
// AssignEmptyPensToBatch godoc
// @Summary 为猪批次分配空栏
// @Description 将一个或多个空闲猪栏分配给指定的猪批次
// @Tags 猪批次管理
// @Tags 猪管理
// @Accept json
// @Produce json
// @Param id path int true "猪批次ID"
@@ -225,7 +225,7 @@ func (c *PigBatchController) AssignEmptyPensToBatch(ctx *gin.Context) {
// ReclassifyPenToNewBatch godoc
// @Summary 将猪栏划拨到新批次
// @Description 将一个猪栏(连同其中的猪只)从一个批次整体划拨到另一个批次
// @Tags 猪批次管理
// @Tags 猪管理
// @Accept json
// @Produce json
// @Param fromBatchID path int true "源猪批次ID"
@@ -268,7 +268,7 @@ func (c *PigBatchController) ReclassifyPenToNewBatch(ctx *gin.Context) {
// RemoveEmptyPenFromBatch godoc
// @Summary 从猪批次移除空栏
// @Description 将一个空闲猪栏从指定的猪批次中移除
// @Tags 猪批次管理
// @Tags 猪管理
// @Produce json
// @Param batchID path int true "猪批次ID"
// @Param penID path int true "待移除的猪栏ID"
@@ -308,7 +308,7 @@ func (c *PigBatchController) RemoveEmptyPenFromBatch(ctx *gin.Context) {
// MovePigsIntoPen godoc
// @Summary 将猪只从“虚拟库存”移入指定猪栏
// @Description 将指定数量的猪只从批次的“虚拟库存”移入一个已分配的猪栏
// @Tags 猪批次管理
// @Tags 猪管理
// @Accept json
// @Produce json
// @Param id path int true "猪批次ID"

View File

@@ -0,0 +1,269 @@
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"
)
// RecordSickPigs godoc
// @Summary 记录新增病猪事件
// @Description 记录猪批次中新增病猪的数量、治疗地点和发生时间
// @Tags 猪批次管理
// @Accept json
// @Produce json
// @Param id path int true "猪批次ID"
// @Param body body dto.RecordSickPigsRequest true "记录病猪请求信息"
// @Success 200 {object} controller.Response "记录成功"
// @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)
}
// RecordSickPigRecovery godoc
// @Summary 记录病猪康复事件
// @Description 记录猪批次中病猪康复的数量、治疗地点和发生时间
// @Tags 猪批次管理
// @Accept json
// @Produce json
// @Param id path int true "猪批次ID"
// @Param body body dto.RecordSickPigRecoveryRequest true "记录病猪康复请求信息"
// @Success 200 {object} controller.Response "记录成功"
// @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)
}
// RecordSickPigDeath godoc
// @Summary 记录病猪死亡事件
// @Description 记录猪批次中病猪死亡的数量、治疗地点和发生时间
// @Tags 猪批次管理
// @Accept json
// @Produce json
// @Param id path int true "猪批次ID"
// @Param body body dto.RecordSickPigDeathRequest true "记录病猪死亡请求信息"
// @Success 200 {object} controller.Response "记录成功"
// @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)
}
// RecordSickPigCull godoc
// @Summary 记录病猪淘汰事件
// @Description 记录猪批次中病猪淘汰的数量、治疗地点和发生时间
// @Tags 猪批次管理
// @Accept json
// @Produce json
// @Param id path int true "猪批次ID"
// @Param body body dto.RecordSickPigCullRequest true "记录病猪淘汰请求信息"
// @Success 200 {object} controller.Response "记录成功"
// @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)
}
// RecordDeath godoc
// @Summary 记录正常猪只死亡事件
// @Description 记录猪批次中正常猪只死亡的数量和发生时间
// @Tags 猪批次管理
// @Accept json
// @Produce json
// @Param id path int true "猪批次ID"
// @Param body body dto.RecordDeathRequest true "记录正常猪只死亡请求信息"
// @Success 200 {object} controller.Response "记录成功"
// @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)
}
// RecordCull godoc
// @Summary 记录正常猪只淘汰事件
// @Description 记录猪批次中正常猪只淘汰的数量和发生时间
// @Tags 猪批次管理
// @Accept json
// @Produce json
// @Param id path int true "猪批次ID"
// @Param body body dto.RecordCullRequest true "记录正常猪只淘汰请求信息"
// @Success 200 {object} controller.Response "记录成功"
// @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)
}

View File

@@ -0,0 +1,97 @@
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"
)
// SellPigs godoc
// @Summary 处理卖猪的业务逻辑
// @Description 记录猪批次中的猪只出售事件
// @Tags 猪批次管理
// @Accept json
// @Produce json
// @Param id path int true "猪批次ID"
// @Param body body dto.SellPigsRequest true "卖猪请求信息"
// @Success 200 {object} controller.Response "卖猪成功"
// @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)
}
// BuyPigs godoc
// @Summary 处理买猪的业务逻辑
// @Description 记录猪批次中的猪只购买事件
// @Tags 猪批次管理
// @Accept json
// @Produce json
// @Param id path int true "猪批次ID"
// @Param body body dto.BuyPigsRequest true "买猪请求信息"
// @Success 200 {object} controller.Response "买猪成功"
// @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)
}

View File

@@ -0,0 +1,97 @@
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"
)
// TransferPigsAcrossBatches godoc
// @Summary 跨猪群调栏
// @Description 将指定数量的猪只从一个猪群的猪栏调动到另一个猪群的猪栏
// @Tags 猪批次管理
// @Accept json
// @Produce json
// @Param sourceBatchID path int true "源猪批次ID"
// @Param body body dto.TransferPigsAcrossBatchesRequest true "跨群调栏请求信息"
// @Success 200 {object} controller.Response "调栏成功"
// @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)
}
// TransferPigsWithinBatch godoc
// @Summary 群内调栏
// @Description 将指定数量的猪只在同一个猪群的不同猪栏间调动
// @Tags 猪批次管理
// @Accept json
// @Produce json
// @Param id path int true "猪批次ID"
// @Param body body dto.TransferPigsWithinBatchRequest true "群内调栏请求信息"
// @Success 200 {object} controller.Response "调栏成功"
// @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)
}

View File

@@ -67,3 +67,94 @@ type MovePigsIntoPenRequest struct {
Quantity int `json:"quantity" binding:"required,min=1"` // 移入猪只数量
Remarks string `json:"remarks"` // 备注
}
// SellPigsRequest 用于处理卖猪的请求体
type SellPigsRequest struct {
PenID uint `json:"penID" binding:"required"` // 猪栏ID
Quantity int `json:"quantity" binding:"required,min=1"` // 卖出猪只数量
UnitPrice float64 `json:"unitPrice" binding:"required,min=0"` // 单价
TotalPrice float64 `json:"totalPrice" binding:"required,min=0"` // 总价
TraderName string `json:"traderName" binding:"required"` // 交易方名称
TradeDate time.Time `json:"tradeDate" binding:"required"` // 交易日期
Remarks string `json:"remarks"` // 备注
}
// BuyPigsRequest 用于处理买猪的请求体
type BuyPigsRequest struct {
PenID uint `json:"penID" binding:"required"` // 猪栏ID
Quantity int `json:"quantity" binding:"required,min=1"` // 买入猪只数量
UnitPrice float64 `json:"unitPrice" binding:"required,min=0"` // 单价
TotalPrice float64 `json:"totalPrice" binding:"required,min=0"` // 总价
TraderName string `json:"traderName" binding:"required"` // 交易方名称
TradeDate time.Time `json:"tradeDate" binding:"required"` // 交易日期
Remarks string `json:"remarks"` // 备注
}
// TransferPigsAcrossBatchesRequest 用于跨猪群调栏的请求体
type TransferPigsAcrossBatchesRequest struct {
DestBatchID uint `json:"destBatchID" binding:"required"` // 目标猪批次ID
FromPenID uint `json:"fromPenID" binding:"required"` // 源猪栏ID
ToPenID uint `json:"toPenID" binding:"required"` // 目标猪栏ID
Quantity uint `json:"quantity" binding:"required,min=1"` // 调栏猪只数量
Remarks string `json:"remarks"` // 备注
}
// TransferPigsWithinBatchRequest 用于群内调栏的请求体
type TransferPigsWithinBatchRequest struct {
FromPenID uint `json:"fromPenID" binding:"required"` // 源猪栏ID
ToPenID uint `json:"toPenID" binding:"required"` // 目标猪栏ID
Quantity uint `json:"quantity" binding:"required,min=1"` // 调栏猪只数量
Remarks string `json:"remarks"` // 备注
}
// RecordSickPigsRequest 用于记录新增病猪事件的请求体
type RecordSickPigsRequest struct {
PenID uint `json:"penID" binding:"required"` // 猪栏ID
Quantity int `json:"quantity" binding:"required,min=1"` // 病猪数量
TreatmentLocation models.PigBatchSickPigTreatmentLocation `json:"treatmentLocation" binding:"required"` // 治疗地点
HappenedAt time.Time `json:"happenedAt" binding:"required"` // 发生时间
Remarks string `json:"remarks"` // 备注
}
// RecordSickPigRecoveryRequest 用于记录病猪康复事件的请求体
type RecordSickPigRecoveryRequest struct {
PenID uint `json:"penID" binding:"required"` // 猪栏ID
Quantity int `json:"quantity" binding:"required,min=1"` // 康复猪数量
TreatmentLocation models.PigBatchSickPigTreatmentLocation `json:"treatmentLocation" binding:"required"` // 治疗地点
HappenedAt time.Time `json:"happenedAt" binding:"required"` // 发生时间
Remarks string `json:"remarks"` // 备注
}
// RecordSickPigDeathRequest 用于记录病猪死亡事件的请求体
type RecordSickPigDeathRequest struct {
PenID uint `json:"penID" binding:"required"` // 猪栏ID
Quantity int `json:"quantity" binding:"required,min=1"` // 死亡猪数量
TreatmentLocation models.PigBatchSickPigTreatmentLocation `json:"treatmentLocation" binding:"required"` // 治疗地点
HappenedAt time.Time `json:"happenedAt" binding:"required"` // 发生时间
Remarks string `json:"remarks"` // 备注
}
// RecordSickPigCullRequest 用于记录病猪淘汰事件的请求体
type RecordSickPigCullRequest struct {
PenID uint `json:"penID" binding:"required"` // 猪栏ID
Quantity int `json:"quantity" binding:"required,min=1"` // 淘汰猪数量
TreatmentLocation models.PigBatchSickPigTreatmentLocation `json:"treatmentLocation" binding:"required"` // 治疗地点
HappenedAt time.Time `json:"happenedAt" binding:"required"` // 发生时间
Remarks string `json:"remarks"` // 备注
}
// RecordDeathRequest 用于记录正常猪只死亡事件的请求体
type RecordDeathRequest struct {
PenID uint `json:"penID" binding:"required"` // 猪栏ID
Quantity int `json:"quantity" binding:"required,min=1"` // 死亡猪数量
HappenedAt time.Time `json:"happenedAt" binding:"required"` // 发生时间
Remarks string `json:"remarks"` // 备注
}
// RecordCullRequest 用于记录正常猪只淘汰事件的请求体
type RecordCullRequest struct {
PenID uint `json:"penID" binding:"required"` // 猪栏ID
Quantity int `json:"quantity" binding:"required,min=1"` // 淘汰猪数量
HappenedAt time.Time `json:"happenedAt" binding:"required"` // 发生时间
Remarks string `json:"remarks"` // 备注
}

View File

@@ -1,6 +1,8 @@
package service
import (
"time"
"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
domain_pig "git.huangwc.com/pig/pig-farm-controller/internal/domain/pig"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
@@ -14,10 +16,30 @@ type PigBatchService interface {
UpdatePigBatch(id uint, dto *dto.PigBatchUpdateDTO) (*dto.PigBatchResponseDTO, error)
DeletePigBatch(id uint) error
ListPigBatches(isActive *bool) ([]*dto.PigBatchResponseDTO, error)
// Pig Pen Management
AssignEmptyPensToBatch(batchID uint, penIDs []uint, operatorID uint) error
ReclassifyPenToNewBatch(fromBatchID uint, toBatchID uint, penID uint, operatorID uint, remarks string) error
RemoveEmptyPenFromBatch(batchID uint, penID uint) error
MovePigsIntoPen(batchID uint, toPenID uint, quantity int, operatorID uint, remarks string) error
// Trade Sub-service
SellPigs(batchID uint, penID uint, quantity int, unitPrice float64, tatalPrice float64, traderName string, tradeDate time.Time, remarks string, operatorID uint) error
BuyPigs(batchID uint, penID uint, quantity int, unitPrice float64, tatalPrice float64, traderName string, tradeDate time.Time, remarks string, operatorID uint) error
// Transfer Sub-service
TransferPigsAcrossBatches(sourceBatchID uint, destBatchID uint, fromPenID uint, toPenID uint, quantity uint, operatorID uint, remarks string) error
TransferPigsWithinBatch(batchID uint, fromPenID uint, toPenID uint, quantity uint, operatorID uint, remarks string) error
// Sick Pig Management
RecordSickPigs(operatorID uint, batchID uint, penID uint, quantity int, treatmentLocation models.PigBatchSickPigTreatmentLocation, happenedAt time.Time, remarks string) error
RecordSickPigRecovery(operatorID uint, batchID uint, penID uint, quantity int, treatmentLocation models.PigBatchSickPigTreatmentLocation, happenedAt time.Time, remarks string) error
RecordSickPigDeath(operatorID uint, batchID uint, penID uint, quantity int, treatmentLocation models.PigBatchSickPigTreatmentLocation, happenedAt time.Time, remarks string) error
RecordSickPigCull(operatorID uint, batchID uint, penID uint, quantity int, treatmentLocation models.PigBatchSickPigTreatmentLocation, happenedAt time.Time, remarks string) error
// Normal Pig Management
RecordDeath(operatorID uint, batchID uint, penID uint, quantity int, happenedAt time.Time, remarks string) error
RecordCull(operatorID uint, batchID uint, penID uint, quantity int, happenedAt time.Time, remarks string) error
}
// pigBatchService 的实现现在依赖于领域服务接口。
@@ -191,3 +213,103 @@ func (s *pigBatchService) MovePigsIntoPen(batchID uint, toPenID uint, quantity i
}
return nil
}
// SellPigs 委托给领域服务
func (s *pigBatchService) SellPigs(batchID uint, penID uint, quantity int, unitPrice float64, tatalPrice float64, traderName string, tradeDate time.Time, remarks string, operatorID uint) error {
err := s.domainService.SellPigs(batchID, penID, quantity, unitPrice, tatalPrice, traderName, tradeDate, remarks, operatorID)
if err != nil {
s.logger.Errorf("应用层: 卖猪失败, 批次ID: %d, 错误: %v", batchID, err)
return MapDomainError(err)
}
return nil
}
// BuyPigs 委托给领域服务
func (s *pigBatchService) BuyPigs(batchID uint, penID uint, quantity int, unitPrice float64, tatalPrice float64, traderName string, tradeDate time.Time, remarks string, operatorID uint) error {
err := s.domainService.BuyPigs(batchID, penID, quantity, unitPrice, tatalPrice, traderName, tradeDate, remarks, operatorID)
if err != nil {
s.logger.Errorf("应用层: 买猪失败, 批次ID: %d, 错误: %v", batchID, err)
return MapDomainError(err)
}
return nil
}
// TransferPigsAcrossBatches 委托给领域服务
func (s *pigBatchService) TransferPigsAcrossBatches(sourceBatchID uint, destBatchID uint, fromPenID uint, toPenID uint, quantity uint, operatorID uint, remarks string) error {
err := s.domainService.TransferPigsAcrossBatches(sourceBatchID, destBatchID, fromPenID, toPenID, quantity, operatorID, remarks)
if err != nil {
s.logger.Errorf("应用层: 跨群调栏失败, 源批次ID: %d, 错误: %v", sourceBatchID, err)
return MapDomainError(err)
}
return nil
}
// TransferPigsWithinBatch 委托给领域服务
func (s *pigBatchService) TransferPigsWithinBatch(batchID uint, fromPenID uint, toPenID uint, quantity uint, operatorID uint, remarks string) error {
err := s.domainService.TransferPigsWithinBatch(batchID, fromPenID, toPenID, quantity, operatorID, remarks)
if err != nil {
s.logger.Errorf("应用层: 群内调栏失败, 批次ID: %d, 错误: %v", batchID, err)
return MapDomainError(err)
}
return nil
}
// RecordSickPigs 委托给领域服务
func (s *pigBatchService) RecordSickPigs(operatorID uint, batchID uint, penID uint, quantity int, treatmentLocation models.PigBatchSickPigTreatmentLocation, happenedAt time.Time, remarks string) error {
err := s.domainService.RecordSickPigs(operatorID, batchID, penID, quantity, treatmentLocation, happenedAt, remarks)
if err != nil {
s.logger.Errorf("应用层: 记录病猪事件失败, 批次ID: %d, 错误: %v", batchID, err)
return MapDomainError(err)
}
return nil
}
// RecordSickPigRecovery 委托给领域服务
func (s *pigBatchService) RecordSickPigRecovery(operatorID uint, batchID uint, penID uint, quantity int, treatmentLocation models.PigBatchSickPigTreatmentLocation, happenedAt time.Time, remarks string) error {
err := s.domainService.RecordSickPigRecovery(operatorID, batchID, penID, quantity, treatmentLocation, happenedAt, remarks)
if err != nil {
s.logger.Errorf("应用层: 记录病猪康复事件失败, 批次ID: %d, 错误: %v", batchID, err)
return MapDomainError(err)
}
return nil
}
// RecordSickPigDeath 委托给领域服务
func (s *pigBatchService) RecordSickPigDeath(operatorID uint, batchID uint, penID uint, quantity int, treatmentLocation models.PigBatchSickPigTreatmentLocation, happenedAt time.Time, remarks string) error {
err := s.domainService.RecordSickPigDeath(operatorID, batchID, penID, quantity, treatmentLocation, happenedAt, remarks)
if err != nil {
s.logger.Errorf("应用层: 记录病猪死亡事件失败, 批次ID: %d, 错误: %v", batchID, err)
return MapDomainError(err)
}
return nil
}
// RecordSickPigCull 委托给领域服务
func (s *pigBatchService) RecordSickPigCull(operatorID uint, batchID uint, penID uint, quantity int, treatmentLocation models.PigBatchSickPigTreatmentLocation, happenedAt time.Time, remarks string) error {
err := s.domainService.RecordSickPigCull(operatorID, batchID, penID, quantity, treatmentLocation, happenedAt, remarks)
if err != nil {
s.logger.Errorf("应用层: 记录病猪淘汰事件失败, 批次ID: %d, 错误: %v", batchID, err)
return MapDomainError(err)
}
return nil
}
// RecordDeath 委托给领域服务
func (s *pigBatchService) RecordDeath(operatorID uint, batchID uint, penID uint, quantity int, happenedAt time.Time, remarks string) error {
err := s.domainService.RecordDeath(operatorID, batchID, penID, quantity, happenedAt, remarks)
if err != nil {
s.logger.Errorf("应用层: 记录正常猪只死亡事件失败, 批次ID: %d, 错误: %v", batchID, err)
return MapDomainError(err)
}
return nil
}
// RecordCull 委托给领域服务
func (s *pigBatchService) RecordCull(operatorID uint, batchID uint, penID uint, quantity int, happenedAt time.Time, remarks string) error {
err := s.domainService.RecordCull(operatorID, batchID, penID, quantity, happenedAt, remarks)
if err != nil {
s.logger.Errorf("应用层: 记录正常猪只淘汰事件失败, 批次ID: %d, 错误: %v", batchID, err)
return MapDomainError(err)
}
return nil
}