From 2aa0f090790b5d5690f79801dab6a1b7b6617151 Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Sun, 5 Oct 2025 18:28:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E6=89=B9=E6=AC=A1=E6=97=B6?= =?UTF-8?q?=E6=8F=92=E5=85=A5=E4=B8=80=E6=9D=A1=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/app/controller/auth_utils.go | 47 ++++++++++++++++ .../management/pig_batch_controller.go | 4 +- internal/app/service/pig_batch_service.go | 6 +- internal/core/application.go | 3 +- internal/domain/pig/pig_batch.go | 2 +- internal/domain/pig/pig_batch_service.go | 56 ++++++++++++++++--- internal/infra/models/pig.go | 18 +++--- .../repository/pig_batch_log_repository.go | 27 +++++++++ .../infra/repository/pig_batch_repository.go | 8 ++- 9 files changed, 145 insertions(+), 26 deletions(-) create mode 100644 internal/app/controller/auth_utils.go create mode 100644 internal/infra/repository/pig_batch_log_repository.go diff --git a/internal/app/controller/auth_utils.go b/internal/app/controller/auth_utils.go new file mode 100644 index 0000000..08a9589 --- /dev/null +++ b/internal/app/controller/auth_utils.go @@ -0,0 +1,47 @@ +package controller + +import ( + "errors" + + "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "github.com/gin-gonic/gin" +) + +var ( + // ErrUserNotFoundInContext 表示在 gin.Context 中未找到用户信息。 + ErrUserNotFoundInContext = errors.New("context中未找到用户信息") + // ErrInvalidUserType 表示从 gin.Context 中获取的用户信息类型不正确。 + ErrInvalidUserType = errors.New("context中用户信息类型不正确") +) + +// GetOperatorIDFromContext 从 gin.Context 中提取操作者ID。 +// 假设操作者ID是由 AuthMiddleware 存储到 context 中的 *models.User 对象的 ID 字段。 +func GetOperatorIDFromContext(c *gin.Context) (uint, error) { + userVal, exists := c.Get(models.ContextUserKey.String()) + if !exists { + return 0, ErrUserNotFoundInContext + } + + user, ok := userVal.(*models.User) + if !ok { + return 0, ErrInvalidUserType + } + + return user.ID, nil +} + +// GetOperatorFromContext 从 gin.Context 中提取操作者。 +// 假设操作者是由 AuthMiddleware 存储到 context 中的 *models.User 对象的 字段。 +func GetOperatorFromContext(c *gin.Context) (*models.User, error) { + userVal, exists := c.Get(models.ContextUserKey.String()) + if !exists { + return nil, ErrUserNotFoundInContext + } + + user, ok := userVal.(*models.User) + if !ok { + return nil, ErrInvalidUserType + } + + return user, nil +} diff --git a/internal/app/controller/management/pig_batch_controller.go b/internal/app/controller/management/pig_batch_controller.go index 46199b6..4debac0 100644 --- a/internal/app/controller/management/pig_batch_controller.go +++ b/internal/app/controller/management/pig_batch_controller.go @@ -45,7 +45,9 @@ func (c *PigBatchController) CreatePigBatch(ctx *gin.Context) { return } - respDTO, err := c.service.CreatePigBatch(&req) + userID, err := controller.GetOperatorIDFromContext(ctx) + + respDTO, err := c.service.CreatePigBatch(userID, &req) if err != nil { c.logger.Errorf("%s: 业务逻辑失败: %v", action, err) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建猪批次失败", action, "业务逻辑失败", req) diff --git a/internal/app/service/pig_batch_service.go b/internal/app/service/pig_batch_service.go index d4a5149..da50327 100644 --- a/internal/app/service/pig_batch_service.go +++ b/internal/app/service/pig_batch_service.go @@ -9,7 +9,7 @@ import ( // PigBatchService 接口定义保持不变,继续作为应用层对外的契约。 type PigBatchService interface { - CreatePigBatch(dto *dto.PigBatchCreateDTO) (*dto.PigBatchResponseDTO, error) + CreatePigBatch(operatorID uint, dto *dto.PigBatchCreateDTO) (*dto.PigBatchResponseDTO, error) GetPigBatch(id uint) (*dto.PigBatchResponseDTO, error) UpdatePigBatch(id uint, dto *dto.PigBatchUpdateDTO) (*dto.PigBatchResponseDTO, error) DeletePigBatch(id uint) error @@ -51,7 +51,7 @@ func (s *pigBatchService) toPigBatchResponseDTO(batch *models.PigBatch) *dto.Pig } // CreatePigBatch 现在将请求委托给领域服务处理。 -func (s *pigBatchService) CreatePigBatch(dto *dto.PigBatchCreateDTO) (*dto.PigBatchResponseDTO, error) { +func (s *pigBatchService) CreatePigBatch(operatorID uint, dto *dto.PigBatchCreateDTO) (*dto.PigBatchResponseDTO, error) { // 1. DTO -> 领域模型 batch := &models.PigBatch{ BatchNumber: dto.BatchNumber, @@ -62,7 +62,7 @@ func (s *pigBatchService) CreatePigBatch(dto *dto.PigBatchCreateDTO) (*dto.PigBa } // 2. 调用领域服务 - createdBatch, err := s.domainService.CreatePigBatch(batch) + createdBatch, err := s.domainService.CreatePigBatch(operatorID, batch) if err != nil { s.logger.Errorf("应用层: 创建猪批次失败: %v", err) return nil, mapDomainError(err) diff --git a/internal/core/application.go b/internal/core/application.go index c99c89e..f11b397 100644 --- a/internal/core/application.go +++ b/internal/core/application.go @@ -73,6 +73,7 @@ func NewApplication(configPath string) (*Application, error) { pendingCollectionRepo := repository.NewGormPendingCollectionRepository(storage.GetDB()) userActionLogRepo := repository.NewGormUserActionLogRepository(storage.GetDB()) pigBatchRepo := repository.NewGormPigBatchRepository(storage.GetDB()) + pigBatchLogRepo := repository.NewGormPigBatchLogRepository(storage.GetDB()) pigFarmRepo := repository.NewGormPigFarmRepository(storage.GetDB()) pigPenRepo := repository.NewGormPigPenRepository(storage.GetDB()) @@ -81,7 +82,7 @@ func NewApplication(configPath string) (*Application, error) { // 初始化猪群管理领域 penTransferManager := pig.NewPenTransferManager(pigPenRepo) - pigBatchDomain := pig.NewPigBatchService(pigBatchRepo, unitOfWork, penTransferManager) + pigBatchDomain := pig.NewPigBatchService(pigBatchRepo, pigBatchLogRepo, unitOfWork, penTransferManager) // --- 业务逻辑处理器初始化 --- pigFarmService := service.NewPigFarmService(pigFarmRepo, pigPenRepo, pigBatchRepo, unitOfWork, logger) diff --git a/internal/domain/pig/pig_batch.go b/internal/domain/pig/pig_batch.go index a404a0d..1983684 100644 --- a/internal/domain/pig/pig_batch.go +++ b/internal/domain/pig/pig_batch.go @@ -37,7 +37,7 @@ type PigBatchService interface { TransferPigsAcrossBatches(sourceBatchID uint, destBatchID uint, fromPenID uint, toPenID uint, quantity uint) error // CreatePigBatch 创建一个新的猪批次。 - CreatePigBatch(batch *models.PigBatch) (*models.PigBatch, error) + CreatePigBatch(operatorID uint, batch *models.PigBatch) (*models.PigBatch, error) // GetPigBatch 根据ID获取单个猪批次的详细信息。 GetPigBatch(id uint) (*models.PigBatch, error) diff --git a/internal/domain/pig/pig_batch_service.go b/internal/domain/pig/pig_batch_service.go index 97c26e1..12d2868 100644 --- a/internal/domain/pig/pig_batch_service.go +++ b/internal/domain/pig/pig_batch_service.go @@ -16,29 +16,67 @@ import ( // pigBatchService 是 PigBatchService 接口的具体实现。 // 它作为猪群领域的主服务,封装了所有业务逻辑。 type pigBatchService struct { - pigBatchRepo repository.PigBatchRepository // 猪批次仓库 - uow repository.UnitOfWork // 工作单元,用于管理事务 - transferSvc PenTransferManager // 调栏子服务 + pigBatchRepo repository.PigBatchRepository // 猪批次仓库 + pigBatchLogRepo repository.PigBatchLogRepository // 猪批次日志仓库 + uow repository.UnitOfWork // 工作单元,用于管理事务 + transferSvc PenTransferManager // 调栏子服务 } // NewPigBatchService 是 pigBatchService 的构造函数。 // 它通过依赖注入的方式,创建并返回一个 PigBatchService 接口的实例。 func NewPigBatchService( pigBatchRepo repository.PigBatchRepository, + pigBatchLogRepo repository.PigBatchLogRepository, uow repository.UnitOfWork, transferSvc PenTransferManager, ) PigBatchService { return &pigBatchService{ - pigBatchRepo: pigBatchRepo, - uow: uow, - transferSvc: transferSvc, + pigBatchRepo: pigBatchRepo, + pigBatchLogRepo: pigBatchLogRepo, + uow: uow, + transferSvc: transferSvc, } } -// CreatePigBatch 实现了创建猪批次的逻辑。 -func (s *pigBatchService) CreatePigBatch(batch *models.PigBatch) (*models.PigBatch, error) { +// CreatePigBatch 实现了创建猪批次的逻辑,并同时创建初始批次日志。 +func (s *pigBatchService) CreatePigBatch(operatorID uint, batch *models.PigBatch) (*models.PigBatch, error) { // 业务规则可以在这里添加,例如检查批次号是否唯一等 - return s.pigBatchRepo.CreatePigBatch(batch) + + var createdBatch *models.PigBatch + err := s.uow.ExecuteInTransaction(func(tx *gorm.DB) error { + // 1. 创建猪批次 + // 注意: 此处依赖一个假设存在的 pigBatchRepo.CreatePigBatchTx 方法 + var err error + createdBatch, err = s.pigBatchRepo.CreatePigBatchTx(tx, batch) + if err != nil { + return fmt.Errorf("创建猪批次失败: %w", err) + } + + // 2. 创建初始批次日志 + initialLog := &models.PigBatchLog{ + PigBatchID: createdBatch.ID, + HappenedAt: time.Now(), + ChangeType: models.ChangeTypeCorrection, // 初始创建可视为一种校正 + ChangeCount: createdBatch.InitialCount, + Reason: fmt.Sprintf("创建了新的猪批次 %s,初始数量 %d", createdBatch.BatchNumber, createdBatch.InitialCount), + BeforeCount: 0, // 初始创建前数量为0 + AfterCount: int(createdBatch.InitialCount), + OperatorID: 0, // 假设初始创建没有特定操作员ID,或需要从上下文传入 + } + + // 3. 记录批次日志 + if err := s.pigBatchLogRepo.Create(tx, initialLog); err != nil { + return fmt.Errorf("记录初始批次日志失败: %w", err) + } + + return nil + }) + + if err != nil { + return nil, err + } + + return createdBatch, nil } // GetPigBatch 实现了获取单个猪批次的逻辑。 diff --git a/internal/infra/models/pig.go b/internal/infra/models/pig.go index 8042528..1fb5313 100644 --- a/internal/infra/models/pig.go +++ b/internal/infra/models/pig.go @@ -67,16 +67,14 @@ const ( // PigBatchLog 记录了猪批次数量或状态的每一次变更 type PigBatchLog struct { gorm.Model - PigBatchID uint `gorm:"not null;index;comment:关联的猪批次ID"` - ChangeType LogChangeType `gorm:"size:20;not null;comment:变更类型"` - ChangeCount int `gorm:"not null;comment:数量变化,负数表示减少"` - Reason string `gorm:"size:255;comment:变更原因描述"` - BeforeCount int `gorm:"not null;comment:变更前总数"` - AfterCount int `gorm:"not null;comment:变更后总数"` - BeforeSickCount int `gorm:"not null;comment:变更前病猪数"` - AfterSickCount int `gorm:"not null;comment:变更后病猪数"` - OperatorID uint `gorm:"comment:操作员ID"` - HappenedAt time.Time `gorm:"primaryKey;comment:事件发生时间"` + PigBatchID uint `gorm:"not null;index;comment:关联的猪批次ID"` + ChangeType LogChangeType `gorm:"size:20;not null;comment:变更类型"` + ChangeCount int `gorm:"not null;comment:数量变化,负数表示减少"` + Reason string `gorm:"size:255;comment:变更原因描述"` + BeforeCount int `gorm:"not null;comment:变更前总数"` + AfterCount int `gorm:"not null;comment:变更后总数"` + OperatorID uint `gorm:"comment:操作员ID"` + HappenedAt time.Time `gorm:"primaryKey;comment:事件发生时间"` } func (PigBatchLog) TableName() string { diff --git a/internal/infra/repository/pig_batch_log_repository.go b/internal/infra/repository/pig_batch_log_repository.go new file mode 100644 index 0000000..aaed13d --- /dev/null +++ b/internal/infra/repository/pig_batch_log_repository.go @@ -0,0 +1,27 @@ +package repository + +import ( + "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" +) + +// PigBatchLogRepository 定义了与猪批次日志相关的数据库操作接口。 +type PigBatchLogRepository interface { + // Create 在指定的事务中创建一条新的猪批次日志。 + Create(tx *gorm.DB, log *models.PigBatchLog) error +} + +// gormPigBatchLogRepository 是 PigBatchLogRepository 的 GORM 实现。 +type gormPigBatchLogRepository struct { + db *gorm.DB +} + +// NewGormPigBatchLogRepository 创建一个新的 PigBatchLogRepository 实例。 +func NewGormPigBatchLogRepository(db *gorm.DB) PigBatchLogRepository { + return &gormPigBatchLogRepository{db: db} +} + +// Create 实现了创建猪批次日志的逻辑。 +func (r *gormPigBatchLogRepository) Create(tx *gorm.DB, log *models.PigBatchLog) error { + return tx.Create(log).Error +} diff --git a/internal/infra/repository/pig_batch_repository.go b/internal/infra/repository/pig_batch_repository.go index 51876a0..3b57b72 100644 --- a/internal/infra/repository/pig_batch_repository.go +++ b/internal/infra/repository/pig_batch_repository.go @@ -8,6 +8,7 @@ import ( // PigBatchRepository 定义了与猪批次相关的数据库操作接口 type PigBatchRepository interface { CreatePigBatch(batch *models.PigBatch) (*models.PigBatch, error) + CreatePigBatchTx(tx *gorm.DB, batch *models.PigBatch) (*models.PigBatch, error) GetPigBatchByID(id uint) (*models.PigBatch, error) GetPigBatchByIDTx(tx *gorm.DB, id uint) (*models.PigBatch, error) // UpdatePigBatch 更新一个猪批次,返回更新后的批次、受影响的行数和错误 @@ -29,7 +30,12 @@ func NewGormPigBatchRepository(db *gorm.DB) PigBatchRepository { // CreatePigBatch 创建一个新的猪批次 func (r *gormPigBatchRepository) CreatePigBatch(batch *models.PigBatch) (*models.PigBatch, error) { - if err := r.db.Create(batch).Error; err != nil { + return r.CreatePigBatchTx(r.db, batch) +} + +// CreatePigBatchTx 在指定的事务中,创建一个新的猪批次 +func (r *gormPigBatchRepository) CreatePigBatchTx(tx *gorm.DB, batch *models.PigBatch) (*models.PigBatch, error) { + if err := tx.Create(batch).Error; err != nil { return nil, err } return batch, nil