456 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			456 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package pig
 | 
						||
 | 
						||
import (
 | 
						||
	"errors"
 | 
						||
	"fmt"
 | 
						||
	"time"
 | 
						||
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
						||
	"github.com/google/uuid"
 | 
						||
	"gorm.io/gorm"
 | 
						||
)
 | 
						||
 | 
						||
// executeTransferAndLog 是一个私有辅助方法,用于封装创建和记录迁移日志的通用逻辑。
 | 
						||
func (s *pigBatchService) executeTransferAndLog(tx *gorm.DB, fromBatchID, toBatchID, fromPenID, toPenID uint, quantity int, transferType models.PigTransferType, operatorID uint, remarks string) error {
 | 
						||
	// 通用校验:任何调出操作都不能超过源猪栏的当前存栏数
 | 
						||
	if quantity < 0 { // 当调出时才需要检查
 | 
						||
		currentPigsInFromPen, err := s.transferSvc.GetCurrentPigsInPen(tx, fromPenID)
 | 
						||
		if err != nil {
 | 
						||
			return fmt.Errorf("获取源猪栏 %d 当前猪只数失败: %w", fromPenID, err)
 | 
						||
		}
 | 
						||
		if currentPigsInFromPen+quantity < 0 {
 | 
						||
			return fmt.Errorf("调出数量 %d 超过源猪栏 %d 当前存栏数 %d", -quantity, fromPenID, currentPigsInFromPen)
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// 1. 生成关联ID
 | 
						||
	correlationID := uuid.New().String()
 | 
						||
 | 
						||
	// 2. 创建调出日志
 | 
						||
	logOut := &models.PigTransferLog{
 | 
						||
		TransferTime:  time.Now(),
 | 
						||
		PigBatchID:    fromBatchID,
 | 
						||
		PenID:         fromPenID,
 | 
						||
		Quantity:      -quantity, // 调出为负数
 | 
						||
		Type:          transferType,
 | 
						||
		CorrelationID: correlationID,
 | 
						||
		OperatorID:    operatorID,
 | 
						||
		Remarks:       remarks,
 | 
						||
	}
 | 
						||
 | 
						||
	// 3. 创建调入日志
 | 
						||
	logIn := &models.PigTransferLog{
 | 
						||
		TransferTime:  time.Now(),
 | 
						||
		PigBatchID:    toBatchID,
 | 
						||
		PenID:         toPenID,
 | 
						||
		Quantity:      quantity, // 调入为正数
 | 
						||
		Type:          transferType,
 | 
						||
		CorrelationID: correlationID,
 | 
						||
		OperatorID:    operatorID,
 | 
						||
		Remarks:       remarks,
 | 
						||
	}
 | 
						||
 | 
						||
	// 4. 调用子服务记录日志
 | 
						||
	if err := s.transferSvc.LogTransfer(tx, logOut); err != nil {
 | 
						||
		return fmt.Errorf("记录调出日志失败: %w", err)
 | 
						||
	}
 | 
						||
	if err := s.transferSvc.LogTransfer(tx, logIn); err != nil {
 | 
						||
		return fmt.Errorf("记录调入日志失败: %w", err)
 | 
						||
	}
 | 
						||
 | 
						||
	return nil
 | 
						||
}
 | 
						||
 | 
						||
// TransferPigsWithinBatch 实现了同一个猪群内部的调栏业务。
 | 
						||
func (s *pigBatchService) TransferPigsWithinBatch(batchID uint, fromPenID uint, toPenID uint, quantity uint, operatorID uint, remarks string) error {
 | 
						||
	if fromPenID == toPenID {
 | 
						||
		return errors.New("源猪栏和目标猪栏不能相同")
 | 
						||
	}
 | 
						||
	if quantity == 0 {
 | 
						||
		return errors.New("迁移数量不能为零")
 | 
						||
	}
 | 
						||
 | 
						||
	return s.uow.ExecuteInTransaction(func(tx *gorm.DB) error {
 | 
						||
		// 1. 核心业务规则校验
 | 
						||
		fromPen, err := s.transferSvc.GetPenByID(tx, fromPenID)
 | 
						||
		if err != nil {
 | 
						||
			return fmt.Errorf("获取源猪栏信息失败: %w", err)
 | 
						||
		}
 | 
						||
		toPen, err := s.transferSvc.GetPenByID(tx, toPenID)
 | 
						||
		if err != nil {
 | 
						||
			return fmt.Errorf("获取目标猪栏信息失败: %w", err)
 | 
						||
		}
 | 
						||
 | 
						||
		if fromPen.PigBatchID == nil || *fromPen.PigBatchID != batchID {
 | 
						||
			return fmt.Errorf("源猪栏 %d 不属于指定的猪群 %d", fromPenID, batchID)
 | 
						||
		}
 | 
						||
		if toPen.PigBatchID != nil && *toPen.PigBatchID != batchID {
 | 
						||
			return fmt.Errorf("目标猪栏 %d 已被其他猪群占用", toPenID)
 | 
						||
		}
 | 
						||
 | 
						||
		// 2. 调用通用辅助方法执行日志记录
 | 
						||
		err = s.executeTransferAndLog(tx, batchID, batchID, fromPenID, toPenID, int(quantity), "群内调栏", operatorID, remarks)
 | 
						||
		if err != nil {
 | 
						||
			return err
 | 
						||
		}
 | 
						||
 | 
						||
		// 3. 群内调栏,猪群总数不变
 | 
						||
		return nil
 | 
						||
	})
 | 
						||
}
 | 
						||
 | 
						||
// TransferPigsAcrossBatches 实现了跨猪群的调栏业务。
 | 
						||
func (s *pigBatchService) TransferPigsAcrossBatches(sourceBatchID uint, destBatchID uint, fromPenID uint, toPenID uint, quantity uint, operatorID uint, remarks string) error {
 | 
						||
	if sourceBatchID == destBatchID {
 | 
						||
		return errors.New("源猪群和目标猪群不能相同")
 | 
						||
	}
 | 
						||
	if quantity == 0 {
 | 
						||
		return errors.New("迁移数量不能为零")
 | 
						||
	}
 | 
						||
 | 
						||
	return s.uow.ExecuteInTransaction(func(tx *gorm.DB) error {
 | 
						||
		// 1. 核心业务规则校验
 | 
						||
		// 1.1 校验猪群存在
 | 
						||
		if _, err := s.pigBatchRepo.GetPigBatchByIDTx(tx, sourceBatchID); err != nil {
 | 
						||
			if errors.Is(err, gorm.ErrRecordNotFound) {
 | 
						||
				return fmt.Errorf("源猪群 %d 不存在", sourceBatchID)
 | 
						||
			}
 | 
						||
			return fmt.Errorf("获取源猪群信息失败: %w", err)
 | 
						||
		}
 | 
						||
		if _, err := s.pigBatchRepo.GetPigBatchByIDTx(tx, destBatchID); err != nil {
 | 
						||
			if errors.Is(err, gorm.ErrRecordNotFound) {
 | 
						||
				return fmt.Errorf("目标猪群 %d 不存在", destBatchID)
 | 
						||
			}
 | 
						||
			return fmt.Errorf("获取目标猪群信息失败: %w", err)
 | 
						||
		}
 | 
						||
 | 
						||
		// 1.2 校验猪栏归属
 | 
						||
		fromPen, err := s.transferSvc.GetPenByID(tx, fromPenID)
 | 
						||
		if err != nil {
 | 
						||
			return fmt.Errorf("获取源猪栏信息失败: %w", err)
 | 
						||
		}
 | 
						||
		if fromPen.PigBatchID == nil || *fromPen.PigBatchID != sourceBatchID {
 | 
						||
			return fmt.Errorf("源猪栏 %d 不属于源猪群 %d", fromPenID, sourceBatchID)
 | 
						||
		}
 | 
						||
 | 
						||
		// 2. 调用通用辅助方法执行猪只物理转移的日志记录
 | 
						||
		err = s.executeTransferAndLog(tx, sourceBatchID, destBatchID, fromPenID, toPenID, int(quantity), "跨群调栏", operatorID, remarks)
 | 
						||
		if err != nil {
 | 
						||
			return err
 | 
						||
		}
 | 
						||
 | 
						||
		// 3. 通过创建批次日志来修改猪群总数,确保数据可追溯
 | 
						||
		now := time.Now()
 | 
						||
		// 3.1 记录源猪群数量减少
 | 
						||
		reasonOut := fmt.Sprintf("跨群调栏: %d头猪从批次 %d 调出至批次 %d。备注: %s", quantity, sourceBatchID, destBatchID, remarks)
 | 
						||
		err = s.updatePigBatchQuantityTx(tx, operatorID, sourceBatchID, models.ChangeTypeTransferOut, -int(quantity), reasonOut, now)
 | 
						||
		if err != nil {
 | 
						||
			return fmt.Errorf("更新源猪群 %d 数量失败: %w", sourceBatchID, err)
 | 
						||
		}
 | 
						||
 | 
						||
		// 3.2 记录目标猪群数量增加
 | 
						||
		reasonIn := fmt.Sprintf("跨群调栏: %d头猪从批次 %d 调入。备注: %s", quantity, sourceBatchID, remarks)
 | 
						||
		err = s.updatePigBatchQuantityTx(tx, operatorID, destBatchID, models.ChangeTypeTransferIn, int(quantity), reasonIn, now)
 | 
						||
		if err != nil {
 | 
						||
			return fmt.Errorf("更新目标猪群 %d 数量失败: %w", destBatchID, err)
 | 
						||
		}
 | 
						||
 | 
						||
		return nil
 | 
						||
	})
 | 
						||
}
 | 
						||
 | 
						||
// AssignEmptyPensToBatch 为猪群分配空栏
 | 
						||
func (s *pigBatchService) AssignEmptyPensToBatch(batchID uint, penIDs []uint, operatorID uint) error {
 | 
						||
	return s.uow.ExecuteInTransaction(func(tx *gorm.DB) error {
 | 
						||
		// 1. 验证猪批次是否存在且活跃
 | 
						||
		pigBatch, err := s.pigBatchRepo.GetPigBatchByIDTx(tx, batchID)
 | 
						||
		if err != nil {
 | 
						||
			if errors.Is(err, gorm.ErrRecordNotFound) {
 | 
						||
				return ErrPigBatchNotFound
 | 
						||
			}
 | 
						||
			return fmt.Errorf("获取猪批次信息失败: %w", err)
 | 
						||
		}
 | 
						||
		if !pigBatch.IsActive() {
 | 
						||
			return ErrPigBatchNotActive
 | 
						||
		}
 | 
						||
 | 
						||
		// 2. 遍历并校验每一个待分配的猪栏
 | 
						||
		for _, penID := range penIDs {
 | 
						||
			pen, err := s.transferSvc.GetPenByID(tx, penID)
 | 
						||
			if err != nil {
 | 
						||
				if errors.Is(err, gorm.ErrRecordNotFound) {
 | 
						||
					return fmt.Errorf("猪栏 %d 不存在: %w", penID, ErrPenNotFound)
 | 
						||
				}
 | 
						||
				return fmt.Errorf("获取猪栏 %d 信息失败: %w", penID, err)
 | 
						||
			}
 | 
						||
 | 
						||
			// 核心业务规则:校验猪栏是否完全空闲
 | 
						||
			if pen.Status != models.PenStatusEmpty {
 | 
						||
				return fmt.Errorf("猪栏 %s 状态不为空 (%s),无法分配", pen.PenNumber, pen.Status)
 | 
						||
			}
 | 
						||
			if pen.PigBatchID != nil {
 | 
						||
				return fmt.Errorf("猪栏 %s 已被其他批次 %d 占用,无法分配", pen.PenNumber, *pen.PigBatchID)
 | 
						||
			}
 | 
						||
 | 
						||
			// 3. 更新猪栏的归属
 | 
						||
			updates := map[string]interface{}{
 | 
						||
				"pig_batch_id": &batchID,
 | 
						||
				"status":       models.PenStatusOccupied,
 | 
						||
			}
 | 
						||
			if err := s.transferSvc.UpdatePenFields(tx, penID, updates); err != nil {
 | 
						||
				return fmt.Errorf("分配猪栏 %d 失败: %w", penID, err)
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		return nil
 | 
						||
	})
 | 
						||
}
 | 
						||
 | 
						||
// MovePigsIntoPen 将猪只从“虚拟库存”移入指定猪栏
 | 
						||
func (s *pigBatchService) MovePigsIntoPen(batchID uint, toPenID uint, quantity int, operatorID uint, remarks string) error {
 | 
						||
	if quantity <= 0 {
 | 
						||
		return errors.New("迁移数量必须大于零")
 | 
						||
	}
 | 
						||
 | 
						||
	return s.uow.ExecuteInTransaction(func(tx *gorm.DB) error {
 | 
						||
		// 1. 验证猪批次是否存在且活跃
 | 
						||
		pigBatch, err := s.pigBatchRepo.GetPigBatchByIDTx(tx, batchID)
 | 
						||
		if err != nil {
 | 
						||
			if errors.Is(err, gorm.ErrRecordNotFound) {
 | 
						||
				return ErrPigBatchNotFound
 | 
						||
			}
 | 
						||
			return fmt.Errorf("获取猪批次信息失败: %w", err)
 | 
						||
		}
 | 
						||
		if !pigBatch.IsActive() {
 | 
						||
			return ErrPigBatchNotActive
 | 
						||
		}
 | 
						||
 | 
						||
		// 2. 校验目标猪栏
 | 
						||
		toPen, err := s.transferSvc.GetPenByID(tx, toPenID)
 | 
						||
		if err != nil {
 | 
						||
			if errors.Is(err, gorm.ErrRecordNotFound) {
 | 
						||
				return fmt.Errorf("目标猪栏 %d 不存在: %w", toPenID, ErrPenNotFound)
 | 
						||
			}
 | 
						||
			return fmt.Errorf("获取目标猪栏 %d 信息失败: %w", toPenID, err)
 | 
						||
		}
 | 
						||
 | 
						||
		// 校验目标猪栏的归属和状态
 | 
						||
		if toPen.PigBatchID == nil {
 | 
						||
			return fmt.Errorf("目标猪栏 %s 不属于当前批次 %s", toPen.PenNumber, batchID)
 | 
						||
		}
 | 
						||
		if toPen.PigBatchID != nil && *toPen.PigBatchID != batchID {
 | 
						||
			return fmt.Errorf("目标猪栏 %s 已被其他批次 %d 占用,无法移入", toPen.PenNumber, *toPen.PigBatchID)
 | 
						||
		}
 | 
						||
 | 
						||
		// 3. 校验猪群中有足够的“未分配”猪只
 | 
						||
		currentBatchTotal, err := s.getCurrentPigQuantityTx(tx, batchID)
 | 
						||
		if err != nil {
 | 
						||
			return fmt.Errorf("获取猪群 %d 当前总数量失败: %w", batchID, err)
 | 
						||
		}
 | 
						||
 | 
						||
		// 获取该批次下所有猪栏的当前总存栏数
 | 
						||
		totalPigsInPens, err := s.transferSvc.GetTotalPigsInPensForBatchTx(tx, batchID)
 | 
						||
		if err != nil {
 | 
						||
			return fmt.Errorf("计算猪群 %d 下属猪栏总存栏失败: %w", batchID, err)
 | 
						||
		}
 | 
						||
 | 
						||
		unassignedPigs := currentBatchTotal - totalPigsInPens
 | 
						||
		if unassignedPigs < quantity {
 | 
						||
			return fmt.Errorf("猪群 %d 未分配猪只不足,当前未分配 %d 头,需要移入 %d 头", batchID, unassignedPigs, quantity)
 | 
						||
		}
 | 
						||
 | 
						||
		// 4. 记录转移日志
 | 
						||
		logIn := &models.PigTransferLog{
 | 
						||
			TransferTime: time.Now(),
 | 
						||
			PigBatchID:   batchID,
 | 
						||
			PenID:        toPenID,
 | 
						||
			Quantity:     quantity,                       // 调入为正数
 | 
						||
			Type:         models.PigTransferTypeInternal, // 首次入栏
 | 
						||
			OperatorID:   operatorID,
 | 
						||
			Remarks:      remarks,
 | 
						||
		}
 | 
						||
		if err := s.transferSvc.LogTransfer(tx, logIn); err != nil {
 | 
						||
			return fmt.Errorf("记录入栏日志失败: %w", err)
 | 
						||
		}
 | 
						||
 | 
						||
		return nil
 | 
						||
	})
 | 
						||
}
 | 
						||
 | 
						||
// ReclassifyPenToNewBatch 连猪带栏,整体划拨到另一个猪群
 | 
						||
func (s *pigBatchService) ReclassifyPenToNewBatch(fromBatchID uint, toBatchID uint, penID uint, operatorID uint, remarks string) error {
 | 
						||
	if fromBatchID == toBatchID {
 | 
						||
		return errors.New("源猪群和目标猪群不能相同")
 | 
						||
	}
 | 
						||
 | 
						||
	return s.uow.ExecuteInTransaction(func(tx *gorm.DB) error {
 | 
						||
		// 1. 核心业务规则校验
 | 
						||
		// 1.1 校验猪群存在
 | 
						||
		fromBatch, err := s.pigBatchRepo.GetPigBatchByIDTx(tx, fromBatchID)
 | 
						||
		if err != nil {
 | 
						||
			if errors.Is(err, gorm.ErrRecordNotFound) {
 | 
						||
				return fmt.Errorf("源猪群 %d 不存在", fromBatchID)
 | 
						||
			}
 | 
						||
			return fmt.Errorf("获取源猪群信息失败: %w", err)
 | 
						||
		}
 | 
						||
		toBatch, err := s.pigBatchRepo.GetPigBatchByIDTx(tx, toBatchID)
 | 
						||
		if err != nil {
 | 
						||
			if errors.Is(err, gorm.ErrRecordNotFound) {
 | 
						||
				return fmt.Errorf("目标猪群 %d 不存在", toBatchID)
 | 
						||
			}
 | 
						||
			return fmt.Errorf("获取目标猪群信息失败: %w", err)
 | 
						||
		}
 | 
						||
 | 
						||
		// 1.2 校验猪栏归属
 | 
						||
		pen, err := s.transferSvc.GetPenByID(tx, penID)
 | 
						||
		if err != nil {
 | 
						||
			if errors.Is(err, gorm.ErrRecordNotFound) {
 | 
						||
				return fmt.Errorf("猪栏 %d 不存在: %w", penID, ErrPenNotFound)
 | 
						||
			}
 | 
						||
			return fmt.Errorf("获取猪栏 %d 信息失败: %w", penID, err)
 | 
						||
		}
 | 
						||
		if pen.PigBatchID == nil || *pen.PigBatchID != fromBatchID {
 | 
						||
			return fmt.Errorf("猪栏 %v 不属于源猪群 %v,无法划拨", pen.PenNumber, fromBatch.BatchNumber)
 | 
						||
		}
 | 
						||
 | 
						||
		// 2. 获取猪栏当前存栏数
 | 
						||
		quantity, err := s.transferSvc.GetCurrentPigsInPen(tx, penID)
 | 
						||
		if err != nil {
 | 
						||
			return fmt.Errorf("获取猪栏 %v 存栏数失败: %w", pen.PenNumber, err)
 | 
						||
		}
 | 
						||
 | 
						||
		// 3. 更新猪栏的归属
 | 
						||
		updates := map[string]interface{}{
 | 
						||
			"pig_batch_id": &toBatchID,
 | 
						||
		}
 | 
						||
		if err := s.transferSvc.UpdatePenFields(tx, penID, updates); err != nil {
 | 
						||
			return fmt.Errorf("更新猪栏 %v 归属失败: %w", pen.PenNumber, err)
 | 
						||
		}
 | 
						||
		// 如果猪栏是空的,则只进行归属变更,不影响猪群数量
 | 
						||
		if quantity == 0 {
 | 
						||
			return nil // 空栏划拨,不涉及猪只数量变更
 | 
						||
		}
 | 
						||
 | 
						||
		// 4. 记录猪只从旧批次“迁出”的猪栏日志
 | 
						||
		correlationID := uuid.New().String()
 | 
						||
		logOut := &models.PigTransferLog{
 | 
						||
			TransferTime:  time.Now(),
 | 
						||
			PigBatchID:    fromBatchID,
 | 
						||
			PenID:         penID,
 | 
						||
			Quantity:      -quantity, // 迁出为负数
 | 
						||
			Type:          models.PigTransferTypeCrossBatch,
 | 
						||
			CorrelationID: correlationID,
 | 
						||
			OperatorID:    operatorID,
 | 
						||
			Remarks:       fmt.Sprintf("整栏划拨迁出: %d头猪从批次 %v 随猪栏 %v 划拨至批次 %v。备注: %s", quantity, fromBatch.BatchNumber, pen.PenNumber, toBatch.BatchNumber, remarks),
 | 
						||
		}
 | 
						||
		if err := s.transferSvc.LogTransfer(tx, logOut); err != nil {
 | 
						||
			return fmt.Errorf("记录猪栏 %d 迁出日志失败: %w", penID, err)
 | 
						||
		}
 | 
						||
 | 
						||
		// 5. 记录猪只到新批次“迁入”的猪栏日志
 | 
						||
		logIn := &models.PigTransferLog{
 | 
						||
			TransferTime:  time.Now(),
 | 
						||
			PigBatchID:    toBatchID,
 | 
						||
			PenID:         penID,
 | 
						||
			Quantity:      quantity, // 迁入为正数
 | 
						||
			Type:          models.PigTransferTypeCrossBatch,
 | 
						||
			CorrelationID: correlationID,
 | 
						||
			OperatorID:    operatorID,
 | 
						||
			Remarks:       fmt.Sprintf("整栏划拨迁入: %v头猪随猪栏 %v 从批次 %v 划拨入。备注: %s", quantity, fromBatch.BatchNumber, pen.PenNumber, remarks),
 | 
						||
		}
 | 
						||
		if err := s.transferSvc.LogTransfer(tx, logIn); err != nil {
 | 
						||
			return fmt.Errorf("记录猪栏 %d 迁入日志失败: %w", penID, err)
 | 
						||
		}
 | 
						||
 | 
						||
		// 7. 通过创建批次日志来修改猪群总数,确保数据可追溯
 | 
						||
		now := time.Now()
 | 
						||
		// 7.1 记录源猪群数量减少
 | 
						||
		reasonOutBatch := fmt.Sprintf("整栏划拨: %d头猪随猪栏 %v 从批次 %v 划拨至批次 %v。备注: %s", quantity, pen.PenNumber, fromBatch.BatchNumber, toBatchID, remarks)
 | 
						||
		err = s.updatePigBatchQuantityTx(tx, operatorID, fromBatchID, models.ChangeTypeTransferOut, -quantity, reasonOutBatch, now)
 | 
						||
		if err != nil {
 | 
						||
			return fmt.Errorf("更新源猪群 %v 数量失败: %w", fromBatch.BatchNumber, err)
 | 
						||
		}
 | 
						||
 | 
						||
		// 7.2 记录目标猪群数量增加
 | 
						||
		reasonInBatch := fmt.Sprintf("整栏划拨: %v头猪随猪栏 %v 从批次 %v 划拨入。备注: %s", quantity, pen.PenNumber, fromBatch.BatchNumber, remarks)
 | 
						||
		err = s.updatePigBatchQuantityTx(tx, operatorID, toBatchID, models.ChangeTypeTransferIn, quantity, reasonInBatch, now)
 | 
						||
		if err != nil {
 | 
						||
			return fmt.Errorf("更新目标猪群 %v 数量失败: %w", toBatch.BatchNumber, err)
 | 
						||
		}
 | 
						||
 | 
						||
		return nil
 | 
						||
	})
 | 
						||
}
 | 
						||
 | 
						||
func (s *pigBatchService) RemoveEmptyPenFromBatch(batchID uint, penID uint) error {
 | 
						||
	return s.uow.ExecuteInTransaction(func(tx *gorm.DB) error {
 | 
						||
		// 1. 检查猪批次是否存在且活跃
 | 
						||
		batch, err := s.pigBatchRepo.GetPigBatchByIDTx(tx, batchID)
 | 
						||
		if err != nil {
 | 
						||
			if errors.Is(err, gorm.ErrRecordNotFound) {
 | 
						||
				return ErrPigBatchNotFound
 | 
						||
			}
 | 
						||
			return err
 | 
						||
		}
 | 
						||
		if !batch.IsActive() {
 | 
						||
			return ErrPigBatchNotActive
 | 
						||
		}
 | 
						||
 | 
						||
		// 2. 检查猪栏是否存在
 | 
						||
		pen, err := s.transferSvc.GetPenByID(tx, penID)
 | 
						||
		if err != nil {
 | 
						||
			if errors.Is(err, gorm.ErrRecordNotFound) {
 | 
						||
				return ErrPenNotFound
 | 
						||
			}
 | 
						||
			return err
 | 
						||
		}
 | 
						||
 | 
						||
		// 3. 检查猪栏是否与当前批次关联
 | 
						||
		if pen.PigBatchID == nil || *pen.PigBatchID != batchID {
 | 
						||
			return ErrPenNotAssociatedWithBatch
 | 
						||
		}
 | 
						||
 | 
						||
		// 4. 检查猪栏是否为空
 | 
						||
		pigsInPen, err := s.transferSvc.GetCurrentPigsInPen(tx, penID)
 | 
						||
		if err != nil {
 | 
						||
			return err
 | 
						||
		}
 | 
						||
		if pigsInPen > 0 {
 | 
						||
			return ErrPenNotEmpty
 | 
						||
		}
 | 
						||
 | 
						||
		// 5. 释放猪栏 (将 pig_batch_id 设置为 nil,状态设置为空闲)
 | 
						||
		if err := s.transferSvc.ReleasePen(tx, penID); err != nil {
 | 
						||
			return err
 | 
						||
		}
 | 
						||
		return nil
 | 
						||
	})
 | 
						||
}
 | 
						||
 | 
						||
func (s *pigBatchService) GetCurrentPigsInPen(penID uint) (int, error) {
 | 
						||
	var currentPigs int
 | 
						||
	err := s.uow.ExecuteInTransaction(func(tx *gorm.DB) error {
 | 
						||
		pigs, err := s.transferSvc.GetCurrentPigsInPen(tx, penID)
 | 
						||
		if err != nil {
 | 
						||
			return err
 | 
						||
		}
 | 
						||
		currentPigs = pigs
 | 
						||
		return nil
 | 
						||
	})
 | 
						||
	return currentPigs, err
 | 
						||
}
 | 
						||
 | 
						||
// GetTotalPigsInPensForBatch 实现了获取指定猪群下所有猪栏的当前总存栏数的逻辑。
 | 
						||
func (s *pigBatchService) GetTotalPigsInPensForBatch(batchID uint) (int, error) {
 | 
						||
	var totalPigs int
 | 
						||
	err := s.uow.ExecuteInTransaction(func(tx *gorm.DB) error {
 | 
						||
		pigs, err := s.transferSvc.GetTotalPigsInPensForBatchTx(tx, batchID)
 | 
						||
		if err != nil {
 | 
						||
			return err
 | 
						||
		}
 | 
						||
		totalPigs = pigs
 | 
						||
		return nil
 | 
						||
	})
 | 
						||
	return totalPigs, err
 | 
						||
}
 |