猪群管理聚合服务 增加调栏管理
This commit is contained in:
63
internal/domain/pig/pen_transfer_manager.go
Normal file
63
internal/domain/pig/pen_transfer_manager.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package pig
|
||||
|
||||
import (
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// PenTransferManager 定义了与猪只位置转移相关的底层数据库操作。
|
||||
// 它是一个内部服务,被主服务 PigBatchService 调用。
|
||||
type PenTransferManager interface {
|
||||
// LogTransfer 在数据库中创建一条猪只迁移日志。
|
||||
LogTransfer(tx *gorm.DB, log *models.PigTransferLog) error
|
||||
|
||||
// GetPenByID 用于获取猪栏的详细信息,供上层服务进行业务校验。
|
||||
// 注意: 此方法依赖于您在 PenRepository 中添加对应的 GetPenByIDTx 方法。
|
||||
GetPenByID(tx *gorm.DB, penID uint) (*models.Pen, error)
|
||||
|
||||
// GetPensByBatchID 获取一个猪群当前关联的所有猪栏。
|
||||
// 注意: 此方法依赖于您在 PenRepository 中添加对应的 GetPensByBatchIDTx 方法。
|
||||
GetPensByBatchID(tx *gorm.DB, batchID uint) ([]*models.Pen, error)
|
||||
|
||||
// UpdatePenFields 更新一个猪栏的指定字段。
|
||||
// 注意: 此方法依赖于您在 PenRepository 中添加对应的 UpdatePenFieldsTx 方法。
|
||||
UpdatePenFields(tx *gorm.DB, penID uint, updates map[string]interface{}) error
|
||||
}
|
||||
|
||||
// penTransferManager 是 PenTransferManager 接口的具体实现。
|
||||
// 它作为调栏管理器,处理底层的数据库交互。
|
||||
type penTransferManager struct {
|
||||
penRepo repository.PenRepository
|
||||
}
|
||||
|
||||
// NewPenTransferManager 是 penTransferManager 的构造函数。
|
||||
func NewPenTransferManager(penRepo repository.PenRepository) PenTransferManager {
|
||||
return &penTransferManager{
|
||||
penRepo: penRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// LogTransfer 实现了在数据库中创建迁移日志的逻辑。
|
||||
func (s *penTransferManager) LogTransfer(tx *gorm.DB, log *models.PigTransferLog) error {
|
||||
// 直接使用事务对象创建记录。
|
||||
return tx.Create(log).Error
|
||||
}
|
||||
|
||||
// GetPenByID 实现了获取猪栏信息的逻辑。
|
||||
// 注意: 此处调用了一个假设存在的方法 GetPenByIDTx。
|
||||
func (s *penTransferManager) GetPenByID(tx *gorm.DB, penID uint) (*models.Pen, error) {
|
||||
return s.penRepo.GetPenByIDTx(tx, penID)
|
||||
}
|
||||
|
||||
// GetPensByBatchID 实现了获取猪群关联猪栏列表的逻辑。
|
||||
// 注意: 此处调用了一个假设存在的方法 GetPensByBatchIDTx。
|
||||
func (s *penTransferManager) GetPensByBatchID(tx *gorm.DB, batchID uint) ([]*models.Pen, error) {
|
||||
return s.penRepo.GetPensByBatchIDTx(tx, batchID)
|
||||
}
|
||||
|
||||
// UpdatePenFields 实现了更新猪栏字段的逻辑。
|
||||
// 注意: 此处调用了一个假设存在的方法 UpdatePenFieldsTx。
|
||||
func (s *penTransferManager) UpdatePenFields(tx *gorm.DB, penID uint, updates map[string]interface{}) error {
|
||||
return s.penRepo.UpdatePenFieldsTx(tx, penID, updates)
|
||||
}
|
||||
@@ -21,6 +21,8 @@ var (
|
||||
ErrPenStatusInvalidForAllocation = errors.New("猪栏状态不允许分配")
|
||||
// ErrPenNotFound 表示猪栏不存在
|
||||
ErrPenNotFound = errors.New("指定的猪栏不存在")
|
||||
// ErrPenNotAssociatedWithBatch 表示猪栏未与该批次关联
|
||||
ErrPenNotAssociatedWithBatch = errors.New("猪栏未与该批次关联")
|
||||
)
|
||||
|
||||
// --- 领域服务接口 ---
|
||||
@@ -28,6 +30,12 @@ var (
|
||||
// PigBatchService 定义了猪批次管理的核心业务逻辑接口。
|
||||
// 它抽象了所有与猪批次相关的操作,使得应用层可以依赖于此接口,而不是具体的实现。
|
||||
type PigBatchService interface {
|
||||
// TransferPigsWithinBatch 处理同一个猪群内部的调栏业务。
|
||||
TransferPigsWithinBatch(batchID uint, fromPenID uint, toPenID uint, quantity uint) error
|
||||
|
||||
// TransferPigsAcrossBatches 处理跨猪群的调栏业务。
|
||||
TransferPigsAcrossBatches(sourceBatchID uint, destBatchID uint, fromPenID uint, toPenID uint, quantity uint) error
|
||||
|
||||
// CreatePigBatch 创建一个新的猪批次。
|
||||
CreatePigBatch(batch *models.PigBatch) (*models.PigBatch, error)
|
||||
|
||||
|
||||
@@ -3,33 +3,35 @@ package pig
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// --- 领域服务实现 ---
|
||||
|
||||
// pigBatchService 是 PigBatchService 接口的具体实现。
|
||||
// 它封装了业务逻辑所需的所有依赖,如数据库仓库和工作单元。
|
||||
// 它作为猪群领域的主服务,封装了所有业务逻辑。
|
||||
type pigBatchService struct {
|
||||
pigBatchRepo repository.PigBatchRepository // 猪批次仓库
|
||||
pigFarmRepo repository.PigFarmRepository // 猪场资产仓库 (包含猪栏操作)
|
||||
uow repository.UnitOfWork // 工作单元,用于管理事务
|
||||
transferSvc PenTransferManager // 调栏子服务
|
||||
}
|
||||
|
||||
// NewPigBatchService 是 pigBatchService 的构造函数。
|
||||
// 它通过依赖注入的方式,创建并返回一个 PigBatchService 接口的实例。
|
||||
func NewPigBatchService(
|
||||
pigBatchRepo repository.PigBatchRepository,
|
||||
pigFarmRepo repository.PigFarmRepository,
|
||||
uow repository.UnitOfWork,
|
||||
transferSvc PenTransferManager,
|
||||
) PigBatchService {
|
||||
return &pigBatchService{
|
||||
pigBatchRepo: pigBatchRepo,
|
||||
pigFarmRepo: pigFarmRepo,
|
||||
uow: uow,
|
||||
transferSvc: transferSvc,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,11 +97,13 @@ func (s *pigBatchService) ListPigBatches(isActive *bool) ([]*models.PigBatch, er
|
||||
}
|
||||
|
||||
// UpdatePigBatchPens 实现了在事务中更新猪批次关联猪栏的复杂逻辑。
|
||||
// 它通过调用底层的 PenTransferManager 来执行数据库操作,从而保持了职责的清晰。
|
||||
func (s *pigBatchService) UpdatePigBatchPens(batchID uint, desiredPenIDs []uint) error {
|
||||
// 使用工作单元来确保操作的原子性
|
||||
return s.uow.ExecuteInTransaction(func(tx *gorm.DB) error {
|
||||
// 1. 验证猪批次是否存在且活跃
|
||||
pigBatch, err := s.pigFarmRepo.GetPigBatchByIDTx(tx, batchID)
|
||||
// 注意: 此处依赖一个假设存在的 pigBatchRepo.GetPigBatchByIDTx 方法
|
||||
pigBatch, err := s.pigBatchRepo.GetPigBatchByIDTx(tx, batchID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return ErrPigBatchNotFound
|
||||
@@ -111,8 +115,8 @@ func (s *pigBatchService) UpdatePigBatchPens(batchID uint, desiredPenIDs []uint)
|
||||
return ErrPigBatchNotActive
|
||||
}
|
||||
|
||||
// 2. 获取当前关联的猪栏
|
||||
currentPens, err := s.pigFarmRepo.GetPensByBatchID(tx, batchID)
|
||||
// 2. 获取当前关联的猪栏 (通过子服务)
|
||||
currentPens, err := s.transferSvc.GetPensByBatchID(tx, batchID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取当前关联猪栏失败: %w", err)
|
||||
}
|
||||
@@ -120,7 +124,7 @@ func (s *pigBatchService) UpdatePigBatchPens(batchID uint, desiredPenIDs []uint)
|
||||
currentPenMap := make(map[uint]models.Pen)
|
||||
currentPenIDsSet := make(map[uint]struct{})
|
||||
for _, pen := range currentPens {
|
||||
currentPenMap[pen.ID] = pen
|
||||
currentPenMap[pen.ID] = *pen
|
||||
currentPenIDsSet[pen.ID] = struct{}{}
|
||||
}
|
||||
|
||||
@@ -155,14 +159,15 @@ func (s *pigBatchService) UpdatePigBatchPens(batchID uint, desiredPenIDs []uint)
|
||||
updates["status"] = models.PenStatusEmpty
|
||||
}
|
||||
|
||||
if err := s.pigFarmRepo.UpdatePenFields(tx, penID, updates); err != nil {
|
||||
if err := s.transferSvc.UpdatePenFields(tx, penID, updates); err != nil {
|
||||
return fmt.Errorf("移除猪栏 %d 失败: %w", penID, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 处理添加猪栏的逻辑
|
||||
for _, penID := range pensToAdd {
|
||||
actualPen, err := s.pigFarmRepo.GetPenByIDTx(tx, penID)
|
||||
// 通过子服务获取猪栏信息
|
||||
actualPen, err := s.transferSvc.GetPenByID(tx, penID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fmt.Errorf("猪栏 %d 不存在: %w", penID, ErrPenNotFound)
|
||||
@@ -182,7 +187,7 @@ func (s *pigBatchService) UpdatePigBatchPens(batchID uint, desiredPenIDs []uint)
|
||||
"pig_batch_id": &batchID,
|
||||
"status": models.PenStatusOccupied,
|
||||
}
|
||||
if err := s.pigFarmRepo.UpdatePenFields(tx, penID, updates); err != nil {
|
||||
if err := s.transferSvc.UpdatePenFields(tx, penID, updates); err != nil {
|
||||
return fmt.Errorf("添加猪栏 %d 失败: %w", penID, err)
|
||||
}
|
||||
}
|
||||
@@ -190,3 +195,128 @@ func (s *pigBatchService) UpdatePigBatchPens(batchID uint, desiredPenIDs []uint)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// --- 新增的调栏业务实现 ---
|
||||
|
||||
// executeTransferAndLog 是一个私有辅助方法,用于封装创建和记录迁移日志的通用逻辑。
|
||||
func (s *pigBatchService) executeTransferAndLog(tx *gorm.DB, fromBatchID, toBatchID, fromPenID, toPenID uint, quantity int, transferType string) error {
|
||||
// 1. 生成关联ID
|
||||
correlationID := uuid.New().String()
|
||||
|
||||
// 2. 创建调出日志
|
||||
logOut := &models.PigTransferLog{
|
||||
TransferTime: time.Now(),
|
||||
PigBatchID: fromBatchID,
|
||||
PenID: fromPenID,
|
||||
Quantity: -quantity, // 调出为负数
|
||||
Type: transferType,
|
||||
CorrelationID: correlationID,
|
||||
}
|
||||
|
||||
// 3. 创建调入日志
|
||||
logIn := &models.PigTransferLog{
|
||||
TransferTime: time.Now(),
|
||||
PigBatchID: toBatchID,
|
||||
PenID: toPenID,
|
||||
Quantity: quantity, // 调入为正数
|
||||
Type: transferType,
|
||||
CorrelationID: correlationID,
|
||||
}
|
||||
|
||||
// 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) 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), "群内调栏")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 群内调栏,猪群总数不变
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// TransferPigsAcrossBatches 实现了跨猪群的调栏业务。
|
||||
func (s *pigBatchService) TransferPigsAcrossBatches(sourceBatchID uint, destBatchID uint, fromPenID uint, toPenID uint, quantity uint) error {
|
||||
if sourceBatchID == destBatchID {
|
||||
return errors.New("源猪群和目标猪群不能相同")
|
||||
}
|
||||
if quantity == 0 {
|
||||
return errors.New("迁移数量不能为零")
|
||||
}
|
||||
|
||||
return s.uow.ExecuteInTransaction(func(tx *gorm.DB) error {
|
||||
// 1. 核心业务规则校验
|
||||
sourceBatch, err := s.pigBatchRepo.GetPigBatchByID(sourceBatchID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取源猪群信息失败: %w", err)
|
||||
}
|
||||
destBatch, err := s.pigBatchRepo.GetPigBatchByID(destBatchID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取目标猪群信息失败: %w", err)
|
||||
}
|
||||
|
||||
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), "跨群调栏")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 修改本聚合的数据(猪群总数)
|
||||
sourceBatch.InitialCount -= int(quantity)
|
||||
destBatch.InitialCount += int(quantity)
|
||||
|
||||
if _, _, err := s.pigBatchRepo.UpdatePigBatch(sourceBatch); err != nil {
|
||||
return fmt.Errorf("更新源猪群数量失败: %w", err)
|
||||
}
|
||||
if _, _, err := s.pigBatchRepo.UpdatePigBatch(destBatch); err != nil {
|
||||
return fmt.Errorf("更新目标猪群数量失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user