package usecase import ( "context" "apps/backend/internal/module/scout/domain" "github.com/google/uuid" ) func (s *Service) GetRun(ctx context.Context, ownerUID int64, runID string) (*domain.Run, error) { if s == nil || s.Repo == nil || runID == "" { return nil, domain.ErrValidation } return s.Repo.GetRun(ctx, ownerUID, runID) } // CreateQueuedRun creates one independent run for every scan request. The // job_id is bound in a second guarded write after job scheduling succeeds. func (s *Service) CreateQueuedRun(ctx context.Context, ownerUID int64, brief *domain.RunBrief) (*domain.Run, error) { if s == nil || s.Repo == nil || brief == nil || ownerUID == 0 { return nil, domain.ErrValidation } run := domain.NewRun("run_"+uuid.NewString(), "", ownerUID, *brief, domain.NowNano()) if err := s.Repo.CreateRun(ctx, run); err != nil { return nil, err } return run, nil } func (s *Service) BindRunJob(ctx context.Context, ownerUID int64, runID, jobID string) (*domain.Run, error) { if s == nil || s.Repo == nil || runID == "" || jobID == "" { return nil, domain.ErrValidation } run, err := s.Repo.GetRun(ctx, ownerUID, runID) if err != nil { return nil, err } if run.JobID != "" && run.JobID != jobID { return nil, domain.ErrIllegalRunStatus } run.JobID = jobID if err := s.Repo.ReplaceRunGuarded(ctx, ownerUID, runID, []string{domain.RunQueued}, run); err != nil { return nil, err } return run, nil } // ListRuns returns only runs owned by ownerUID. Pagination is normalized here // so every caller (HTTP, worker and tests) observes the same page contract. func (s *Service) ListRuns(ctx context.Context, ownerUID int64, brandID, mode string, page, pageSize int) (domain.RunPage, error) { if s == nil || s.Repo == nil { return domain.RunPage{}, domain.ErrValidation } pageInfo := domain.NormalizePage(page, pageSize) return s.Repo.ListRuns(ctx, domain.RunFilter{ OwnerUID: ownerUID, BrandID: brandID, Mode: mode, Page: pageInfo.Page, PageSize: pageInfo.PageSize, }) } // ListRunPosts returns the selected run and its independently paged results. // Repository implementations hide staged results until the run succeeds. func (s *Service) ListRunPosts(ctx context.Context, ownerUID int64, runID string, page, pageSize int) (domain.RunPostPage, error) { if s == nil || s.Repo == nil { return domain.RunPostPage{}, domain.ErrValidation } if runID == "" { return domain.RunPostPage{}, domain.ErrValidation } pageInfo := domain.NormalizePage(page, pageSize) return s.Repo.ListRunPosts(ctx, ownerUID, runID, pageInfo.Page, pageInfo.PageSize) } // RemoveRun only permits terminal runs to be removed. Returning the same // guarded transition error for queued/running keeps the API from exposing // implementation details while allowing response mapping to a 409 conflict. func (s *Service) RemoveRun(ctx context.Context, ownerUID int64, runID string) error { if s == nil || s.Repo == nil { return domain.ErrValidation } if runID == "" { return domain.ErrValidation } r, err := s.Repo.GetRun(ctx, ownerUID, runID) if err != nil { return err } if !domain.IsRunTerminal(r.Status) { return domain.ErrIllegalRunStatus } return s.Repo.DeleteRun(ctx, ownerUID, runID) }