Files
chatroom/app/Services/IdiomGameService.php
T

125 lines
3.5 KiB
PHP
Raw Normal View History

2026-04-29 10:32:12 +08:00
<?php
/**
* 文件功能:猜成语游戏回合服务
*
* 统一处理猜成语回合的过期判定、超时结算和系统消息广播,
* 避免控制器与定时任务各自维护一套回合状态逻辑。
*/
namespace App\Services;
use App\Events\MessageSent;
use App\Models\GameConfig;
use App\Models\IdiomGameRound;
/**
* 类功能:提供猜成语回合过期与结算能力。
*/
class IdiomGameService
{
/**
* 方法功能:注入聊天室状态服务,复用现有公屏消息推送链路。
*/
public function __construct(
private readonly ChatStateService $chatState,
) {}
/**
* 方法功能:读取猜成语题目的有效时长配置,单位分钟。
*/
public function getExpireMinutes(): int
{
return max(0, (int) GameConfig::param('idiom', 'expire_minutes', 5));
}
/**
* 方法功能:判断指定回合是否已经超过有效时长。
*/
public function isRoundExpired(IdiomGameRound $round): bool
{
$expireMinutes = $this->getExpireMinutes();
if ($expireMinutes <= 0) {
return false;
}
if (! in_array($round->status, ['pending', 'active'], true)) {
return false;
}
if (! $round->started_at) {
return false;
}
return $round->started_at->copy()->addMinutes($expireMinutes)->lte(now());
}
/**
* 方法功能:结算并结束已过期的回合,必要时发送超时公告。
*/
public function expireRound(IdiomGameRound $round, bool $announce = true): bool
{
if (! $this->isRoundExpired($round)) {
return false;
}
$round->loadMissing('idiom');
// 已过期的回合统一落为 ended,防止继续答题或阻塞新开题。
$round->update([
'status' => 'ended',
'ended_at' => $round->ended_at ?? now(),
]);
if ($announce) {
$this->pushExpiredRoundMessage($round);
}
return true;
}
/**
* 方法功能:批量清理指定房间内已超时但仍处于进行中的回合。
*/
public function expireActiveRoundsForRoom(int $roomId, bool $announce = true): int
{
$expiredCount = 0;
IdiomGameRound::with('idiom')
->where('room_id', $roomId)
->whereIn('status', ['pending', 'active'])
->orderBy('id')
->get()
->each(function (IdiomGameRound $round) use ($announce, &$expiredCount): void {
if ($this->expireRound($round, $announce)) {
$expiredCount++;
}
});
return $expiredCount;
}
/**
* 方法功能:向公屏推送猜成语超时公告。
*/
public function pushExpiredRoundMessage(IdiomGameRound $round): void
{
$answer = $round->idiom?->answer ?? '未知答案';
$message = [
'id' => $this->chatState->nextMessageId($round->room_id),
'room_id' => $round->room_id,
'from_user' => '星海小博士',
'to_user' => '大家',
'content' => "⌛ 本轮猜成语已超时结束,正确答案是「{$answer}」。",
'is_secret' => false,
'font_color' => '#f59e0b',
'action' => '',
'idiom_game_round_ended_id' => $round->id,
'sent_at' => now()->toDateTimeString(),
];
$this->chatState->pushMessage($round->room_id, $message);
broadcast(new MessageSent($round->room_id, $message));
}
}