refactor(audit): extract SystemAuditRowPresenter shared by data + export

Move the row formatting that was duplicated between system-audit's
data() and export() endpoints into a single presenter. Both endpoints
now call presentAll() on the same object, so enum label translation,
actor resolution, and timestamp formatting cannot drift between the
Grid.js UI and the CSV download.

- SystemAuditRowPresenter::present()/presentAll(): canonical row shape
  with outcome + outcome_label + outcome_badge, channel + channel_label,
  actor_user_label with display-name-then-email fallback, and safe
  defaults for every missing field.
- data().php shrinks from ~45 to ~15 lines; export().php drops its
  inline outcome/channel/actor resolvers and reads the already-
  resolved fields from the presenter output.
- AuditContainerRegistrar registers the presenter.
- tests/Module/Audit/Service/SystemAuditRowPresenterTest: 9 cases
  covering enum normalization, unknown-value fallback, actor label
  precedence (display name → email → "-"), whitespace trimming, safe
  defaults for missing keys, and iterable input.
- StatusTaxonomyContractFiles: the taxonomy data contract now points
  at the presenter (the single source of truth for badge/label
  resolution) instead of the thin data endpoint, and the presenter is
  added to the literal-guard file list.

Gates: PHPUnit 1889 OK, PHPStan 0 errors, module:sync ok.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-21 22:20:39 +02:00
parent 0386ee08a6
commit 3b6896266e
6 changed files with 216 additions and 57 deletions

View File

@@ -23,6 +23,7 @@ use MintyPHP\Module\Audit\Service\AuditMetadataEnricher;
use MintyPHP\Module\Audit\Service\FrontendTelemetryIngestService;
use MintyPHP\Module\Audit\Service\ImportAuditService;
use MintyPHP\Module\Audit\Service\SystemAuditRedactionService;
use MintyPHP\Module\Audit\Service\SystemAuditRowPresenter;
use MintyPHP\Module\Audit\Service\SystemAuditService;
use MintyPHP\Module\Audit\Service\UserLifecycleAuditService;
use MintyPHP\Repository\User\UserReadRepository;
@@ -47,6 +48,7 @@ final class AuditContainerRegistrar implements ContainerRegistrar
// Redaction
$container->set(SystemAuditRedactionService::class, static fn (): SystemAuditRedactionService => new SystemAuditRedactionService());
$container->set(SystemAuditRowPresenter::class, static fn (): SystemAuditRowPresenter => new SystemAuditRowPresenter());
// Concrete audit services
$container->set(SystemAuditService::class, static fn (AppContainer $c): SystemAuditService => new SystemAuditService(