feat: add step tracking, retry and article lookup to AIPipelineJob
- currentStep column (migration 20260517230000) written per pipeline stage - recordStep() stores per-step output data and updates currentStep - resetForRetry() requeues a failed/needs-review job - getInventoryNumber() / getStatusLabel() helpers for admin display - markCompleted() now merges rather than replacing outputData - findByArticleId() added to repository for re-run lookups Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9e59123683
commit
6d8a06f151
4 changed files with 75 additions and 1 deletions
26
migrations/Version20260517230000.php
Normal file
26
migrations/Version20260517230000.php
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20260517230000 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Add current_step column to ai_pipeline_jobs for step tracking';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE app.ai_pipeline_jobs ADD COLUMN current_step VARCHAR(50) NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE app.ai_pipeline_jobs DROP COLUMN current_step');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -42,6 +42,9 @@ class AIPipelineJob
|
||||||
#[ORM\Column(type: 'text', nullable: true)]
|
#[ORM\Column(type: 'text', nullable: true)]
|
||||||
private ?string $errorMessage = null;
|
private ?string $errorMessage = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: 'string', length: 50, nullable: true)]
|
||||||
|
private ?string $currentStep = null;
|
||||||
|
|
||||||
#[ORM\Column(type: 'datetime_immutable')]
|
#[ORM\Column(type: 'datetime_immutable')]
|
||||||
private \DateTimeImmutable $createdAt;
|
private \DateTimeImmutable $createdAt;
|
||||||
|
|
||||||
|
|
@ -83,6 +86,16 @@ class AIPipelineJob
|
||||||
return $this->status;
|
return $this->status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getStatusLabel(): string
|
||||||
|
{
|
||||||
|
return $this->status->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAiResults(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
public function getAttemptCount(): int
|
public function getAttemptCount(): int
|
||||||
{
|
{
|
||||||
return $this->attemptCount;
|
return $this->attemptCount;
|
||||||
|
|
@ -111,6 +124,30 @@ class AIPipelineJob
|
||||||
return $this->errorMessage;
|
return $this->errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCurrentStep(): ?string
|
||||||
|
{
|
||||||
|
return $this->currentStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInventoryNumber(): ?string
|
||||||
|
{
|
||||||
|
return $this->inputData['inventoryNumber'] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param array<string, mixed> $data */
|
||||||
|
public function recordStep(string $step, array $data): void
|
||||||
|
{
|
||||||
|
$this->currentStep = $step;
|
||||||
|
$this->outputData[$step] = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resetForRetry(): void
|
||||||
|
{
|
||||||
|
$this->status = AIPipelineJobStatus::Queued;
|
||||||
|
$this->errorMessage = null;
|
||||||
|
$this->completedAt = null;
|
||||||
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): \DateTimeImmutable
|
public function getCreatedAt(): \DateTimeImmutable
|
||||||
{
|
{
|
||||||
return $this->createdAt;
|
return $this->createdAt;
|
||||||
|
|
@ -130,7 +167,7 @@ class AIPipelineJob
|
||||||
public function markCompleted(array $outputData = []): void
|
public function markCompleted(array $outputData = []): void
|
||||||
{
|
{
|
||||||
$this->status = AIPipelineJobStatus::Completed;
|
$this->status = AIPipelineJobStatus::Completed;
|
||||||
$this->outputData = $outputData;
|
$this->outputData = array_merge($this->outputData, $outputData);
|
||||||
$this->completedAt = new \DateTimeImmutable();
|
$this->completedAt = new \DateTimeImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,5 +15,7 @@ interface AIPipelineJobRepositoryInterface
|
||||||
/** @return list<AIPipelineJob> */
|
/** @return list<AIPipelineJob> */
|
||||||
public function findByStatus(AIPipelineJobStatus $status): array;
|
public function findByStatus(AIPipelineJobStatus $status): array;
|
||||||
|
|
||||||
|
public function findByArticleId(Uuid $articleId): ?AIPipelineJob;
|
||||||
|
|
||||||
public function save(AIPipelineJob $job): void;
|
public function save(AIPipelineJob $job): void;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,15 @@ final class DoctrineAIPipelineJobRepository implements AIPipelineJobRepositoryIn
|
||||||
return $this->em->getRepository(AIPipelineJob::class)->findBy(['status' => $status]);
|
return $this->em->getRepository(AIPipelineJob::class)->findBy(['status' => $status]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findByArticleId(Uuid $articleId): ?AIPipelineJob
|
||||||
|
{
|
||||||
|
/** @var AIPipelineJob|null */
|
||||||
|
return $this->em->getRepository(AIPipelineJob::class)->findOneBy(
|
||||||
|
['articleId' => $articleId],
|
||||||
|
['createdAt' => 'DESC'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function save(AIPipelineJob $job): void
|
public function save(AIPipelineJob $job): void
|
||||||
{
|
{
|
||||||
$this->em->persist($job);
|
$this->em->persist($job);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue