12-05-2026 Frontend dev
This commit is contained in:
parent
405df0a122
commit
5b8bdf4182
779 changed files with 480564 additions and 6241 deletions
196
app/Services/PressRelease/PressReleaseService.php
Normal file
196
app/Services/PressRelease/PressReleaseService.php
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\PressRelease;
|
||||
|
||||
use App\Enums\PressReleaseStatus;
|
||||
use App\Mail\PressReleasePublished;
|
||||
use App\Mail\PressReleaseRejected;
|
||||
use App\Models\AdminPreset;
|
||||
use App\Models\PressRelease;
|
||||
use App\Models\PressReleaseStatusLog;
|
||||
use App\Services\Admin\AdminPerformanceCache;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
/**
|
||||
* Kapselt Statusübergänge für Pressemitteilungen inkl. Benachrichtigungs-Mails.
|
||||
*
|
||||
* Jede Methode verändert exklusiv den Status und versendet optional eine Mail
|
||||
* an den Autor (User). Die Caller-Schicht (Volt, Command, API) muss nur noch
|
||||
* diese Methoden aufrufen – keine Mail-Logik außerhalb dieser Klasse.
|
||||
*/
|
||||
class PressReleaseService
|
||||
{
|
||||
public function __construct(private readonly BlacklistService $blacklist) {}
|
||||
|
||||
public function submitForReview(PressRelease $pressRelease): void
|
||||
{
|
||||
$this->assertStatus($pressRelease, [PressReleaseStatus::Draft, PressReleaseStatus::Rejected]);
|
||||
|
||||
$previous = $pressRelease->status;
|
||||
|
||||
if ($word = $this->blacklist->findInPressRelease($pressRelease)) {
|
||||
$reason = sprintf('Automatische Ablehnung: unzulässiges Wort "%s" gefunden.', $word);
|
||||
|
||||
$pressRelease->update(['status' => PressReleaseStatus::Rejected->value]);
|
||||
$this->logStatusChange($pressRelease, $previous, PressReleaseStatus::Rejected, $reason, 'blacklist');
|
||||
$this->notifyAuthor($pressRelease, 'rejected', $reason);
|
||||
|
||||
throw new BlacklistViolationException($reason, $word);
|
||||
}
|
||||
|
||||
$pressRelease->update(['status' => PressReleaseStatus::Review->value]);
|
||||
$this->logStatusChange($pressRelease, $previous, PressReleaseStatus::Review, null, 'customer');
|
||||
}
|
||||
|
||||
public function publish(PressRelease $pressRelease): void
|
||||
{
|
||||
$this->assertStatus($pressRelease, [PressReleaseStatus::Review]);
|
||||
|
||||
$previous = $pressRelease->status;
|
||||
|
||||
if ($word = $this->blacklist->findInPressRelease($pressRelease)) {
|
||||
$reason = sprintf('Automatische Ablehnung: unzulässiges Wort "%s" gefunden.', $word);
|
||||
|
||||
$pressRelease->update(['status' => PressReleaseStatus::Rejected->value]);
|
||||
$this->logStatusChange($pressRelease, $previous, PressReleaseStatus::Rejected, $reason, 'blacklist');
|
||||
$this->notifyAuthor($pressRelease, 'rejected', $reason);
|
||||
|
||||
throw new BlacklistViolationException($reason, $word);
|
||||
}
|
||||
|
||||
$pressRelease->update([
|
||||
'status' => PressReleaseStatus::Published->value,
|
||||
'published_at' => $pressRelease->published_at ?? now(),
|
||||
]);
|
||||
|
||||
$this->logStatusChange($pressRelease, $previous, PressReleaseStatus::Published, null, 'admin');
|
||||
$this->notifyAuthor($pressRelease, 'published');
|
||||
}
|
||||
|
||||
public function reject(PressRelease $pressRelease, ?string $reason = null): void
|
||||
{
|
||||
$this->assertStatus($pressRelease, [PressReleaseStatus::Review]);
|
||||
|
||||
$previous = $pressRelease->status;
|
||||
|
||||
$pressRelease->update(['status' => PressReleaseStatus::Rejected->value]);
|
||||
|
||||
$this->logStatusChange($pressRelease, $previous, PressReleaseStatus::Rejected, $reason, 'admin');
|
||||
$this->notifyAuthor($pressRelease, 'rejected', $reason);
|
||||
}
|
||||
|
||||
public function backToDraft(PressRelease $pressRelease): void
|
||||
{
|
||||
$this->assertStatus($pressRelease, [PressReleaseStatus::Review, PressReleaseStatus::Rejected]);
|
||||
|
||||
$previous = $pressRelease->status;
|
||||
|
||||
$pressRelease->update(['status' => PressReleaseStatus::Draft->value]);
|
||||
$this->logStatusChange($pressRelease, $previous, PressReleaseStatus::Draft, null, 'admin');
|
||||
}
|
||||
|
||||
public function archive(PressRelease $pressRelease): void
|
||||
{
|
||||
$this->assertStatus($pressRelease, [PressReleaseStatus::Published]);
|
||||
|
||||
$previous = $pressRelease->status;
|
||||
|
||||
$pressRelease->update(['status' => PressReleaseStatus::Archived->value]);
|
||||
$this->logStatusChange($pressRelease, $previous, PressReleaseStatus::Archived, null, 'admin');
|
||||
}
|
||||
|
||||
public function changeStatusFromAdmin(PressRelease $pressRelease, PressReleaseStatus $status, ?string $reason = null): void
|
||||
{
|
||||
$previous = $pressRelease->status;
|
||||
|
||||
$pressRelease->update([
|
||||
'status' => $status->value,
|
||||
'published_at' => $status === PressReleaseStatus::Published
|
||||
? ($pressRelease->published_at ?? now())
|
||||
: $pressRelease->published_at,
|
||||
]);
|
||||
|
||||
if ($previous !== $status) {
|
||||
$this->logStatusChange($pressRelease, $previous, $status, $reason, 'admin');
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteFromAdmin(PressRelease $pressRelease): void
|
||||
{
|
||||
if ($pressRelease->status === PressReleaseStatus::Published) {
|
||||
$pressRelease->update([
|
||||
'status' => PressReleaseStatus::Archived->value,
|
||||
'text' => AdminPreset::activeValue(
|
||||
AdminPreset::PRESS_RELEASE_DELETED_PUBLISHED_TEXT,
|
||||
"Diese Pressemitteilung wurde entfernt.\n\nDer Inhalt ist nicht mehr verfuegbar."
|
||||
),
|
||||
'keywords' => null,
|
||||
'backlink_url' => null,
|
||||
'no_export' => true,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$pressRelease->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PressReleaseStatus[] $allowed
|
||||
*/
|
||||
private function assertStatus(PressRelease $pressRelease, array $allowed): void
|
||||
{
|
||||
if (! in_array($pressRelease->status, $allowed, true)) {
|
||||
$allowedValues = implode(', ', array_map(fn ($s) => $s->value, $allowed));
|
||||
$currentStatus = $pressRelease->status instanceof PressReleaseStatus
|
||||
? $pressRelease->status->value
|
||||
: (string) $pressRelease->status;
|
||||
|
||||
throw new \LogicException(
|
||||
"Statusübergang nicht erlaubt. Aktueller Status: {$currentStatus}, erwartet: {$allowedValues}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function logStatusChange(
|
||||
PressRelease $pressRelease,
|
||||
?PressReleaseStatus $from,
|
||||
PressReleaseStatus $to,
|
||||
?string $reason,
|
||||
string $source,
|
||||
): void {
|
||||
PressReleaseStatusLog::query()->create([
|
||||
'press_release_id' => $pressRelease->id,
|
||||
'changed_by_user_id' => Auth::id(),
|
||||
'from_status' => $from?->value,
|
||||
'to_status' => $to->value,
|
||||
'reason' => $reason,
|
||||
'source' => $source,
|
||||
'created_at' => now(),
|
||||
]);
|
||||
|
||||
Cache::forget(AdminPerformanceCache::PressReleaseStats);
|
||||
Cache::forget(AdminPerformanceCache::PressReleaseReviewCount);
|
||||
}
|
||||
|
||||
private function notifyAuthor(PressRelease $pressRelease, string $event, ?string $reason = null): void
|
||||
{
|
||||
$user = $pressRelease->user;
|
||||
|
||||
if (! $user || ! $user->email) {
|
||||
return;
|
||||
}
|
||||
|
||||
$mailable = match ($event) {
|
||||
'published' => new PressReleasePublished($user, $pressRelease),
|
||||
'rejected' => new PressReleaseRejected($user, $pressRelease, $reason),
|
||||
default => null,
|
||||
};
|
||||
|
||||
if ($mailable) {
|
||||
Mail::to($user->email)->queue($mailable);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue