find($this->pressReleaseId); if ($pressRelease === null) { return; } $analyzedHash = $this->contentHash ?? $pressRelease->content_hash; $result = $this->score($manager, $pressRelease); $tier = PressReleaseContentTier::fromScore($result->score); // Bedingtes UPDATE: schreibt nur, wenn der Inhalt noch die bewertete // Prüfsumme trägt. Altbestand ohne Summe (null) wird unbedingt // geschrieben. 0 Zeilen = Inhalt inzwischen geändert oder PM weg. $scoredAt = now(); $stored = PressRelease::withoutGlobalScope(PortalScope::class) ->whereKey($pressRelease->id) ->when($analyzedHash !== null, fn ($query) => $query->where('content_hash', $analyzedHash)) ->update([ 'content_score' => $result->score, 'content_tier' => $tier->value, 'scored_at' => $scoredAt, 'updated_at' => $scoredAt, ]); if ($stored === 0) { $currentHash = PressRelease::withoutGlobalScope(PortalScope::class)->withTrashed()->whereKey($pressRelease->id)->value('content_hash'); if ($currentHash === null) { // Hart gelöscht: nichts mehr zu binden, kein Audit möglich (FK). return; } KiAudit::query()->create([ 'press_release_id' => $pressRelease->id, 'type' => KiAudit::TYPE_CONTENT_SCORE, 'provider' => $result->provider, 'model' => $result->model, 'result' => (string) $result->score, 'reason' => 'Verworfen: Inhalt wurde während der Bewertung geändert.', 'raw_response' => ['breakdown' => $result->breakdown] + $result->rawResponse, 'content_hash' => $analyzedHash, 'discarded_at' => now(), 'created_at' => now(), ]); self::dispatch($pressRelease->id, $this->providerOverride, $currentHash)->onQueue('classification'); return; } KiAudit::query()->create([ 'press_release_id' => $pressRelease->id, 'type' => KiAudit::TYPE_CONTENT_SCORE, 'provider' => $result->provider, 'model' => $result->model, 'result' => (string) $result->score, 'reason' => $tier->label(), 'raw_response' => ['breakdown' => $result->breakdown] + $result->rawResponse, 'content_hash' => $analyzedHash, 'created_at' => now(), ]); } /** * Bewertet über den aktiven (oder explizit gewählten) Treiber; bei Fehler * greift der deterministische Fallback. */ private function score(ContentScoreManager $manager, PressRelease $pressRelease): ContentScoreResult { $provider = $this->providerOverride ?: $manager->getDefaultDriver(); try { return $manager->driver($provider)->score($pressRelease); } catch (\Throwable $exception) { Log::channel('ki')->warning('Content-Score fiel auf den deterministischen Treiber zurück.', [ 'press_release_id' => $pressRelease->id, 'provider' => $provider, 'error' => $exception->getMessage(), ]); return $manager->driver('deterministic')->score($pressRelease); } } }