实现交易管理器主服务入口
This commit is contained in:
@@ -2,6 +2,7 @@ package pig
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
)
|
)
|
||||||
@@ -30,29 +31,24 @@ var (
|
|||||||
// PigBatchService 定义了猪批次管理的核心业务逻辑接口。
|
// PigBatchService 定义了猪批次管理的核心业务逻辑接口。
|
||||||
// 它抽象了所有与猪批次相关的操作,使得应用层可以依赖于此接口,而不是具体的实现。
|
// 它抽象了所有与猪批次相关的操作,使得应用层可以依赖于此接口,而不是具体的实现。
|
||||||
type PigBatchService interface {
|
type PigBatchService interface {
|
||||||
// TransferPigsWithinBatch 处理同一个猪群内部的调栏业务。
|
// CreatePigBatch 创建猪批次,并记录初始日志。
|
||||||
TransferPigsWithinBatch(batchID uint, fromPenID uint, toPenID uint, quantity uint, operatorID uint, remarks string) error
|
|
||||||
|
|
||||||
// TransferPigsAcrossBatches 处理跨猪群的调栏业务。
|
|
||||||
TransferPigsAcrossBatches(sourceBatchID uint, destBatchID uint, fromPenID uint, toPenID uint, quantity uint, operatorID uint, remarks string) error
|
|
||||||
|
|
||||||
// CreatePigBatch 创建一个新的猪批次。
|
|
||||||
CreatePigBatch(operatorID uint, batch *models.PigBatch) (*models.PigBatch, error)
|
CreatePigBatch(operatorID uint, batch *models.PigBatch) (*models.PigBatch, error)
|
||||||
|
// GetPigBatch 获取单个猪批次。
|
||||||
// GetPigBatch 根据ID获取单个猪批次的详细信息。
|
|
||||||
GetPigBatch(id uint) (*models.PigBatch, error)
|
GetPigBatch(id uint) (*models.PigBatch, error)
|
||||||
|
// UpdatePigBatch 更新猪批次信息。
|
||||||
// UpdatePigBatch 更新一个已存在的猪批次信息。
|
|
||||||
UpdatePigBatch(batch *models.PigBatch) (*models.PigBatch, error)
|
UpdatePigBatch(batch *models.PigBatch) (*models.PigBatch, error)
|
||||||
|
// DeletePigBatch 删除猪批次,包含业务规则校验。
|
||||||
// DeletePigBatch 删除一个指定的猪批次。
|
|
||||||
// 实现时需要包含业务规则校验,例如,活跃的批次不能被删除。
|
|
||||||
DeletePigBatch(id uint) error
|
DeletePigBatch(id uint) error
|
||||||
|
// ListPigBatches 批量查询猪批次。
|
||||||
// ListPigBatches 根据是否活跃的状态,列出所有符合条件的猪批次。
|
|
||||||
ListPigBatches(isActive *bool) ([]*models.PigBatch, error)
|
ListPigBatches(isActive *bool) ([]*models.PigBatch, error)
|
||||||
|
// UpdatePigBatchPens 更新猪批次关联的猪栏。
|
||||||
// UpdatePigBatchPens 负责原子性地更新一个猪批次所关联的所有猪栏。
|
|
||||||
// 它会处理猪栏的添加、移除,并确保数据的一致性。
|
|
||||||
UpdatePigBatchPens(batchID uint, desiredPenIDs []uint) error
|
UpdatePigBatchPens(batchID uint, desiredPenIDs []uint) error
|
||||||
|
|
||||||
|
// GetCurrentPigQuantity 获取指定猪批次的当前猪只数量。
|
||||||
|
GetCurrentPigQuantity(batchID uint) (int, error)
|
||||||
|
|
||||||
|
// SellPigs 处理卖猪的业务逻辑。
|
||||||
|
SellPigs(batchID uint, quantity int, unitPrice float64, tatalPrice float64, traderName string, tradeDate time.Time, remarks string, operatorID uint) error
|
||||||
|
// BuyPigs 处理买猪的业务逻辑。
|
||||||
|
BuyPigs(batchID uint, quantity int, unitPrice float64, tatalPrice float64, traderName string, tradeDate time.Time, remarks string, operatorID uint) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -235,3 +235,42 @@ func (s *pigBatchService) UpdatePigBatchPens(batchID uint, desiredPenIDs []uint)
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCurrentPigQuantity 实现了获取指定猪批次的当前猪只数量的逻辑。
|
||||||
|
func (s *pigBatchService) GetCurrentPigQuantity(batchID uint) (int, error) {
|
||||||
|
var getErr error
|
||||||
|
var quantity int
|
||||||
|
err := s.uow.ExecuteInTransaction(func(tx *gorm.DB) error {
|
||||||
|
quantity, getErr = s.getCurrentPigQuantityTx(tx, batchID)
|
||||||
|
return getErr
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return quantity, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCurrentPigQuantityTx 实现了获取指定猪批次的当前猪只数量的逻辑。
|
||||||
|
func (s *pigBatchService) getCurrentPigQuantityTx(tx *gorm.DB, batchID uint) (int, error) {
|
||||||
|
// 1. 获取猪批次初始信息
|
||||||
|
batch, err := s.pigBatchRepo.GetPigBatchByIDTx(tx, batchID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return 0, ErrPigBatchNotFound
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("获取猪批次 %d 初始信息失败: %w", batchID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 尝试获取该批次的最后一条日志记录
|
||||||
|
lastLog, err := s.pigBatchLogRepo.GetLastLogByBatchIDTx(tx, batchID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
// 如果没有找到任何日志记录(除了初始创建),则当前数量就是初始数量
|
||||||
|
return batch.InitialCount, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("获取猪批次 %d 最后一条日志失败: %w", batchID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 如果找到最后一条日志,则当前数量为该日志的 AfterCount
|
||||||
|
return lastLog.AfterCount, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,126 @@
|
|||||||
package pig
|
package pig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SellPigs 处理批量销售猪的业务逻辑。
|
||||||
|
func (s *pigBatchService) SellPigs(batchID uint, quantity int, unitPrice float64, tatalPrice float64, traderName string, tradeDate time.Time, remarks string, operatorID uint) error {
|
||||||
|
return s.uow.ExecuteInTransaction(func(tx *gorm.DB) error {
|
||||||
|
if quantity <= 0 {
|
||||||
|
return errors.New("销售数量必须大于0")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 获取猪批次信息
|
||||||
|
_, err := s.pigBatchRepo.GetPigBatchByIDTx(tx, batchID) // 仅用于校验批次是否存在
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return ErrPigBatchNotFound
|
||||||
|
}
|
||||||
|
return fmt.Errorf("获取猪批次 %d 信息失败: %w", batchID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 业务校验:检查销售数量是否超过当前批次数量
|
||||||
|
currentQuantity, err := s.getCurrentPigQuantityTx(tx, batchID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("获取猪批次 %d 当前数量失败: %w", batchID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if quantity > currentQuantity {
|
||||||
|
return fmt.Errorf("销售数量 %d 超过当前批次 %d 数量 %d", quantity, batchID, currentQuantity)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 记录销售交易
|
||||||
|
sale := &models.PigSale{
|
||||||
|
PigBatchID: batchID,
|
||||||
|
SaleDate: tradeDate,
|
||||||
|
Buyer: traderName,
|
||||||
|
Quantity: quantity,
|
||||||
|
UnitPrice: unitPrice,
|
||||||
|
TotalPrice: tatalPrice, // 总价不一定是单价x数量, 所以要传进来
|
||||||
|
Remarks: remarks,
|
||||||
|
OperatorID: operatorID,
|
||||||
|
}
|
||||||
|
if err := s.tradeSvc.SellPig(tx, sale); err != nil {
|
||||||
|
return fmt.Errorf("记录销售交易失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 记录批次日志
|
||||||
|
log := &models.PigBatchLog{
|
||||||
|
PigBatchID: batchID,
|
||||||
|
HappenedAt: time.Now(),
|
||||||
|
ChangeType: models.ChangeTypeSale,
|
||||||
|
ChangeCount: -quantity,
|
||||||
|
Reason: fmt.Sprintf("猪批次 %d 销售 %d 头猪给 %s", batchID, quantity, traderName),
|
||||||
|
BeforeCount: currentQuantity,
|
||||||
|
AfterCount: currentQuantity - quantity,
|
||||||
|
OperatorID: operatorID,
|
||||||
|
}
|
||||||
|
if err := s.pigBatchLogRepo.Create(tx, log); err != nil {
|
||||||
|
return fmt.Errorf("记录销售批次日志失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuyPigs 处理批量购买猪的业务逻辑。
|
||||||
|
func (s *pigBatchService) BuyPigs(batchID uint, quantity int, unitPrice float64, totalPrice float64, traderName string, tradeDate time.Time, remarks string, operatorID uint) error {
|
||||||
|
return s.uow.ExecuteInTransaction(func(tx *gorm.DB) error {
|
||||||
|
if quantity <= 0 {
|
||||||
|
return errors.New("采购数量必须大于0")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 获取猪批次信息
|
||||||
|
_, err := s.pigBatchRepo.GetPigBatchByIDTx(tx, batchID) // 仅用于校验批次是否存在
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return ErrPigBatchNotFound
|
||||||
|
}
|
||||||
|
return fmt.Errorf("获取猪批次 %d 信息失败: %w", batchID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取当前猪批次数量
|
||||||
|
currentQuantity, err := s.getCurrentPigQuantityTx(tx, batchID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("获取猪批次 %d 当前数量失败: %w", batchID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 记录采购交易
|
||||||
|
purchase := &models.PigPurchase{
|
||||||
|
PigBatchID: batchID,
|
||||||
|
PurchaseDate: tradeDate,
|
||||||
|
Supplier: traderName,
|
||||||
|
Quantity: quantity,
|
||||||
|
UnitPrice: unitPrice,
|
||||||
|
TotalPrice: totalPrice, // 总价不一定是单价x数量, 所以要传进来
|
||||||
|
Remarks: remarks,
|
||||||
|
OperatorID: operatorID,
|
||||||
|
}
|
||||||
|
if err := s.tradeSvc.BuyPig(tx, purchase); err != nil {
|
||||||
|
return fmt.Errorf("记录采购交易失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 记录批次日志
|
||||||
|
log := &models.PigBatchLog{
|
||||||
|
PigBatchID: batchID,
|
||||||
|
HappenedAt: time.Now(),
|
||||||
|
ChangeType: models.ChangeTypeBuy,
|
||||||
|
ChangeCount: quantity,
|
||||||
|
Reason: fmt.Sprintf("猪批次 %d 采购 %d 头猪从 %s", batchID, quantity, traderName),
|
||||||
|
BeforeCount: currentQuantity,
|
||||||
|
AfterCount: currentQuantity + quantity,
|
||||||
|
OperatorID: operatorID,
|
||||||
|
}
|
||||||
|
if err := s.pigBatchLogRepo.Create(tx, log); err != nil {
|
||||||
|
return fmt.Errorf("记录采购批次日志失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ const (
|
|||||||
ChangeTypeDeath LogChangeType = "死亡"
|
ChangeTypeDeath LogChangeType = "死亡"
|
||||||
ChangeTypeCull LogChangeType = "淘汰"
|
ChangeTypeCull LogChangeType = "淘汰"
|
||||||
ChangeTypeSale LogChangeType = "销售"
|
ChangeTypeSale LogChangeType = "销售"
|
||||||
|
ChangeTypeBuy LogChangeType = "购买"
|
||||||
ChangeTypeTransferIn LogChangeType = "转入"
|
ChangeTypeTransferIn LogChangeType = "转入"
|
||||||
ChangeTypeTransferOut LogChangeType = "转出"
|
ChangeTypeTransferOut LogChangeType = "转出"
|
||||||
ChangeTypeCorrection LogChangeType = "盘点校正"
|
ChangeTypeCorrection LogChangeType = "盘点校正"
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time" // 引入 time 包
|
||||||
|
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -9,6 +11,12 @@ import (
|
|||||||
type PigBatchLogRepository interface {
|
type PigBatchLogRepository interface {
|
||||||
// Create 在指定的事务中创建一条新的猪批次日志。
|
// Create 在指定的事务中创建一条新的猪批次日志。
|
||||||
Create(tx *gorm.DB, log *models.PigBatchLog) error
|
Create(tx *gorm.DB, log *models.PigBatchLog) error
|
||||||
|
|
||||||
|
// GetLogsByBatchIDAndDateRangeTx 在指定的事务中,获取指定批次在特定时间范围内的所有日志记录。
|
||||||
|
GetLogsByBatchIDAndDateRangeTx(tx *gorm.DB, batchID uint, startDate, endDate time.Time) ([]*models.PigBatchLog, error)
|
||||||
|
|
||||||
|
// GetLastLogByBatchIDTx 在指定的事务中,获取某批次的最后一条日志记录。
|
||||||
|
GetLastLogByBatchIDTx(tx *gorm.DB, batchID uint) (*models.PigBatchLog, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// gormPigBatchLogRepository 是 PigBatchLogRepository 的 GORM 实现。
|
// gormPigBatchLogRepository 是 PigBatchLogRepository 的 GORM 实现。
|
||||||
@@ -25,3 +33,23 @@ func NewGormPigBatchLogRepository(db *gorm.DB) PigBatchLogRepository {
|
|||||||
func (r *gormPigBatchLogRepository) Create(tx *gorm.DB, log *models.PigBatchLog) error {
|
func (r *gormPigBatchLogRepository) Create(tx *gorm.DB, log *models.PigBatchLog) error {
|
||||||
return tx.Create(log).Error
|
return tx.Create(log).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLogsByBatchIDAndDateRangeTx 实现了在指定的事务中,获取指定批次在特定时间范围内的所有日志记录的逻辑。
|
||||||
|
func (r *gormPigBatchLogRepository) GetLogsByBatchIDAndDateRangeTx(tx *gorm.DB, batchID uint, startDate, endDate time.Time) ([]*models.PigBatchLog, error) {
|
||||||
|
var logs []*models.PigBatchLog
|
||||||
|
err := tx.Where("pig_batch_id = ? AND created_at >= ? AND created_at <= ?", batchID, startDate, endDate).Find(&logs).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return logs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLastLogByBatchIDTx 实现了在指定的事务中,获取某批次的最后一条日志记录的逻辑。
|
||||||
|
func (r *gormPigBatchLogRepository) GetLastLogByBatchIDTx(tx *gorm.DB, batchID uint) (*models.PigBatchLog, error) {
|
||||||
|
var log models.PigBatchLog
|
||||||
|
err := tx.Where("pig_batch_id = ?", batchID).Order("id DESC").First(&log).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &log, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user