569 lines
19 KiB
PHP
569 lines
19 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 文件功能:猜成语控制器与后台参数测试
|
||
|
|
*
|
||
|
|
* 覆盖猜成语后台过期时间配置、手动出题清理超时回合、
|
||
|
|
* 以及超时后禁止继续答题的关键行为。
|
||
|
|
*/
|
||
|
|
|
||
|
|
namespace Tests\Feature;
|
||
|
|
|
||
|
|
use App\Models\GameConfig;
|
||
|
|
use App\Models\Riddle;
|
||
|
|
use App\Models\RiddleGameRound;
|
||
|
|
use App\Models\Room;
|
||
|
|
use App\Models\User;
|
||
|
|
use App\Services\ChatStateService;
|
||
|
|
use App\Services\RiddleGameService;
|
||
|
|
use Illuminate\Database\Schema\Blueprint;
|
||
|
|
use Illuminate\Support\Facades\Schema;
|
||
|
|
use Tests\TestCase;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 类功能:验证猜成语后台配置与回合过期逻辑。
|
||
|
|
*/
|
||
|
|
class RiddleQuizControllerTest extends TestCase
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* 方法功能:为本组测试准备隔离表结构,不触碰本地业务数据库。
|
||
|
|
*/
|
||
|
|
protected function setUp(): void
|
||
|
|
{
|
||
|
|
parent::setUp();
|
||
|
|
|
||
|
|
$this->rebuildTestingTables();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 方法功能:验证后台可以保存题目过期时间配置。
|
||
|
|
*/
|
||
|
|
public function test_service_uses_legacy_idiom_config_for_brain_teaser_scope_and_interval(): void
|
||
|
|
{
|
||
|
|
$room = Room::create(['room_name' => '测试房间']);
|
||
|
|
|
||
|
|
GameConfig::create([
|
||
|
|
'game_key' => 'idiom',
|
||
|
|
'name' => '猜谜活动',
|
||
|
|
'icon' => '🧩',
|
||
|
|
'enabled' => true,
|
||
|
|
'params' => [
|
||
|
|
'reward_gold' => 88,
|
||
|
|
'reward_exp' => 66,
|
||
|
|
'auto_start_interval' => 9,
|
||
|
|
'expire_minutes' => 7,
|
||
|
|
'room_scope_mode' => 'single',
|
||
|
|
'room_ids' => [$room->id],
|
||
|
|
],
|
||
|
|
]);
|
||
|
|
|
||
|
|
/** @var RiddleGameService $service */
|
||
|
|
$service = $this->app->make(RiddleGameService::class);
|
||
|
|
|
||
|
|
$this->assertSame(9, $service->getAutoStartInterval(Riddle::TYPE_BRAIN_TEASER));
|
||
|
|
$this->assertSame(7, $service->getExpireMinutes(Riddle::TYPE_BRAIN_TEASER));
|
||
|
|
$this->assertSame([$room->id], $service->getScopedRoomIds(Riddle::TYPE_BRAIN_TEASER));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 方法功能:验证 single 房间范围模式只取配置中的首个房间。
|
||
|
|
*/
|
||
|
|
public function test_service_only_uses_first_room_when_scope_mode_is_single(): void
|
||
|
|
{
|
||
|
|
$roomOne = Room::create(['room_name' => '测试房间1']);
|
||
|
|
$roomTwo = Room::create(['room_name' => '测试房间2']);
|
||
|
|
|
||
|
|
GameConfig::create([
|
||
|
|
'game_key' => 'idiom',
|
||
|
|
'name' => '猜谜活动',
|
||
|
|
'icon' => '🧩',
|
||
|
|
'enabled' => true,
|
||
|
|
'params' => [
|
||
|
|
'reward_gold' => 50,
|
||
|
|
'reward_exp' => 30,
|
||
|
|
'auto_start_interval' => 3,
|
||
|
|
'expire_minutes' => 5,
|
||
|
|
'room_scope_mode' => 'single',
|
||
|
|
'room_ids' => [$roomOne->id, $roomTwo->id],
|
||
|
|
],
|
||
|
|
]);
|
||
|
|
|
||
|
|
/** @var RiddleGameService $service */
|
||
|
|
$service = $this->app->make(RiddleGameService::class);
|
||
|
|
|
||
|
|
$this->assertSame([$roomOne->id], $service->getScopedRoomIds(Riddle::TYPE_IDIOM));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 方法功能:验证手动出题前会清理已超时的旧回合,避免阻塞新题。
|
||
|
|
*/
|
||
|
|
public function test_start_clears_expired_round_before_creating_new_round(): void
|
||
|
|
{
|
||
|
|
$this->mockChatStateService();
|
||
|
|
|
||
|
|
$admin = $this->createSiteOwner();
|
||
|
|
$room = Room::create(['room_name' => '猜成语房间']);
|
||
|
|
$idiom = $this->createActiveIdiom();
|
||
|
|
|
||
|
|
GameConfig::create([
|
||
|
|
'game_key' => 'idiom',
|
||
|
|
'name' => '猜成语',
|
||
|
|
'icon' => '🧩',
|
||
|
|
'enabled' => true,
|
||
|
|
'params' => [
|
||
|
|
'reward_gold' => 50,
|
||
|
|
'reward_exp' => 30,
|
||
|
|
'auto_start_interval' => 0,
|
||
|
|
'expire_minutes' => 5,
|
||
|
|
'room_scope_mode' => 'single',
|
||
|
|
'room_ids' => [$room->id],
|
||
|
|
],
|
||
|
|
]);
|
||
|
|
|
||
|
|
$expiredRound = RiddleGameRound::create([
|
||
|
|
'room_id' => $room->id,
|
||
|
|
'idiom_id' => $idiom->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_IDIOM,
|
||
|
|
'status' => 'active',
|
||
|
|
'reward_gold' => 50,
|
||
|
|
'reward_exp' => 30,
|
||
|
|
'started_at' => now()->subMinutes(10),
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($admin)->postJson(route('riddle-quiz.start'), [
|
||
|
|
'room_id' => $room->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertOk()
|
||
|
|
->assertJsonPath('status', 'success');
|
||
|
|
|
||
|
|
$expiredRound->refresh();
|
||
|
|
|
||
|
|
$this->assertSame('ended', $expiredRound->status);
|
||
|
|
$this->assertNotNull($expiredRound->ended_at);
|
||
|
|
$this->assertDatabaseCount('idiom_game_rounds', 2);
|
||
|
|
$this->assertDatabaseHas('idiom_game_rounds', [
|
||
|
|
'room_id' => $room->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_IDIOM,
|
||
|
|
'status' => 'active',
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 方法功能:验证旧路由在传入脑筋急转弯题型时也能正常手动开题。
|
||
|
|
*/
|
||
|
|
public function test_start_supports_brain_teaser_quiz_type_on_legacy_route(): void
|
||
|
|
{
|
||
|
|
$this->mockChatStateService();
|
||
|
|
|
||
|
|
$admin = $this->createSiteOwner();
|
||
|
|
$room = Room::create(['room_name' => '猜谜房间']);
|
||
|
|
$brainTeaser = $this->createQuestion(
|
||
|
|
type: Riddle::TYPE_BRAIN_TEASER,
|
||
|
|
answer: '影子',
|
||
|
|
hint: '🧠 天天跟着你走,白天有晚上无,看得见摸不着,是什么?',
|
||
|
|
);
|
||
|
|
|
||
|
|
GameConfig::create([
|
||
|
|
'game_key' => 'idiom',
|
||
|
|
'name' => '猜谜活动',
|
||
|
|
'icon' => '🧩',
|
||
|
|
'enabled' => true,
|
||
|
|
'params' => [
|
||
|
|
'reward_gold' => 66,
|
||
|
|
'reward_exp' => 44,
|
||
|
|
'auto_start_interval' => 0,
|
||
|
|
'expire_minutes' => 5,
|
||
|
|
'room_scope_mode' => 'single',
|
||
|
|
'room_ids' => [$room->id],
|
||
|
|
],
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($admin)->postJson(route('riddle-quiz.start'), [
|
||
|
|
'room_id' => $room->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_BRAIN_TEASER,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertOk()
|
||
|
|
->assertJsonPath('status', 'success')
|
||
|
|
->assertJsonPath('data.quiz_type', Riddle::TYPE_BRAIN_TEASER)
|
||
|
|
->assertJsonPath('data.hint', $brainTeaser->hint);
|
||
|
|
|
||
|
|
$this->assertDatabaseHas('idiom_game_rounds', [
|
||
|
|
'room_id' => $room->id,
|
||
|
|
'idiom_id' => $brainTeaser->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_BRAIN_TEASER,
|
||
|
|
'status' => 'active',
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 方法功能:验证手动出题会结束当前同题型旧回合并直接开启新题。
|
||
|
|
*/
|
||
|
|
public function test_start_manually_ends_previous_active_round_of_same_quiz_type(): void
|
||
|
|
{
|
||
|
|
$this->mockChatStateService();
|
||
|
|
|
||
|
|
$admin = $this->createSiteOwner();
|
||
|
|
$room = Room::create(['room_name' => '手动出题房间']);
|
||
|
|
$questionOne = $this->createQuestion(
|
||
|
|
type: Riddle::TYPE_BRAIN_TEASER,
|
||
|
|
answer: '影子',
|
||
|
|
hint: '🧠 天天跟着你走,白天有晚上无,看得见摸不着,是什么?',
|
||
|
|
);
|
||
|
|
$questionTwo = $this->createQuestion(
|
||
|
|
type: Riddle::TYPE_BRAIN_TEASER,
|
||
|
|
answer: '回声',
|
||
|
|
hint: '🧠 你喊它也喊,你停它就停,山谷里最常见,是什么?',
|
||
|
|
);
|
||
|
|
|
||
|
|
GameConfig::create([
|
||
|
|
'game_key' => 'idiom',
|
||
|
|
'name' => '猜谜活动',
|
||
|
|
'icon' => '🧩',
|
||
|
|
'enabled' => true,
|
||
|
|
'params' => [
|
||
|
|
'reward_gold' => 66,
|
||
|
|
'reward_exp' => 44,
|
||
|
|
'auto_start_interval' => 0,
|
||
|
|
'expire_minutes' => 5,
|
||
|
|
'room_scope_mode' => 'single',
|
||
|
|
'room_ids' => [$room->id],
|
||
|
|
],
|
||
|
|
]);
|
||
|
|
|
||
|
|
$oldRound = RiddleGameRound::create([
|
||
|
|
'room_id' => $room->id,
|
||
|
|
'idiom_id' => $questionOne->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_BRAIN_TEASER,
|
||
|
|
'status' => 'active',
|
||
|
|
'reward_gold' => 66,
|
||
|
|
'reward_exp' => 44,
|
||
|
|
'started_at' => now()->subMinute(),
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($admin)->postJson(route('riddle-quiz.start'), [
|
||
|
|
'room_id' => $room->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_BRAIN_TEASER,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertOk()
|
||
|
|
->assertJsonPath('status', 'success')
|
||
|
|
->assertJsonPath('data.quiz_type', Riddle::TYPE_BRAIN_TEASER);
|
||
|
|
|
||
|
|
$oldRound->refresh();
|
||
|
|
|
||
|
|
$this->assertSame('ended', $oldRound->status);
|
||
|
|
$this->assertNotNull($oldRound->ended_at);
|
||
|
|
$this->assertDatabaseHas('idiom_game_rounds', [
|
||
|
|
'room_id' => $room->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_BRAIN_TEASER,
|
||
|
|
'status' => 'active',
|
||
|
|
]);
|
||
|
|
|
||
|
|
$this->assertSame(2, RiddleGameRound::query()->where('room_id', $room->id)->count());
|
||
|
|
$this->assertSame(1, RiddleGameRound::query()
|
||
|
|
->where('room_id', $room->id)
|
||
|
|
->where('quiz_type', Riddle::TYPE_BRAIN_TEASER)
|
||
|
|
->where('status', 'active')
|
||
|
|
->count());
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 方法功能:验证游戏总开关关闭时返回明确提示,而不是误报题库为空。
|
||
|
|
*/
|
||
|
|
public function test_start_returns_clear_message_when_riddle_activity_is_disabled(): void
|
||
|
|
{
|
||
|
|
$this->mockChatStateService();
|
||
|
|
|
||
|
|
$admin = $this->createSiteOwner();
|
||
|
|
$room = Room::create(['room_name' => '猜谜房间']);
|
||
|
|
$this->createQuestion(
|
||
|
|
type: Riddle::TYPE_BRAIN_TEASER,
|
||
|
|
answer: '影子',
|
||
|
|
hint: '🧠 天天跟着你走,白天有晚上无,看得见摸不着,是什么?',
|
||
|
|
);
|
||
|
|
|
||
|
|
GameConfig::create([
|
||
|
|
'game_key' => 'idiom',
|
||
|
|
'name' => '猜谜活动',
|
||
|
|
'icon' => '🧩',
|
||
|
|
'enabled' => false,
|
||
|
|
'params' => [
|
||
|
|
'reward_gold' => 66,
|
||
|
|
'reward_exp' => 44,
|
||
|
|
'auto_start_interval' => 0,
|
||
|
|
'expire_minutes' => 5,
|
||
|
|
'room_scope_mode' => 'single',
|
||
|
|
'room_ids' => [$room->id],
|
||
|
|
],
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($admin)->postJson(route('riddle-quiz.start'), [
|
||
|
|
'room_id' => $room->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_BRAIN_TEASER,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertStatus(400)
|
||
|
|
->assertJsonPath('status', 'error')
|
||
|
|
->assertJsonPath('message', '猜谜活动未开启,请先到游戏管理中开启后再出题。');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 方法功能:验证回合超时后不能继续答题,也不会发放奖励。
|
||
|
|
*/
|
||
|
|
public function test_answer_rejects_expired_round_and_marks_it_ended(): void
|
||
|
|
{
|
||
|
|
$this->mockChatStateService();
|
||
|
|
|
||
|
|
$player = User::factory()->create([
|
||
|
|
'username' => '答题用户',
|
||
|
|
'exp_num' => 10,
|
||
|
|
]);
|
||
|
|
$room = Room::create(['room_name' => '答题房间']);
|
||
|
|
$idiom = $this->createActiveIdiom();
|
||
|
|
|
||
|
|
GameConfig::create([
|
||
|
|
'game_key' => 'idiom',
|
||
|
|
'name' => '猜成语',
|
||
|
|
'icon' => '🧩',
|
||
|
|
'enabled' => true,
|
||
|
|
'params' => [
|
||
|
|
'reward_gold' => 20,
|
||
|
|
'reward_exp' => 15,
|
||
|
|
'auto_start_interval' => 0,
|
||
|
|
'expire_minutes' => 5,
|
||
|
|
'room_scope_mode' => 'single',
|
||
|
|
'room_ids' => [$room->id],
|
||
|
|
],
|
||
|
|
]);
|
||
|
|
|
||
|
|
$round = RiddleGameRound::create([
|
||
|
|
'room_id' => $room->id,
|
||
|
|
'idiom_id' => $idiom->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_IDIOM,
|
||
|
|
'status' => 'active',
|
||
|
|
'reward_gold' => 20,
|
||
|
|
'reward_exp' => 15,
|
||
|
|
'started_at' => now()->subMinutes(6),
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($player)->postJson(route('riddle-quiz.answer'), [
|
||
|
|
'round_id' => $round->id,
|
||
|
|
'room_id' => $room->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_IDIOM,
|
||
|
|
'answer' => '画蛇添足',
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertStatus(400)
|
||
|
|
->assertJsonPath('status', 'error')
|
||
|
|
->assertJsonPath('message', '该回合已超时结束');
|
||
|
|
|
||
|
|
$round->refresh();
|
||
|
|
$player->refresh();
|
||
|
|
|
||
|
|
$this->assertSame('ended', $round->status);
|
||
|
|
$this->assertNotNull($round->ended_at);
|
||
|
|
$this->assertNull($round->winner_id);
|
||
|
|
$this->assertSame(10, $player->exp_num);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 方法功能:验证自动出题会按房间和题型两个维度独立判断。
|
||
|
|
*/
|
||
|
|
public function test_auto_start_checks_room_and_quiz_type_independently(): void
|
||
|
|
{
|
||
|
|
$this->mockChatStateService();
|
||
|
|
|
||
|
|
$roomOne = Room::create(['room_name' => '一号房']);
|
||
|
|
$roomTwo = Room::create(['room_name' => '二号房']);
|
||
|
|
$idiom = $this->createQuestion(
|
||
|
|
type: Riddle::TYPE_IDIOM,
|
||
|
|
answer: '画蛇添足',
|
||
|
|
hint: '🧩 四人比赛画蛇,最慢的那个反而多此一举。猜一成语',
|
||
|
|
);
|
||
|
|
$brainTeaser = $this->createQuestion(
|
||
|
|
type: Riddle::TYPE_BRAIN_TEASER,
|
||
|
|
answer: '影子',
|
||
|
|
hint: '🧠 天天跟着你走,白天有晚上无,看得见摸不着,是什么?',
|
||
|
|
);
|
||
|
|
|
||
|
|
GameConfig::create([
|
||
|
|
'game_key' => 'idiom',
|
||
|
|
'name' => '猜谜活动',
|
||
|
|
'icon' => '🧩',
|
||
|
|
'enabled' => true,
|
||
|
|
'params' => [
|
||
|
|
'reward_gold' => 30,
|
||
|
|
'reward_exp' => 18,
|
||
|
|
'auto_start_interval' => 1,
|
||
|
|
'expire_minutes' => 5,
|
||
|
|
'room_scope_mode' => 'multiple',
|
||
|
|
'room_ids' => [$roomOne->id, $roomTwo->id],
|
||
|
|
],
|
||
|
|
]);
|
||
|
|
|
||
|
|
// 一号房已有进行中的成语题,只允许系统补开脑筋急转弯题。
|
||
|
|
RiddleGameRound::create([
|
||
|
|
'room_id' => $roomOne->id,
|
||
|
|
'idiom_id' => $idiom->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_IDIOM,
|
||
|
|
'status' => 'active',
|
||
|
|
'reward_gold' => 30,
|
||
|
|
'reward_exp' => 18,
|
||
|
|
'started_at' => now()->subSeconds(30),
|
||
|
|
]);
|
||
|
|
|
||
|
|
/** @var RiddleGameService $service */
|
||
|
|
$service = $this->app->make(RiddleGameService::class);
|
||
|
|
$startedCount = $service->autoStartEligibleRounds();
|
||
|
|
|
||
|
|
$this->assertSame(3, $startedCount);
|
||
|
|
$this->assertDatabaseHas('idiom_game_rounds', [
|
||
|
|
'room_id' => $roomOne->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_IDIOM,
|
||
|
|
'idiom_id' => $idiom->id,
|
||
|
|
'status' => 'active',
|
||
|
|
]);
|
||
|
|
$this->assertDatabaseHas('idiom_game_rounds', [
|
||
|
|
'room_id' => $roomOne->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_BRAIN_TEASER,
|
||
|
|
'idiom_id' => $brainTeaser->id,
|
||
|
|
'status' => 'active',
|
||
|
|
]);
|
||
|
|
$this->assertDatabaseHas('idiom_game_rounds', [
|
||
|
|
'room_id' => $roomTwo->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_IDIOM,
|
||
|
|
'idiom_id' => $idiom->id,
|
||
|
|
'status' => 'active',
|
||
|
|
]);
|
||
|
|
$this->assertDatabaseHas('idiom_game_rounds', [
|
||
|
|
'room_id' => $roomTwo->id,
|
||
|
|
'quiz_type' => Riddle::TYPE_BRAIN_TEASER,
|
||
|
|
'idiom_id' => $brainTeaser->id,
|
||
|
|
'status' => 'active',
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 方法功能:创建站长账号,满足后台权限与手动出题权限要求。
|
||
|
|
*/
|
||
|
|
private function createSiteOwner(): User
|
||
|
|
{
|
||
|
|
return User::factory()->create([
|
||
|
|
'id' => 1,
|
||
|
|
'user_level' => 100,
|
||
|
|
'username' => '站长',
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 方法功能:创建一条可用于猜成语测试的启用题目。
|
||
|
|
*/
|
||
|
|
private function createActiveIdiom(): Riddle
|
||
|
|
{
|
||
|
|
return $this->createQuestion(
|
||
|
|
type: Riddle::TYPE_IDIOM,
|
||
|
|
answer: '画蛇添足',
|
||
|
|
hint: '🧩 四人比赛画蛇,最慢的那个反而多此一举。猜一成语',
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 方法功能:按指定题型创建一条启用中的测试题目。
|
||
|
|
*/
|
||
|
|
private function createQuestion(string $type, string $answer, string $hint): Riddle
|
||
|
|
{
|
||
|
|
return Riddle::create([
|
||
|
|
'type' => $type,
|
||
|
|
'answer' => $answer,
|
||
|
|
'hint' => $hint,
|
||
|
|
'sort' => 1,
|
||
|
|
'is_active' => true,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 方法功能:重建本组测试所需的最小表结构。
|
||
|
|
*/
|
||
|
|
private function rebuildTestingTables(): void
|
||
|
|
{
|
||
|
|
Schema::disableForeignKeyConstraints();
|
||
|
|
Schema::dropIfExists('idiom_game_rounds');
|
||
|
|
Schema::dropIfExists('idioms');
|
||
|
|
Schema::dropIfExists('game_configs');
|
||
|
|
Schema::dropIfExists('rooms');
|
||
|
|
Schema::dropIfExists('users');
|
||
|
|
Schema::enableForeignKeyConstraints();
|
||
|
|
|
||
|
|
Schema::create('users', function (Blueprint $table): void {
|
||
|
|
$table->id();
|
||
|
|
$table->string('username')->nullable();
|
||
|
|
$table->string('email')->nullable();
|
||
|
|
$table->string('password')->nullable();
|
||
|
|
$table->string('remember_token', 100)->nullable();
|
||
|
|
$table->unsignedTinyInteger('sex')->default(1);
|
||
|
|
$table->text('custom_join_message')->nullable();
|
||
|
|
$table->text('custom_leave_message')->nullable();
|
||
|
|
$table->integer('user_level')->default(1);
|
||
|
|
$table->integer('exp_num')->default(0);
|
||
|
|
$table->timestamps();
|
||
|
|
});
|
||
|
|
|
||
|
|
Schema::create('rooms', function (Blueprint $table): void {
|
||
|
|
$table->id();
|
||
|
|
$table->string('room_name')->nullable();
|
||
|
|
$table->string('room_owner')->nullable();
|
||
|
|
$table->timestamps();
|
||
|
|
});
|
||
|
|
|
||
|
|
Schema::create('game_configs', function (Blueprint $table): void {
|
||
|
|
$table->id();
|
||
|
|
$table->string('game_key')->unique();
|
||
|
|
$table->string('name')->nullable();
|
||
|
|
$table->string('icon')->nullable();
|
||
|
|
$table->text('description')->nullable();
|
||
|
|
$table->boolean('enabled')->default(false);
|
||
|
|
$table->json('params')->nullable();
|
||
|
|
$table->timestamps();
|
||
|
|
});
|
||
|
|
|
||
|
|
Schema::create('idioms', function (Blueprint $table): void {
|
||
|
|
$table->id();
|
||
|
|
$table->string('type', 30)->default(Riddle::TYPE_IDIOM);
|
||
|
|
$table->string('answer', 50);
|
||
|
|
$table->string('hint', 255);
|
||
|
|
$table->boolean('is_active')->default(true);
|
||
|
|
$table->integer('sort')->default(0);
|
||
|
|
$table->timestamps();
|
||
|
|
});
|
||
|
|
|
||
|
|
Schema::create('idiom_game_rounds', function (Blueprint $table): void {
|
||
|
|
$table->id();
|
||
|
|
$table->unsignedBigInteger('room_id');
|
||
|
|
$table->unsignedBigInteger('idiom_id');
|
||
|
|
$table->string('quiz_type', 30)->default(Riddle::TYPE_IDIOM);
|
||
|
|
$table->string('status', 20)->default('pending');
|
||
|
|
$table->integer('reward_gold')->default(0);
|
||
|
|
$table->integer('reward_exp')->default(0);
|
||
|
|
$table->unsignedBigInteger('winner_id')->nullable();
|
||
|
|
$table->string('winner_username', 50)->nullable();
|
||
|
|
$table->timestamp('started_at')->nullable();
|
||
|
|
$table->timestamp('ended_at')->nullable();
|
||
|
|
$table->timestamps();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 方法功能:替换聊天室状态服务,避免测试依赖真实 Redis。
|
||
|
|
*/
|
||
|
|
private function mockChatStateService(): void
|
||
|
|
{
|
||
|
|
$chatStateService = \Mockery::mock(ChatStateService::class);
|
||
|
|
$chatStateService->shouldReceive('nextMessageId')->andReturn(1);
|
||
|
|
$chatStateService->shouldReceive('pushMessage')->andReturnNull();
|
||
|
|
|
||
|
|
$this->app->instance(ChatStateService::class, $chatStateService);
|
||
|
|
}
|
||
|
|
}
|