猪群管理聚合服务

This commit is contained in:
2025-10-05 16:37:12 +08:00
parent 740e14e6cc
commit 6d080d250d
4 changed files with 290 additions and 186 deletions

View File

@@ -0,0 +1,192 @@
package pig
import (
"errors"
"fmt"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
"gorm.io/gorm"
)
// --- 领域服务实现 ---
// pigBatchService 是 PigBatchService 接口的具体实现。
// 它封装了业务逻辑所需的所有依赖,如数据库仓库和工作单元。
type pigBatchService struct {
pigBatchRepo repository.PigBatchRepository // 猪批次仓库
pigFarmRepo repository.PigFarmRepository // 猪场资产仓库 (包含猪栏操作)
uow repository.UnitOfWork // 工作单元,用于管理事务
}
// NewPigBatchService 是 pigBatchService 的构造函数。
// 它通过依赖注入的方式,创建并返回一个 PigBatchService 接口的实例。
func NewPigBatchService(
pigBatchRepo repository.PigBatchRepository,
pigFarmRepo repository.PigFarmRepository,
uow repository.UnitOfWork,
) PigBatchService {
return &pigBatchService{
pigBatchRepo: pigBatchRepo,
pigFarmRepo: pigFarmRepo,
uow: uow,
}
}
// CreatePigBatch 实现了创建猪批次的逻辑。
func (s *pigBatchService) CreatePigBatch(batch *models.PigBatch) (*models.PigBatch, error) {
// 业务规则可以在这里添加,例如检查批次号是否唯一等
return s.pigBatchRepo.CreatePigBatch(batch)
}
// GetPigBatch 实现了获取单个猪批次的逻辑。
func (s *pigBatchService) GetPigBatch(id uint) (*models.PigBatch, error) {
batch, err := s.pigBatchRepo.GetPigBatchByID(id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrPigBatchNotFound
}
return nil, err
}
return batch, nil
}
// UpdatePigBatch 实现了更新猪批次的逻辑。
func (s *pigBatchService) UpdatePigBatch(batch *models.PigBatch) (*models.PigBatch, error) {
// 可以在这里添加更新前的业务校验
updatedBatch, rowsAffected, err := s.pigBatchRepo.UpdatePigBatch(batch)
if err != nil {
return nil, err
}
if rowsAffected == 0 {
return nil, ErrPigBatchNotFound // 如果没有行被更新,可能意味着记录不存在
}
return updatedBatch, nil
}
// DeletePigBatch 实现了删除猪批次的逻辑,并包含业务规则校验。
func (s *pigBatchService) DeletePigBatch(id uint) error {
// 1. 获取猪批次信息
batch, err := s.GetPigBatch(id) // 复用 GetPigBatch 方法
if err != nil {
return err // GetPigBatch 已经处理了 ErrRecordNotFound 的情况
}
// 2. 核心业务规则:检查猪批次是否为活跃状态
if batch.IsActive() {
return ErrPigBatchActive // 如果活跃,则不允许删除
}
// 3. 执行删除
rowsAffected, err := s.pigBatchRepo.DeletePigBatch(id)
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrPigBatchNotFound
}
return nil
}
// ListPigBatches 实现了批量查询猪批次的逻辑。
func (s *pigBatchService) ListPigBatches(isActive *bool) ([]*models.PigBatch, error) {
return s.pigBatchRepo.ListPigBatches(isActive)
}
// UpdatePigBatchPens 实现了在事务中更新猪批次关联猪栏的复杂逻辑。
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)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return ErrPigBatchNotFound
}
return fmt.Errorf("获取猪批次信息失败: %w", err)
}
if !pigBatch.IsActive() {
return ErrPigBatchNotActive
}
// 2. 获取当前关联的猪栏
currentPens, err := s.pigFarmRepo.GetPensByBatchID(tx, batchID)
if err != nil {
return fmt.Errorf("获取当前关联猪栏失败: %w", err)
}
currentPenMap := make(map[uint]models.Pen)
currentPenIDsSet := make(map[uint]struct{})
for _, pen := range currentPens {
currentPenMap[pen.ID] = pen
currentPenIDsSet[pen.ID] = struct{}{}
}
// 3. 构建期望猪栏ID集合
desiredPenIDsSet := make(map[uint]struct{})
for _, penID := range desiredPenIDs {
desiredPenIDsSet[penID] = struct{}{}
}
// 4. 计算需要添加和移除的猪栏
var pensToRemove []uint
for penID := range currentPenIDsSet {
if _, found := desiredPenIDsSet[penID]; !found {
pensToRemove = append(pensToRemove, penID)
}
}
var pensToAdd []uint
for _, penID := range desiredPenIDs {
if _, found := currentPenIDsSet[penID]; !found {
pensToAdd = append(pensToAdd, penID)
}
}
// 5. 处理移除猪栏的逻辑
for _, penID := range pensToRemove {
currentPen := currentPenMap[penID]
updates := make(map[string]interface{})
updates["pig_batch_id"] = nil
if currentPen.Status == models.PenStatusOccupied {
updates["status"] = models.PenStatusEmpty
}
if err := s.pigFarmRepo.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)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("猪栏 %d 不存在: %w", penID, ErrPenNotFound)
}
return fmt.Errorf("获取猪栏 %d 信息失败: %w", penID, err)
}
// 核心业务规则:校验猪栏是否可被分配
if actualPen.Status != models.PenStatusEmpty {
return fmt.Errorf("猪栏 %s 状态为 %s无法分配: %w", actualPen.PenNumber, actualPen.Status, ErrPenStatusInvalidForAllocation)
}
if actualPen.PigBatchID != nil {
return fmt.Errorf("猪栏 %s 已被其他批次 %d 使用: %w", actualPen.PenNumber, *actualPen.PigBatchID, ErrPenOccupiedByOtherBatch)
}
updates := map[string]interface{}{
"pig_batch_id": &batchID,
"status": models.PenStatusOccupied,
}
if err := s.pigFarmRepo.UpdatePenFields(tx, penID, updates); err != nil {
return fmt.Errorf("添加猪栏 %d 失败: %w", penID, err)
}
}
return nil
})
}