From 10b123ab93531b04cdde9825fa46fcb8bae3d3b8 Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Wed, 5 Nov 2025 23:00:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9infra.repository=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/core/component_initializers.go | 11 +- internal/core/data_initializer.go | 38 +-- .../repository/area_controller_repository.go | 55 ++-- .../device_command_log_repository.go | 38 ++- .../infra/repository/device_repository.go | 102 +++--- .../repository/device_template_repository.go | 59 ++-- .../repository/execution_log_repository.go | 159 ++++++---- .../repository/medication_log_repository.go | 24 +- .../repository/notification_repository.go | 38 ++- .../pending_collection_repository.go | 45 +-- .../repository/pending_task_repository.go | 101 +++--- .../repository/pig_batch_log_repository.go | 38 ++- .../infra/repository/pig_batch_repository.go | 80 +++-- .../infra/repository/pig_farm_repository.go | 53 ++-- .../infra/repository/pig_pen_repository.go | 67 ++-- .../infra/repository/pig_sick_repository.go | 38 ++- .../infra/repository/pig_trade_repository.go | 38 ++- .../repository/pig_transfer_log_repository.go | 31 +- internal/infra/repository/plan_repository.go | 293 ++++++++++-------- .../repository/raw_material_repository.go | 31 +- .../repository/sensor_data_repository.go | 31 +- internal/infra/repository/unit_of_work.go | 29 +- .../repository/user_action_log_repository.go | 24 +- internal/infra/repository/user_repository.go | 46 +-- project_structure.txt | 16 +- 25 files changed, 877 insertions(+), 608 deletions(-) diff --git a/internal/core/component_initializers.go b/internal/core/component_initializers.go index 9cf119f..fb94a45 100644 --- a/internal/core/component_initializers.go +++ b/internal/core/component_initializers.go @@ -40,7 +40,7 @@ func initInfrastructure(ctx context.Context, cfg *config.Config) (*Infrastructur return nil, err } - repos := initRepositories(ctx, storage.GetDB()) + repos := initRepositories(ctx, storage.GetDB(ctx)) lora, err := initLora(ctx, cfg, repos) if err != nil { @@ -279,12 +279,12 @@ func initLora( if cfg.Lora.Mode == config.LoraMode_LoRaWAN { logger.Info("当前运行模式: lora_wan。初始化 ChirpStack 监听器和传输层。") listenHandler = webhook.NewChirpStackListener(logs.AddCompName(baseCtx, "ChirpStackListener"), repos.sensorDataRepo, repos.deviceRepo, repos.areaControllerRepo, repos.deviceCommandLogRepo, repos.pendingCollectionRepo) - comm = lora.NewChirpStackTransport(cfg.ChirpStack, logs.AddCompName(baseCtx, "ChirpStackTransport")) + comm = lora.NewChirpStackTransport(logs.AddCompName(baseCtx, "ChirpStackTransport"), cfg.ChirpStack) loraListener = lora.NewPlaceholderTransport(logs.AddCompName(baseCtx, "PlaceholderTransport")) } else { logger.Info("当前运行模式: lora_mesh。初始化 LoRa Mesh 传输层和占位符监听器。") listenHandler = webhook.NewPlaceholderListener(logs.AddCompName(baseCtx, "PlaceholderListener")) - tp, err := lora.NewLoRaMeshUartPassthroughTransport(cfg.LoraMesh, logs.AddCompName(baseCtx, "LoRaMeshTransport"), repos.areaControllerRepo, repos.pendingCollectionRepo, repos.deviceRepo, repos.sensorDataRepo) + tp, err := lora.NewLoRaMeshUartPassthroughTransport(logs.AddCompName(baseCtx, "LoRaMeshTransport"), cfg.LoraMesh, repos.areaControllerRepo, repos.pendingCollectionRepo, repos.deviceRepo, repos.sensorDataRepo) if err != nil { return nil, fmt.Errorf("无法初始化 LoRa Mesh 模块: %w", err) } @@ -319,6 +319,7 @@ func initNotifyService( // 2. 根据配置,按需创建并收集所有启用的其他 Notifier 实例 if cfg.SMTP.Enabled { smtpNotifier := notify.NewSMTPNotifier( + logs.AddCompName(baseCtx, "SMTPNotifier"), cfg.SMTP.Host, cfg.SMTP.Port, cfg.SMTP.Username, @@ -331,6 +332,7 @@ func initNotifyService( if cfg.WeChat.Enabled { wechatNotifier := notify.NewWechatNotifier( + logs.AddCompName(baseCtx, "WechatNotifier"), cfg.WeChat.CorpID, cfg.WeChat.AgentID, cfg.WeChat.Secret, @@ -341,6 +343,7 @@ func initNotifyService( if cfg.Lark.Enabled { larkNotifier := notify.NewLarkNotifier( + logs.AddCompName(baseCtx, "LarkNotifier"), cfg.Lark.AppID, cfg.Lark.AppSecret, ) @@ -387,7 +390,7 @@ func initNotifyService( func initStorage(ctx context.Context, cfg config.DatabaseConfig) (database.Storage, error) { // 创建存储实例 storage := database.NewStorage(logs.AddCompName(context.Background(), "Storage"), cfg) - if err := storage.Connect(); err != nil { + if err := storage.Connect(ctx); err != nil { // 错误已在 Connect 内部被记录,这里只需包装并返回 return nil, fmt.Errorf("数据库连接失败: %w", err) } diff --git a/internal/core/data_initializer.go b/internal/core/data_initializer.go index 0582fb2..a827c24 100644 --- a/internal/core/data_initializer.go +++ b/internal/core/data_initializer.go @@ -17,19 +17,19 @@ const ( // initializeState 在应用启动时准备其初始数据状态。 // 这包括清理任何因上次异常关闭而留下的悬空任务或请求。 func (app *Application) initializeState(ctx context.Context) error { - newCtx, logger := logs.Trace(ctx, app.Ctx, "InitializeState") + appCtx, logger := logs.Trace(ctx, app.Ctx, "InitializeState") // 初始化预定义系统计划 (致命错误) if err := app.initializeSystemPlans(ctx); err != nil { return fmt.Errorf("初始化预定义系统计划失败: %w", err) } // 清理待采集任务 (非致命错误) - if err := app.initializePendingCollections(newCtx); err != nil { + if err := app.initializePendingCollections(appCtx); err != nil { logger.Errorw("清理待采集任务时发生非致命错误", "error", err) } // 初始化待执行任务列表 (致命错误) - if err := app.initializePendingTasks(newCtx); err != nil { + if err := app.initializePendingTasks(appCtx); err != nil { return fmt.Errorf("初始化待执行任务列表失败: %w", err) } @@ -38,14 +38,14 @@ func (app *Application) initializeState(ctx context.Context) error { // initializeSystemPlans 确保预定义的系统计划在数据库中存在并保持最新。 func (app *Application) initializeSystemPlans(ctx context.Context) error { - logger := logs.TraceLogger(ctx, app.Ctx, "InitializeSystemPlans") + appCtx, logger := logs.Trace(ctx, app.Ctx, "InitializeSystemPlans") logger.Info("开始检查并更新预定义的系统计划...") // 动态构建预定义计划列表 predefinedSystemPlans := app.getPredefinedSystemPlans() // 1. 获取所有已存在的系统计划 - existingPlans, _, err := app.Infra.repos.planRepo.ListPlans(repository.ListPlansOptions{ + existingPlans, _, err := app.Infra.repos.planRepo.ListPlans(appCtx, repository.ListPlansOptions{ PlanType: repository.PlanTypeFilterSystem, }, 1, 99999) // 使用一个较大的 pageSize 来获取所有系统计划 if err != nil { @@ -70,7 +70,7 @@ func (app *Application) initializeSystemPlans(ctx context.Context) error { predefinedPlan.ID = foundExistingPlan.ID predefinedPlan.ExecuteCount = foundExistingPlan.ExecuteCount - if err := app.Infra.repos.planRepo.UpdatePlan(predefinedPlan); err != nil { + if err := app.Infra.repos.planRepo.UpdatePlan(appCtx, predefinedPlan); err != nil { return fmt.Errorf("更新预定义计划 '%s' 失败: %w", predefinedPlan.Name, err) } else { logger.Infof("成功更新预定义计划 '%s'。", predefinedPlan.Name) @@ -78,7 +78,7 @@ func (app *Application) initializeSystemPlans(ctx context.Context) error { } else { // 如果计划不存在, 则创建 logger.Infof("预定义计划 '%s' 不存在,正在创建...", predefinedPlan.Name) - if err := app.Infra.repos.planRepo.CreatePlan(predefinedPlan); err != nil { + if err := app.Infra.repos.planRepo.CreatePlan(appCtx, predefinedPlan); err != nil { return fmt.Errorf("创建预定义计划 '%s' 失败: %w", predefinedPlan.Name, err) } else { logger.Infof("成功创建预定义计划 '%s'。", predefinedPlan.Name) @@ -124,11 +124,11 @@ func (app *Application) getPredefinedSystemPlans() []models.Plan { // 我们的策略是:任何在程序重启前仍处于“待处理”状态的请求,都应被视为已失败。 // 这保证了系统在每次启动时都处于一个干净、确定的状态。 func (app *Application) initializePendingCollections(ctx context.Context) error { - logger := logs.TraceLogger(ctx, app.Ctx, "InitializePendingCollections") + appCtx, logger := logs.Trace(ctx, app.Ctx, "InitializePendingCollections") logger.Info("开始清理所有未完成的采集请求...") // 直接将所有 'pending' 状态的请求更新为 'timed_out'。 - count, err := app.Infra.repos.pendingCollectionRepo.MarkAllPendingAsTimedOut() + count, err := app.Infra.repos.pendingCollectionRepo.MarkAllPendingAsTimedOut(appCtx) if err != nil { return fmt.Errorf("清理未完成的采集请求失败: %v", err) } else if count > 0 { @@ -142,7 +142,7 @@ func (app *Application) initializePendingCollections(ctx context.Context) error // initializePendingTasks 在应用启动时清理并刷新待执行任务列表。 func (app *Application) initializePendingTasks(ctx context.Context) error { - logger := logs.TraceLogger(ctx, app.Ctx, "InitializePendingTasks") + appCtx, logger := logs.Trace(ctx, app.Ctx, "InitializePendingTasks") planRepo := app.Infra.repos.planRepo pendingTaskRepo := app.Infra.repos.pendingTaskRepo executionLogRepo := app.Infra.repos.executionLogRepo @@ -152,7 +152,7 @@ func (app *Application) initializePendingTasks(ctx context.Context) error { // 阶段一:修正因崩溃导致状态不一致的固定次数计划 logger.Info("阶段一:开始修正因崩溃导致状态不一致的固定次数计划...") - plansToCorrect, err := planRepo.FindPlansWithPendingTasks() + plansToCorrect, err := planRepo.FindPlansWithPendingTasks(appCtx) if err != nil { return fmt.Errorf("查找需要修正的计划失败: %w", err) } @@ -172,7 +172,7 @@ func (app *Application) initializePendingTasks(ctx context.Context) error { } // 保存更新后的计划 - if err := planRepo.UpdatePlan(plan); err != nil { + if err := planRepo.UpdatePlan(appCtx, plan); err != nil { logger.Errorf("修正计划 #%d 状态失败: %v", plan.ID, err) // 这是一个非阻塞性错误,继续处理其他计划 } @@ -184,7 +184,7 @@ func (app *Application) initializePendingTasks(ctx context.Context) error { // --- 新增逻辑:处理因崩溃导致状态不一致的计划主表状态 --- // 1. 查找所有未完成的计划执行日志 (状态为 Started 或 Waiting) - incompletePlanLogs, err := executionLogRepo.FindIncompletePlanExecutionLogs() + incompletePlanLogs, err := executionLogRepo.FindIncompletePlanExecutionLogs(appCtx) if err != nil { return fmt.Errorf("查找未完成的计划执行日志失败: %w", err) } @@ -198,7 +198,7 @@ func (app *Application) initializePendingTasks(ctx context.Context) error { // 3. 对于每个受影响的 PlanID,重置其 execute_count 并将其状态设置为 Failed, 系统计划不受此影响 for planID := range affectedPlanIDs { // 首先,获取计划的详细信息以判断其类型 - plan, err := planRepo.GetBasicPlanByID(planID) + plan, err := planRepo.GetBasicPlanByID(appCtx, planID) if err != nil { logger.Errorf("在尝试修正计划状态时,获取计划 #%d 的基本信息失败: %v", planID, err) continue // 获取失败,跳过此计划 @@ -213,7 +213,7 @@ func (app *Application) initializePendingTasks(ctx context.Context) error { // 对于非系统计划,执行原有的失败标记逻辑 logger.Warnf("检测到计划 #%d 在应用崩溃前处于未完成状态,将重置其计数并标记为失败。", planID) // 使用 UpdatePlanStateAfterExecution 来更新主表状态,避免影响关联数据 - if err := planRepo.UpdatePlanStateAfterExecution(planID, 0, models.PlanStatusFailed); err != nil { + if err := planRepo.UpdatePlanStateAfterExecution(appCtx, planID, 0, models.PlanStatusFailed); err != nil { logger.Errorf("重置计划 #%d 计数并标记为失败时出错: %v", planID, err) // 这是一个非阻塞性错误,继续处理其他计划 } @@ -221,26 +221,26 @@ func (app *Application) initializePendingTasks(ctx context.Context) error { logger.Info("阶段二:计划主表状态修正完成。") // 直接调用新的方法来更新计划执行日志状态为失败 - if err := executionLogRepo.FailAllIncompletePlanExecutionLogs(); err != nil { + if err := executionLogRepo.FailAllIncompletePlanExecutionLogs(appCtx); err != nil { logger.Errorf("更新所有未完成计划执行日志状态为失败失败: %v", err) // 这是一个非阻塞性错误,继续执行 } // 直接调用新的方法来更新任务执行日志状态为取消 - if err := executionLogRepo.CancelAllIncompleteTaskExecutionLogs(); err != nil { + if err := executionLogRepo.CancelAllIncompleteTaskExecutionLogs(appCtx); err != nil { logger.Errorf("更新所有未完成任务执行日志状态为取消失败: %v", err) // 这是一个非阻塞性错误,继续执行 } // 清空待执行列表 - if err := pendingTaskRepo.ClearAllPendingTasks(); err != nil { + if err := pendingTaskRepo.ClearAllPendingTasks(appCtx); err != nil { return fmt.Errorf("清空待执行任务列表失败: %w", err) } logger.Info("阶段二:待执行任务和相关日志清理完成。") // 阶段三:初始刷新 logger.Info("阶段三:开始刷新待执行列表...") - if err := planService.RefreshPlanTriggers(); err != nil { + if err := planService.RefreshPlanTriggers(appCtx); err != nil { return fmt.Errorf("刷新待执行任务列表失败: %w", err) } logger.Info("阶段三:待执行任务列表初始化完成。") diff --git a/internal/infra/repository/area_controller_repository.go b/internal/infra/repository/area_controller_repository.go index 45b5737..4c0b62f 100644 --- a/internal/infra/repository/area_controller_repository.go +++ b/internal/infra/repository/area_controller_repository.go @@ -1,72 +1,85 @@ package repository import ( + "context" "fmt" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) // AreaControllerRepository 定义了对 AreaController 模型的数据库操作接口 type AreaControllerRepository interface { - FindByID(id uint) (*models.AreaController, error) - FindByNetworkID(networkID string) (*models.AreaController, error) - Create(ac *models.AreaController) error - ListAll() ([]*models.AreaController, error) - Update(ac *models.AreaController) error - Delete(id uint) error + FindByID(ctx context.Context, id uint) (*models.AreaController, error) + FindByNetworkID(ctx context.Context, networkID string) (*models.AreaController, error) + Create(ctx context.Context, ac *models.AreaController) error + ListAll(ctx context.Context) ([]*models.AreaController, error) + Update(ctx context.Context, ac *models.AreaController) error + Delete(ctx context.Context, id uint) error } // gormAreaControllerRepository 是 AreaControllerRepository 的 GORM 实现。 type gormAreaControllerRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormAreaControllerRepository 创建一个新的 AreaControllerRepository GORM 实现实例。 -func NewGormAreaControllerRepository(db *gorm.DB) AreaControllerRepository { - return &gormAreaControllerRepository{db: db} +func NewGormAreaControllerRepository(ctx context.Context, db *gorm.DB) AreaControllerRepository { + return &gormAreaControllerRepository{ + ctx: ctx, + db: db, + } } // Create 创建一个新的 AreaController 记录。 -func (r *gormAreaControllerRepository) Create(ac *models.AreaController) error { - return r.db.Create(ac).Error +func (r *gormAreaControllerRepository) Create(ctx context.Context, ac *models.AreaController) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Create") + return r.db.WithContext(repoCtx).Create(ac).Error } // ListAll 返回所有 AreaController 的列表。 -func (r *gormAreaControllerRepository) ListAll() ([]*models.AreaController, error) { +func (r *gormAreaControllerRepository) ListAll(ctx context.Context) ([]*models.AreaController, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListAll") var areaControllers []*models.AreaController - if err := r.db.Find(&areaControllers).Error; err != nil { + if err := r.db.WithContext(repoCtx).Find(&areaControllers).Error; err != nil { return nil, err } return areaControllers, nil } // Update 更新一个已存在的 AreaController 记录。 -func (r *gormAreaControllerRepository) Update(ac *models.AreaController) error { - return r.db.Save(ac).Error +func (r *gormAreaControllerRepository) Update(ctx context.Context, ac *models.AreaController) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Update") + return r.db.WithContext(repoCtx).Save(ac).Error } // Delete 删除一个 AreaController 记录。 -func (r *gormAreaControllerRepository) Delete(id uint) error { - if err := r.db.Delete(&models.AreaController{}, id).Error; err != nil { +func (r *gormAreaControllerRepository) Delete(ctx context.Context, id uint) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Delete") + if err := r.db.WithContext(repoCtx).Delete(&models.AreaController{}, id).Error; err != nil { return fmt.Errorf("删除区域主控失败: %w", err) } return nil } // FindByID 通过 ID 查找一个 AreaController。 -func (r *gormAreaControllerRepository) FindByID(id uint) (*models.AreaController, error) { +func (r *gormAreaControllerRepository) FindByID(ctx context.Context, id uint) (*models.AreaController, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByID") var areaController models.AreaController - if err := r.db.First(&areaController, id).Error; err != nil { + if err := r.db.WithContext(repoCtx).First(&areaController, id).Error; err != nil { return nil, err } return &areaController, nil } // FindByNetworkID 通过 NetworkID 查找一个 AreaController。 -func (r *gormAreaControllerRepository) FindByNetworkID(networkID string) (*models.AreaController, error) { +func (r *gormAreaControllerRepository) FindByNetworkID(ctx context.Context, networkID string) (*models.AreaController, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByNetworkID") var areaController models.AreaController - if err := r.db.Where("network_id = ?", networkID).First(&areaController).Error; err != nil { + if err := r.db.WithContext(repoCtx).Where("network_id = ?", networkID).First(&areaController).Error; err != nil { return nil, err } return &areaController, nil diff --git a/internal/infra/repository/device_command_log_repository.go b/internal/infra/repository/device_command_log_repository.go index b8a219b..b4bc0bb 100644 --- a/internal/infra/repository/device_command_log_repository.go +++ b/internal/infra/repository/device_command_log_repository.go @@ -1,9 +1,12 @@ package repository import ( + "context" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) @@ -18,40 +21,44 @@ type DeviceCommandLogListOptions struct { // DeviceCommandLogRepository 定义了设备下行命令历史记录的数据访问接口 type DeviceCommandLogRepository interface { - Create(record *models.DeviceCommandLog) error - FindByMessageID(messageID string) (*models.DeviceCommandLog, error) - UpdateAcknowledgedAt(messageID string, acknowledgedAt time.Time, receivedSuccess bool) error + Create(ctx context.Context, record *models.DeviceCommandLog) error + FindByMessageID(ctx context.Context, messageID string) (*models.DeviceCommandLog, error) + UpdateAcknowledgedAt(ctx context.Context, messageID string, acknowledgedAt time.Time, receivedSuccess bool) error // List 支持分页和过滤的列表查询 - List(opts DeviceCommandLogListOptions, page, pageSize int) ([]models.DeviceCommandLog, int64, error) + List(ctx context.Context, opts DeviceCommandLogListOptions, page, pageSize int) ([]models.DeviceCommandLog, int64, error) } // gormDeviceCommandLogRepository 是 DeviceCommandLogRepository 接口的 GORM 实现 type gormDeviceCommandLogRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormDeviceCommandLogRepository 创建一个新的 DeviceCommandLogRepository GORM 实现 -func NewGormDeviceCommandLogRepository(db *gorm.DB) DeviceCommandLogRepository { - return &gormDeviceCommandLogRepository{db: db} +func NewGormDeviceCommandLogRepository(ctx context.Context, db *gorm.DB) DeviceCommandLogRepository { + return &gormDeviceCommandLogRepository{ctx: ctx, db: db} } // Create 实现 DeviceCommandLogRepository 接口的 Create 方法 -func (r *gormDeviceCommandLogRepository) Create(record *models.DeviceCommandLog) error { - return r.db.Create(record).Error +func (r *gormDeviceCommandLogRepository) Create(ctx context.Context, record *models.DeviceCommandLog) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Create") + return r.db.WithContext(repoCtx).Create(record).Error } // FindByMessageID 实现 DeviceCommandLogRepository 接口的 FindByMessageID 方法 -func (r *gormDeviceCommandLogRepository) FindByMessageID(messageID string) (*models.DeviceCommandLog, error) { +func (r *gormDeviceCommandLogRepository) FindByMessageID(ctx context.Context, messageID string) (*models.DeviceCommandLog, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByMessageID") var record models.DeviceCommandLog - if err := r.db.Where("message_id = ?", messageID).First(&record).Error; err != nil { + if err := r.db.WithContext(repoCtx).Where("message_id = ?", messageID).First(&record).Error; err != nil { return nil, err } return &record, nil } // UpdateAcknowledgedAt 实现 DeviceCommandLogRepository 接口的 UpdateAcknowledgedAt 方法 -func (r *gormDeviceCommandLogRepository) UpdateAcknowledgedAt(messageID string, acknowledgedAt time.Time, receivedSuccess bool) error { - return r.db.Model(&models.DeviceCommandLog{}). +func (r *gormDeviceCommandLogRepository) UpdateAcknowledgedAt(ctx context.Context, messageID string, acknowledgedAt time.Time, receivedSuccess bool) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdateAcknowledgedAt") + return r.db.WithContext(repoCtx).Model(&models.DeviceCommandLog{}). Where("message_id = ?", messageID). Updates(map[string]interface{}{ "acknowledged_at": acknowledgedAt, @@ -60,7 +67,8 @@ func (r *gormDeviceCommandLogRepository) UpdateAcknowledgedAt(messageID string, } // List 实现了分页和过滤查询设备命令日志的功能 -func (r *gormDeviceCommandLogRepository) List(opts DeviceCommandLogListOptions, page, pageSize int) ([]models.DeviceCommandLog, int64, error) { +func (r *gormDeviceCommandLogRepository) List(ctx context.Context, opts DeviceCommandLogListOptions, page, pageSize int) ([]models.DeviceCommandLog, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "List") // --- 校验分页参数 --- if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination @@ -69,7 +77,7 @@ func (r *gormDeviceCommandLogRepository) List(opts DeviceCommandLogListOptions, var results []models.DeviceCommandLog var total int64 - query := r.db.Model(&models.DeviceCommandLog{}) + query := r.db.WithContext(repoCtx).Model(&models.DeviceCommandLog{}) // --- 应用过滤条件 --- if opts.DeviceID != nil { diff --git a/internal/infra/repository/device_repository.go b/internal/infra/repository/device_repository.go index 8b2d167..32757e3 100644 --- a/internal/infra/repository/device_repository.go +++ b/internal/infra/repository/device_repository.go @@ -1,10 +1,13 @@ package repository import ( + "context" "fmt" "strconv" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) @@ -12,83 +15,89 @@ import ( // 这一层抽象使得上层业务逻辑无需关心底层数据库的具体实现。 type DeviceRepository interface { // Create 创建一个新设备记录 - Create(device *models.Device) error + Create(ctx context.Context, device *models.Device) error // FindByID 根据主键 ID 查找设备 - FindByID(id uint) (*models.Device, error) + FindByID(ctx context.Context, id uint) (*models.Device, error) // FindByIDString 根据字符串形式的主键 ID 查找设备 - FindByIDString(id string) (*models.Device, error) + FindByIDString(ctx context.Context, id string) (*models.Device, error) // ListAll 获取所有设备的列表 - ListAll() ([]*models.Device, error) + ListAll(ctx context.Context) ([]*models.Device, error) // ListAllSensors 获取所有传感器类型的设备列表 - ListAllSensors() ([]*models.Device, error) + ListAllSensors(ctx context.Context) ([]*models.Device, error) // ListByAreaControllerID 根据区域主控 ID 列出所有子设备 - ListByAreaControllerID(areaControllerID uint) ([]*models.Device, error) + ListByAreaControllerID(ctx context.Context, areaControllerID uint) ([]*models.Device, error) // FindByDeviceTemplateID 根据设备模板ID查找所有使用该模板的设备 - FindByDeviceTemplateID(deviceTemplateID uint) ([]*models.Device, error) + FindByDeviceTemplateID(ctx context.Context, deviceTemplateID uint) ([]*models.Device, error) // Update 更新一个已有的设备信息 - Update(device *models.Device) error + Update(ctx context.Context, device *models.Device) error // Delete 根据主键 ID 删除一个设备 - Delete(id uint) error + Delete(ctx context.Context, id uint) error // FindByAreaControllerAndPhysicalAddress 根据区域主控ID和物理地址(总线号、总线地址)查找设备 - FindByAreaControllerAndPhysicalAddress(areaControllerID uint, busNumber int, busAddress int) (*models.Device, error) + FindByAreaControllerAndPhysicalAddress(ctx context.Context, areaControllerID uint, busNumber int, busAddress int) (*models.Device, error) // GetDevicesByIDsTx 在指定事务中根据ID列表获取设备 - GetDevicesByIDsTx(tx *gorm.DB, ids []uint) ([]models.Device, error) + GetDevicesByIDsTx(ctx context.Context, tx *gorm.DB, ids []uint) ([]models.Device, error) // IsDeviceInUse 检查设备是否被任何任务使用 - IsDeviceInUse(deviceID uint) (bool, error) + IsDeviceInUse(ctx context.Context, deviceID uint) (bool, error) // IsAreaControllerInUse 检查区域主控是否被任何设备使用 - IsAreaControllerInUse(areaControllerID uint) (bool, error) + IsAreaControllerInUse(ctx context.Context, areaControllerID uint) (bool, error) } // gormDeviceRepository 是 DeviceRepository 的 GORM 实现 type gormDeviceRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormDeviceRepository 创建一个新的 DeviceRepository GORM 实现实例 -func NewGormDeviceRepository(db *gorm.DB) DeviceRepository { - return &gormDeviceRepository{db: db} +func NewGormDeviceRepository(ctx context.Context, db *gorm.DB) DeviceRepository { + return &gormDeviceRepository{ctx: ctx, db: db} } // Create 创建一个新的设备记录 -func (r *gormDeviceRepository) Create(device *models.Device) error { - return r.db.Create(device).Error +func (r *gormDeviceRepository) Create(ctx context.Context, device *models.Device) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Create") + // BeforeSave 钩子会在这里被自动触发 + return r.db.WithContext(repoCtx).Create(device).Error } // FindByID 根据 ID 查找设备 -func (r *gormDeviceRepository) FindByID(id uint) (*models.Device, error) { +func (r *gormDeviceRepository) FindByID(ctx context.Context, id uint) (*models.Device, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByID") var device models.Device - if err := r.db.Preload("AreaController").Preload("DeviceTemplate").First(&device, id).Error; err != nil { + if err := r.db.WithContext(repoCtx).Preload("AreaController").Preload("DeviceTemplate").First(&device, id).Error; err != nil { return nil, err } return &device, nil } // GetDevicesByIDsTx 在指定事务中根据ID列表获取设备 -func (r *gormDeviceRepository) GetDevicesByIDsTx(tx *gorm.DB, ids []uint) ([]models.Device, error) { +func (r *gormDeviceRepository) GetDevicesByIDsTx(ctx context.Context, tx *gorm.DB, ids []uint) ([]models.Device, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetDevicesByIDsTx") var devices []models.Device if len(ids) == 0 { return devices, nil } - if err := tx.Where("id IN ?", ids).Find(&devices).Error; err != nil { + if err := tx.WithContext(repoCtx).Where("id IN ?", ids).Find(&devices).Error; err != nil { return nil, err } return devices, nil } // FindByIDString 根据字符串形式的主键 ID 查找设备 -func (r *gormDeviceRepository) FindByIDString(id string) (*models.Device, error) { +func (r *gormDeviceRepository) FindByIDString(ctx context.Context, id string) (*models.Device, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByIDString") // 将字符串ID转换为uint64 idInt, err := strconv.ParseUint(id, 10, 64) if err != nil { @@ -96,22 +105,24 @@ func (r *gormDeviceRepository) FindByIDString(id string) (*models.Device, error) return nil, fmt.Errorf("无效的设备ID格式: %w", err) } // 调用已有的 FindByID 方法 - return r.FindByID(uint(idInt)) + return r.FindByID(repoCtx, uint(idInt)) } // ListAll 获取所有设备的列表 -func (r *gormDeviceRepository) ListAll() ([]*models.Device, error) { +func (r *gormDeviceRepository) ListAll(ctx context.Context) ([]*models.Device, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListAll") var devices []*models.Device - if err := r.db.Preload("AreaController").Preload("DeviceTemplate").Find(&devices).Error; err != nil { + if err := r.db.WithContext(repoCtx).Preload("AreaController").Preload("DeviceTemplate").Find(&devices).Error; err != nil { return nil, err } return devices, nil } // ListAllSensors 检索归类为传感器的所有设备 -func (r *gormDeviceRepository) ListAllSensors() ([]*models.Device, error) { +func (r *gormDeviceRepository) ListAllSensors(ctx context.Context) ([]*models.Device, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListAllSensors") var sensors []*models.Device - err := r.db.Preload("AreaController").Preload("DeviceTemplate"). + err := r.db.WithContext(repoCtx).Preload("AreaController").Preload("DeviceTemplate"). Joins("JOIN device_templates ON device_templates.id = devices.device_template_id"). Where("device_templates.category = ?", models.CategorySensor). Find(&sensors).Error @@ -122,9 +133,10 @@ func (r *gormDeviceRepository) ListAllSensors() ([]*models.Device, error) { } // ListByAreaControllerID 根据区域主控 ID 列出所有子设备 -func (r *gormDeviceRepository) ListByAreaControllerID(areaControllerID uint) ([]*models.Device, error) { +func (r *gormDeviceRepository) ListByAreaControllerID(ctx context.Context, areaControllerID uint) ([]*models.Device, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListByAreaControllerID") var devices []*models.Device - err := r.db.Preload("AreaController").Preload("DeviceTemplate").Where("area_controller_id = ?", areaControllerID).Find(&devices).Error + err := r.db.WithContext(repoCtx).Preload("AreaController").Preload("DeviceTemplate").Where("area_controller_id = ?", areaControllerID).Find(&devices).Error if err != nil { return nil, err } @@ -132,9 +144,10 @@ func (r *gormDeviceRepository) ListByAreaControllerID(areaControllerID uint) ([] } // FindByDeviceTemplateID 根据设备模板ID查找所有使用该模板的设备 -func (r *gormDeviceRepository) FindByDeviceTemplateID(deviceTemplateID uint) ([]*models.Device, error) { +func (r *gormDeviceRepository) FindByDeviceTemplateID(ctx context.Context, deviceTemplateID uint) ([]*models.Device, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByDeviceTemplateID") var devices []*models.Device - err := r.db.Where("device_template_id = ?", deviceTemplateID).Find(&devices).Error + err := r.db.WithContext(repoCtx).Where("device_template_id = ?", deviceTemplateID).Find(&devices).Error if err != nil { return nil, fmt.Errorf("查询使用设备模板ID %d 的设备失败: %w", deviceTemplateID, err) } @@ -143,20 +156,23 @@ func (r *gormDeviceRepository) FindByDeviceTemplateID(deviceTemplateID uint) ([] // Update 更新一个已有的设备信息 // GORM 的 Save 方法会自动处理主键存在时更新,不存在时创建的逻辑,但这里我们明确用于更新。 -func (r *gormDeviceRepository) Update(device *models.Device) error { - return r.db.Save(device).Error +func (r *gormDeviceRepository) Update(ctx context.Context, device *models.Device) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Update") + return r.db.WithContext(repoCtx).Save(device).Error } // Delete 根据 ID 删除一个设备 // GORM 使用软删除,记录不会从数据库中物理移除,而是设置 DeletedAt 字段。 -func (r *gormDeviceRepository) Delete(id uint) error { - return r.db.Delete(&models.Device{}, id).Error +func (r *gormDeviceRepository) Delete(ctx context.Context, id uint) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Delete") + return r.db.WithContext(repoCtx).Delete(&models.Device{}, id).Error } // FindByAreaControllerAndPhysicalAddress 根据区域主控ID和物理地址(总线号、总线地址)查找设备 -func (r *gormDeviceRepository) FindByAreaControllerAndPhysicalAddress(areaControllerID uint, busNumber int, busAddress int) (*models.Device, error) { +func (r *gormDeviceRepository) FindByAreaControllerAndPhysicalAddress(ctx context.Context, areaControllerID uint, busNumber int, busAddress int) (*models.Device, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByAreaControllerAndPhysicalAddress") var device models.Device - err := r.db.Preload("AreaController").Preload("DeviceTemplate"). + err := r.db.WithContext(repoCtx).Preload("AreaController").Preload("DeviceTemplate"). Where("area_controller_id = ?", areaControllerID). Where("properties->>'bus_number' = ?", strconv.Itoa(busNumber)). Where("properties->>'bus_address' = ?", strconv.Itoa(busAddress)). @@ -169,10 +185,11 @@ func (r *gormDeviceRepository) FindByAreaControllerAndPhysicalAddress(areaContro } // IsDeviceInUse 检查设备是否被任何任务使用 -func (r *gormDeviceRepository) IsDeviceInUse(deviceID uint) (bool, error) { +func (r *gormDeviceRepository) IsDeviceInUse(ctx context.Context, deviceID uint) (bool, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "IsDeviceInUse") var count int64 // 直接对 device_tasks 关联表进行 COUNT 操作,性能最高 - err := r.db.Model(&models.DeviceTask{}).Where("device_id = ?", deviceID).Count(&count).Error + err := r.db.WithContext(repoCtx).Model(&models.DeviceTask{}).Where("device_id = ?", deviceID).Count(&count).Error if err != nil { return false, fmt.Errorf("查询设备任务关联失败: %w", err) } @@ -180,9 +197,10 @@ func (r *gormDeviceRepository) IsDeviceInUse(deviceID uint) (bool, error) { } // IsAreaControllerInUse 检查区域主控是否被任何设备使用 -func (r *gormDeviceRepository) IsAreaControllerInUse(areaControllerID uint) (bool, error) { +func (r *gormDeviceRepository) IsAreaControllerInUse(ctx context.Context, areaControllerID uint) (bool, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "IsAreaControllerInUse") var count int64 - if err := r.db.Model(&models.Device{}).Where("area_controller_id = ?", areaControllerID).Count(&count).Error; err != nil { + if err := r.db.WithContext(repoCtx).Model(&models.Device{}).Where("area_controller_id = ?", areaControllerID).Count(&count).Error; err != nil { return false, fmt.Errorf("检查区域主控使用情况失败: %w", err) } return count > 0, nil diff --git a/internal/infra/repository/device_template_repository.go b/internal/infra/repository/device_template_repository.go index 5872f50..dd9c0b1 100644 --- a/internal/infra/repository/device_template_repository.go +++ b/internal/infra/repository/device_template_repository.go @@ -1,43 +1,49 @@ package repository import ( + "context" "errors" "fmt" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) // DeviceTemplateRepository 定义了设备模板数据访问的接口 type DeviceTemplateRepository interface { - Create(deviceTemplate *models.DeviceTemplate) error - FindByID(id uint) (*models.DeviceTemplate, error) - FindByName(name string) (*models.DeviceTemplate, error) - ListAll() ([]*models.DeviceTemplate, error) - Update(deviceTemplate *models.DeviceTemplate) error - Delete(id uint) error - IsInUse(id uint) (bool, error) + Create(ctx context.Context, deviceTemplate *models.DeviceTemplate) error + FindByID(ctx context.Context, id uint) (*models.DeviceTemplate, error) + FindByName(ctx context.Context, name string) (*models.DeviceTemplate, error) + ListAll(ctx context.Context) ([]*models.DeviceTemplate, error) + Update(ctx context.Context, deviceTemplate *models.DeviceTemplate) error + Delete(ctx context.Context, id uint) error + IsInUse(ctx context.Context, id uint) (bool, error) } // gormDeviceTemplateRepository 是 DeviceTemplateRepository 的 GORM 实现 type gormDeviceTemplateRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormDeviceTemplateRepository 创建一个新的 gormDeviceTemplateRepository 实例 -func NewGormDeviceTemplateRepository(db *gorm.DB) DeviceTemplateRepository { - return &gormDeviceTemplateRepository{db: db} +func NewGormDeviceTemplateRepository(ctx context.Context, db *gorm.DB) DeviceTemplateRepository { + return &gormDeviceTemplateRepository{ctx: ctx, db: db} } // Create 在数据库中创建一个新的设备模板 -func (r *gormDeviceTemplateRepository) Create(deviceTemplate *models.DeviceTemplate) error { - return r.db.Create(deviceTemplate).Error +func (r *gormDeviceTemplateRepository) Create(ctx context.Context, deviceTemplate *models.DeviceTemplate) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Create") + return r.db.WithContext(repoCtx).Create(deviceTemplate).Error } // FindByID 根据ID查找设备模板 -func (r *gormDeviceTemplateRepository) FindByID(id uint) (*models.DeviceTemplate, error) { +func (r *gormDeviceTemplateRepository) FindByID(ctx context.Context, id uint) (*models.DeviceTemplate, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByID") var deviceTemplate models.DeviceTemplate - if err := r.db.First(&deviceTemplate, id).Error; err != nil { + if err := r.db.WithContext(repoCtx).First(&deviceTemplate, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, fmt.Errorf("设备模板未找到: %w", err) } @@ -47,9 +53,10 @@ func (r *gormDeviceTemplateRepository) FindByID(id uint) (*models.DeviceTemplate } // FindByName 根据名称查找设备模板 -func (r *gormDeviceTemplateRepository) FindByName(name string) (*models.DeviceTemplate, error) { +func (r *gormDeviceTemplateRepository) FindByName(ctx context.Context, name string) (*models.DeviceTemplate, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByName") var deviceTemplate models.DeviceTemplate - if err := r.db.Where("name = ?", name).First(&deviceTemplate).Error; err != nil { + if err := r.db.WithContext(repoCtx).Where("name = ?", name).First(&deviceTemplate).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, fmt.Errorf("设备模板未找到: %w", err) } @@ -59,31 +66,35 @@ func (r *gormDeviceTemplateRepository) FindByName(name string) (*models.DeviceTe } // ListAll 获取所有设备模板 -func (r *gormDeviceTemplateRepository) ListAll() ([]*models.DeviceTemplate, error) { +func (r *gormDeviceTemplateRepository) ListAll(ctx context.Context) ([]*models.DeviceTemplate, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListAll") var deviceTemplates []*models.DeviceTemplate - if err := r.db.Find(&deviceTemplates).Error; err != nil { + if err := r.db.WithContext(repoCtx).Find(&deviceTemplates).Error; err != nil { return nil, fmt.Errorf("获取设备模板列表失败: %w", err) } return deviceTemplates, nil } // Update 更新数据库中的设备模板 -func (r *gormDeviceTemplateRepository) Update(deviceTemplate *models.DeviceTemplate) error { - return r.db.Save(deviceTemplate).Error +func (r *gormDeviceTemplateRepository) Update(ctx context.Context, deviceTemplate *models.DeviceTemplate) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Update") + return r.db.WithContext(repoCtx).Save(deviceTemplate).Error } // IsInUse 检查设备模板是否正在被设备使用 -func (r *gormDeviceTemplateRepository) IsInUse(id uint) (bool, error) { +func (r *gormDeviceTemplateRepository) IsInUse(ctx context.Context, id uint) (bool, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "IsInUse") var count int64 - if err := r.db.Model(&models.Device{}).Where("device_template_id = ?", id).Count(&count).Error; err != nil { + if err := r.db.WithContext(repoCtx).Model(&models.Device{}).Where("device_template_id = ?", id).Count(&count).Error; err != nil { return false, fmt.Errorf("检查设备模板使用情况失败: %w", err) } return count > 0, nil } // Delete 软删除数据库中的设备模板 -func (r *gormDeviceTemplateRepository) Delete(id uint) error { - if err := r.db.Delete(&models.DeviceTemplate{}, id).Error; err != nil { +func (r *gormDeviceTemplateRepository) Delete(ctx context.Context, id uint) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Delete") + if err := r.db.WithContext(repoCtx).Delete(&models.DeviceTemplate{}, id).Error; err != nil { return fmt.Errorf("删除设备模板失败: %w", err) } return nil diff --git a/internal/infra/repository/execution_log_repository.go b/internal/infra/repository/execution_log_repository.go index beac35e..bd4a1fb 100644 --- a/internal/infra/repository/execution_log_repository.go +++ b/internal/infra/repository/execution_log_repository.go @@ -1,10 +1,13 @@ package repository import ( + "context" "errors" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) @@ -30,61 +33,63 @@ type TaskExecutionLogListOptions struct { // ExecutionLogRepository 定义了与执行日志交互的接口。 type ExecutionLogRepository interface { // --- Existing methods --- - UpdateTaskExecutionLogStatusByIDs(logIDs []uint, status models.ExecutionStatus) error - UpdateTaskExecutionLogStatus(logID uint, status models.ExecutionStatus) error - CreateTaskExecutionLog(log *models.TaskExecutionLog) error - CreatePlanExecutionLog(log *models.PlanExecutionLog) error - UpdatePlanExecutionLog(log *models.PlanExecutionLog) error - CreateTaskExecutionLogsInBatch(logs []*models.TaskExecutionLog) error - UpdateTaskExecutionLog(log *models.TaskExecutionLog) error - FindTaskExecutionLogByID(id uint) (*models.TaskExecutionLog, error) + UpdateTaskExecutionLogStatusByIDs(ctx context.Context, logIDs []uint, status models.ExecutionStatus) error + UpdateTaskExecutionLogStatus(ctx context.Context, logID uint, status models.ExecutionStatus) error + CreateTaskExecutionLog(ctx context.Context, log *models.TaskExecutionLog) error + CreatePlanExecutionLog(ctx context.Context, log *models.PlanExecutionLog) error + UpdatePlanExecutionLog(ctx context.Context, log *models.PlanExecutionLog) error + CreateTaskExecutionLogsInBatch(ctx context.Context, logs []*models.TaskExecutionLog) error + UpdateTaskExecutionLog(ctx context.Context, log *models.TaskExecutionLog) error + FindTaskExecutionLogByID(ctx context.Context, id uint) (*models.TaskExecutionLog, error) // UpdatePlanExecutionLogStatus 更新计划执行日志的状态 - UpdatePlanExecutionLogStatus(logID uint, status models.ExecutionStatus) error + UpdatePlanExecutionLogStatus(ctx context.Context, logID uint, status models.ExecutionStatus) error // UpdatePlanExecutionLogsStatusByIDs 批量更新计划执行日志的状态 - UpdatePlanExecutionLogsStatusByIDs(logIDs []uint, status models.ExecutionStatus) error + UpdatePlanExecutionLogsStatusByIDs(ctx context.Context, logIDs []uint, status models.ExecutionStatus) error // FindIncompletePlanExecutionLogs 查找所有未完成的计划执行日志 - FindIncompletePlanExecutionLogs() ([]models.PlanExecutionLog, error) + FindIncompletePlanExecutionLogs(ctx context.Context) ([]models.PlanExecutionLog, error) // FindInProgressPlanExecutionLogByPlanID 根据 PlanID 查找正在进行的计划执行日志 - FindInProgressPlanExecutionLogByPlanID(planID uint) (*models.PlanExecutionLog, error) + FindInProgressPlanExecutionLogByPlanID(ctx context.Context, planID uint) (*models.PlanExecutionLog, error) // FindIncompleteTaskExecutionLogsByPlanLogID 根据计划日志ID查找所有未完成的任务日志 - FindIncompleteTaskExecutionLogsByPlanLogID(planLogID uint) ([]models.TaskExecutionLog, error) + FindIncompleteTaskExecutionLogsByPlanLogID(ctx context.Context, planLogID uint) ([]models.TaskExecutionLog, error) // FailAllIncompletePlanExecutionLogs 将所有状态为 ExecutionStatusStarted 和 ExecutionStatusWaiting 的计划状态都修改为 ExecutionStatusFailed - FailAllIncompletePlanExecutionLogs() error + FailAllIncompletePlanExecutionLogs(ctx context.Context) error // CancelAllIncompleteTaskExecutionLogs 将所有状态为 ExecutionStatusStarted 和 ExecutionStatusWaiting 的任务状态修改为 ExecutionStatusCancelled - CancelAllIncompleteTaskExecutionLogs() error + CancelAllIncompleteTaskExecutionLogs(ctx context.Context) error // FindPlanExecutionLogByID 根据ID查找计划执行日志 - FindPlanExecutionLogByID(id uint) (*models.PlanExecutionLog, error) + FindPlanExecutionLogByID(ctx context.Context, id uint) (*models.PlanExecutionLog, error) // CountIncompleteTasksByPlanLogID 计算一个计划执行中未完成的任务数量 - CountIncompleteTasksByPlanLogID(planLogID uint) (int64, error) + CountIncompleteTasksByPlanLogID(ctx context.Context, planLogID uint) (int64, error) // FailPlanExecution 将指定的计划执行标记为失败 - FailPlanExecution(planLogID uint, errorMessage string) error + FailPlanExecution(ctx context.Context, planLogID uint, errorMessage string) error // CancelIncompleteTasksByPlanLogID 取消一个计划执行中的所有未完成任务 - CancelIncompleteTasksByPlanLogID(planLogID uint, reason string) error + CancelIncompleteTasksByPlanLogID(ctx context.Context, planLogID uint, reason string) error // --- New methods --- - ListPlanExecutionLogs(opts PlanExecutionLogListOptions, page, pageSize int) ([]models.PlanExecutionLog, int64, error) - ListTaskExecutionLogs(opts TaskExecutionLogListOptions, page, pageSize int) ([]models.TaskExecutionLog, int64, error) + ListPlanExecutionLogs(ctx context.Context, opts PlanExecutionLogListOptions, page, pageSize int) ([]models.PlanExecutionLog, int64, error) + ListTaskExecutionLogs(ctx context.Context, opts TaskExecutionLogListOptions, page, pageSize int) ([]models.TaskExecutionLog, int64, error) } // gormExecutionLogRepository 是使用 GORM 的具体实现。 type gormExecutionLogRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormExecutionLogRepository 创建一个新的执行日志仓库。 -func NewGormExecutionLogRepository(db *gorm.DB) ExecutionLogRepository { - return &gormExecutionLogRepository{db: db} +func NewGormExecutionLogRepository(ctx context.Context, db *gorm.DB) ExecutionLogRepository { + return &gormExecutionLogRepository{ctx: ctx, db: db} } // ListPlanExecutionLogs 实现了分页和过滤查询计划执行日志的功能 -func (r *gormExecutionLogRepository) ListPlanExecutionLogs(opts PlanExecutionLogListOptions, page, pageSize int) ([]models.PlanExecutionLog, int64, error) { +func (r *gormExecutionLogRepository) ListPlanExecutionLogs(ctx context.Context, opts PlanExecutionLogListOptions, page, pageSize int) ([]models.PlanExecutionLog, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListPlanExecutionLogs") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -92,7 +97,7 @@ func (r *gormExecutionLogRepository) ListPlanExecutionLogs(opts PlanExecutionLog var results []models.PlanExecutionLog var total int64 - query := r.db.Model(&models.PlanExecutionLog{}) + query := r.db.WithContext(repoCtx).Model(&models.PlanExecutionLog{}) if opts.PlanID != nil { query = query.Where("plan_id = ?", *opts.PlanID) @@ -124,7 +129,8 @@ func (r *gormExecutionLogRepository) ListPlanExecutionLogs(opts PlanExecutionLog } // ListTaskExecutionLogs 实现了分页和过滤查询任务执行日志的功能 -func (r *gormExecutionLogRepository) ListTaskExecutionLogs(opts TaskExecutionLogListOptions, page, pageSize int) ([]models.TaskExecutionLog, int64, error) { +func (r *gormExecutionLogRepository) ListTaskExecutionLogs(ctx context.Context, opts TaskExecutionLogListOptions, page, pageSize int) ([]models.TaskExecutionLog, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListTaskExecutionLogs") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -132,7 +138,7 @@ func (r *gormExecutionLogRepository) ListTaskExecutionLogs(opts TaskExecutionLog var results []models.TaskExecutionLog var total int64 - query := r.db.Model(&models.TaskExecutionLog{}) + query := r.db.WithContext(repoCtx).Model(&models.TaskExecutionLog{}) if opts.PlanExecutionLogID != nil { query = query.Where("plan_execution_log_id = ?", *opts.PlanExecutionLogID) @@ -169,56 +175,64 @@ func (r *gormExecutionLogRepository) ListTaskExecutionLogs(opts TaskExecutionLog // --- Existing method implementations --- -func (r *gormExecutionLogRepository) UpdateTaskExecutionLogStatusByIDs(logIDs []uint, status models.ExecutionStatus) error { +func (r *gormExecutionLogRepository) UpdateTaskExecutionLogStatusByIDs(ctx context.Context, logIDs []uint, status models.ExecutionStatus) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdateTaskExecutionLogStatusByIDs") if len(logIDs) == 0 { return nil } - return r.db.Model(&models.TaskExecutionLog{}).Where("id IN ?", logIDs).Update("status", status).Error + return r.db.WithContext(repoCtx).Model(&models.TaskExecutionLog{}).Where("id IN ?", logIDs).Update("status", status).Error } -func (r *gormExecutionLogRepository) UpdateTaskExecutionLogStatus(logID uint, status models.ExecutionStatus) error { - return r.db.Model(&models.TaskExecutionLog{}).Where("id = ?", logID).Update("status", status).Error +func (r *gormExecutionLogRepository) UpdateTaskExecutionLogStatus(ctx context.Context, logID uint, status models.ExecutionStatus) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdateTaskExecutionLogStatus") + return r.db.WithContext(repoCtx).Model(&models.TaskExecutionLog{}).Where("id = ?", logID).Update("status", status).Error } -func (r *gormExecutionLogRepository) CreateTaskExecutionLog(log *models.TaskExecutionLog) error { - return r.db.Create(log).Error +func (r *gormExecutionLogRepository) CreateTaskExecutionLog(ctx context.Context, log *models.TaskExecutionLog) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateTaskExecutionLog") + return r.db.WithContext(repoCtx).Create(log).Error } // CreatePlanExecutionLog 为一次计划执行创建一条新的日志条目。 -func (r *gormExecutionLogRepository) CreatePlanExecutionLog(log *models.PlanExecutionLog) error { - return r.db.Create(log).Error +func (r *gormExecutionLogRepository) CreatePlanExecutionLog(ctx context.Context, log *models.PlanExecutionLog) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePlanExecutionLog") + return r.db.WithContext(repoCtx).Create(log).Error } // UpdatePlanExecutionLog 使用 Updates 方法更新一个计划执行日志。 // GORM 的 Updates 传入 struct 时,只会更新非零值字段。 // 在这里,我们期望传入的对象一定包含一个有效的 ID。 -func (r *gormExecutionLogRepository) UpdatePlanExecutionLog(log *models.PlanExecutionLog) error { - return r.db.Updates(log).Error +func (r *gormExecutionLogRepository) UpdatePlanExecutionLog(ctx context.Context, log *models.PlanExecutionLog) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePlanExecutionLog") + return r.db.WithContext(repoCtx).Updates(log).Error } // CreateTaskExecutionLogsInBatch 在一次数据库调用中创建多个任务执行日志条目。 // 这是“预写日志”步骤的关键。 -func (r *gormExecutionLogRepository) CreateTaskExecutionLogsInBatch(logs []*models.TaskExecutionLog) error { - if len(logs) == 0 { +func (r *gormExecutionLogRepository) CreateTaskExecutionLogsInBatch(ctx context.Context, executionLogs []*models.TaskExecutionLog) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateTaskExecutionLogsInBatch") + if len(executionLogs) == 0 { return nil } // GORM 的 CreateTx 传入一个切片指针会执行批量插入。 - return r.db.Create(&logs).Error + return r.db.WithContext(repoCtx).Create(&executionLogs).Error } // UpdateTaskExecutionLog 使用 Updates 方法更新一个任务执行日志。 // GORM 的 Updates 传入 struct 时,只会更新非零值字段。 // 这种方式代码更直观,上层服务可以直接修改模型对象后进行保存。 -func (r *gormExecutionLogRepository) UpdateTaskExecutionLog(log *models.TaskExecutionLog) error { - return r.db.Updates(log).Error +func (r *gormExecutionLogRepository) UpdateTaskExecutionLog(ctx context.Context, log *models.TaskExecutionLog) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdateTaskExecutionLog") + return r.db.WithContext(repoCtx).Updates(log).Error } // FindTaskExecutionLogByID 根据 ID 查找单个任务执行日志。 // 它会预加载关联的 Task 信息。 -func (r *gormExecutionLogRepository) FindTaskExecutionLogByID(id uint) (*models.TaskExecutionLog, error) { +func (r *gormExecutionLogRepository) FindTaskExecutionLogByID(ctx context.Context, id uint) (*models.TaskExecutionLog, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindTaskExecutionLogByID") var log models.TaskExecutionLog // 使用 Preload("Task") 来确保关联的任务信息被一并加载 - err := r.db.Preload("Task").First(&log, id).Error + err := r.db.WithContext(repoCtx).Preload("Task").First(&log, id).Error if err != nil { return nil, err } @@ -226,29 +240,33 @@ func (r *gormExecutionLogRepository) FindTaskExecutionLogByID(id uint) (*models. } // UpdatePlanExecutionLogStatus 更新计划执行日志的状态 -func (r *gormExecutionLogRepository) UpdatePlanExecutionLogStatus(logID uint, status models.ExecutionStatus) error { - return r.db.Model(&models.PlanExecutionLog{}).Where("id = ?", logID).Update("status", status).Error +func (r *gormExecutionLogRepository) UpdatePlanExecutionLogStatus(ctx context.Context, logID uint, status models.ExecutionStatus) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePlanExecutionLogStatus") + return r.db.WithContext(repoCtx).Model(&models.PlanExecutionLog{}).Where("id = ?", logID).Update("status", status).Error } // UpdatePlanExecutionLogsStatusByIDs 批量更新计划执行日志的状态 -func (r *gormExecutionLogRepository) UpdatePlanExecutionLogsStatusByIDs(logIDs []uint, status models.ExecutionStatus) error { +func (r *gormExecutionLogRepository) UpdatePlanExecutionLogsStatusByIDs(ctx context.Context, logIDs []uint, status models.ExecutionStatus) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePlanExecutionLogsStatusByIDs") if len(logIDs) == 0 { return nil } - return r.db.Model(&models.PlanExecutionLog{}).Where("id IN ?", logIDs).Update("status", status).Error + return r.db.WithContext(repoCtx).Model(&models.PlanExecutionLog{}).Where("id IN ?", logIDs).Update("status", status).Error } // FindIncompletePlanExecutionLogs 查找所有未完成的计划执行日志 -func (r *gormExecutionLogRepository) FindIncompletePlanExecutionLogs() ([]models.PlanExecutionLog, error) { +func (r *gormExecutionLogRepository) FindIncompletePlanExecutionLogs(ctx context.Context) ([]models.PlanExecutionLog, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindIncompletePlanExecutionLogs") var logs []models.PlanExecutionLog - err := r.db.Where("status = ? OR status = ?", models.ExecutionStatusStarted, models.ExecutionStatusWaiting).Find(&logs).Error + err := r.db.WithContext(repoCtx).Where("status = ? OR status = ?", models.ExecutionStatusStarted, models.ExecutionStatusWaiting).Find(&logs).Error return logs, err } // FindInProgressPlanExecutionLogByPlanID 根据 PlanID 查找正在进行的计划执行日志 -func (r *gormExecutionLogRepository) FindInProgressPlanExecutionLogByPlanID(planID uint) (*models.PlanExecutionLog, error) { +func (r *gormExecutionLogRepository) FindInProgressPlanExecutionLogByPlanID(ctx context.Context, planID uint) (*models.PlanExecutionLog, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindInProgressPlanExecutionLogByPlanID") var log models.PlanExecutionLog - err := r.db.Where("plan_id = ? AND status = ?", planID, models.ExecutionStatusStarted).First(&log).Error + err := r.db.WithContext(repoCtx).Where("plan_id = ? AND status = ?", planID, models.ExecutionStatusStarted).First(&log).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { // 未找到不是一个需要上报的错误,代表计划当前没有在运行 @@ -261,31 +279,35 @@ func (r *gormExecutionLogRepository) FindInProgressPlanExecutionLogByPlanID(plan } // FindIncompleteTaskExecutionLogsByPlanLogID 根据计划日志ID查找所有未完成的任务日志 -func (r *gormExecutionLogRepository) FindIncompleteTaskExecutionLogsByPlanLogID(planLogID uint) ([]models.TaskExecutionLog, error) { +func (r *gormExecutionLogRepository) FindIncompleteTaskExecutionLogsByPlanLogID(ctx context.Context, planLogID uint) ([]models.TaskExecutionLog, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindIncompleteTaskExecutionLogsByPlanLogID") var logs []models.TaskExecutionLog - err := r.db.Where("plan_execution_log_id = ? AND (status = ? OR status = ?)", + err := r.db.WithContext(repoCtx).Where("plan_execution_log_id = ? AND (status = ? OR status = ?)", planLogID, models.ExecutionStatusWaiting, models.ExecutionStatusStarted).Find(&logs).Error return logs, err } // FailAllIncompletePlanExecutionLogs 将所有状态为 ExecutionStatusStarted 和 ExecutionStatusWaiting 的计划状态都修改为 ExecutionStatusFailed -func (r *gormExecutionLogRepository) FailAllIncompletePlanExecutionLogs() error { - return r.db.Model(&models.PlanExecutionLog{}). +func (r *gormExecutionLogRepository) FailAllIncompletePlanExecutionLogs(ctx context.Context) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FailAllIncompletePlanExecutionLogs") + return r.db.WithContext(repoCtx).Model(&models.PlanExecutionLog{}). Where("status IN (?, ?)", models.ExecutionStatusStarted, models.ExecutionStatusWaiting). Updates(map[string]interface{}{"status": models.ExecutionStatusFailed, "ended_at": time.Now(), "error": "系统中断"}).Error } // CancelAllIncompleteTaskExecutionLogs 将所有状态为 ExecutionStatusStarted 和 ExecutionStatusWaiting 的任务状态修改为 ExecutionStatusCancelled -func (r *gormExecutionLogRepository) CancelAllIncompleteTaskExecutionLogs() error { - return r.db.Model(&models.TaskExecutionLog{}). +func (r *gormExecutionLogRepository) CancelAllIncompleteTaskExecutionLogs(ctx context.Context) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CancelAllIncompleteTaskExecutionLogs") + return r.db.WithContext(repoCtx).Model(&models.TaskExecutionLog{}). Where("status IN (?, ?)", models.ExecutionStatusStarted, models.ExecutionStatusWaiting). Updates(map[string]interface{}{"status": models.ExecutionStatusCancelled, "ended_at": time.Now(), "output": "系统中断"}).Error } // FindPlanExecutionLogByID 根据ID查找计划执行日志 -func (r *gormExecutionLogRepository) FindPlanExecutionLogByID(id uint) (*models.PlanExecutionLog, error) { +func (r *gormExecutionLogRepository) FindPlanExecutionLogByID(ctx context.Context, id uint) (*models.PlanExecutionLog, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindPlanExecutionLogByID") var log models.PlanExecutionLog - err := r.db.First(&log, id).Error + err := r.db.WithContext(repoCtx).First(&log, id).Error if err != nil { return nil, err } @@ -293,9 +315,10 @@ func (r *gormExecutionLogRepository) FindPlanExecutionLogByID(id uint) (*models. } // CountIncompleteTasksByPlanLogID 计算一个计划执行中未完成的任务数量 -func (r *gormExecutionLogRepository) CountIncompleteTasksByPlanLogID(planLogID uint) (int64, error) { +func (r *gormExecutionLogRepository) CountIncompleteTasksByPlanLogID(ctx context.Context, planLogID uint) (int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CountIncompleteTasksByPlanLogID") var count int64 - err := r.db.Model(&models.TaskExecutionLog{}). + err := r.db.WithContext(repoCtx).Model(&models.TaskExecutionLog{}). Where("plan_execution_log_id = ? AND status IN (?, ?)", planLogID, models.ExecutionStatusWaiting, models.ExecutionStatusStarted). Count(&count).Error @@ -303,8 +326,9 @@ func (r *gormExecutionLogRepository) CountIncompleteTasksByPlanLogID(planLogID u } // FailPlanExecution 将指定的计划执行标记为失败 -func (r *gormExecutionLogRepository) FailPlanExecution(planLogID uint, errorMessage string) error { - return r.db.Model(&models.PlanExecutionLog{}). +func (r *gormExecutionLogRepository) FailPlanExecution(ctx context.Context, planLogID uint, errorMessage string) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FailPlanExecution") + return r.db.WithContext(repoCtx).Model(&models.PlanExecutionLog{}). Where("id = ?", planLogID). Updates(map[string]interface{}{ "status": models.ExecutionStatusFailed, @@ -314,8 +338,9 @@ func (r *gormExecutionLogRepository) FailPlanExecution(planLogID uint, errorMess } // CancelIncompleteTasksByPlanLogID 取消一个计划执行中的所有未完成任务 -func (r *gormExecutionLogRepository) CancelIncompleteTasksByPlanLogID(planLogID uint, reason string) error { - return r.db.Model(&models.TaskExecutionLog{}). +func (r *gormExecutionLogRepository) CancelIncompleteTasksByPlanLogID(ctx context.Context, planLogID uint, reason string) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CancelIncompleteTasksByPlanLogID") + return r.db.WithContext(repoCtx).Model(&models.TaskExecutionLog{}). Where("plan_execution_log_id = ? AND status IN (?, ?)", planLogID, models.ExecutionStatusWaiting, models.ExecutionStatusStarted). Updates(map[string]interface{}{ diff --git a/internal/infra/repository/medication_log_repository.go b/internal/infra/repository/medication_log_repository.go index 1d91de0..6db863d 100644 --- a/internal/infra/repository/medication_log_repository.go +++ b/internal/infra/repository/medication_log_repository.go @@ -1,9 +1,12 @@ package repository import ( + "context" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) @@ -20,27 +23,30 @@ type MedicationLogListOptions struct { // MedicationLogRepository 定义了与群体用药日志模型相关的数据库操作接口。 type MedicationLogRepository interface { - CreateMedicationLog(log *models.MedicationLog) error - ListMedicationLogs(opts MedicationLogListOptions, page, pageSize int) ([]models.MedicationLog, int64, error) + CreateMedicationLog(ctx context.Context, log *models.MedicationLog) error + ListMedicationLogs(ctx context.Context, opts MedicationLogListOptions, page, pageSize int) ([]models.MedicationLog, int64, error) } // gormMedicationLogRepository 是 MedicationLogRepository 接口的 GORM 实现。 type gormMedicationLogRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormMedicationLogRepository 创建一个新的 MedicationLogRepository GORM 实现实例。 -func NewGormMedicationLogRepository(db *gorm.DB) MedicationLogRepository { - return &gormMedicationLogRepository{db: db} +func NewGormMedicationLogRepository(ctx context.Context, db *gorm.DB) MedicationLogRepository { + return &gormMedicationLogRepository{ctx: ctx, db: db} } // CreateMedicationLog 创建一条新的群体用药日志记录 -func (r *gormMedicationLogRepository) CreateMedicationLog(log *models.MedicationLog) error { - return r.db.Create(log).Error +func (r *gormMedicationLogRepository) CreateMedicationLog(ctx context.Context, log *models.MedicationLog) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateMedicationLog") + return r.db.WithContext(repoCtx).Create(log).Error } // ListMedicationLogs 实现了分页和过滤查询用药记录的功能 -func (r *gormMedicationLogRepository) ListMedicationLogs(opts MedicationLogListOptions, page, pageSize int) ([]models.MedicationLog, int64, error) { +func (r *gormMedicationLogRepository) ListMedicationLogs(ctx context.Context, opts MedicationLogListOptions, page, pageSize int) ([]models.MedicationLog, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListMedicationLogs") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -48,7 +54,7 @@ func (r *gormMedicationLogRepository) ListMedicationLogs(opts MedicationLogListO var results []models.MedicationLog var total int64 - query := r.db.Model(&models.MedicationLog{}) + query := r.db.WithContext(repoCtx).Model(&models.MedicationLog{}) if opts.PigBatchID != nil { query = query.Where("pig_batch_id = ?", *opts.PigBatchID) diff --git a/internal/infra/repository/notification_repository.go b/internal/infra/repository/notification_repository.go index 5843502..4055966 100644 --- a/internal/infra/repository/notification_repository.go +++ b/internal/infra/repository/notification_repository.go @@ -1,10 +1,13 @@ package repository import ( + "context" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" "git.huangwc.com/pig/pig-farm-controller/internal/infra/notify" + "go.uber.org/zap/zapcore" "gorm.io/gorm" ) @@ -23,44 +26,49 @@ type NotificationListOptions struct { // NotificationRepository 定义了与通知记录相关的数据库操作接口。 type NotificationRepository interface { // Create 将一条新的通知记录插入数据库。 - Create(notification *models.Notification) error + Create(ctx context.Context, notification *models.Notification) error // CreateInTx 在给定的事务中插入一条新的通知记录。 - CreateInTx(tx *gorm.DB, notification *models.Notification) error + CreateInTx(ctx context.Context, tx *gorm.DB, notification *models.Notification) error // BatchCreate 批量插入多条通知记录。 - BatchCreate(notifications []*models.Notification) error + BatchCreate(ctx context.Context, notifications []*models.Notification) error // List 支持分页和过滤的通知列表查询。 // 返回通知列表、总记录数和错误。 - List(opts NotificationListOptions, page, pageSize int) ([]models.Notification, int64, error) + List(ctx context.Context, opts NotificationListOptions, page, pageSize int) ([]models.Notification, int64, error) } // gormNotificationRepository 是 NotificationRepository 的 GORM 实现。 type gormNotificationRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormNotificationRepository 创建一个新的 NotificationRepository GORM 实现实例。 -func NewGormNotificationRepository(db *gorm.DB) NotificationRepository { - return &gormNotificationRepository{db: db} +func NewGormNotificationRepository(ctx context.Context, db *gorm.DB) NotificationRepository { + return &gormNotificationRepository{ctx: ctx, db: db} } // Create 将一条新的通知记录插入数据库。 -func (r *gormNotificationRepository) Create(notification *models.Notification) error { - return r.db.Create(notification).Error +func (r *gormNotificationRepository) Create(ctx context.Context, notification *models.Notification) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Create") + return r.db.WithContext(repoCtx).Create(notification).Error } // CreateInTx 在给定的事务中插入一条新的通知记录。 -func (r *gormNotificationRepository) CreateInTx(tx *gorm.DB, notification *models.Notification) error { - return tx.Create(notification).Error +func (r *gormNotificationRepository) CreateInTx(ctx context.Context, tx *gorm.DB, notification *models.Notification) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateInTx") + return tx.WithContext(repoCtx).Create(notification).Error } // BatchCreate 批量插入多条通知记录。 -func (r *gormNotificationRepository) BatchCreate(notifications []*models.Notification) error { +func (r *gormNotificationRepository) BatchCreate(ctx context.Context, notifications []*models.Notification) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "BatchCreate") // GORM 的 Create 方法在传入切片时会自动进行批量插入 - return r.db.Create(¬ifications).Error + return r.db.WithContext(repoCtx).Create(¬ifications).Error } // List 实现了分页和过滤查询通知记录的功能 -func (r *gormNotificationRepository) List(opts NotificationListOptions, page, pageSize int) ([]models.Notification, int64, error) { +func (r *gormNotificationRepository) List(ctx context.Context, opts NotificationListOptions, page, pageSize int) ([]models.Notification, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "List") // --- 校验分页参数 --- if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination // 复用已定义的错误 @@ -69,7 +77,7 @@ func (r *gormNotificationRepository) List(opts NotificationListOptions, page, pa var results []models.Notification var total int64 - query := r.db.Model(&models.Notification{}) + query := r.db.WithContext(repoCtx).Model(&models.Notification{}) // --- 应用过滤条件 --- if opts.UserID != nil { diff --git a/internal/infra/repository/pending_collection_repository.go b/internal/infra/repository/pending_collection_repository.go index 68adf05..77ab3ad 100644 --- a/internal/infra/repository/pending_collection_repository.go +++ b/internal/infra/repository/pending_collection_repository.go @@ -1,9 +1,12 @@ package repository import ( + "context" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) @@ -19,48 +22,52 @@ type PendingCollectionListOptions struct { // PendingCollectionRepository 定义了与待采集请求相关的数据库操作接口。 type PendingCollectionRepository interface { // Create 创建一个新的待采集请求。 - Create(req *models.PendingCollection) error + Create(ctx context.Context, req *models.PendingCollection) error // FindByCorrelationID 根据关联ID查找一个待采集请求。 - FindByCorrelationID(correlationID string) (*models.PendingCollection, error) + FindByCorrelationID(ctx context.Context, correlationID string) (*models.PendingCollection, error) // UpdateStatusToFulfilled 将指定关联ID的请求状态更新为“已完成”。 - UpdateStatusToFulfilled(correlationID string, fulfilledAt time.Time) error + UpdateStatusToFulfilled(ctx context.Context, correlationID string, fulfilledAt time.Time) error // MarkAllPendingAsTimedOut 将所有“待处理”请求更新为“已超时”。 - MarkAllPendingAsTimedOut() (int64, error) + MarkAllPendingAsTimedOut(ctx context.Context) (int64, error) // List 支持分页和过滤的列表查询 - List(opts PendingCollectionListOptions, page, pageSize int) ([]models.PendingCollection, int64, error) + List(ctx context.Context, opts PendingCollectionListOptions, page, pageSize int) ([]models.PendingCollection, int64, error) } // gormPendingCollectionRepository 是 PendingCollectionRepository 的 GORM 实现。 type gormPendingCollectionRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormPendingCollectionRepository 创建一个新的 PendingCollectionRepository GORM 实现实例。 -func NewGormPendingCollectionRepository(db *gorm.DB) PendingCollectionRepository { - return &gormPendingCollectionRepository{db: db} +func NewGormPendingCollectionRepository(ctx context.Context, db *gorm.DB) PendingCollectionRepository { + return &gormPendingCollectionRepository{ctx: ctx, db: db} } // Create 创建一个新的待采集请求。 -func (r *gormPendingCollectionRepository) Create(req *models.PendingCollection) error { - return r.db.Create(req).Error +func (r *gormPendingCollectionRepository) Create(ctx context.Context, req *models.PendingCollection) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Create") + return r.db.WithContext(repoCtx).Create(req).Error } // FindByCorrelationID 根据关联ID查找一个待采集请求。 -func (r *gormPendingCollectionRepository) FindByCorrelationID(correlationID string) (*models.PendingCollection, error) { +func (r *gormPendingCollectionRepository) FindByCorrelationID(ctx context.Context, correlationID string) (*models.PendingCollection, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByCorrelationID") var req models.PendingCollection - if err := r.db.First(&req, "correlation_id = ?", correlationID).Error; err != nil { + if err := r.db.WithContext(repoCtx).First(&req, "correlation_id = ?", correlationID).Error; err != nil { return nil, err } return &req, nil } // UpdateStatusToFulfilled 将指定关联ID的请求状态更新为“已完成”。 -func (r *gormPendingCollectionRepository) UpdateStatusToFulfilled(correlationID string, fulfilledAt time.Time) error { - return r.db.Model(&models.PendingCollection{}). +func (r *gormPendingCollectionRepository) UpdateStatusToFulfilled(ctx context.Context, correlationID string, fulfilledAt time.Time) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdateStatusToFulfilled") + return r.db.WithContext(repoCtx).Model(&models.PendingCollection{}). Where("correlation_id = ?", correlationID). Updates(map[string]interface{}{ "status": models.PendingStatusFulfilled, @@ -70,8 +77,9 @@ func (r *gormPendingCollectionRepository) UpdateStatusToFulfilled(correlationID // MarkAllPendingAsTimedOut 将所有状态为 'pending' 的记录更新为 'timed_out'。 // 返回被更新的记录数量和错误。 -func (r *gormPendingCollectionRepository) MarkAllPendingAsTimedOut() (int64, error) { - result := r.db.Model(&models.PendingCollection{}). +func (r *gormPendingCollectionRepository) MarkAllPendingAsTimedOut(ctx context.Context) (int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "MarkAllPendingAsTimedOut") + result := r.db.WithContext(repoCtx).Model(&models.PendingCollection{}). Where("status = ?", models.PendingStatusPending). Update("status", models.PendingStatusTimedOut) @@ -79,7 +87,8 @@ func (r *gormPendingCollectionRepository) MarkAllPendingAsTimedOut() (int64, err } // List 实现了分页和过滤查询待采集请求的功能 -func (r *gormPendingCollectionRepository) List(opts PendingCollectionListOptions, page, pageSize int) ([]models.PendingCollection, int64, error) { +func (r *gormPendingCollectionRepository) List(ctx context.Context, opts PendingCollectionListOptions, page, pageSize int) ([]models.PendingCollection, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "List") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -87,7 +96,7 @@ func (r *gormPendingCollectionRepository) List(opts PendingCollectionListOptions var results []models.PendingCollection var total int64 - query := r.db.Model(&models.PendingCollection{}) + query := r.db.WithContext(repoCtx).Model(&models.PendingCollection{}) if opts.DeviceID != nil { query = query.Where("device_id = ?", *opts.DeviceID) diff --git a/internal/infra/repository/pending_task_repository.go b/internal/infra/repository/pending_task_repository.go index ceae6bb..06824df 100644 --- a/internal/infra/repository/pending_task_repository.go +++ b/internal/infra/repository/pending_task_repository.go @@ -1,63 +1,69 @@ package repository import ( + "context" "errors" "fmt" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" "gorm.io/gorm/clause" ) // PendingTaskRepository 定义了与待执行任务队列交互的接口。 type PendingTaskRepository interface { - FindAllPendingTasks() ([]models.PendingTask, error) - FindPendingTriggerByPlanID(planID uint) (*models.PendingTask, error) - DeletePendingTasksByIDs(ids []uint) error - CreatePendingTask(task *models.PendingTask) error - CreatePendingTasksInBatch(tasks []*models.PendingTask) error + FindAllPendingTasks(ctx context.Context) ([]models.PendingTask, error) + FindPendingTriggerByPlanID(ctx context.Context, planID uint) (*models.PendingTask, error) + DeletePendingTasksByIDs(ctx context.Context, ids []uint) error + CreatePendingTask(ctx context.Context, task *models.PendingTask) error + CreatePendingTasksInBatch(ctx context.Context, tasks []*models.PendingTask) error // UpdatePendingTaskExecuteAt 更新指定待执行任务的执行时间 - UpdatePendingTaskExecuteAt(id uint, executeAt time.Time) error + UpdatePendingTaskExecuteAt(ctx context.Context, id uint, executeAt time.Time) error // ClearAllPendingTasks 清空所有待执行任务 - ClearAllPendingTasks() error + ClearAllPendingTasks(ctx context.Context) error // ClaimNextAvailableTask 原子地认领下一个可用的任务。 // 它会同时返回被认领任务对应的日志对象,以及被删除的待办任务对象的内存副本。 - ClaimNextAvailableTask(excludePlanIDs []uint) (*models.TaskExecutionLog, *models.PendingTask, error) + ClaimNextAvailableTask(ctx context.Context, excludePlanIDs []uint) (*models.TaskExecutionLog, *models.PendingTask, error) // RequeueTask 安全地将一个任务重新放回队列。 - RequeueTask(originalPendingTask *models.PendingTask) error + RequeueTask(ctx context.Context, originalPendingTask *models.PendingTask) error // FindPendingTasksByTaskLogIDs 根据 TaskExecutionLogID 列表查找对应的待执行任务 - FindPendingTasksByTaskLogIDs(taskLogIDs []uint) ([]models.PendingTask, error) + FindPendingTasksByTaskLogIDs(ctx context.Context, taskLogIDs []uint) ([]models.PendingTask, error) // DeletePendingTasksByPlanLogID 删除与指定计划执行日志ID相关的所有待执行任务 - DeletePendingTasksByPlanLogID(planLogID uint) error + DeletePendingTasksByPlanLogID(ctx context.Context, planLogID uint) error } // gormPendingTaskRepository 是使用 GORM 的具体实现。 type gormPendingTaskRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormPendingTaskRepository 创建一个新的待执行任务队列仓库。 -func NewGormPendingTaskRepository(db *gorm.DB) PendingTaskRepository { - return &gormPendingTaskRepository{db: db} +func NewGormPendingTaskRepository(ctx context.Context, db *gorm.DB) PendingTaskRepository { + return &gormPendingTaskRepository{ctx: ctx, db: db} } -func (r *gormPendingTaskRepository) FindAllPendingTasks() ([]models.PendingTask, error) { +func (r *gormPendingTaskRepository) FindAllPendingTasks(ctx context.Context) ([]models.PendingTask, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindAllPendingTasks") var tasks []models.PendingTask // 预加载 Task 以便后续访问 Task.PlanID // 预加载 TaskExecutionLog 以便后续访问 PlanExecutionLogID - err := r.db.Preload("Task").Preload("TaskExecutionLog").Find(&tasks).Error + err := r.db.WithContext(repoCtx).Preload("Task").Preload("TaskExecutionLog").Find(&tasks).Error return tasks, err } -func (r *gormPendingTaskRepository) FindPendingTriggerByPlanID(planID uint) (*models.PendingTask, error) { +func (r *gormPendingTaskRepository) FindPendingTriggerByPlanID(ctx context.Context, planID uint) (*models.PendingTask, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindPendingTriggerByPlanID") var pendingTask models.PendingTask // 关键修改:通过 JOIN tasks 表并查询 parameters JSON 字段来查找触发器,而不是依赖 task.plan_id - err := r.db. + err := r.db.WithContext(repoCtx). Joins("JOIN tasks ON tasks.id = pending_tasks.task_id"). Where("tasks.type = ? AND tasks.parameters->>'plan_id' = ?", models.TaskPlanAnalysis, fmt.Sprintf("%d", planID)). First(&pendingTask).Error @@ -67,42 +73,48 @@ func (r *gormPendingTaskRepository) FindPendingTriggerByPlanID(planID uint) (*mo return &pendingTask, err } -func (r *gormPendingTaskRepository) DeletePendingTasksByIDs(ids []uint) error { +func (r *gormPendingTaskRepository) DeletePendingTasksByIDs(ctx context.Context, ids []uint) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "DeletePendingTasksByIDs") if len(ids) == 0 { return nil } - return r.db.Where("id IN ?", ids).Delete(&models.PendingTask{}).Error + return r.db.WithContext(repoCtx).Where("id IN ?", ids).Delete(&models.PendingTask{}).Error } -func (r *gormPendingTaskRepository) CreatePendingTask(task *models.PendingTask) error { - return r.db.Create(task).Error +func (r *gormPendingTaskRepository) CreatePendingTask(ctx context.Context, task *models.PendingTask) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePendingTask") + return r.db.WithContext(repoCtx).Create(task).Error } // CreatePendingTasksInBatch 在一次数据库调用中创建多个待执行任务条目。 -func (r *gormPendingTaskRepository) CreatePendingTasksInBatch(tasks []*models.PendingTask) error { +func (r *gormPendingTaskRepository) CreatePendingTasksInBatch(ctx context.Context, tasks []*models.PendingTask) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePendingTasksInBatch") if len(tasks) == 0 { return nil } - return r.db.Create(&tasks).Error + return r.db.WithContext(repoCtx).Create(&tasks).Error } // UpdatePendingTaskExecuteAt 更新指定待执行任务的执行时间 -func (r *gormPendingTaskRepository) UpdatePendingTaskExecuteAt(id uint, executeAt time.Time) error { - return r.db.Model(&models.PendingTask{}).Where("id = ?", id).Update("execute_at", executeAt).Error +func (r *gormPendingTaskRepository) UpdatePendingTaskExecuteAt(ctx context.Context, id uint, executeAt time.Time) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePendingTaskExecuteAt") + return r.db.WithContext(repoCtx).Model(&models.PendingTask{}).Where("id = ?", id).Update("execute_at", executeAt).Error } // ClearAllPendingTasks 清空所有待执行任务 -func (r *gormPendingTaskRepository) ClearAllPendingTasks() error { - return r.db.Where("1 = 1").Delete(&models.PendingTask{}).Error +func (r *gormPendingTaskRepository) ClearAllPendingTasks(ctx context.Context) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ClearAllPendingTasks") + return r.db.WithContext(repoCtx).Where("1 = 1").Delete(&models.PendingTask{}).Error } // ClaimNextAvailableTask 以原子方式认领下一个可用的任务。 -func (r *gormPendingTaskRepository) ClaimNextAvailableTask(excludePlanIDs []uint) (*models.TaskExecutionLog, *models.PendingTask, error) { +func (r *gormPendingTaskRepository) ClaimNextAvailableTask(ctx context.Context, excludePlanIDs []uint) (*models.TaskExecutionLog, *models.PendingTask, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ClaimNextAvailableTask") var log models.TaskExecutionLog var pendingTask models.PendingTask - err := r.db.Transaction(func(tx *gorm.DB) error { - query := tx.Clauses(clause.Locking{Strength: "UPDATE"}). + err := r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error { + query := tx.WithContext(repoCtx).Clauses(clause.Locking{Strength: "UPDATE"}). Where("execute_at <= ?", time.Now()). Order("execute_at ASC") @@ -114,7 +126,7 @@ func (r *gormPendingTaskRepository) ClaimNextAvailableTask(excludePlanIDs []uint return err } - if err := tx.Unscoped().Delete(&pendingTask).Error; err != nil { + if err := tx.WithContext(repoCtx).Unscoped().Delete(&pendingTask).Error; err != nil { return err } @@ -122,12 +134,12 @@ func (r *gormPendingTaskRepository) ClaimNextAvailableTask(excludePlanIDs []uint "status": models.ExecutionStatusStarted, "started_at": time.Now(), } - if err := tx.Model(&models.TaskExecutionLog{}).Where("id = ?", pendingTask.TaskExecutionLogID).Updates(updates).Error; err != nil { + if err := tx.WithContext(repoCtx).Model(&models.TaskExecutionLog{}).Where("id = ?", pendingTask.TaskExecutionLogID).Updates(updates).Error; err != nil { return err } // 关键修改:在 Preload("Task") 时,使用 Unscoped() 来忽略 Task 的软删除状态 - if err := tx.Preload("Task", func(db *gorm.DB) *gorm.DB { + if err := tx.WithContext(repoCtx).Preload("Task", func(db *gorm.DB) *gorm.DB { return db.Unscoped() }).First(&log, pendingTask.TaskExecutionLogID).Error; err != nil { return err @@ -145,10 +157,11 @@ func (r *gormPendingTaskRepository) ClaimNextAvailableTask(excludePlanIDs []uint // RequeueTask 安全地将一个任务重新放回队列。 // 它通过将原始 PendingTask 的 ID 重置为 0,并重新创建它来实现。 -func (r *gormPendingTaskRepository) RequeueTask(originalPendingTask *models.PendingTask) error { - return r.db.Transaction(func(tx *gorm.DB) error { +func (r *gormPendingTaskRepository) RequeueTask(ctx context.Context, originalPendingTask *models.PendingTask) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "RequeueTask") + return r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error { // 1. 将日志状态恢复为 waiting - if err := tx.Model(&models.TaskExecutionLog{}).Where("id = ?", originalPendingTask.TaskExecutionLogID).Update("status", models.ExecutionStatusWaiting).Error; err != nil { + if err := tx.WithContext(repoCtx).Model(&models.TaskExecutionLog{}).Where("id = ?", originalPendingTask.TaskExecutionLogID).Update("status", models.ExecutionStatusWaiting).Error; err != nil { return err } @@ -157,25 +170,27 @@ func (r *gormPendingTaskRepository) RequeueTask(originalPendingTask *models.Pend originalPendingTask.ID = 0 // 3. 重新创建待办任务。GORM 会忽略掉已被重置的 ID,并让数据库生成一个新的主键。 - return tx.Create(originalPendingTask).Error + return tx.WithContext(repoCtx).Create(originalPendingTask).Error }) } // FindPendingTasksByTaskLogIDs 根据 TaskExecutionLogID 列表查找对应的待执行任务 -func (r *gormPendingTaskRepository) FindPendingTasksByTaskLogIDs(taskLogIDs []uint) ([]models.PendingTask, error) { +func (r *gormPendingTaskRepository) FindPendingTasksByTaskLogIDs(ctx context.Context, taskLogIDs []uint) ([]models.PendingTask, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindPendingTasksByTaskLogIDs") if len(taskLogIDs) == 0 { return []models.PendingTask{}, nil } var pendingTasks []models.PendingTask - err := r.db.Where("task_execution_log_id IN ?", taskLogIDs).Find(&pendingTasks).Error + err := r.db.WithContext(repoCtx).Where("task_execution_log_id IN ?", taskLogIDs).Find(&pendingTasks).Error return pendingTasks, err } // DeletePendingTasksByPlanLogID 删除与指定计划执行日志ID相关的所有待执行任务 -func (r *gormPendingTaskRepository) DeletePendingTasksByPlanLogID(planLogID uint) error { +func (r *gormPendingTaskRepository) DeletePendingTasksByPlanLogID(ctx context.Context, planLogID uint) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "DeletePendingTasksByPlanLogID") // 使用子查询找到所有与 planLogID 相关的 task_execution_log_id - subQuery := r.db.Model(&models.TaskExecutionLog{}).Select("id").Where("plan_execution_log_id = ?", planLogID) + subQuery := r.db.WithContext(repoCtx).Model(&models.TaskExecutionLog{}).Select("id").Where("plan_execution_log_id = ?", planLogID) // 使用子查询的结果来删除待执行任务 - return r.db.Where("task_execution_log_id IN (?)", subQuery).Delete(&models.PendingTask{}).Error + return r.db.WithContext(repoCtx).Where("task_execution_log_id IN (?)", subQuery).Delete(&models.PendingTask{}).Error } diff --git a/internal/infra/repository/pig_batch_log_repository.go b/internal/infra/repository/pig_batch_log_repository.go index a85edb1..c07b70c 100644 --- a/internal/infra/repository/pig_batch_log_repository.go +++ b/internal/infra/repository/pig_batch_log_repository.go @@ -1,9 +1,12 @@ package repository import ( + "context" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) @@ -20,37 +23,40 @@ type PigBatchLogListOptions struct { // PigBatchLogRepository 定义了与猪批次日志相关的数据库操作接口。 type PigBatchLogRepository interface { // CreateTx 在指定的事务中创建一条新的猪批次日志。 - CreateTx(tx *gorm.DB, log *models.PigBatchLog) error + CreateTx(ctx context.Context, tx *gorm.DB, log *models.PigBatchLog) error // GetLogsByBatchIDAndDateRangeTx 在指定的事务中,获取指定批次在特定时间范围内的所有日志记录。 - GetLogsByBatchIDAndDateRangeTx(tx *gorm.DB, batchID uint, startDate, endDate time.Time) ([]*models.PigBatchLog, error) + GetLogsByBatchIDAndDateRangeTx(ctx context.Context, tx *gorm.DB, batchID uint, startDate, endDate time.Time) ([]*models.PigBatchLog, error) // GetLastLogByBatchIDTx 在指定的事务中,获取某批次的最后一条日志记录。 - GetLastLogByBatchIDTx(tx *gorm.DB, batchID uint) (*models.PigBatchLog, error) + GetLastLogByBatchIDTx(ctx context.Context, tx *gorm.DB, batchID uint) (*models.PigBatchLog, error) // List 支持分页和过滤的列表查询 - List(opts PigBatchLogListOptions, page, pageSize int) ([]models.PigBatchLog, int64, error) + List(ctx context.Context, opts PigBatchLogListOptions, page, pageSize int) ([]models.PigBatchLog, int64, error) } // gormPigBatchLogRepository 是 PigBatchLogRepository 的 GORM 实现。 type gormPigBatchLogRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormPigBatchLogRepository 创建一个新的 PigBatchLogRepository 实例。 -func NewGormPigBatchLogRepository(db *gorm.DB) PigBatchLogRepository { - return &gormPigBatchLogRepository{db: db} +func NewGormPigBatchLogRepository(ctx context.Context, db *gorm.DB) PigBatchLogRepository { + return &gormPigBatchLogRepository{ctx: ctx, db: db} } // CreateTx 实现了在事务中创建猪批次日志的逻辑。 -func (r *gormPigBatchLogRepository) CreateTx(tx *gorm.DB, log *models.PigBatchLog) error { - return tx.Create(log).Error +func (r *gormPigBatchLogRepository) CreateTx(ctx context.Context, tx *gorm.DB, log *models.PigBatchLog) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateTx") + return tx.WithContext(repoCtx).Create(log).Error } // GetLogsByBatchIDAndDateRangeTx 实现了在指定的事务中,获取指定批次在特定时间范围内的所有日志记录的逻辑。 -func (r *gormPigBatchLogRepository) GetLogsByBatchIDAndDateRangeTx(tx *gorm.DB, batchID uint, startDate, endDate time.Time) ([]*models.PigBatchLog, error) { +func (r *gormPigBatchLogRepository) GetLogsByBatchIDAndDateRangeTx(ctx context.Context, tx *gorm.DB, batchID uint, startDate, endDate time.Time) ([]*models.PigBatchLog, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetLogsByBatchIDAndDateRangeTx") var logs []*models.PigBatchLog - err := tx.Where("pig_batch_id = ? AND created_at >= ? AND created_at <= ?", batchID, startDate, endDate).Find(&logs).Error + err := tx.WithContext(repoCtx).Where("pig_batch_id = ? AND created_at >= ? AND created_at <= ?", batchID, startDate, endDate).Find(&logs).Error if err != nil { return nil, err } @@ -58,9 +64,10 @@ func (r *gormPigBatchLogRepository) GetLogsByBatchIDAndDateRangeTx(tx *gorm.DB, } // GetLastLogByBatchIDTx 实现了在指定的事务中,获取某批次的最后一条日志记录的逻辑。 -func (r *gormPigBatchLogRepository) GetLastLogByBatchIDTx(tx *gorm.DB, batchID uint) (*models.PigBatchLog, error) { +func (r *gormPigBatchLogRepository) GetLastLogByBatchIDTx(ctx context.Context, tx *gorm.DB, batchID uint) (*models.PigBatchLog, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetLastLogByBatchIDTx") var log models.PigBatchLog - err := tx.Where("pig_batch_id = ?", batchID).Order("id DESC").First(&log).Error + err := tx.WithContext(repoCtx).Where("pig_batch_id = ?", batchID).Order("id DESC").First(&log).Error if err != nil { return nil, err } @@ -68,7 +75,8 @@ func (r *gormPigBatchLogRepository) GetLastLogByBatchIDTx(tx *gorm.DB, batchID u } // List 实现了分页和过滤查询猪批次日志的功能 -func (r *gormPigBatchLogRepository) List(opts PigBatchLogListOptions, page, pageSize int) ([]models.PigBatchLog, int64, error) { +func (r *gormPigBatchLogRepository) List(ctx context.Context, opts PigBatchLogListOptions, page, pageSize int) ([]models.PigBatchLog, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "List") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -76,7 +84,7 @@ func (r *gormPigBatchLogRepository) List(opts PigBatchLogListOptions, page, page var results []models.PigBatchLog var total int64 - query := r.db.Model(&models.PigBatchLog{}) + query := r.db.WithContext(repoCtx).Model(&models.PigBatchLog{}) if opts.PigBatchID != nil { query = query.Where("pig_batch_id = ?", *opts.PigBatchID) diff --git a/internal/infra/repository/pig_batch_repository.go b/internal/infra/repository/pig_batch_repository.go index 520b98c..ae37ecd 100644 --- a/internal/infra/repository/pig_batch_repository.go +++ b/internal/infra/repository/pig_batch_repository.go @@ -1,30 +1,33 @@ package repository import ( + "context" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) // 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) + CreatePigBatch(ctx context.Context, batch *models.PigBatch) (*models.PigBatch, error) + CreatePigBatchTx(ctx context.Context, tx *gorm.DB, batch *models.PigBatch) (*models.PigBatch, error) + GetPigBatchByID(ctx context.Context, id uint) (*models.PigBatch, error) + GetPigBatchByIDTx(ctx context.Context, tx *gorm.DB, id uint) (*models.PigBatch, error) // UpdatePigBatch 更新一个猪批次,返回更新后的批次、受影响的行数和错误 - UpdatePigBatch(batch *models.PigBatch) (*models.PigBatch, int64, error) + UpdatePigBatch(ctx context.Context, batch *models.PigBatch) (*models.PigBatch, int64, error) // DeletePigBatch 根据ID删除一个猪批次,返回受影响的行数和错误 - DeletePigBatch(id uint) (int64, error) - DeletePigBatchTx(tx *gorm.DB, id uint) (int64, error) - ListPigBatches(isActive *bool) ([]*models.PigBatch, error) + DeletePigBatch(ctx context.Context, id uint) (int64, error) + DeletePigBatchTx(ctx context.Context, tx *gorm.DB, id uint) (int64, error) + ListPigBatches(ctx context.Context, isActive *bool) ([]*models.PigBatch, error) // ListWeighingBatches 支持分页和过滤的批次称重列表查询 - ListWeighingBatches(opts WeighingBatchListOptions, page, pageSize int) ([]models.WeighingBatch, int64, error) + ListWeighingBatches(ctx context.Context, opts WeighingBatchListOptions, page, pageSize int) ([]models.WeighingBatch, int64, error) // ListWeighingRecords 支持分页和过滤的单次称重记录列表查询 - ListWeighingRecords(opts WeighingRecordListOptions, page, pageSize int) ([]models.WeighingRecord, int64, error) + ListWeighingRecords(ctx context.Context, opts WeighingRecordListOptions, page, pageSize int) ([]models.WeighingRecord, int64, error) } // WeighingBatchListOptions 定义了查询批次称重记录时的可选参数 @@ -47,35 +50,40 @@ type WeighingRecordListOptions struct { // gormPigBatchRepository 是 PigBatchRepository 的 GORM 实现 type gormPigBatchRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormPigBatchRepository 创建一个新的 PigBatchRepository GORM 实现实例 -func NewGormPigBatchRepository(db *gorm.DB) PigBatchRepository { - return &gormPigBatchRepository{db: db} +func NewGormPigBatchRepository(ctx context.Context, db *gorm.DB) PigBatchRepository { + return &gormPigBatchRepository{ctx: ctx, db: db} } // CreatePigBatch 创建一个新的猪批次 -func (r *gormPigBatchRepository) CreatePigBatch(batch *models.PigBatch) (*models.PigBatch, error) { - return r.CreatePigBatchTx(r.db, batch) +func (r *gormPigBatchRepository) CreatePigBatch(ctx context.Context, batch *models.PigBatch) (*models.PigBatch, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePigBatch") + return r.CreatePigBatchTx(repoCtx, 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 { +func (r *gormPigBatchRepository) CreatePigBatchTx(ctx context.Context, tx *gorm.DB, batch *models.PigBatch) (*models.PigBatch, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePigBatchTx") + if err := tx.WithContext(repoCtx).Create(batch).Error; err != nil { return nil, err } return batch, nil } // GetPigBatchByID 根据ID获取单个猪批次 -func (r *gormPigBatchRepository) GetPigBatchByID(id uint) (*models.PigBatch, error) { - return r.GetPigBatchByIDTx(r.db, id) +func (r *gormPigBatchRepository) GetPigBatchByID(ctx context.Context, id uint) (*models.PigBatch, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetPigBatchByID") + return r.GetPigBatchByIDTx(repoCtx, r.db, id) } // UpdatePigBatch 更新一个猪批次 -func (r *gormPigBatchRepository) UpdatePigBatch(batch *models.PigBatch) (*models.PigBatch, int64, error) { - result := r.db.Model(&models.PigBatch{}).Where("id = ?", batch.ID).Updates(batch) +func (r *gormPigBatchRepository) UpdatePigBatch(ctx context.Context, batch *models.PigBatch) (*models.PigBatch, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePigBatch") + result := r.db.WithContext(repoCtx).Model(&models.PigBatch{}).Where("id = ?", batch.ID).Updates(batch) if result.Error != nil { return nil, 0, result.Error } @@ -84,12 +92,14 @@ func (r *gormPigBatchRepository) UpdatePigBatch(batch *models.PigBatch) (*models } // DeletePigBatch 根据ID删除一个猪批次 (GORM 会执行软删除) -func (r *gormPigBatchRepository) DeletePigBatch(id uint) (int64, error) { - return r.DeletePigBatchTx(r.db, id) +func (r *gormPigBatchRepository) DeletePigBatch(ctx context.Context, id uint) (int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "DeletePigBatch") + return r.DeletePigBatchTx(repoCtx, r.db, id) } -func (r *gormPigBatchRepository) DeletePigBatchTx(tx *gorm.DB, id uint) (int64, error) { - result := tx.Delete(&models.PigBatch{}, id) +func (r *gormPigBatchRepository) DeletePigBatchTx(ctx context.Context, tx *gorm.DB, id uint) (int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "DeletePigBatchTx") + result := tx.WithContext(repoCtx).Delete(&models.PigBatch{}, id) if result.Error != nil { return 0, result.Error } @@ -98,9 +108,10 @@ func (r *gormPigBatchRepository) DeletePigBatchTx(tx *gorm.DB, id uint) (int64, } // ListPigBatches 批量查询猪批次,支持根据 IsActive 筛选 -func (r *gormPigBatchRepository) ListPigBatches(isActive *bool) ([]*models.PigBatch, error) { +func (r *gormPigBatchRepository) ListPigBatches(ctx context.Context, isActive *bool) ([]*models.PigBatch, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListPigBatches") var batches []*models.PigBatch - query := r.db.Model(&models.PigBatch{}) + query := r.db.WithContext(repoCtx).Model(&models.PigBatch{}) if isActive != nil { if *isActive { @@ -119,16 +130,18 @@ func (r *gormPigBatchRepository) ListPigBatches(isActive *bool) ([]*models.PigBa } // GetPigBatchByIDTx 在指定的事务中,通过ID获取单个猪批次 -func (r *gormPigBatchRepository) GetPigBatchByIDTx(tx *gorm.DB, id uint) (*models.PigBatch, error) { +func (r *gormPigBatchRepository) GetPigBatchByIDTx(ctx context.Context, tx *gorm.DB, id uint) (*models.PigBatch, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetPigBatchByIDTx") var batch models.PigBatch - if err := tx.First(&batch, id).Error; err != nil { + if err := tx.WithContext(repoCtx).First(&batch, id).Error; err != nil { return nil, err } return &batch, nil } // ListWeighingBatches 实现了分页和过滤查询批次称重记录的功能 -func (r *gormPigBatchRepository) ListWeighingBatches(opts WeighingBatchListOptions, page, pageSize int) ([]models.WeighingBatch, int64, error) { +func (r *gormPigBatchRepository) ListWeighingBatches(ctx context.Context, opts WeighingBatchListOptions, page, pageSize int) ([]models.WeighingBatch, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListWeighingBatches") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -136,7 +149,7 @@ func (r *gormPigBatchRepository) ListWeighingBatches(opts WeighingBatchListOptio var results []models.WeighingBatch var total int64 - query := r.db.Model(&models.WeighingBatch{}) + query := r.db.WithContext(repoCtx).Model(&models.WeighingBatch{}) if opts.PigBatchID != nil { query = query.Where("pig_batch_id = ?", *opts.PigBatchID) @@ -165,7 +178,8 @@ func (r *gormPigBatchRepository) ListWeighingBatches(opts WeighingBatchListOptio } // ListWeighingRecords 实现了分页和过滤查询单次称重记录的功能 -func (r *gormPigBatchRepository) ListWeighingRecords(opts WeighingRecordListOptions, page, pageSize int) ([]models.WeighingRecord, int64, error) { +func (r *gormPigBatchRepository) ListWeighingRecords(ctx context.Context, opts WeighingRecordListOptions, page, pageSize int) ([]models.WeighingRecord, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListWeighingRecords") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -173,7 +187,7 @@ func (r *gormPigBatchRepository) ListWeighingRecords(opts WeighingRecordListOpti var results []models.WeighingRecord var total int64 - query := r.db.Model(&models.WeighingRecord{}) + query := r.db.WithContext(repoCtx).Model(&models.WeighingRecord{}) if opts.WeighingBatchID != nil { query = query.Where("weighing_batch_id = ?", *opts.WeighingBatchID) diff --git a/internal/infra/repository/pig_farm_repository.go b/internal/infra/repository/pig_farm_repository.go index 954f1f5..f70c75b 100644 --- a/internal/infra/repository/pig_farm_repository.go +++ b/internal/infra/repository/pig_farm_repository.go @@ -1,61 +1,70 @@ package repository import ( + "context" + + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) // PigFarmRepository 定义了与猪场资产(猪舍、猪栏)相关的数据库操作接口 type PigFarmRepository interface { // PigHouse methods - CreatePigHouse(house *models.PigHouse) error - GetPigHouseByID(id uint) (*models.PigHouse, error) - ListPigHouses() ([]models.PigHouse, error) + CreatePigHouse(ctx context.Context, house *models.PigHouse) error + GetPigHouseByID(ctx context.Context, id uint) (*models.PigHouse, error) + ListPigHouses(ctx context.Context) ([]models.PigHouse, error) // UpdatePigHouse 更新一个猪舍,返回受影响的行数和错误 - UpdatePigHouse(house *models.PigHouse) (int64, error) + UpdatePigHouse(ctx context.Context, house *models.PigHouse) (int64, error) // DeletePigHouse 根据ID删除一个猪舍,返回受影响的行数和错误 - DeletePigHouse(id uint) (int64, error) - CountPensInHouse(houseID uint) (int64, error) + DeletePigHouse(ctx context.Context, id uint) (int64, error) + CountPensInHouse(ctx context.Context, houseID uint) (int64, error) } // gormPigFarmRepository 是 PigFarmRepository 的 GORM 实现 type gormPigFarmRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormPigFarmRepository 创建一个新的 PigFarmRepository GORM 实现实例 -func NewGormPigFarmRepository(db *gorm.DB) PigFarmRepository { - return &gormPigFarmRepository{db: db} +func NewGormPigFarmRepository(ctx context.Context, db *gorm.DB) PigFarmRepository { + return &gormPigFarmRepository{ctx: ctx, db: db} } // --- PigHouse Implementation --- // CreatePigHouse 创建一个新的猪舍 -func (r *gormPigFarmRepository) CreatePigHouse(house *models.PigHouse) error { - return r.db.Create(house).Error +func (r *gormPigFarmRepository) CreatePigHouse(ctx context.Context, house *models.PigHouse) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePigHouse") + return r.db.WithContext(repoCtx).Create(house).Error } // GetPigHouseByID 根据ID获取单个猪舍 -func (r *gormPigFarmRepository) GetPigHouseByID(id uint) (*models.PigHouse, error) { +func (r *gormPigFarmRepository) GetPigHouseByID(ctx context.Context, id uint) (*models.PigHouse, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetPigHouseByID") var house models.PigHouse - if err := r.db.First(&house, id).Error; err != nil { + if err := r.db.WithContext(repoCtx).First(&house, id).Error; err != nil { return nil, err } return &house, nil } // ListPigHouses 列出所有猪舍 -func (r *gormPigFarmRepository) ListPigHouses() ([]models.PigHouse, error) { +func (r *gormPigFarmRepository) ListPigHouses(ctx context.Context) ([]models.PigHouse, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListPigHouses") var houses []models.PigHouse - if err := r.db.Find(&houses).Error; err != nil { + if err := r.db.WithContext(repoCtx).Find(&houses).Error; err != nil { return nil, err } return houses, nil } // UpdatePigHouse 更新一个猪舍,返回受影响的行数和错误 -func (r *gormPigFarmRepository) UpdatePigHouse(house *models.PigHouse) (int64, error) { - result := r.db.Model(&models.PigHouse{}).Where("id = ?", house.ID).Updates(house) +func (r *gormPigFarmRepository) UpdatePigHouse(ctx context.Context, house *models.PigHouse) (int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePigHouse") + result := r.db.WithContext(repoCtx).Model(&models.PigHouse{}).Where("id = ?", house.ID).Updates(house) if result.Error != nil { return 0, result.Error } @@ -63,8 +72,9 @@ func (r *gormPigFarmRepository) UpdatePigHouse(house *models.PigHouse) (int64, e } // DeletePigHouse 根据ID删除一个猪舍,返回受影响的行数和错误 -func (r *gormPigFarmRepository) DeletePigHouse(id uint) (int64, error) { - result := r.db.Delete(&models.PigHouse{}, id) +func (r *gormPigFarmRepository) DeletePigHouse(ctx context.Context, id uint) (int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "DeletePigHouse") + result := r.db.WithContext(repoCtx).Delete(&models.PigHouse{}, id) if result.Error != nil { return 0, result.Error } @@ -72,8 +82,9 @@ func (r *gormPigFarmRepository) DeletePigHouse(id uint) (int64, error) { } // CountPensInHouse 统计猪舍中的猪栏数量 -func (r *gormPigFarmRepository) CountPensInHouse(houseID uint) (int64, error) { +func (r *gormPigFarmRepository) CountPensInHouse(ctx context.Context, houseID uint) (int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CountPensInHouse") var count int64 - err := r.db.Model(&models.Pen{}).Where("house_id = ?", houseID).Count(&count).Error + err := r.db.WithContext(repoCtx).Model(&models.Pen{}).Where("house_id = ?", houseID).Count(&count).Error return count, err } diff --git a/internal/infra/repository/pig_pen_repository.go b/internal/infra/repository/pig_pen_repository.go index 84a1b69..ac83df9 100644 --- a/internal/infra/repository/pig_pen_repository.go +++ b/internal/infra/repository/pig_pen_repository.go @@ -1,69 +1,79 @@ package repository import ( + "context" + + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) // PigPenRepository 定义了与猪栏模型相关的数据库操作接口。 type PigPenRepository interface { - CreatePen(pen *models.Pen) error + CreatePen(ctx context.Context, pen *models.Pen) error // GetPenByID 根据ID获取单个猪栏 (非事务性) - GetPenByID(id uint) (*models.Pen, error) + GetPenByID(ctx context.Context, id uint) (*models.Pen, error) // GetPenByIDTx 根据ID获取单个猪栏 (事务性) - GetPenByIDTx(tx *gorm.DB, id uint) (*models.Pen, error) - ListPens() ([]models.Pen, error) + GetPenByIDTx(ctx context.Context, tx *gorm.DB, id uint) (*models.Pen, error) + ListPens(ctx context.Context) ([]models.Pen, error) // UpdatePen 更新一个猪栏,返回受影响的行数和错误 - UpdatePen(pen *models.Pen) (int64, error) + UpdatePen(ctx context.Context, pen *models.Pen) (int64, error) // DeletePen 根据ID删除一个猪栏,返回受影响的行数和错误 - DeletePen(id uint) (int64, error) + DeletePen(ctx context.Context, id uint) (int64, error) // GetPensByBatchIDTx 根据批次ID获取所有关联的猪栏 (事务性) - GetPensByBatchIDTx(tx *gorm.DB, batchID uint) ([]*models.Pen, error) + GetPensByBatchIDTx(ctx context.Context, tx *gorm.DB, batchID uint) ([]*models.Pen, error) // UpdatePenFieldsTx 更新猪栏的指定字段 (事务性) - UpdatePenFieldsTx(tx *gorm.DB, penID uint, updates map[string]interface{}) error + UpdatePenFieldsTx(ctx context.Context, tx *gorm.DB, penID uint, updates map[string]interface{}) error } // gormPigPenRepository 是 PigPenRepository 接口的 GORM 实现。 type gormPigPenRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormPigPenRepository 创建一个新的 PigPenRepository GORM 实现实例。 -func NewGormPigPenRepository(db *gorm.DB) PigPenRepository { - return &gormPigPenRepository{db: db} +func NewGormPigPenRepository(ctx context.Context, db *gorm.DB) PigPenRepository { + return &gormPigPenRepository{ctx: ctx, db: db} } // CreatePen 创建一个新的猪栏 -func (r *gormPigPenRepository) CreatePen(pen *models.Pen) error { - return r.db.Create(pen).Error +func (r *gormPigPenRepository) CreatePen(ctx context.Context, pen *models.Pen) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePen") + return r.db.WithContext(repoCtx).Create(pen).Error } // GetPenByID 根据ID获取单个猪栏 (非事务性) -func (r *gormPigPenRepository) GetPenByID(id uint) (*models.Pen, error) { - return r.GetPenByIDTx(r.db, id) // 非Tx方法直接调用Tx方法 +func (r *gormPigPenRepository) GetPenByID(ctx context.Context, id uint) (*models.Pen, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetPenByID") + return r.GetPenByIDTx(repoCtx, r.db, id) // 非Tx方法直接调用Tx方法 } // GetPenByIDTx 在指定的事务中,通过ID获取单个猪栏信息。 -func (r *gormPigPenRepository) GetPenByIDTx(tx *gorm.DB, id uint) (*models.Pen, error) { +func (r *gormPigPenRepository) GetPenByIDTx(ctx context.Context, tx *gorm.DB, id uint) (*models.Pen, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetPenByIDTx") var pen models.Pen - if err := tx.First(&pen, id).Error; err != nil { + if err := tx.WithContext(repoCtx).First(&pen, id).Error; err != nil { return nil, err } return &pen, nil } // ListPens 列出所有猪栏 -func (r *gormPigPenRepository) ListPens() ([]models.Pen, error) { +func (r *gormPigPenRepository) ListPens(ctx context.Context) ([]models.Pen, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListPens") var pens []models.Pen - if err := r.db.Find(&pens).Error; err != nil { + if err := r.db.WithContext(repoCtx).Find(&pens).Error; err != nil { return nil, err } return pens, nil } // UpdatePen 更新一个猪栏,返回受影响的行数和错误 -func (r *gormPigPenRepository) UpdatePen(pen *models.Pen) (int64, error) { - result := r.db.Model(&models.Pen{}).Where("id = ?", pen.ID).Updates(pen) +func (r *gormPigPenRepository) UpdatePen(ctx context.Context, pen *models.Pen) (int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePen") + result := r.db.WithContext(repoCtx).Model(&models.Pen{}).Where("id = ?", pen.ID).Updates(pen) if result.Error != nil { return 0, result.Error } @@ -71,8 +81,9 @@ func (r *gormPigPenRepository) UpdatePen(pen *models.Pen) (int64, error) { } // DeletePen 根据ID删除一个猪栏,返回受影响的行数和错误 -func (r *gormPigPenRepository) DeletePen(id uint) (int64, error) { - result := r.db.Delete(&models.Pen{}, id) +func (r *gormPigPenRepository) DeletePen(ctx context.Context, id uint) (int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "DeletePen") + result := r.db.WithContext(repoCtx).Delete(&models.Pen{}, id) if result.Error != nil { return 0, result.Error } @@ -80,10 +91,11 @@ func (r *gormPigPenRepository) DeletePen(id uint) (int64, error) { } // GetPensByBatchIDTx 在指定的事务中,获取一个猪群当前关联的所有猪栏。 -func (r *gormPigPenRepository) GetPensByBatchIDTx(tx *gorm.DB, batchID uint) ([]*models.Pen, error) { +func (r *gormPigPenRepository) GetPensByBatchIDTx(ctx context.Context, tx *gorm.DB, batchID uint) ([]*models.Pen, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetPensByBatchIDTx") var pens []*models.Pen // 注意:PigBatchID 是指针类型,需要处理 nil 值 - result := tx.Where("pig_batch_id = ?", batchID).Find(&pens) + result := tx.WithContext(repoCtx).Where("pig_batch_id = ?", batchID).Find(&pens) if result.Error != nil { return nil, result.Error } @@ -91,7 +103,8 @@ func (r *gormPigPenRepository) GetPensByBatchIDTx(tx *gorm.DB, batchID uint) ([] } // UpdatePenFieldsTx 在指定的事务中,更新一个猪栏的指定字段。 -func (r *gormPigPenRepository) UpdatePenFieldsTx(tx *gorm.DB, penID uint, updates map[string]interface{}) error { - result := tx.Model(&models.Pen{}).Where("id = ?", penID).Updates(updates) +func (r *gormPigPenRepository) UpdatePenFieldsTx(ctx context.Context, tx *gorm.DB, penID uint, updates map[string]interface{}) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePenFieldsTx") + result := tx.WithContext(repoCtx).Model(&models.Pen{}).Where("id = ?", penID).Updates(updates) return result.Error } diff --git a/internal/infra/repository/pig_sick_repository.go b/internal/infra/repository/pig_sick_repository.go index 749446a..61f63e9 100644 --- a/internal/infra/repository/pig_sick_repository.go +++ b/internal/infra/repository/pig_sick_repository.go @@ -1,10 +1,13 @@ package repository import ( + "context" "errors" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) @@ -23,38 +26,42 @@ type PigSickLogListOptions struct { // PigSickLogRepository 定义了与病猪日志模型相关的数据库操作接口。 type PigSickLogRepository interface { // CreatePigSickLog 创建一条新的病猪日志记录 - CreatePigSickLog(log *models.PigSickLog) error - CreatePigSickLogTx(tx *gorm.DB, log *models.PigSickLog) error + CreatePigSickLog(ctx context.Context, log *models.PigSickLog) error + CreatePigSickLogTx(ctx context.Context, tx *gorm.DB, log *models.PigSickLog) error // GetLastLogByBatchTx 在事务中获取指定批次和猪栏的最新一条 PigSickLog 记录 - GetLastLogByBatchTx(tx *gorm.DB, batchID uint) (*models.PigSickLog, error) + GetLastLogByBatchTx(ctx context.Context, tx *gorm.DB, batchID uint) (*models.PigSickLog, error) // ListPigSickLogs 支持分页和过滤的病猪日志列表查询 - ListPigSickLogs(opts PigSickLogListOptions, page, pageSize int) ([]models.PigSickLog, int64, error) + ListPigSickLogs(ctx context.Context, opts PigSickLogListOptions, page, pageSize int) ([]models.PigSickLog, int64, error) } // gormPigSickLogRepository 是 PigSickLogRepository 接口的 GORM 实现。 type gormPigSickLogRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormPigSickLogRepository 创建一个新的 PigSickLogRepository GORM 实现实例。 -func NewGormPigSickLogRepository(db *gorm.DB) PigSickLogRepository { - return &gormPigSickLogRepository{db: db} +func NewGormPigSickLogRepository(ctx context.Context, db *gorm.DB) PigSickLogRepository { + return &gormPigSickLogRepository{ctx: ctx, db: db} } // CreatePigSickLog 创建一条新的病猪日志记录 -func (r *gormPigSickLogRepository) CreatePigSickLog(log *models.PigSickLog) error { - return r.CreatePigSickLogTx(r.db, log) +func (r *gormPigSickLogRepository) CreatePigSickLog(ctx context.Context, log *models.PigSickLog) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePigSickLog") + return r.CreatePigSickLogTx(repoCtx, r.db, log) } -func (r *gormPigSickLogRepository) CreatePigSickLogTx(tx *gorm.DB, log *models.PigSickLog) error { - return tx.Create(log).Error +func (r *gormPigSickLogRepository) CreatePigSickLogTx(ctx context.Context, tx *gorm.DB, log *models.PigSickLog) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePigSickLogTx") + return tx.WithContext(repoCtx).Create(log).Error } // GetLastLogByBatchTx 在事务中获取指定批次和猪栏的最新一条 PigSickLog 记录 -func (r *gormPigSickLogRepository) GetLastLogByBatchTx(tx *gorm.DB, batchID uint) (*models.PigSickLog, error) { +func (r *gormPigSickLogRepository) GetLastLogByBatchTx(ctx context.Context, tx *gorm.DB, batchID uint) (*models.PigSickLog, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetLastLogByBatchTx") var lastLog models.PigSickLog - err := tx. + err := tx.WithContext(repoCtx). Where("pig_batch_id = ?", batchID). Order("happened_at DESC"). // 按时间降序排列 First(&lastLog).Error // 获取第一条记录 (即最新一条) @@ -69,7 +76,8 @@ func (r *gormPigSickLogRepository) GetLastLogByBatchTx(tx *gorm.DB, batchID uint } // ListPigSickLogs 实现了分页和过滤查询病猪日志的功能 -func (r *gormPigSickLogRepository) ListPigSickLogs(opts PigSickLogListOptions, page, pageSize int) ([]models.PigSickLog, int64, error) { +func (r *gormPigSickLogRepository) ListPigSickLogs(ctx context.Context, opts PigSickLogListOptions, page, pageSize int) ([]models.PigSickLog, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListPigSickLogs") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -77,7 +85,7 @@ func (r *gormPigSickLogRepository) ListPigSickLogs(opts PigSickLogListOptions, p var results []models.PigSickLog var total int64 - query := r.db.Model(&models.PigSickLog{}) + query := r.db.WithContext(repoCtx).Model(&models.PigSickLog{}) if opts.PigBatchID != nil { query = query.Where("pig_batch_id = ?", *opts.PigBatchID) diff --git a/internal/infra/repository/pig_trade_repository.go b/internal/infra/repository/pig_trade_repository.go index eeb0c21..3e6cd25 100644 --- a/internal/infra/repository/pig_trade_repository.go +++ b/internal/infra/repository/pig_trade_repository.go @@ -1,9 +1,12 @@ package repository import ( + "context" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) @@ -31,40 +34,44 @@ type PigSaleListOptions struct { // 领域服务通过此接口与数据层交互,实现解耦。 type PigTradeRepository interface { // CreatePigSaleTx 在数据库中创建一条猪只销售记录。 - CreatePigSaleTx(tx *gorm.DB, sale *models.PigSale) error + CreatePigSaleTx(ctx context.Context, tx *gorm.DB, sale *models.PigSale) error // CreatePigPurchaseTx 在数据库中创建一条猪只采购记录。 - CreatePigPurchaseTx(tx *gorm.DB, purchase *models.PigPurchase) error + CreatePigPurchaseTx(ctx context.Context, tx *gorm.DB, purchase *models.PigPurchase) error // ListPigPurchases 支持分页和过滤的猪只采购记录列表查询 - ListPigPurchases(opts PigPurchaseListOptions, page, pageSize int) ([]models.PigPurchase, int64, error) + ListPigPurchases(ctx context.Context, opts PigPurchaseListOptions, page, pageSize int) ([]models.PigPurchase, int64, error) // ListPigSales 支持分页和过滤的猪只销售记录列表查询 - ListPigSales(opts PigSaleListOptions, page, pageSize int) ([]models.PigSale, int64, error) + ListPigSales(ctx context.Context, opts PigSaleListOptions, page, pageSize int) ([]models.PigSale, int64, error) } // gormPigTradeRepository 是 PigTradeRepository 接口的 GORM 实现。 type gormPigTradeRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormPigTradeRepository 创建一个新的 PigTradeRepository GORM 实现实例。 -func NewGormPigTradeRepository(db *gorm.DB) PigTradeRepository { - return &gormPigTradeRepository{db: db} +func NewGormPigTradeRepository(ctx context.Context, db *gorm.DB) PigTradeRepository { + return &gormPigTradeRepository{ctx: ctx, db: db} } // CreatePigSaleTx 实现了在数据库中创建猪只销售记录的逻辑。 -func (r *gormPigTradeRepository) CreatePigSaleTx(tx *gorm.DB, sale *models.PigSale) error { - return tx.Create(sale).Error +func (r *gormPigTradeRepository) CreatePigSaleTx(ctx context.Context, tx *gorm.DB, sale *models.PigSale) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePigSaleTx") + return tx.WithContext(repoCtx).Create(sale).Error } // CreatePigPurchaseTx 实现了在数据库中创建猪只采购记录的逻辑。 -func (r *gormPigTradeRepository) CreatePigPurchaseTx(tx *gorm.DB, purchase *models.PigPurchase) error { - return tx.Create(purchase).Error +func (r *gormPigTradeRepository) CreatePigPurchaseTx(ctx context.Context, tx *gorm.DB, purchase *models.PigPurchase) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePigPurchaseTx") + return tx.WithContext(repoCtx).Create(purchase).Error } // ListPigPurchases 实现了分页和过滤查询猪只采购记录的功能 -func (r *gormPigTradeRepository) ListPigPurchases(opts PigPurchaseListOptions, page, pageSize int) ([]models.PigPurchase, int64, error) { +func (r *gormPigTradeRepository) ListPigPurchases(ctx context.Context, opts PigPurchaseListOptions, page, pageSize int) ([]models.PigPurchase, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListPigPurchases") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -72,7 +79,7 @@ func (r *gormPigTradeRepository) ListPigPurchases(opts PigPurchaseListOptions, p var results []models.PigPurchase var total int64 - query := r.db.Model(&models.PigPurchase{}) + query := r.db.WithContext(repoCtx).Model(&models.PigPurchase{}) if opts.PigBatchID != nil { query = query.Where("pig_batch_id = ?", *opts.PigBatchID) @@ -107,7 +114,8 @@ func (r *gormPigTradeRepository) ListPigPurchases(opts PigPurchaseListOptions, p } // ListPigSales 实现了分页和过滤查询猪只销售记录的功能 -func (r *gormPigTradeRepository) ListPigSales(opts PigSaleListOptions, page, pageSize int) ([]models.PigSale, int64, error) { +func (r *gormPigTradeRepository) ListPigSales(ctx context.Context, opts PigSaleListOptions, page, pageSize int) ([]models.PigSale, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListPigSales") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -115,7 +123,7 @@ func (r *gormPigTradeRepository) ListPigSales(opts PigSaleListOptions, page, pag var results []models.PigSale var total int64 - query := r.db.Model(&models.PigSale{}) + query := r.db.WithContext(repoCtx).Model(&models.PigSale{}) if opts.PigBatchID != nil { query = query.Where("pig_batch_id = ?", *opts.PigBatchID) diff --git a/internal/infra/repository/pig_transfer_log_repository.go b/internal/infra/repository/pig_transfer_log_repository.go index 9db57e1..ca0d5e5 100644 --- a/internal/infra/repository/pig_transfer_log_repository.go +++ b/internal/infra/repository/pig_transfer_log_repository.go @@ -1,9 +1,12 @@ package repository import ( + "context" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) @@ -22,39 +25,43 @@ type PigTransferLogListOptions struct { // PigTransferLogRepository 定义了猪只迁移日志数据持久化的接口。 type PigTransferLogRepository interface { // CreatePigTransferLog 在数据库中创建一条猪只迁移日志记录。 - CreatePigTransferLog(tx *gorm.DB, log *models.PigTransferLog) error + CreatePigTransferLog(ctx context.Context, tx *gorm.DB, log *models.PigTransferLog) error // GetLogsForPenSince 获取指定猪栏自特定时间点以来的所有迁移日志,按时间倒序排列。 - GetLogsForPenSince(tx *gorm.DB, penID uint, since time.Time) ([]*models.PigTransferLog, error) + GetLogsForPenSince(ctx context.Context, tx *gorm.DB, penID uint, since time.Time) ([]*models.PigTransferLog, error) // ListPigTransferLogs 支持分页和过滤的猪只迁移日志列表查询 - ListPigTransferLogs(opts PigTransferLogListOptions, page, pageSize int) ([]models.PigTransferLog, int64, error) + ListPigTransferLogs(ctx context.Context, opts PigTransferLogListOptions, page, pageSize int) ([]models.PigTransferLog, int64, error) } // gormPigTransferLogRepository 是 PigTransferLogRepository 接口的 GORM 实现。 type gormPigTransferLogRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormPigTransferLogRepository 创建一个新的 PigTransferLogRepository GORM 实现实例。 -func NewGormPigTransferLogRepository(db *gorm.DB) PigTransferLogRepository { - return &gormPigTransferLogRepository{db: db} +func NewGormPigTransferLogRepository(ctx context.Context, db *gorm.DB) PigTransferLogRepository { + return &gormPigTransferLogRepository{ctx: ctx, db: db} } // CreatePigTransferLog 实现了在数据库中创建猪只迁移日志记录的逻辑。 -func (r *gormPigTransferLogRepository) CreatePigTransferLog(tx *gorm.DB, log *models.PigTransferLog) error { - return tx.Create(log).Error +func (r *gormPigTransferLogRepository) CreatePigTransferLog(ctx context.Context, tx *gorm.DB, log *models.PigTransferLog) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePigTransferLog") + return tx.WithContext(repoCtx).Create(log).Error } // GetLogsForPenSince 实现了获取猪栏自特定时间点以来所有迁移日志的逻辑。 -func (r *gormPigTransferLogRepository) GetLogsForPenSince(tx *gorm.DB, penID uint, since time.Time) ([]*models.PigTransferLog, error) { +func (r *gormPigTransferLogRepository) GetLogsForPenSince(ctx context.Context, tx *gorm.DB, penID uint, since time.Time) ([]*models.PigTransferLog, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetLogsForPenSince") var logs []*models.PigTransferLog - err := tx.Where("pen_id = ? AND transfer_time >= ?", penID, since).Order("transfer_time DESC").Find(&logs).Error + err := tx.WithContext(repoCtx).Where("pen_id = ? AND transfer_time >= ?", penID, since).Order("transfer_time DESC").Find(&logs).Error return logs, err } // ListPigTransferLogs 实现了分页和过滤查询猪只迁移日志的功能 -func (r *gormPigTransferLogRepository) ListPigTransferLogs(opts PigTransferLogListOptions, page, pageSize int) ([]models.PigTransferLog, int64, error) { +func (r *gormPigTransferLogRepository) ListPigTransferLogs(ctx context.Context, opts PigTransferLogListOptions, page, pageSize int) ([]models.PigTransferLog, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListPigTransferLogs") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -62,7 +69,7 @@ func (r *gormPigTransferLogRepository) ListPigTransferLogs(opts PigTransferLogLi var results []models.PigTransferLog var total int64 - query := r.db.Model(&models.PigTransferLog{}) + query := r.db.WithContext(repoCtx).Model(&models.PigTransferLog{}) if opts.PigBatchID != nil { query = query.Where("pig_batch_id = ?", *opts.PigBatchID) diff --git a/internal/infra/repository/plan_repository.go b/internal/infra/repository/plan_repository.go index 25fdbc0..cfaf01d 100644 --- a/internal/infra/repository/plan_repository.go +++ b/internal/infra/repository/plan_repository.go @@ -1,11 +1,14 @@ package repository import ( + "context" "encoding/json" "errors" "fmt" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/datatypes" "gorm.io/gorm" ) @@ -39,67 +42,71 @@ type ListPlansOptions struct { // 这是为了让业务逻辑层依赖于抽象,而不是具体的数据库实现 type PlanRepository interface { // ListPlans 获取计划列表,支持过滤和分页 - ListPlans(opts ListPlansOptions, page, pageSize int) ([]models.Plan, int64, error) + ListPlans(ctx context.Context, opts ListPlansOptions, page, pageSize int) ([]models.Plan, int64, error) // GetBasicPlanByID 根据ID获取计划的基本信息,不包含子计划和任务详情 - GetBasicPlanByID(id uint) (*models.Plan, error) + GetBasicPlanByID(ctx context.Context, id uint) (*models.Plan, error) // GetPlanByID 根据ID获取计划,包含子计划和任务详情 - GetPlanByID(id uint) (*models.Plan, error) + GetPlanByID(ctx context.Context, id uint) (*models.Plan, error) // GetPlansByIDs 根据ID列表获取计划,不包含子计划和任务详情 - GetPlansByIDs(ids []uint) ([]models.Plan, error) + GetPlansByIDs(ctx context.Context, ids []uint) ([]models.Plan, error) // CreatePlan 创建一个新的计划 - CreatePlan(plan *models.Plan) error + CreatePlan(ctx context.Context, plan *models.Plan) error // CreatePlanTx 在指定事务中创建一个新的计划 - CreatePlanTx(tx *gorm.DB, plan *models.Plan) error + CreatePlanTx(ctx context.Context, tx *gorm.DB, plan *models.Plan) error // UpdatePlanMetadataAndStructure 更新计划的元数据和结构,但不包括状态等运行时信息 - UpdatePlanMetadataAndStructure(plan *models.Plan) error + UpdatePlanMetadataAndStructure(ctx context.Context, plan *models.Plan) error // UpdatePlan 更新计划的所有字段 - UpdatePlan(plan *models.Plan) error + UpdatePlan(ctx context.Context, plan *models.Plan) error // UpdatePlanStatus 更新指定计划的状态 - UpdatePlanStatus(id uint, status models.PlanStatus) error + UpdatePlanStatus(ctx context.Context, id uint, status models.PlanStatus) error // UpdateExecuteCount 更新指定计划的执行计数 - UpdateExecuteCount(id uint, count uint) error + UpdateExecuteCount(ctx context.Context, id uint, count uint) error // DeletePlan 根据ID删除计划,同时删除其关联的任务(非子任务)或子计划关联 - DeletePlan(id uint) error + DeletePlan(ctx context.Context, id uint) error // FlattenPlanTasks 递归展开计划,返回按执行顺序排列的所有任务列表 - FlattenPlanTasks(planID uint) ([]models.Task, error) + FlattenPlanTasks(ctx context.Context, planID uint) ([]models.Task, error) // DeleteTask 根据ID删除任务 - DeleteTask(id int) error + DeleteTask(ctx context.Context, id int) error // FindPlanAnalysisTaskByParamsPlanID 根据Parameters中的ParamsPlanID字段值查找TaskPlanAnalysis类型的Task - FindPlanAnalysisTaskByParamsPlanID(paramsPlanID uint) (*models.Task, error) + FindPlanAnalysisTaskByParamsPlanID(ctx context.Context, paramsPlanID uint) (*models.Task, error) // FindRunnablePlans 获取所有应执行的计划 - FindRunnablePlans() ([]*models.Plan, error) + FindRunnablePlans(ctx context.Context) ([]*models.Plan, error) // FindInactivePlans 获取所有已禁用或已停止的计划 - FindInactivePlans() ([]*models.Plan, error) + FindInactivePlans(ctx context.Context) ([]*models.Plan, error) + // FindPlanAnalysisTaskByPlanID 根据 PlanID 找到其关联的 'plan_analysis' 任务 - FindPlanAnalysisTaskByPlanID(planID uint) (*models.Task, error) + FindPlanAnalysisTaskByPlanID(ctx context.Context, planID uint) (*models.Task, error) // CreatePlanAnalysisTask 创建一个 plan_analysis 类型的任务并返回它 - CreatePlanAnalysisTask(plan *models.Plan) (*models.Task, error) + CreatePlanAnalysisTask(ctx context.Context, plan *models.Plan) (*models.Task, error) // FindPlansWithPendingTasks 查找所有正在执行的计划 - FindPlansWithPendingTasks() ([]*models.Plan, error) + FindPlansWithPendingTasks(ctx context.Context) ([]*models.Plan, error) // StopPlanTransactionally 停止一个计划的执行,包括更新状态、移除待执行任务和更新执行日志 - StopPlanTransactionally(planID uint) error + StopPlanTransactionally(ctx context.Context, planID uint) error // UpdatePlanStateAfterExecution 更新计划执行后的状态(计数和状态) - UpdatePlanStateAfterExecution(planID uint, newCount uint, newStatus models.PlanStatus) error + UpdatePlanStateAfterExecution(ctx context.Context, planID uint, newCount uint, newStatus models.PlanStatus) error } // gormPlanRepository 是 PlanRepository 的 GORM 实现 type gormPlanRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormPlanRepository 创建一个新的 PlanRepository GORM 实现实例 -func NewGormPlanRepository(db *gorm.DB) PlanRepository { +func NewGormPlanRepository(ctx context.Context, db *gorm.DB) PlanRepository { return &gormPlanRepository{ - db: db, + ctx: ctx, + db: db, } } // ListPlans 获取计划列表,支持过滤和分页 -func (r *gormPlanRepository) ListPlans(opts ListPlansOptions, page, pageSize int) ([]models.Plan, int64, error) { +func (r *gormPlanRepository) ListPlans(ctx context.Context, opts ListPlansOptions, page, pageSize int) ([]models.Plan, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListPlans") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -107,7 +114,7 @@ func (r *gormPlanRepository) ListPlans(opts ListPlansOptions, page, pageSize int var plans []models.Plan var total int64 - query := r.db.Model(&models.Plan{}) + query := r.db.WithContext(repoCtx).Model(&models.Plan{}) switch opts.PlanType { case PlanTypeFilterCustom: @@ -132,10 +139,11 @@ func (r *gormPlanRepository) ListPlans(opts ListPlansOptions, page, pageSize int } // GetBasicPlanByID 根据ID获取计划的基本信息,不包含子计划和任务详情 -func (r *gormPlanRepository) GetBasicPlanByID(id uint) (*models.Plan, error) { +func (r *gormPlanRepository) GetBasicPlanByID(ctx context.Context, id uint) (*models.Plan, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetBasicPlanByID") var plan models.Plan // GORM 默认不会加载关联,除非使用 Preload,所以直接 First 即可满足要求 - result := r.db.First(&plan, id) + result := r.db.WithContext(repoCtx).First(&plan, id) if result.Error != nil { return nil, result.Error } @@ -143,12 +151,13 @@ func (r *gormPlanRepository) GetBasicPlanByID(id uint) (*models.Plan, error) { } // GetPlansByIDs 根据ID列表获取计划,不包含子计划和任务详情 -func (r *gormPlanRepository) GetPlansByIDs(ids []uint) ([]models.Plan, error) { +func (r *gormPlanRepository) GetPlansByIDs(ctx context.Context, ids []uint) ([]models.Plan, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetPlansByIDs") var plans []models.Plan if len(ids) == 0 { return plans, nil } - err := r.db.Where("id IN ?", ids).Find(&plans).Error + err := r.db.WithContext(repoCtx).Where("id IN ?", ids).Find(&plans).Error if err != nil { return nil, err } @@ -156,11 +165,12 @@ func (r *gormPlanRepository) GetPlansByIDs(ids []uint) ([]models.Plan, error) { } // GetPlanByID 根据ID获取计划,包含子计划和任务详情 -func (r *gormPlanRepository) GetPlanByID(id uint) (*models.Plan, error) { +func (r *gormPlanRepository) GetPlanByID(ctx context.Context, id uint) (*models.Plan, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetPlanByID") var plan models.Plan // 先获取基本计划信息 - result := r.db.First(&plan, id) + result := r.db.WithContext(repoCtx).First(&plan, id) if result.Error != nil { return nil, result.Error } @@ -170,14 +180,14 @@ func (r *gormPlanRepository) GetPlanByID(id uint) (*models.Plan, error) { case models.PlanContentTypeSubPlans: // 加载子计划引用 var subPlans []models.SubPlan - result = r.db.Where("parent_plan_id = ?", plan.ID).Order("execution_order").Find(&subPlans) + result = r.db.WithContext(repoCtx).Where("parent_plan_id = ?", plan.ID).Order("execution_order").Find(&subPlans) if result.Error != nil { return nil, result.Error } // 递归加载每个子计划的完整信息 for i := range subPlans { - childPlan, err := r.GetPlanByID(subPlans[i].ChildPlanID) + childPlan, err := r.GetPlanByID(repoCtx, subPlans[i].ChildPlanID) if err != nil { return nil, err } @@ -187,7 +197,7 @@ func (r *gormPlanRepository) GetPlanByID(id uint) (*models.Plan, error) { plan.SubPlans = subPlans case models.PlanContentTypeTasks: // 加载任务 - result = r.db.Preload("Tasks", func(taskDB *gorm.DB) *gorm.DB { + result = r.db.WithContext(repoCtx).Preload("Tasks", func(taskDB *gorm.DB) *gorm.DB { return taskDB.Order("execution_order") }).First(&plan, id) if result.Error != nil { @@ -201,12 +211,14 @@ func (r *gormPlanRepository) GetPlanByID(id uint) (*models.Plan, error) { } // CreatePlan 创建一个新的计划 -func (r *gormPlanRepository) CreatePlan(plan *models.Plan) error { - return r.CreatePlanTx(r.db, plan) +func (r *gormPlanRepository) CreatePlan(ctx context.Context, plan *models.Plan) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePlan") + return r.CreatePlanTx(repoCtx, r.db, plan) } // CreatePlanTx 在指定事务中创建一个新的计划 -func (r *gormPlanRepository) CreatePlanTx(tx *gorm.DB, plan *models.Plan) error { +func (r *gormPlanRepository) CreatePlanTx(ctx context.Context, tx *gorm.DB, plan *models.Plan) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePlanTx") // 1. 前置校验 if plan.ID != 0 { return ErrCreateWithNonZeroID @@ -239,7 +251,7 @@ func (r *gormPlanRepository) CreatePlanTx(tx *gorm.DB, plan *models.Plan) error if len(ids) > 0 { var count int64 - if err := tx.Model(&models.Plan{}).Where("id IN ?", ids).Count(&count).Error; err != nil { + if err := tx.WithContext(repoCtx).Model(&models.Plan{}).Where("id IN ?", ids).Count(&count).Error; err != nil { return fmt.Errorf("验证子计划存在性失败: %w", err) } if int(count) != len(ids) { @@ -251,13 +263,13 @@ func (r *gormPlanRepository) CreatePlanTx(tx *gorm.DB, plan *models.Plan) error // 2. 创建根计划 // GORM 会自动处理关联的 Tasks (如果 ContentType 是 tasks 且 Task.ID 为 0), // 以及 Tasks 内部已经填充好的 Devices 关联。 - if err := tx.Create(plan).Error; err != nil { + if err := tx.WithContext(repoCtx).Create(plan).Error; err != nil { return err } // 3. 创建触发器Task // 关键修改:调用 createPlanAnalysisTask 并处理其返回的 Task 对象 - _, err := r.createPlanAnalysisTask(tx, plan) + _, err := r.createPlanAnalysisTask(repoCtx, tx, plan) if err != nil { return err } @@ -265,32 +277,36 @@ func (r *gormPlanRepository) CreatePlanTx(tx *gorm.DB, plan *models.Plan) error } // UpdatePlan 更新计划 -func (r *gormPlanRepository) UpdatePlan(plan *models.Plan) error { - return r.db.Save(plan).Error +func (r *gormPlanRepository) UpdatePlan(ctx context.Context, plan *models.Plan) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePlan") + return r.db.WithContext(repoCtx).Save(plan).Error } // UpdatePlanMetadataAndStructure 是更新计划元数据和结构的公共入口点 -func (r *gormPlanRepository) UpdatePlanMetadataAndStructure(plan *models.Plan) error { - return r.db.Transaction(func(tx *gorm.DB) error { - return r.updatePlanMetadataAndStructureTx(tx, plan) +func (r *gormPlanRepository) UpdatePlanMetadataAndStructure(ctx context.Context, plan *models.Plan) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePlanMetadataAndStructure") + return r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error { + return r.updatePlanMetadataAndStructureTx(repoCtx, tx, plan) }) } // updatePlanMetadataAndStructureTx 在事务中协调整个更新过程 -func (r *gormPlanRepository) updatePlanMetadataAndStructureTx(tx *gorm.DB, plan *models.Plan) error { - if err := r.validatePlanTree(tx, plan); err != nil { +func (r *gormPlanRepository) updatePlanMetadataAndStructureTx(ctx context.Context, tx *gorm.DB, plan *models.Plan) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "updatePlanMetadataAndStructureTx") + if err := r.validatePlanTree(repoCtx, tx, plan); err != nil { return err } - if err := r.reconcilePlanNode(tx, plan); err != nil { + if err := r.reconcilePlanNode(repoCtx, tx, plan); err != nil { return err } // 更新Plan触发器 - return r.updatePlanAnalysisTask(tx, plan) + return r.updatePlanAnalysisTask(repoCtx, tx, plan) } // validatePlanTree 对整个计划树进行全面的只读健康检查 -func (r *gormPlanRepository) validatePlanTree(tx *gorm.DB, plan *models.Plan) error { +func (r *gormPlanRepository) validatePlanTree(ctx context.Context, tx *gorm.DB, plan *models.Plan) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "validatePlanTree") // 1. 检查根节点 if plan == nil || plan.ID == 0 { return ErrUpdateWithInvalidRoot @@ -319,7 +335,7 @@ func (r *gormPlanRepository) validatePlanTree(tx *gorm.DB, plan *models.Plan) er if len(idsToCheck) > 0 { var count int64 - if err := tx.Model(&models.Plan{}).Where("id IN ?", idsToCheck).Count(&count).Error; err != nil { + if err := tx.WithContext(repoCtx).Model(&models.Plan{}).Where("id IN ?", idsToCheck).Count(&count).Error; err != nil { return fmt.Errorf("检查计划存在性时出错: %w", err) } if int(count) != len(idsToCheck) { @@ -358,12 +374,13 @@ func validateNodeAndDetectCycles(plan *models.Plan, allIDs, recursionStack map[u } // reconcilePlanNode 递归地同步数据库状态以匹配给定的计划节点 -func (r *gormPlanRepository) reconcilePlanNode(tx *gorm.DB, plan *models.Plan) error { +func (r *gormPlanRepository) reconcilePlanNode(ctx context.Context, tx *gorm.DB, plan *models.Plan) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "reconcilePlanNode") if plan == nil { return nil } // 1. 更新节点本身的基础字段 - if err := tx.Model(plan).Select("Name", "Description", "ExecutionType", "ExecuteNum", "CronExpression", "ContentType").Updates(plan).Error; err != nil { + if err := tx.WithContext(repoCtx).Model(plan).Select("Name", "Description", "ExecutionType", "ExecuteNum", "CronExpression", "ContentType").Updates(plan).Error; err != nil { return err } @@ -371,26 +388,27 @@ func (r *gormPlanRepository) reconcilePlanNode(tx *gorm.DB, plan *models.Plan) e switch plan.ContentType { case models.PlanContentTypeTasks: // 清理旧的子计划关联 - if err := tx.Where("parent_plan_id = ?", plan.ID).Delete(&models.SubPlan{}).Error; err != nil { + if err := tx.WithContext(repoCtx).Where("parent_plan_id = ?", plan.ID).Delete(&models.SubPlan{}).Error; err != nil { return fmt.Errorf("更新时清理旧的子计划关联失败: %w", err) } // 协调任务列表 - return r.reconcileTasks(tx, plan) + return r.reconcileTasks(repoCtx, tx, plan) case models.PlanContentTypeSubPlans: // 清理旧的任务 - if err := tx.Where("plan_id = ?", plan.ID).Delete(&models.Task{}).Error; err != nil { + if err := tx.WithContext(repoCtx).Where("plan_id = ?", plan.ID).Delete(&models.Task{}).Error; err != nil { return fmt.Errorf("更新时清理旧的任务失败: %w", err) } // 协调子计划关联 - return r.reconcileSubPlans(tx, plan) + return r.reconcileSubPlans(repoCtx, tx, plan) } return nil } // reconcileTasks 精确同步任务列表 -func (r *gormPlanRepository) reconcileTasks(tx *gorm.DB, plan *models.Plan) error { +func (r *gormPlanRepository) reconcileTasks(ctx context.Context, tx *gorm.DB, plan *models.Plan) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "reconcileTasks") var existingTasks []models.Task - if err := tx.Where("plan_id = ?", plan.ID).Find(&existingTasks).Error; err != nil { + if err := tx.WithContext(repoCtx).Where("plan_id = ?", plan.ID).Find(&existingTasks).Error; err != nil { return err } @@ -403,12 +421,12 @@ func (r *gormPlanRepository) reconcileTasks(tx *gorm.DB, plan *models.Plan) erro task := &plan.Tasks[i] if task.ID == 0 { task.PlanID = plan.ID - if err := tx.Create(task).Error; err != nil { + if err := tx.WithContext(repoCtx).Create(task).Error; err != nil { return err } } else { delete(existingTaskMap, task.ID) // 从待删除map中移除 - if err := tx.Model(task).Updates(task).Error; err != nil { + if err := tx.WithContext(repoCtx).Model(task).Updates(task).Error; err != nil { return err } } @@ -420,15 +438,16 @@ func (r *gormPlanRepository) reconcileTasks(tx *gorm.DB, plan *models.Plan) erro } if len(tasksToDelete) > 0 { - return r.deleteTasksTx(tx, tasksToDelete) + return r.deleteTasksTx(repoCtx, tx, tasksToDelete) } return nil } // reconcileSubPlans 精确同步子计划关联并递归 -func (r *gormPlanRepository) reconcileSubPlans(tx *gorm.DB, plan *models.Plan) error { +func (r *gormPlanRepository) reconcileSubPlans(ctx context.Context, tx *gorm.DB, plan *models.Plan) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "reconcileSubPlans") var existingLinks []models.SubPlan - if err := tx.Where("parent_plan_id = ?", plan.ID).Find(&existingLinks).Error; err != nil { + if err := tx.WithContext(repoCtx).Where("parent_plan_id = ?", plan.ID).Find(&existingLinks).Error; err != nil { return err } @@ -441,12 +460,12 @@ func (r *gormPlanRepository) reconcileSubPlans(tx *gorm.DB, plan *models.Plan) e link := &plan.SubPlans[i] link.ParentPlanID = plan.ID if link.ID == 0 { - if err := tx.Create(link).Error; err != nil { + if err := tx.WithContext(repoCtx).Create(link).Error; err != nil { return err } } else { delete(existingLinkMap, link.ID) // 从待删除map中移除 - if err := tx.Model(link).Updates(link).Error; err != nil { + if err := tx.WithContext(repoCtx).Model(link).Updates(link).Error; err != nil { return err } } @@ -458,7 +477,7 @@ func (r *gormPlanRepository) reconcileSubPlans(tx *gorm.DB, plan *models.Plan) e } if len(linksToDelete) > 0 { - if err := tx.Delete(&models.SubPlan{}, linksToDelete).Error; err != nil { + if err := tx.WithContext(repoCtx).Delete(&models.SubPlan{}, linksToDelete).Error; err != nil { return err } } @@ -466,11 +485,12 @@ func (r *gormPlanRepository) reconcileSubPlans(tx *gorm.DB, plan *models.Plan) e } // DeletePlan 根据ID删除计划,同时删除其关联的任务(非子任务)或子计划关联 -func (r *gormPlanRepository) DeletePlan(id uint) error { - return r.db.Transaction(func(tx *gorm.DB) error { +func (r *gormPlanRepository) DeletePlan(ctx context.Context, id uint) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "DeletePlan") + return r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error { // 1. 检查该计划是否是其他计划的子计划 var count int64 - if err := tx.Model(&models.SubPlan{}).Where("child_plan_id = ?", id).Count(&count).Error; err != nil { + if err := tx.WithContext(repoCtx).Model(&models.SubPlan{}).Where("child_plan_id = ?", id).Count(&count).Error; err != nil { return fmt.Errorf("检查计划是否为子计划失败: %w", err) } if count > 0 { @@ -479,7 +499,7 @@ func (r *gormPlanRepository) DeletePlan(id uint) error { var plan models.Plan // 2. 获取计划以确定其内容类型 - if err := tx.First(&plan, id).Error; err != nil { + if err := tx.WithContext(repoCtx).First(&plan, id).Error; err != nil { return err } @@ -487,18 +507,18 @@ func (r *gormPlanRepository) DeletePlan(id uint) error { switch plan.ContentType { case models.PlanContentTypeTasks: // 删除与此计划关联的所有非子任务 - if err := tx.Where("plan_id = ?", id).Delete(&models.Task{}).Error; err != nil { + if err := tx.WithContext(repoCtx).Where("plan_id = ?", id).Delete(&models.Task{}).Error; err != nil { return fmt.Errorf("删除计划ID %d 的任务失败: %w", id, err) } case models.PlanContentTypeSubPlans: // 删除与此计划关联的所有子计划链接 - if err := tx.Where("parent_plan_id = ?", id).Delete(&models.SubPlan{}).Error; err != nil { + if err := tx.WithContext(repoCtx).Where("parent_plan_id = ?", id).Delete(&models.SubPlan{}).Error; err != nil { return fmt.Errorf("删除计划ID %d 的子计划关联失败: %w", id, err) } } // 4. 删除计划本身 - if err := tx.Delete(&models.Plan{}, id).Error; err != nil { + if err := tx.WithContext(repoCtx).Delete(&models.Plan{}, id).Error; err != nil { return fmt.Errorf("删除计划ID %d 失败: %w", id, err) } @@ -507,17 +527,19 @@ func (r *gormPlanRepository) DeletePlan(id uint) error { } // FlattenPlanTasks 递归展开计划,返回按执行顺序排列的所有任务列表 -func (r *gormPlanRepository) FlattenPlanTasks(planID uint) ([]models.Task, error) { - plan, err := r.GetPlanByID(planID) +func (r *gormPlanRepository) FlattenPlanTasks(ctx context.Context, planID uint) ([]models.Task, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FlattenPlanTasks") + plan, err := r.GetPlanByID(repoCtx, planID) if err != nil { return nil, fmt.Errorf("获取计划(ID: %d)失败: %w", planID, err) } - return r.flattenPlanTasksRecursive(plan) + return r.flattenPlanTasksRecursive(repoCtx, plan) } // flattenPlanTasksRecursive 递归展开计划的内部实现 -func (r *gormPlanRepository) flattenPlanTasksRecursive(plan *models.Plan) ([]models.Task, error) { +func (r *gormPlanRepository) flattenPlanTasksRecursive(ctx context.Context, plan *models.Plan) ([]models.Task, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "flattenPlanTasksRecursive") var tasks []models.Task switch plan.ContentType { @@ -535,10 +557,10 @@ func (r *gormPlanRepository) flattenPlanTasksRecursive(plan *models.Plan) ([]mod // 确保子计划已经被加载 if subPlan.ChildPlan != nil { - subTasks, err = r.flattenPlanTasksRecursive(subPlan.ChildPlan) + subTasks, err = r.flattenPlanTasksRecursive(repoCtx, subPlan.ChildPlan) } else { // 如果子计划未加载,则从数据库获取并递归展开 - subTasks, err = r.FlattenPlanTasks(subPlan.ChildPlanID) + subTasks, err = r.FlattenPlanTasks(repoCtx, subPlan.ChildPlanID) } if err != nil { @@ -556,22 +578,24 @@ func (r *gormPlanRepository) flattenPlanTasksRecursive(plan *models.Plan) ([]mod } // DeleteTask 根据ID删除任务 -func (r *gormPlanRepository) DeleteTask(id int) error { +func (r *gormPlanRepository) DeleteTask(ctx context.Context, id int) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "DeleteTask") // 使用事务确保操作的原子性 - return r.db.Transaction(func(tx *gorm.DB) error { - return r.deleteTasksTx(tx, []int{id}) + return r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error { + return r.deleteTasksTx(repoCtx, tx, []int{id}) }) } // deleteTasksTx 在事务中批量软删除任务,并物理删除其在关联表中的记录 -func (r *gormPlanRepository) deleteTasksTx(tx *gorm.DB, ids []int) error { +func (r *gormPlanRepository) deleteTasksTx(ctx context.Context, tx *gorm.DB, ids []int) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "deleteTasksTx") if len(ids) == 0 { return nil } // 检查是否有待执行任务引用了这些任务 var pendingTaskCount int64 - if err := tx.Model(&models.PendingTask{}).Where("task_id IN ?", ids).Count(&pendingTaskCount).Error; err != nil { + if err := tx.WithContext(repoCtx).Model(&models.PendingTask{}).Where("task_id IN ?", ids).Count(&pendingTaskCount).Error; err != nil { return fmt.Errorf("检查待执行任务时出错: %w", err) } @@ -584,12 +608,12 @@ func (r *gormPlanRepository) deleteTasksTx(tx *gorm.DB, ids []int) error { // 1. 直接、高效地从关联表中物理删除所有相关记录 // 这是最关键的优化,避免了不必要的查询和循环 - if err := tx.Where("task_id IN ?", ids).Delete(&models.DeviceTask{}).Error; err != nil { + if err := tx.WithContext(repoCtx).Where("task_id IN ?", ids).Delete(&models.DeviceTask{}).Error; err != nil { return fmt.Errorf("清理任务的设备关联失败: %w", err) } // 2. 对任务本身进行软删除 - result := tx.Delete(&models.Task{}, ids) + result := tx.WithContext(repoCtx).Delete(&models.Task{}, ids) if result.Error != nil { return result.Error } @@ -603,13 +627,15 @@ func (r *gormPlanRepository) deleteTasksTx(tx *gorm.DB, ids []int) error { } // FindPlanAnalysisTaskByParamsPlanID 根据Parameters中的ParamsPlanID字段值查找TaskPlanAnalysis类型的Task -func (r *gormPlanRepository) FindPlanAnalysisTaskByParamsPlanID(paramsPlanID uint) (*models.Task, error) { - return r.findPlanAnalysisTask(r.db, paramsPlanID) +func (r *gormPlanRepository) FindPlanAnalysisTaskByParamsPlanID(ctx context.Context, paramsPlanID uint) (*models.Task, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindPlanAnalysisTaskByParamsPlanID") + return r.findPlanAnalysisTask(repoCtx, r.db, paramsPlanID) } // createPlanAnalysisTask 用于创建一个TaskPlanAnalysis类型的Task // 关键修改:Task.PlanID 设置为 0,实际 PlanID 存储在 Parameters 中,并返回创建的 Task -func (r *gormPlanRepository) createPlanAnalysisTask(tx *gorm.DB, plan *models.Plan) (*models.Task, error) { +func (r *gormPlanRepository) createPlanAnalysisTask(ctx context.Context, tx *gorm.DB, plan *models.Plan) (*models.Task, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "createPlanAnalysisTask") m := map[string]interface{}{ models.ParamsPlanID: plan.ID, } @@ -627,22 +653,23 @@ func (r *gormPlanRepository) createPlanAnalysisTask(tx *gorm.DB, plan *models.Pl Parameters: datatypes.JSON(parameters), } - if err := tx.Create(task).Error; err != nil { + if err := tx.WithContext(repoCtx).Create(task).Error; err != nil { return nil, err } return task, nil } // updatePlanAnalysisTask 使用更安全的方式更新触发器任务 -func (r *gormPlanRepository) updatePlanAnalysisTask(tx *gorm.DB, plan *models.Plan) error { - task, err := r.findPlanAnalysisTask(tx, plan.ID) +func (r *gormPlanRepository) updatePlanAnalysisTask(ctx context.Context, tx *gorm.DB, plan *models.Plan) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "updatePlanAnalysisTask") + task, err := r.findPlanAnalysisTask(repoCtx, tx, plan.ID) if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("查找现有计划分析任务失败: %w", err) } // 如果触发器任务不存在,则创建一个 if task == nil { - _, err := r.createPlanAnalysisTask(tx, plan) + _, err := r.createPlanAnalysisTask(repoCtx, tx, plan) return err } @@ -650,24 +677,26 @@ func (r *gormPlanRepository) updatePlanAnalysisTask(tx *gorm.DB, plan *models.Pl task.Name = fmt.Sprintf("'%s'计划触发器", plan.Name) task.Description = fmt.Sprintf("计划名: %s, 计划ID: %d", plan.Name, plan.ID) - return tx.Save(task).Error + return tx.WithContext(repoCtx).Save(task).Error } -func (r *gormPlanRepository) FindRunnablePlans() ([]*models.Plan, error) { +func (r *gormPlanRepository) FindRunnablePlans(ctx context.Context) ([]*models.Plan, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindRunnablePlans") var plans []*models.Plan - err := r.db. + err := r.db.WithContext(repoCtx). Where("status = ?", models.PlanStatusEnabled). Where( - r.db.Where("execution_type = ?", models.PlanExecutionTypeManual). + r.db.WithContext(repoCtx).Where("execution_type = ?", models.PlanExecutionTypeManual). Or("execution_type = ? AND (execute_num = 0 OR execute_count < execute_num)", models.PlanExecutionTypeAutomatic), ). Find(&plans).Error return plans, err } -func (r *gormPlanRepository) FindInactivePlans() ([]*models.Plan, error) { +func (r *gormPlanRepository) FindInactivePlans(ctx context.Context) ([]*models.Plan, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindInactivePlans") var plans []*models.Plan - err := r.db. + err := r.db.WithContext(repoCtx). Where("status != ?", models.PlanStatusEnabled). Find(&plans).Error return plans, err @@ -675,9 +704,10 @@ func (r *gormPlanRepository) FindInactivePlans() ([]*models.Plan, error) { // findPlanAnalysisTask 是一个内部使用的、更高效的查找方法 // 关键修改:通过查询 parameters JSON 字段来查找 -func (r *gormPlanRepository) findPlanAnalysisTask(tx *gorm.DB, planID uint) (*models.Task, error) { +func (r *gormPlanRepository) findPlanAnalysisTask(ctx context.Context, tx *gorm.DB, planID uint) (*models.Task, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "findPlanAnalysisTask") var task models.Task - err := tx.Where("type = ? AND parameters->>'plan_id' = ?", models.TaskPlanAnalysis, fmt.Sprintf("%d", planID)).First(&task).Error + err := tx.WithContext(repoCtx).Where("type = ? AND parameters->>'plan_id' = ?", models.TaskPlanAnalysis, fmt.Sprintf("%d", planID)).First(&task).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil // 未找到不是错误,返回nil, nil } @@ -686,28 +716,31 @@ func (r *gormPlanRepository) findPlanAnalysisTask(tx *gorm.DB, planID uint) (*mo // FindPlanAnalysisTaskByPlanID 是暴露给外部的公共方法 // 关键修改:通过查询 parameters JSON 字段来查找 -func (r *gormPlanRepository) FindPlanAnalysisTaskByPlanID(planID uint) (*models.Task, error) { - return r.findPlanAnalysisTask(r.db, planID) +func (r *gormPlanRepository) FindPlanAnalysisTaskByPlanID(ctx context.Context, planID uint) (*models.Task, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindPlanAnalysisTaskByPlanID") + return r.findPlanAnalysisTask(repoCtx, r.db, planID) } // CreatePlanAnalysisTask 创建一个 plan_analysis 类型的任务并返回它 // 这个方法是公开的,主要由 TaskManager 在发现触发器任务定义丢失时调用。 -func (r *gormPlanRepository) CreatePlanAnalysisTask(plan *models.Plan) (*models.Task, error) { +func (r *gormPlanRepository) CreatePlanAnalysisTask(ctx context.Context, plan *models.Plan) (*models.Task, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePlanAnalysisTask") var createdTask *models.Task - err := r.db.Transaction(func(tx *gorm.DB) error { + err := r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error { var err error - createdTask, err = r.createPlanAnalysisTask(tx, plan) + createdTask, err = r.createPlanAnalysisTask(repoCtx, tx, plan) return err }) return createdTask, err } // FindPlansWithPendingTasks 查找所有正在执行的计划 -func (r *gormPlanRepository) FindPlansWithPendingTasks() ([]*models.Plan, error) { +func (r *gormPlanRepository) FindPlansWithPendingTasks(ctx context.Context) ([]*models.Plan, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindPlansWithPendingTasks") var plans []*models.Plan // 关联 pending_tasks, task_execution_logs, tasks 表来查找符合条件的计划 - err := r.db.Table("plans"). + err := r.db.WithContext(repoCtx).Table("plans"). Joins("JOIN tasks ON plans.id = tasks.plan_id"). Joins("JOIN task_execution_logs ON tasks.id = task_execution_logs.task_id"). Joins("JOIN pending_tasks ON task_execution_logs.id = pending_tasks.task_execution_log_id"). @@ -718,20 +751,21 @@ func (r *gormPlanRepository) FindPlansWithPendingTasks() ([]*models.Plan, error) } // StopPlanTransactionally 停止一个计划的执行,包括更新状态、移除待执行任务和更新执行日志。 -func (r *gormPlanRepository) StopPlanTransactionally(planID uint) error { - return r.db.Transaction(func(tx *gorm.DB) error { +func (r *gormPlanRepository) StopPlanTransactionally(ctx context.Context, planID uint) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "StopPlanTransactionally") + return r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error { // 使用事务创建新的仓库实例,确保所有操作都在同一个事务中 - planRepoTx := NewGormPlanRepository(tx) - executionLogRepoTx := NewGormExecutionLogRepository(tx) - pendingTaskRepoTx := NewGormPendingTaskRepository(tx) + planRepoTx := NewGormPlanRepository(repoCtx, tx) + executionLogRepoTx := NewGormExecutionLogRepository(repoCtx, tx) + pendingTaskRepoTx := NewGormPendingTaskRepository(repoCtx, tx) // 1. 更新计划状态为“已停止” - if err := planRepoTx.UpdatePlanStatus(planID, models.PlanStatusDisabled); err != nil { + if err := planRepoTx.UpdatePlanStatus(repoCtx, planID, models.PlanStatusDisabled); err != nil { return fmt.Errorf("更新计划 #%d 状态为 '已停止' 失败: %w", planID, err) } // 2. 查找当前正在进行的计划执行日志 - planLog, err := executionLogRepoTx.FindInProgressPlanExecutionLogByPlanID(planID) + planLog, err := executionLogRepoTx.FindInProgressPlanExecutionLogByPlanID(repoCtx, planID) if err != nil { return fmt.Errorf("查找计划 #%d 正在进行的执行日志失败: %w", planID, err) } @@ -742,7 +776,7 @@ func (r *gormPlanRepository) StopPlanTransactionally(planID uint) error { } // 3. 查找所有需要被取消的任务执行日志 - taskLogs, err := executionLogRepoTx.FindIncompleteTaskExecutionLogsByPlanLogID(planLog.ID) + taskLogs, err := executionLogRepoTx.FindIncompleteTaskExecutionLogsByPlanLogID(repoCtx, planLog.ID) if err != nil { return fmt.Errorf("查找计划执行日志 #%d 下未完成的任务日志失败: %w", planLog.ID, err) } @@ -754,12 +788,12 @@ func (r *gormPlanRepository) StopPlanTransactionally(planID uint) error { } // 3.1 批量更新任务执行日志状态为“已取消” - if err := executionLogRepoTx.UpdateTaskExecutionLogStatusByIDs(taskLogIDs, models.ExecutionStatusCancelled); err != nil { + if err := executionLogRepoTx.UpdateTaskExecutionLogStatusByIDs(repoCtx, taskLogIDs, models.ExecutionStatusCancelled); err != nil { return fmt.Errorf("批量更新任务执行日志状态为 '已取消' 失败: %w", err) } // 3.2 查找并删除待执行队列中对应的任务 - pendingTasks, err := pendingTaskRepoTx.FindPendingTasksByTaskLogIDs(taskLogIDs) + pendingTasks, err := pendingTaskRepoTx.FindPendingTasksByTaskLogIDs(repoCtx, taskLogIDs) if err != nil { return fmt.Errorf("查找计划执行日志 #%d 下对应的待执行任务失败: %w", planLog.ID, err) } @@ -769,14 +803,14 @@ func (r *gormPlanRepository) StopPlanTransactionally(planID uint) error { for _, pt := range pendingTasks { pendingTaskIDs = append(pendingTaskIDs, pt.ID) } - if err := pendingTaskRepoTx.DeletePendingTasksByIDs(pendingTaskIDs); err != nil { + if err := pendingTaskRepoTx.DeletePendingTasksByIDs(repoCtx, pendingTaskIDs); err != nil { return fmt.Errorf("批量删除待执行任务失败: %w", err) } } } // 4. 更新计划执行历史的总状态为“失败” - if err := executionLogRepoTx.UpdatePlanExecutionLogStatus(planLog.ID, models.ExecutionStatusFailed); err != nil { + if err := executionLogRepoTx.UpdatePlanExecutionLogStatus(repoCtx, planLog.ID, models.ExecutionStatusFailed); err != nil { return fmt.Errorf("更新计划执行日志 #%d 状态为 '失败' 失败: %w", planLog.ID, err) } @@ -785,8 +819,9 @@ func (r *gormPlanRepository) StopPlanTransactionally(planID uint) error { } // UpdatePlanStatus 更新指定计划的状态 -func (r *gormPlanRepository) UpdatePlanStatus(id uint, status models.PlanStatus) error { - result := r.db.Model(&models.Plan{}).Where("id = ?", id).Update("status", status) +func (r *gormPlanRepository) UpdatePlanStatus(ctx context.Context, id uint, status models.PlanStatus) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePlanStatus") + result := r.db.WithContext(repoCtx).Model(&models.Plan{}).Where("id = ?", id).Update("status", status) if result.Error != nil { return result.Error } @@ -796,16 +831,18 @@ func (r *gormPlanRepository) UpdatePlanStatus(id uint, status models.PlanStatus) return nil } -func (r *gormPlanRepository) UpdatePlanStateAfterExecution(planID uint, newCount uint, newStatus models.PlanStatus) error { - return r.db.Model(&models.Plan{}).Where("id = ?", planID).Updates(map[string]interface{}{ +func (r *gormPlanRepository) UpdatePlanStateAfterExecution(ctx context.Context, planID uint, newCount uint, newStatus models.PlanStatus) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePlanStateAfterExecution") + return r.db.WithContext(repoCtx).Model(&models.Plan{}).Where("id = ?", planID).Updates(map[string]interface{}{ "execute_count": newCount, "status": newStatus, }).Error } // UpdateExecuteCount 更新指定计划的执行计数 -func (r *gormPlanRepository) UpdateExecuteCount(id uint, count uint) error { - result := r.db.Model(&models.Plan{}).Where("id = ?", id).Update("execute_count", count) +func (r *gormPlanRepository) UpdateExecuteCount(ctx context.Context, id uint, count uint) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdateExecuteCount") + result := r.db.WithContext(repoCtx).Model(&models.Plan{}).Where("id = ?", id).Update("execute_count", count) if result.Error != nil { return result.Error } diff --git a/internal/infra/repository/raw_material_repository.go b/internal/infra/repository/raw_material_repository.go index 1d6d230..ba6a757 100644 --- a/internal/infra/repository/raw_material_repository.go +++ b/internal/infra/repository/raw_material_repository.go @@ -1,9 +1,12 @@ package repository import ( + "context" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) @@ -38,23 +41,25 @@ type FeedUsageRecordListOptions struct { // RawMaterialRepository 定义了与原料相关的数据库操作接口 type RawMaterialRepository interface { - ListRawMaterialPurchases(opts RawMaterialPurchaseListOptions, page, pageSize int) ([]models.RawMaterialPurchase, int64, error) - ListRawMaterialStockLogs(opts RawMaterialStockLogListOptions, page, pageSize int) ([]models.RawMaterialStockLog, int64, error) - ListFeedUsageRecords(opts FeedUsageRecordListOptions, page, pageSize int) ([]models.FeedUsageRecord, int64, error) + ListRawMaterialPurchases(ctx context.Context, opts RawMaterialPurchaseListOptions, page, pageSize int) ([]models.RawMaterialPurchase, int64, error) + ListRawMaterialStockLogs(ctx context.Context, opts RawMaterialStockLogListOptions, page, pageSize int) ([]models.RawMaterialStockLog, int64, error) + ListFeedUsageRecords(ctx context.Context, opts FeedUsageRecordListOptions, page, pageSize int) ([]models.FeedUsageRecord, int64, error) } // gormRawMaterialRepository 是 RawMaterialRepository 的 GORM 实现 type gormRawMaterialRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormRawMaterialRepository 创建一个新的 RawMaterialRepository GORM 实现实例 -func NewGormRawMaterialRepository(db *gorm.DB) RawMaterialRepository { - return &gormRawMaterialRepository{db: db} +func NewGormRawMaterialRepository(ctx context.Context, db *gorm.DB) RawMaterialRepository { + return &gormRawMaterialRepository{ctx: ctx, db: db} } // ListRawMaterialPurchases 实现了分页和过滤查询原料采购记录的功能 -func (r *gormRawMaterialRepository) ListRawMaterialPurchases(opts RawMaterialPurchaseListOptions, page, pageSize int) ([]models.RawMaterialPurchase, int64, error) { +func (r *gormRawMaterialRepository) ListRawMaterialPurchases(ctx context.Context, opts RawMaterialPurchaseListOptions, page, pageSize int) ([]models.RawMaterialPurchase, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListRawMaterialPurchases") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -62,7 +67,7 @@ func (r *gormRawMaterialRepository) ListRawMaterialPurchases(opts RawMaterialPur var results []models.RawMaterialPurchase var total int64 - query := r.db.Model(&models.RawMaterialPurchase{}) + query := r.db.WithContext(repoCtx).Model(&models.RawMaterialPurchase{}) if opts.RawMaterialID != nil { query = query.Where("raw_material_id = ?", *opts.RawMaterialID) @@ -94,7 +99,8 @@ func (r *gormRawMaterialRepository) ListRawMaterialPurchases(opts RawMaterialPur } // ListRawMaterialStockLogs 实现了分页和过滤查询原料库存日志的功能 -func (r *gormRawMaterialRepository) ListRawMaterialStockLogs(opts RawMaterialStockLogListOptions, page, pageSize int) ([]models.RawMaterialStockLog, int64, error) { +func (r *gormRawMaterialRepository) ListRawMaterialStockLogs(ctx context.Context, opts RawMaterialStockLogListOptions, page, pageSize int) ([]models.RawMaterialStockLog, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListRawMaterialStockLogs") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -102,7 +108,7 @@ func (r *gormRawMaterialRepository) ListRawMaterialStockLogs(opts RawMaterialSto var results []models.RawMaterialStockLog var total int64 - query := r.db.Model(&models.RawMaterialStockLog{}) + query := r.db.WithContext(repoCtx).Model(&models.RawMaterialStockLog{}) if opts.RawMaterialID != nil { query = query.Where("raw_material_id = ?", *opts.RawMaterialID) @@ -137,7 +143,8 @@ func (r *gormRawMaterialRepository) ListRawMaterialStockLogs(opts RawMaterialSto } // ListFeedUsageRecords 实现了分页和过滤查询饲料使用记录的功能 -func (r *gormRawMaterialRepository) ListFeedUsageRecords(opts FeedUsageRecordListOptions, page, pageSize int) ([]models.FeedUsageRecord, int64, error) { +func (r *gormRawMaterialRepository) ListFeedUsageRecords(ctx context.Context, opts FeedUsageRecordListOptions, page, pageSize int) ([]models.FeedUsageRecord, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "ListFeedUsageRecords") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -145,7 +152,7 @@ func (r *gormRawMaterialRepository) ListFeedUsageRecords(opts FeedUsageRecordLis var results []models.FeedUsageRecord var total int64 - query := r.db.Model(&models.FeedUsageRecord{}) + query := r.db.WithContext(repoCtx).Model(&models.FeedUsageRecord{}) if opts.PenID != nil { query = query.Where("pen_id = ?", *opts.PenID) diff --git a/internal/infra/repository/sensor_data_repository.go b/internal/infra/repository/sensor_data_repository.go index ee01117..70c8810 100644 --- a/internal/infra/repository/sensor_data_repository.go +++ b/internal/infra/repository/sensor_data_repository.go @@ -1,9 +1,12 @@ package repository import ( + "context" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) @@ -18,39 +21,43 @@ type SensorDataListOptions struct { // SensorDataRepository 定义了与传感器数据相关的数据库操作接口。 type SensorDataRepository interface { - Create(sensorData *models.SensorData) error - GetLatestSensorDataByDeviceIDAndSensorType(deviceID uint, sensorType models.SensorType) (*models.SensorData, error) + Create(ctx context.Context, sensorData *models.SensorData) error + GetLatestSensorDataByDeviceIDAndSensorType(ctx context.Context, deviceID uint, sensorType models.SensorType) (*models.SensorData, error) // List 支持分页和过滤的列表查询 - List(opts SensorDataListOptions, page, pageSize int) ([]models.SensorData, int64, error) + List(ctx context.Context, opts SensorDataListOptions, page, pageSize int) ([]models.SensorData, int64, error) } // gormSensorDataRepository 是 SensorDataRepository 的 GORM 实现。 type gormSensorDataRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormSensorDataRepository 创建一个新的 SensorDataRepository GORM 实现实例。 -func NewGormSensorDataRepository(db *gorm.DB) SensorDataRepository { - return &gormSensorDataRepository{db: db} +func NewGormSensorDataRepository(ctx context.Context, db *gorm.DB) SensorDataRepository { + return &gormSensorDataRepository{ctx: ctx, db: db} } // Create 将一条新的传感器数据记录插入数据库。 -func (r *gormSensorDataRepository) Create(sensorData *models.SensorData) error { - return r.db.Create(sensorData).Error +func (r *gormSensorDataRepository) Create(ctx context.Context, sensorData *models.SensorData) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Create") + return r.db.WithContext(repoCtx).Create(sensorData).Error } // GetLatestSensorDataByDeviceIDAndSensorType 根据设备ID和传感器类型查询最新的传感器数据。 -func (r *gormSensorDataRepository) GetLatestSensorDataByDeviceIDAndSensorType(deviceID uint, sensorType models.SensorType) (*models.SensorData, error) { +func (r *gormSensorDataRepository) GetLatestSensorDataByDeviceIDAndSensorType(ctx context.Context, deviceID uint, sensorType models.SensorType) (*models.SensorData, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "GetLatestSensorDataByDeviceIDAndSensorType") var sensorData models.SensorData // 增加一个时间范围来缩小查询范围, 从而加快查找速度, 当使用时序数据库时时间范围可以让数据库忽略时间靠前的分片 - err := r.db.Where("device_id = ? AND sensor_type = ? AND time >=?", deviceID, sensorType, time.Now().Add(-24*time.Hour)). + err := r.db.WithContext(repoCtx).Where("device_id = ? AND sensor_type = ? AND time >=?", deviceID, sensorType, time.Now().Add(-24*time.Hour)). Order("time DESC"). First(&sensorData).Error return &sensorData, err } // List 实现了分页和过滤查询传感器数据的功能 -func (r *gormSensorDataRepository) List(opts SensorDataListOptions, page, pageSize int) ([]models.SensorData, int64, error) { +func (r *gormSensorDataRepository) List(ctx context.Context, opts SensorDataListOptions, page, pageSize int) ([]models.SensorData, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "List") // --- 校验分页参数 --- if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination @@ -59,7 +66,7 @@ func (r *gormSensorDataRepository) List(opts SensorDataListOptions, page, pageSi var results []models.SensorData var total int64 - query := r.db.Model(&models.SensorData{}) + query := r.db.WithContext(repoCtx).Model(&models.SensorData{}) // --- 应用过滤条件 --- if opts.DeviceID != nil { diff --git a/internal/infra/repository/unit_of_work.go b/internal/infra/repository/unit_of_work.go index 7929476..9e189cc 100644 --- a/internal/infra/repository/unit_of_work.go +++ b/internal/infra/repository/unit_of_work.go @@ -1,9 +1,11 @@ package repository import ( + "context" "fmt" "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" + "gorm.io/gorm" ) @@ -12,35 +14,40 @@ type UnitOfWork interface { // ExecuteInTransaction 在一个数据库事务中执行给定的函数。 // 如果函数返回错误,事务将被回滚;否则,事务将被提交。 // tx 参数是当前事务的 GORM DB 实例,应传递给所有仓库方法。 - ExecuteInTransaction(fn func(tx *gorm.DB) error) error + ExecuteInTransaction(ctx context.Context, fn func(tx *gorm.DB) error) error } // gormUnitOfWork 是 UnitOfWork 接口的 GORM 实现 type gormUnitOfWork struct { - db *gorm.DB - logger *logs.Logger // 添加日志记录器 + ctx context.Context + db *gorm.DB } // NewGormUnitOfWork 创建一个新的 gormUnitOfWork 实例 -func NewGormUnitOfWork(db *gorm.DB, logger *logs.Logger) UnitOfWork { - return &gormUnitOfWork{db: db, logger: logger} +func NewGormUnitOfWork(ctx context.Context, db *gorm.DB) UnitOfWork { + return &gormUnitOfWork{ + ctx: ctx, + db: db, + } } // ExecuteInTransaction 实现了 UnitOfWork 接口的事务执行逻辑 -func (u *gormUnitOfWork) ExecuteInTransaction(fn func(tx *gorm.DB) error) error { - tx := u.db.Begin() +func (u *gormUnitOfWork) ExecuteInTransaction(ctx context.Context, fn func(tx *gorm.DB) error) error { + uowCtx, logger := logs.Trace(ctx, u.ctx, "ExecuteInTransaction") + + tx := u.db.WithContext(uowCtx).Begin() if tx.Error != nil { - u.logger.Errorf("开启数据库事务失败: %v", tx.Error) // 记录错误日志 + logger.Errorf("开启数据库事务失败: %v", tx.Error) // 记录错误日志 return fmt.Errorf("开启事务失败: %w", tx.Error) } defer func() { if r := recover(); r != nil { tx.Rollback() - u.logger.Errorf("事务中发生 panic,已回滚: %v", r) // 记录 panic 日志 + logger.Errorf("事务中发生 panic,已回滚: %v", r) // 记录 panic 日志 } else if tx.Error != nil { // 如果函数执行过程中返回错误,或者事务本身有错误,则回滚 tx.Rollback() - u.logger.Errorf("事务执行失败,已回滚: %v", tx.Error) // 记录错误日志 + logger.Errorf("事务执行失败,已回滚: %v", tx.Error) // 记录错误日志 } }() @@ -52,7 +59,7 @@ func (u *gormUnitOfWork) ExecuteInTransaction(fn func(tx *gorm.DB) error) error // 提交事务 if err := tx.Commit().Error; err != nil { - u.logger.Errorf("提交数据库事务失败: %v", err) // 记录错误日志 + logger.Errorf("提交数据库事务失败: %v", err) // 记录错误日志 return fmt.Errorf("提交事务失败: %w", err) } diff --git a/internal/infra/repository/user_action_log_repository.go b/internal/infra/repository/user_action_log_repository.go index b501c0a..e0102e9 100644 --- a/internal/infra/repository/user_action_log_repository.go +++ b/internal/infra/repository/user_action_log_repository.go @@ -1,9 +1,12 @@ package repository import ( + "context" "time" + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) @@ -20,27 +23,30 @@ type UserActionLogListOptions struct { // UserActionLogRepository 定义了与用户操作日志相关的数据库操作接口 type UserActionLogRepository interface { - Create(log *models.UserActionLog) error - List(opts UserActionLogListOptions, page, pageSize int) ([]models.UserActionLog, int64, error) + Create(ctx context.Context, log *models.UserActionLog) error + List(ctx context.Context, opts UserActionLogListOptions, page, pageSize int) ([]models.UserActionLog, int64, error) } // gormUserActionLogRepository 是 UserActionLogRepository 的 GORM 实现 type gormUserActionLogRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormUserActionLogRepository 创建一个新的 UserActionLogRepository GORM 实现实例 -func NewGormUserActionLogRepository(db *gorm.DB) UserActionLogRepository { - return &gormUserActionLogRepository{db: db} +func NewGormUserActionLogRepository(ctx context.Context, db *gorm.DB) UserActionLogRepository { + return &gormUserActionLogRepository{ctx: ctx, db: db} } // Create 创建一条新的用户操作日志记录 -func (r *gormUserActionLogRepository) Create(log *models.UserActionLog) error { - return r.db.Create(log).Error +func (r *gormUserActionLogRepository) Create(ctx context.Context, log *models.UserActionLog) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Create") + return r.db.WithContext(repoCtx).Create(log).Error } // List 根据选项查询用户操作日志,并返回总数 -func (r *gormUserActionLogRepository) List(opts UserActionLogListOptions, page, pageSize int) ([]models.UserActionLog, int64, error) { +func (r *gormUserActionLogRepository) List(ctx context.Context, opts UserActionLogListOptions, page, pageSize int) ([]models.UserActionLog, int64, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "List") if page <= 0 || pageSize <= 0 { return nil, 0, ErrInvalidPagination } @@ -48,7 +54,7 @@ func (r *gormUserActionLogRepository) List(opts UserActionLogListOptions, page, var logs []models.UserActionLog var total int64 - query := r.db.Model(&models.UserActionLog{}) + query := r.db.WithContext(repoCtx).Model(&models.UserActionLog{}) if opts.UserID != nil { query = query.Where("user_id = ?", *opts.UserID) diff --git a/internal/infra/repository/user_repository.go b/internal/infra/repository/user_repository.go index 71c2503..97b8bb2 100644 --- a/internal/infra/repository/user_repository.go +++ b/internal/infra/repository/user_repository.go @@ -2,40 +2,47 @@ package repository import ( + "context" + + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" + "gorm.io/gorm" ) // UserRepository 定义了与用户模型相关的数据库操作接口 // 这是为了让业务逻辑层依赖于抽象,而不是具体的数据库实现 type UserRepository interface { - Create(user *models.User) error - FindByUsername(username string) (*models.User, error) - FindByID(id uint) (*models.User, error) - FindUserForLogin(identifier string) (*models.User, error) - FindAll() ([]*models.User, error) + Create(ctx context.Context, user *models.User) error + FindByUsername(ctx context.Context, username string) (*models.User, error) + FindByID(ctx context.Context, id uint) (*models.User, error) + FindUserForLogin(ctx context.Context, identifier string) (*models.User, error) + FindAll(ctx context.Context) ([]*models.User, error) } // gormUserRepository 是 UserRepository 的 GORM 实现 type gormUserRepository struct { - db *gorm.DB + ctx context.Context + db *gorm.DB } // NewGormUserRepository 创建一个新的 UserRepository GORM 实现实例 -func NewGormUserRepository(db *gorm.DB) UserRepository { - return &gormUserRepository{db: db} +func NewGormUserRepository(ctx context.Context, db *gorm.DB) UserRepository { + return &gormUserRepository{ctx: ctx, db: db} } // Create 创建一个新的用户记录 -func (r *gormUserRepository) Create(user *models.User) error { +func (r *gormUserRepository) Create(ctx context.Context, user *models.User) error { + repoCtx := logs.AddFuncName(ctx, r.ctx, "Create") // BeforeSave 钩子会在这里被自动触发 - return r.db.Create(user).Error + return r.db.WithContext(repoCtx).Create(user).Error } // FindByUsername 根据用户名查找用户 -func (r *gormUserRepository) FindByUsername(username string) (*models.User, error) { +func (r *gormUserRepository) FindByUsername(ctx context.Context, username string) (*models.User, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByUsername") var user models.User - if err := r.db.Where("username = ?", username).First(&user).Error; err != nil { + if err := r.db.WithContext(repoCtx).Where("username = ?", username).First(&user).Error; err != nil { return nil, err } return &user, nil @@ -43,10 +50,11 @@ func (r *gormUserRepository) FindByUsername(username string) (*models.User, erro // FindUserForLogin 根据提供的标识符查找用户,可用于登录验证 // 标识符可以是用户名、邮箱、手机号、微信号或飞书账号 -func (r *gormUserRepository) FindUserForLogin(identifier string) (*models.User, error) { +func (r *gormUserRepository) FindUserForLogin(ctx context.Context, identifier string) (*models.User, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindUserForLogin") var user models.User // 使用 ->> 操作符来查询 JSONB 字段中的文本值 - err := r.db.Where( + err := r.db.WithContext(repoCtx).Where( "username = ? OR contact ->> 'email' = ? OR contact ->> 'phone' = ? OR contact ->> 'wechat' = ? OR contact ->> 'feishu' = ?", identifier, identifier, identifier, identifier, identifier, ).First(&user).Error @@ -58,18 +66,20 @@ func (r *gormUserRepository) FindUserForLogin(identifier string) (*models.User, } // FindByID 根据 ID 查找用户 -func (r *gormUserRepository) FindByID(id uint) (*models.User, error) { +func (r *gormUserRepository) FindByID(ctx context.Context, id uint) (*models.User, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByID") var user models.User - if err := r.db.First(&user, id).Error; err != nil { + if err := r.db.WithContext(repoCtx).First(&user, id).Error; err != nil { return nil, err } return &user, nil } // FindAll 返回数据库中的所有用户 -func (r *gormUserRepository) FindAll() ([]*models.User, error) { +func (r *gormUserRepository) FindAll(ctx context.Context) ([]*models.User, error) { + repoCtx := logs.AddFuncName(ctx, r.ctx, "FindAll") var users []*models.User - if err := r.db.Where("1 = 1").Find(&users).Error; err != nil { + if err := r.db.WithContext(repoCtx).Where("1 = 1").Find(&users).Error; err != nil { return nil, err } return users, nil diff --git a/project_structure.txt b/project_structure.txt index 372465c..d7b9dd9 100644 --- a/project_structure.txt +++ b/project_structure.txt @@ -21,6 +21,15 @@ design/archive/2025-11-3-verification-before-device-deletion/refactor_deletion_c design/archive/2025-11-3-verification-before-device-deletion/refactor_id_conversion.md design/provide-logger-with-mothed/implementation.md design/provide-logger-with-mothed/index.md +design/provide-logger-with-mothed/task-api.md +design/provide-logger-with-mothed/task-controller.md +design/provide-logger-with-mothed/task-domain.md +design/provide-logger-with-mothed/task-infra.md +design/provide-logger-with-mothed/task-list.md +design/provide-logger-with-mothed/task-middleware.md +design/provide-logger-with-mothed/task-repository.md +design/provide-logger-with-mothed/task-service.md +design/provide-logger-with-mothed/task-webhook.md docs/docs.go docs/swagger.json docs/swagger.yaml @@ -53,6 +62,7 @@ internal/app/dto/plan_dto.go internal/app/dto/user_dto.go internal/app/middleware/audit.go internal/app/middleware/auth.go +internal/app/service/audit_service.go internal/app/service/device_service.go internal/app/service/monitor_service.go internal/app/service/pig_batch_service.go @@ -67,7 +77,6 @@ internal/app/webhook/transport.go internal/core/application.go internal/core/component_initializers.go internal/core/data_initializer.go -internal/domain/audit/service.go internal/domain/device/device_service.go internal/domain/device/general_device_service.go internal/domain/notify/notify.go @@ -80,7 +89,6 @@ internal/domain/pig/pig_batch_service_pig_trade.go internal/domain/pig/pig_sick_manager.go internal/domain/pig/pig_trade_manager.go internal/domain/plan/analysis_plan_task_manager.go -internal/domain/plan/device_id_extractor.go internal/domain/plan/plan_execution_manager.go internal/domain/plan/plan_service.go internal/domain/plan/task.go @@ -88,10 +96,11 @@ internal/domain/task/delay_task.go internal/domain/task/full_collection_task.go internal/domain/task/release_feed_weight_task.go internal/domain/task/task.go -internal/domain/token/token_service.go internal/infra/config/config.go internal/infra/database/postgres.go internal/infra/database/storage.go +internal/infra/logs/context.go +internal/infra/logs/encoder.go internal/infra/logs/logs.go internal/infra/models/device.go internal/infra/models/device_template.go @@ -145,6 +154,7 @@ internal/infra/transport/proto/device.proto internal/infra/transport/transport.go internal/infra/utils/command_generater/modbus_rtu.go internal/infra/utils/time.go +internal/infra/utils/token/token_service.go internal/infra/utils/validation.go main.go openspec/AGENTS.md