176 lines
6.0 KiB
Go
176 lines
6.0 KiB
Go
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"
|
||
)
|
||
|
||
// PigPenTransferManager 定义了与猪只位置转移相关的底层数据库操作。
|
||
// 它是一个内部服务,被主服务 PigBatchService 调用。
|
||
type PigPenTransferManager interface {
|
||
// LogTransfer 在数据库中创建一条猪只迁移日志。
|
||
LogTransfer(tx *gorm.DB, log *models.PigTransferLog) error
|
||
|
||
// GetPenByID 用于获取猪栏的详细信息,供上层服务进行业务校验。
|
||
GetPenByID(tx *gorm.DB, penID uint) (*models.Pen, error)
|
||
|
||
// GetPensByBatchID 获取一个猪群当前关联的所有猪栏。
|
||
GetPensByBatchID(tx *gorm.DB, batchID uint) ([]*models.Pen, error)
|
||
|
||
// UpdatePenFields 更新一个猪栏的指定字段。
|
||
UpdatePenFields(tx *gorm.DB, penID uint, updates map[string]interface{}) error
|
||
|
||
// GetCurrentPigsInPen 通过汇总猪只迁移日志,计算给定猪栏中的当前猪只数量。
|
||
GetCurrentPigsInPen(tx *gorm.DB, penID uint) (int, error)
|
||
|
||
// GetTotalPigsInPensForBatchTx 计算指定猪群下所有猪栏的当前总存栏数
|
||
GetTotalPigsInPensForBatchTx(tx *gorm.DB, batchID uint) (int, error)
|
||
|
||
// ReleasePen 将猪栏的猪群归属移除,并将其状态标记为空闲。
|
||
ReleasePen(tx *gorm.DB, penID uint) error
|
||
}
|
||
|
||
// pigPenTransferManager 是 PigPenTransferManager 接口的具体实现。
|
||
// 它作为调栏管理器,处理底层的数据库交互。
|
||
type pigPenTransferManager struct {
|
||
penRepo repository.PigPenRepository
|
||
logRepo repository.PigTransferLogRepository
|
||
pigBatchRepo repository.PigBatchRepository
|
||
}
|
||
|
||
// NewPigPenTransferManager 是 pigPenTransferManager 的构造函数。
|
||
func NewPigPenTransferManager(penRepo repository.PigPenRepository, logRepo repository.PigTransferLogRepository, pigBatchRepo repository.PigBatchRepository) PigPenTransferManager {
|
||
return &pigPenTransferManager{
|
||
penRepo: penRepo,
|
||
logRepo: logRepo,
|
||
pigBatchRepo: pigBatchRepo,
|
||
}
|
||
}
|
||
|
||
// LogTransfer 实现了在数据库中创建迁移日志的逻辑。
|
||
func (s *pigPenTransferManager) LogTransfer(tx *gorm.DB, log *models.PigTransferLog) error {
|
||
return s.logRepo.CreatePigTransferLog(tx, log)
|
||
}
|
||
|
||
// GetPenByID 实现了获取猪栏信息的逻辑。
|
||
func (s *pigPenTransferManager) GetPenByID(tx *gorm.DB, penID uint) (*models.Pen, error) {
|
||
return s.penRepo.GetPenByIDTx(tx, penID)
|
||
}
|
||
|
||
// GetPensByBatchID 实现了获取猪群关联猪栏列表的逻辑。
|
||
func (s *pigPenTransferManager) GetPensByBatchID(tx *gorm.DB, batchID uint) ([]*models.Pen, error) {
|
||
return s.penRepo.GetPensByBatchIDTx(tx, batchID)
|
||
}
|
||
|
||
// UpdatePenFields 实现了更新猪栏字段的逻辑。
|
||
func (s *pigPenTransferManager) UpdatePenFields(tx *gorm.DB, penID uint, updates map[string]interface{}) error {
|
||
return s.penRepo.UpdatePenFieldsTx(tx, penID, updates)
|
||
}
|
||
|
||
// GetCurrentPigsInPen 实现了计算猪栏当前猪只数量的逻辑。
|
||
func (s *pigPenTransferManager) GetCurrentPigsInPen(tx *gorm.DB, penID uint) (int, error) {
|
||
// 1. 通过猪栏ID查出所属猪群信息
|
||
pen, err := s.penRepo.GetPenByIDTx(tx, penID)
|
||
if err != nil {
|
||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return 0, ErrPenNotFound
|
||
}
|
||
return 0, err
|
||
}
|
||
|
||
// 如果猪栏没有关联任何猪群,那么猪只数必为0
|
||
if pen.PigBatchID == nil || *pen.PigBatchID == 0 {
|
||
return 0, nil
|
||
}
|
||
currentBatchID := *pen.PigBatchID
|
||
|
||
// 2. 根据猪群ID获取猪群的起始日期
|
||
batch, err := s.pigBatchRepo.GetPigBatchByIDTx(tx, currentBatchID)
|
||
if err != nil {
|
||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return 0, ErrPigBatchNotFound
|
||
}
|
||
return 0, err
|
||
}
|
||
batchStartDate := batch.StartDate
|
||
|
||
// 3. 调用仓库方法,获取从猪群开始至今,该猪栏的所有倒序日志
|
||
logs, err := s.logRepo.GetLogsForPenSince(tx, penID, batchStartDate)
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
|
||
// 如果没有日志,猪只数为0
|
||
if len(logs) == 0 {
|
||
return 0, nil
|
||
}
|
||
|
||
// 4. 在内存中筛选出最后一段连续日志,并进行计算
|
||
var totalPigs int
|
||
// 再次确认当前猪群ID,以最新的日志为准,防止在极小时间窗口内猪栏被快速切换
|
||
latestBatchID := *pen.PigBatchID
|
||
|
||
for _, log := range logs {
|
||
// 一旦发现日志不属于最新的猪群,立即停止计算
|
||
if log.PigBatchID != latestBatchID {
|
||
break
|
||
}
|
||
totalPigs += log.Quantity
|
||
}
|
||
|
||
return totalPigs, nil
|
||
}
|
||
|
||
// GetTotalPigsInPensForBatchTx 计算指定猪群下所有猪栏的当前总存栏数
|
||
// 该方法通过遍历猪群下的每个猪栏,并调用 GetCurrentPigsInPen 来累加存栏数。
|
||
func (s *pigPenTransferManager) GetTotalPigsInPensForBatchTx(tx *gorm.DB, batchID uint) (int, error) {
|
||
// 1. 获取该批次下所有猪栏的列表
|
||
pensInBatch, err := s.GetPensByBatchID(tx, batchID)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("获取猪群 %d 下属猪栏失败: %w", batchID, err)
|
||
}
|
||
|
||
totalPigs := 0
|
||
// 2. 遍历每个猪栏,累加其存栏数
|
||
for _, pen := range pensInBatch {
|
||
pigsInPen, err := s.GetCurrentPigsInPen(tx, pen.ID)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("获取猪栏 %d 存栏数失败: %w", pen.ID, err)
|
||
}
|
||
totalPigs += pigsInPen
|
||
}
|
||
|
||
return totalPigs, nil
|
||
}
|
||
|
||
// ReleasePen 将猪栏的猪群归属移除,并将其状态标记为空闲。
|
||
// 此操作通常在猪栏被清空后调用。
|
||
func (s *pigPenTransferManager) ReleasePen(tx *gorm.DB, penID uint) error {
|
||
// 1. 获取猪栏信息
|
||
pen, err := s.penRepo.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)
|
||
}
|
||
|
||
// 2. 更新猪栏字段
|
||
// 将 pig_batch_id 设置为 nil (SQL NULL)
|
||
// 将 status 设置为 PenStatusEmpty
|
||
updates := map[string]interface{}{
|
||
"pig_batch_id": nil, // 使用 nil 来表示 SQL NULL
|
||
"status": models.PenStatusEmpty,
|
||
}
|
||
|
||
if err := s.penRepo.UpdatePenFieldsTx(tx, penID, updates); err != nil {
|
||
return fmt.Errorf("释放猪栏 %v 失败: %w", pen.PenNumber, err)
|
||
}
|
||
|
||
return nil
|
||
}
|