move controller service logics into a service

This commit is contained in:
team2
2026-02-27 20:15:43 +01:00
parent f0dfdaf4a8
commit 8096ac0de9
2 changed files with 175 additions and 75 deletions

View File

@@ -0,0 +1,135 @@
<?php
declare(strict_types=1);
namespace App\Service\Admin;
use App\Entity\IngestProfile;
use App\Index\IndexConfigurationProvider;
use App\Index\IndexMetaManager;
use App\Index\IndexStructureComparator;
use App\Repository\IngestProfileRepository;
use Doctrine\ORM\EntityManagerInterface;
final readonly class IngestProfileAdminService
{
public function __construct(
private IngestProfileRepository $repo,
private EntityManagerInterface $em,
private IndexMetaManager $metaManager,
private IndexConfigurationProvider $provider,
private IndexStructureComparator $comparator,
) {}
/**
* @return array{
* profiles: array,
* activeProfile: ?IngestProfile,
* indexMeta: mixed,
* diff: array,
* structureMismatch: bool
* }
*/
public function listData(): array
{
$profiles = $this->repo->findBy([], ['version' => 'DESC']);
$activeProfile = $this->repo->findActive();
$meta = $this->metaManager->readMeta();
$currentStructure = $this->provider->getConfiguration()->toStructureArray();
$diff = $this->comparator->compare($meta, $currentStructure);
$structureMismatch = false;
foreach ($diff as $row) {
if (!($row['equal'] ?? false)) {
$structureMismatch = true;
break;
}
}
return [
'profiles' => $profiles,
'activeProfile' => $activeProfile,
'indexMeta' => $meta,
'diff' => $diff,
'structureMismatch' => $structureMismatch,
];
}
/**
* Erzeugt ein neues Profil mit nextVersion aus Repo.
*
* @param array{
* chunk_size:mixed,
* chunk_overlap:mixed,
* embedding_model:mixed,
* embedding_dimension:mixed,
* scoring_version:mixed
* } $data
*/
public function create(array $data): IngestProfile
{
$latest = $this->repo->findLatestVersion();
$nextVersion = $latest ? $latest->getVersion() + 1 : 1;
$chunkSize = $this->requireInt($data['chunk_size'] ?? null, 'chunk_size');
$chunkOverlap = $this->requireInt($data['chunk_overlap'] ?? null, 'chunk_overlap');
$embeddingModel = $this->requireString($data['embedding_model'] ?? null, 'embedding_model');
$embeddingDim = $this->requireInt($data['embedding_dimension'] ?? null, 'embedding_dimension');
$scoringVersion = $this->requireInt($data['scoring_version'] ?? null, 'scoring_version');
$profile = new IngestProfile(
$nextVersion,
$chunkSize,
$chunkOverlap,
$embeddingModel,
$embeddingDim,
$scoringVersion
);
$this->em->persist($profile);
$this->em->flush();
return $profile;
}
public function activate(IngestProfile $profile): void
{
$active = $this->repo->findActive();
if ($active instanceof IngestProfile) {
$active->deactivate();
}
$profile->activate();
$this->em->flush();
}
public function remove(string $id): void
{
// Repo macht intern flush/remove - wie bei dir bereits
$this->repo->remove($id);
}
private function requireInt(mixed $value, string $field): int
{
if ($value === null || $value === '') {
throw new \InvalidArgumentException("Missing field: {$field}");
}
if (is_int($value)) {
return $value;
}
if (is_numeric($value)) {
return (int)$value;
}
throw new \InvalidArgumentException("Invalid int for {$field}");
}
private function requireString(mixed $value, string $field): string
{
if (!is_string($value) || trim($value) === '') {
throw new \InvalidArgumentException("Invalid string for {$field}");
}
return trim($value);
}
}