package pig import ( "errors" "fmt" "time" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" "gorm.io/gorm" ) // RecordSickPigs 记录新增病猪事件。 func (s *pigBatchService) RecordSickPigs(operatorID uint, batchID uint, penID uint, quantity int, treatmentLocation models.PigBatchSickPigTreatmentLocation, happenedAt time.Time, remarks string) error { if quantity <= 0 { return errors.New("新增病猪数量必须大于0") } var err error // 1. 开启事务 err = s.uow.ExecuteInTransaction(func(tx *gorm.DB) error { // 1.1 检查批次是否活跃 batch, err := s.pigBatchRepo.GetPigBatchByIDTx(tx, batchID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrPigBatchNotFound } return fmt.Errorf("获取批次 %d 失败: %w", batchID, err) } if !batch.IsActive() { return fmt.Errorf("批次 %d 不活跃,无法记录病猪事件", batchID) } // 1.2 检查猪栏是否关联 pen, err := s.transferSvc.GetPenByID(tx, penID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrPenNotFound } return fmt.Errorf("获取猪栏 %d 失败: %w", penID, err) } if pen.PigBatchID == nil || *pen.PigBatchID != batchID { return fmt.Errorf("猪栏 %d 未与批次 %d 关联", penID, batchID) } // 1.3 检查剩余健康猪不能少于即将转化的病猪数量 totalPigsInBatch, err := s.getCurrentPigQuantityTx(tx, batchID) if err != nil { return fmt.Errorf("获取批次 %d 总猪只数量失败: %w", batchID, err) } currentSickPigs, err := s.sickSvc.GetCurrentSickPigCount(tx, batchID) if err != nil { return fmt.Errorf("获取批次 %d 当前病猪数量失败: %w", batchID, err) } healthyPigs := totalPigsInBatch - currentSickPigs if healthyPigs < quantity { return fmt.Errorf("健康猪数量不足,当前健康猪 %d 头,尝试记录病猪 %d 头", healthyPigs, quantity) } // 1.4 创建病猪日志 sickLog := &models.PigSickLog{ PigBatchID: batchID, PenID: penID, ChangeCount: quantity, // 新增病猪,ChangeCount 为正数 Reason: models.SickPigReasonTypeIllness, TreatmentLocation: treatmentLocation, Remarks: remarks, OperatorID: operatorID, HappenedAt: happenedAt, } if err := s.sickSvc.ProcessSickPigLog(tx, sickLog); err != nil { return fmt.Errorf("处理病猪日志失败: %w", err) } return nil }) if err != nil { return fmt.Errorf("记录新增病猪事件失败: %w", 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 { if quantity <= 0 { return errors.New("康复猪只数量必须大于0") } var err error err = 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 fmt.Errorf("获取批次 %d 失败: %w", batchID, err) } if !batch.IsActive() { return fmt.Errorf("批次 %d 不活跃,无法记录病猪康复事件", batchID) } // 2. 检查猪栏是否关联 pen, err := s.transferSvc.GetPenByID(tx, penID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrPenNotFound } return fmt.Errorf("获取猪栏 %d 失败: %w", penID, err) } if pen.PigBatchID == nil || *pen.PigBatchID != batchID { return fmt.Errorf("猪栏 %d 未与批次 %d 关联", penID, batchID) } // 3. 检查当前病猪数量是否足够康复 currentSickPigs, err := s.sickSvc.GetCurrentSickPigCount(tx, batchID) if err != nil { return fmt.Errorf("获取批次 %d 当前病猪数量失败: %w", batchID, err) } if currentSickPigs < quantity { return fmt.Errorf("当前病猪数量不足,当前病猪 %d 头,尝试康复 %d 头", currentSickPigs, quantity) } // 4. 创建病猪日志 sickLog := &models.PigSickLog{ PigBatchID: batchID, PenID: penID, ChangeCount: -quantity, // 康复病猪,ChangeCount 为负数 Reason: models.SickPigReasonTypeRecovery, TreatmentLocation: treatmentLocation, Remarks: remarks, OperatorID: operatorID, HappenedAt: happenedAt, } if err := s.sickSvc.ProcessSickPigLog(tx, sickLog); err != nil { return fmt.Errorf("处理病猪康复日志失败: %w", err) } return nil }) if err != nil { return fmt.Errorf("记录病猪康复事件失败: %w", 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 { if quantity <= 0 { return errors.New("死亡猪只数量必须大于0") } var err error err = 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 fmt.Errorf("获取批次 %d 失败: %w", batchID, err) } if !batch.IsActive() { return fmt.Errorf("批次 %d 不活跃,无法记录病猪死亡事件", batchID) } // 2. 检查猪栏是否关联 pen, err := s.transferSvc.GetPenByID(tx, penID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrPenNotFound } return fmt.Errorf("获取猪栏 %d 失败: %w", penID, err) } if pen.PigBatchID == nil || *pen.PigBatchID != batchID { return fmt.Errorf("猪栏 %d 未与批次 %d 关联", penID, batchID) } // 3. 检查当前病猪数量是否足够死亡 currentSickPigs, err := s.sickSvc.GetCurrentSickPigCount(tx, batchID) if err != nil { return fmt.Errorf("获取批次 %d 当前病猪数量失败: %w", batchID, err) } if currentSickPigs < quantity { return fmt.Errorf("当前病猪数量不足,当前病猪 %d 头,尝试记录死亡 %d 头", currentSickPigs, quantity) } // 4. 检查猪栏内猪只数量是否足够死亡 currentPigsInPen, err := s.transferSvc.GetCurrentPigsInPen(tx, penID) if err != nil { return fmt.Errorf("获取猪栏 %d 当前猪只数量失败: %w", penID, err) } if currentPigsInPen < quantity { return fmt.Errorf("猪栏 %d 内猪只数量不足,当前 %d 头,尝试记录死亡 %d 头", penID, currentPigsInPen, quantity) } // 5. 创建病猪日志 (减少病猪数量) sickLog := &models.PigSickLog{ PigBatchID: batchID, PenID: penID, ChangeCount: -quantity, // 死亡病猪,ChangeCount 为负数 Reason: models.SickPigReasonTypeDeath, TreatmentLocation: treatmentLocation, Remarks: remarks, OperatorID: operatorID, HappenedAt: happenedAt, } if err := s.sickSvc.ProcessSickPigLog(tx, sickLog); err != nil { return fmt.Errorf("处理病猪死亡日志失败: %w", err) } // 6. 更新批次总猪只数量 (减少批次总数) if err := s.UpdatePigBatchQuantity(operatorID, batchID, models.ChangeTypeDeath, -quantity, remarks, happenedAt); err != nil { return fmt.Errorf("更新批次 %d 总猪只数量失败: %w", batchID, err) } // 7. 记录猪只转移日志 (减少猪栏内猪只数量) transferLog := &models.PigTransferLog{ TransferTime: happenedAt, PigBatchID: batchID, PenID: penID, Quantity: -quantity, // 减少猪只数量 Type: models.PigTransferTypeDeath, OperatorID: operatorID, Remarks: remarks, } if err := s.transferSvc.LogTransfer(tx, transferLog); err != nil { return fmt.Errorf("记录猪只死亡转移日志失败: %w", err) } return nil }) if err != nil { return fmt.Errorf("记录病猪死亡事件失败: %w", 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 { if quantity <= 0 { return errors.New("淘汰猪只数量必须大于0") } var err error err = 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 fmt.Errorf("获取批次 %d 失败: %w", batchID, err) } if !batch.IsActive() { return fmt.Errorf("批次 %d 不活跃,无法记录病猪淘汰事件", batchID) } // 2. 检查猪栏是否关联 pen, err := s.transferSvc.GetPenByID(tx, penID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrPenNotFound } return fmt.Errorf("获取猪栏 %d 失败: %w", penID, err) } if pen.PigBatchID == nil || *pen.PigBatchID != batchID { return fmt.Errorf("猪栏 %d 未与批次 %d 关联", penID, batchID) } // 3. 检查当前病猪数量是否足够淘汰 currentSickPigs, err := s.sickSvc.GetCurrentSickPigCount(tx, batchID) if err != nil { return fmt.Errorf("获取批次 %d 当前病猪数量失败: %w", batchID, err) } if currentSickPigs < quantity { return fmt.Errorf("当前病猪数量不足,当前病猪 %d 头,尝试淘汰 %d 头", currentSickPigs, quantity) } // 4. 检查猪栏内猪只数量是否足够淘汰 currentPigsInPen, err := s.transferSvc.GetCurrentPigsInPen(tx, penID) if err != nil { return fmt.Errorf("获取猪栏 %d 当前猪只数量失败: %w", penID, err) } if currentPigsInPen < quantity { return fmt.Errorf("猪栏 %d 内猪只数量不足,当前 %d 头,尝试记录淘汰 %d 头", penID, currentPigsInPen, quantity) } // 5. 创建病猪日志 (减少病猪数量) sickLog := &models.PigSickLog{ PigBatchID: batchID, PenID: penID, ChangeCount: -quantity, // 淘汰病猪,ChangeCount 为负数 Reason: models.SickPigReasonTypeEliminate, TreatmentLocation: treatmentLocation, Remarks: remarks, OperatorID: operatorID, HappenedAt: happenedAt, } if err := s.sickSvc.ProcessSickPigLog(tx, sickLog); err != nil { return fmt.Errorf("处理病猪淘汰日志失败: %w", err) } // 6. 更新批次总猪只数量 (减少批次总数) if err := s.UpdatePigBatchQuantity(operatorID, batchID, models.ChangeTypeCull, -quantity, remarks, happenedAt); err != nil { return fmt.Errorf("更新批次 %d 总猪只数量失败: %w", batchID, err) } // 7. 记录猪只转移日志 (减少猪栏内猪只数量) transferLog := &models.PigTransferLog{ TransferTime: happenedAt, PigBatchID: batchID, PenID: penID, Quantity: -quantity, // 减少猪只数量 Type: models.PigTransferTypeCull, // 淘汰类型 OperatorID: operatorID, Remarks: remarks, } if err := s.transferSvc.LogTransfer(tx, transferLog); err != nil { return fmt.Errorf("记录猪只淘汰转移日志失败: %w", err) } return nil }) if err != nil { return fmt.Errorf("记录病猪淘汰事件失败: %w", err) } return nil } // RecordDeath 记录正常猪只死亡事件。 func (s *pigBatchService) RecordDeath(operatorID uint, batchID uint, penID uint, quantity int, happenedAt time.Time, remarks string) error { if quantity <= 0 { return errors.New("死亡猪只数量必须大于0") } var err error err = 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 fmt.Errorf("获取批次 %d 失败: %w", batchID, err) } if !batch.IsActive() { return fmt.Errorf("批次 %d 不活跃,无法记录死亡事件", batchID) } // 2. 检查猪栏是否关联 pen, err := s.transferSvc.GetPenByID(tx, penID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrPenNotFound } return fmt.Errorf("获取猪栏 %d 失败: %w", penID, err) } if pen.PigBatchID == nil || *pen.PigBatchID != batchID { return fmt.Errorf("猪栏 %d 未与批次 %d 关联", penID, batchID) } // 3. 检查猪栏内猪只数量是否足够死亡 currentPigsInPen, err := s.transferSvc.GetCurrentPigsInPen(tx, penID) if err != nil { return fmt.Errorf("获取猪栏 %d 当前猪只数量失败: %w", penID, err) } if currentPigsInPen < quantity { return fmt.Errorf("猪栏 %d 内猪只数量不足,当前 %d 头,尝试记录死亡 %d 头", penID, currentPigsInPen, quantity) } // 4. 更新批次总猪只数量 (减少批次总数) if err := s.UpdatePigBatchQuantity(operatorID, batchID, models.ChangeTypeDeath, -quantity, remarks, happenedAt); err != nil { return fmt.Errorf("更新批次 %d 总猪只数量失败: %w", batchID, err) } // 5. 记录猪只转移日志 (减少猪栏内猪只数量) transferLog := &models.PigTransferLog{ TransferTime: happenedAt, PigBatchID: batchID, PenID: penID, Quantity: -quantity, // 减少猪只数量 Type: models.PigTransferTypeDeath, OperatorID: operatorID, Remarks: remarks, } if err := s.transferSvc.LogTransfer(tx, transferLog); err != nil { return fmt.Errorf("记录猪只死亡转移日志失败: %w", err) } return nil }) if err != nil { return fmt.Errorf("记录正常猪只死亡事件失败: %w", err) } return nil } // RecordCull 记录正常猪只淘汰事件。 func (s *pigBatchService) RecordCull(operatorID uint, batchID uint, penID uint, quantity int, happenedAt time.Time, remarks string) error { if quantity <= 0 { return errors.New("淘汰猪只数量必须大于0") } var err error err = 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 fmt.Errorf("获取批次 %d 失败: %w", batchID, err) } if !batch.IsActive() { return fmt.Errorf("批次 %d 不活跃,无法记录淘汰事件", batchID) } // 2. 检查猪栏是否关联 pen, err := s.transferSvc.GetPenByID(tx, penID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrPenNotFound } return fmt.Errorf("获取猪栏 %d 失败: %w", penID, err) } if pen.PigBatchID == nil || *pen.PigBatchID != batchID { return fmt.Errorf("猪栏 %d 未与批次 %d 关联", penID, batchID) } // 3. 检查猪栏内猪只数量是否足够淘汰 currentPigsInPen, err := s.transferSvc.GetCurrentPigsInPen(tx, penID) if err != nil { return fmt.Errorf("获取猪栏 %d 当前猪只数量失败: %w", penID, err) } if currentPigsInPen < quantity { return fmt.Errorf("猪栏 %d 内猪只数量不足,当前 %d 头,尝试记录淘汰 %d 头", penID, currentPigsInPen, quantity) } // 4. 更新批次总猪只数量 (减少批次总数) if err := s.UpdatePigBatchQuantity(operatorID, batchID, models.ChangeTypeCull, -quantity, remarks, happenedAt); err != nil { return fmt.Errorf("更新批次 %d 总猪只数量失败: %w", batchID, err) } // 5. 记录猪只转移日志 (减少猪栏内猪只数量) transferLog := &models.PigTransferLog{ TransferTime: happenedAt, PigBatchID: batchID, PenID: penID, Quantity: -quantity, // 减少猪只数量 Type: models.PigTransferTypeCull, OperatorID: operatorID, Remarks: remarks, } if err := s.transferSvc.LogTransfer(tx, transferLog); err != nil { return fmt.Errorf("记录猪只淘汰转移日志失败: %w", err) } return nil }) if err != nil { return fmt.Errorf("记录正常猪只淘汰事件失败: %w", err) } return nil }