完善猜成语过期与答题记录逻辑
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
<?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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user