newsroom: Beschreibung pflegen, durch die Rot-Ebene geprüft (Roadmap 1b.3)
Die Firmenseite im Kundenbereich bekommt den Beschreibungstext (max. 2 000 Zeichen, reiner Text). Er läuft durch dieselbe Klassifikation wie eine Meldung (CompanyProfileCheck, KI mit deterministischem Fallback); Rot wird abgewiesen. Links im Profiltext gibt es nicht — die Website ist das eine Link-Feld. Bilder zurückgestellt (BALD-Platzhalter), bis die Bild-Kennzeichnung angepasst ist. Stufe 3 der Konversionsleiter (newsroom_edited) wird hier geschrieben, einmal je Firma, mit der Herkunft des zuletzt eingelösten Magic Links. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
079477a423
commit
87f95fb7a7
6 changed files with 253 additions and 2 deletions
53
app/Services/Company/CompanyProfileCheck.php
Normal file
53
app/Services/Company/CompanyProfileCheck.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\Company;
|
||||
|
||||
use App\Enums\PressReleaseClassification;
|
||||
use App\Models\Company;
|
||||
use App\Models\PressRelease;
|
||||
use App\Services\PressRelease\Classification\ClassificationManager;
|
||||
use App\Services\PressRelease\Classification\ClassificationResult;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Prüfung des Beschreibungstexts eines Newsrooms (Roadmap 1b.3, Leitplanke
|
||||
* „mindestens durch die Rot-Ebene der bestehenden Prüfung").
|
||||
*
|
||||
* Kein eigenes Regelwerk: Der Text läuft durch dieselbe Klassifikation wie
|
||||
* eine Meldung — KI-Treiber, bei Ausfall die deterministische Blacklist,
|
||||
* genau wie die Vorprüfung einer kostenpflichtigen Änderung (Spur D). Nur
|
||||
* das Ergebnis Rot zählt hier; Gelb ist für ein Firmenprofil kein Grund,
|
||||
* den Text abzuweisen.
|
||||
*
|
||||
* Geprüft wird auf einer NICHT gespeicherten Meldung mit Firmenname als
|
||||
* Titel und Beschreibung als Text — die Treiber kennen nur Meldungen, und
|
||||
* mehr als Titel und Text lesen sie nicht.
|
||||
*/
|
||||
class CompanyProfileCheck
|
||||
{
|
||||
public function __construct(private readonly ClassificationManager $classification) {}
|
||||
|
||||
public function isRed(Company $company, string $description): bool
|
||||
{
|
||||
return $this->check($company, $description)->classification === PressReleaseClassification::Red;
|
||||
}
|
||||
|
||||
public function check(Company $company, string $description): ClassificationResult
|
||||
{
|
||||
$proposed = new PressRelease([
|
||||
'title' => (string) $company->name,
|
||||
'text' => $description,
|
||||
]);
|
||||
|
||||
try {
|
||||
return $this->classification->driver()->classify($proposed);
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('Profilprüfung (Rot-Ebene) fehlgeschlagen — deterministischer Fallback.', [
|
||||
'company_id' => $company->getKey(),
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return $this->classification->driver('deterministic')->classify($proposed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -128,6 +128,31 @@ class ConversionRecorder
|
|||
$this->recordOnce(ConversionEventType::FirstBooking, ConversionSource::Organic, $company);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stufe 3 der Konversionsleiter: Newsroom gepflegt (Roadmap 1b.3).
|
||||
*
|
||||
* Einmal je Firma. Die Herkunft ist die des letzten eingelösten Magic
|
||||
* Links dieses Kontos — wer über „Diesen Newsroom verwalten" aus der Mail
|
||||
* kam und dann die Beschreibung schreibt, zählt für diese Mail. Ohne
|
||||
* eingelösten Link (Passwort-Login) bleibt es `organic`.
|
||||
*/
|
||||
public function recordNewsroomEdited(Company $company, ?User $user): void
|
||||
{
|
||||
try {
|
||||
$source = $user === null ? null : MagicLink::query()
|
||||
->where('user_id', $user->getKey())
|
||||
->where('purpose', 'login')
|
||||
->whereNotNull('consumed_at')
|
||||
->latest('consumed_at')
|
||||
->value('source');
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
$source = null;
|
||||
}
|
||||
|
||||
$this->recordOnce(ConversionEventType::NewsroomEdited, ConversionSource::parse($source), $company);
|
||||
}
|
||||
|
||||
private function alreadyRecorded(ConversionEventType $type, Company $company): bool
|
||||
{
|
||||
return ConversionEvent::query()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue