Files
scrum-poker-internal/app/Jobs/CloseInactiveRooms.php
T
2026-09-02 09:37:11 +02:00

80 lines
2.9 KiB
PHP

<?php
namespace App\Jobs;
use App\Enums\RoomStateChange;
use App\Events\RoomStateChanged;
use App\Models\Room;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\DB;
class CloseInactiveRooms implements ShouldQueue
{
use Queueable;
public function handle(): void
{
$closedAt = now()->toImmutable();
$guestCutoff = $closedAt->subDays(config('rooms.guest_retention_days'));
$accountCutoff = $closedAt->subDays(config('rooms.account_retention_days'));
Room::query()
->whereNull('closed_at')
->where('is_persistent', false)
->where(function ($query) use ($guestCutoff, $accountCutoff): void {
$query
->where(function ($query) use ($guestCutoff): void {
$query->whereNull('owner_id')
->where('last_activity_at', '<=', $guestCutoff);
})
->orWhere(function ($query) use ($accountCutoff): void {
$query->whereNotNull('owner_id')
->where('last_activity_at', '<=', $accountCutoff);
});
})
->select('id')
->chunkById(100, function ($rooms) use ($closedAt, $guestCutoff, $accountCutoff): void {
foreach ($rooms->pluck('id') as $roomId) {
$stateVersion = DB::transaction(function () use ($roomId, $closedAt, $guestCutoff, $accountCutoff): ?int {
$room = Room::query()
->lockForUpdate()
->find((string) $roomId);
if (
$room === null
|| $room->isClosed()
|| $room->is_persistent
|| $room->last_activity_at === null
) {
return null;
}
$cutoff = $room->owner_id === null
? $guestCutoff
: $accountCutoff;
if ($room->last_activity_at->greaterThan($cutoff)) {
return null;
}
$room->update([
'closed_at' => $closedAt,
'state_version' => $room->state_version + 1,
]);
return $room->state_version;
});
if ($stateVersion !== null) {
RoomStateChanged::dispatch(
(string) $roomId,
$stateVersion,
RoomStateChange::RoomClosed,
);
}
}
}, 'id');
}
}