From d70da24a757e55647cb928cca7b2962bbba835e5 Mon Sep 17 00:00:00 2001 From: "allen.yan" Date: Thu, 4 Sep 2025 18:12:15 +0800 Subject: [PATCH] =?UTF-8?q?202509041808=20=E5=8A=A0=E5=85=A5RoomApiToken?= =?UTF-8?q?=20=E9=A9=97=E8=A8=BC=20=E5=8A=A0=E5=85=A5=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E9=A1=AF=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Middleware/RoomApiTokenMiddleware.php | 33 +++++++ app/Livewire/Pages/Home.php | 31 ++++--- app/Livewire/Pages/LoveMessage.php | 9 ++ app/Livewire/Pages/OrderedSongList.php | 32 +++---- app/Livewire/Pages/SearchSong.php | 17 +++- app/Livewire/Pages/SoundControl.php | 87 ++++++++++++++----- app/Models/OrderedSong.php | 30 +++++++ app/Models/RoomSession.php | 14 +++ bootstrap/app.php | 1 + .../views/livewire/app/love-message.blade.php | 2 +- .../views/livewire/app/new-songs.blade.php | 1 + .../views/livewire/app/search-song.blade.php | 1 + .../livewire/app/sound-control.blade.php | 2 +- .../views/livewire/app/top-ranking.blade.php | 1 + resources/views/livewire/pages/home.blade.php | 14 +-- .../livewire/pages/search-song.blade.php | 10 ++- .../livewire/pages/sound-control.blade.php | 5 +- routes/web.php | 8 +- 開發手冊.ini | 30 ++++++- 19 files changed, 249 insertions(+), 79 deletions(-) create mode 100644 app/Http/Middleware/RoomApiTokenMiddleware.php diff --git a/app/Http/Middleware/RoomApiTokenMiddleware.php b/app/Http/Middleware/RoomApiTokenMiddleware.php new file mode 100644 index 0000000..c959bac --- /dev/null +++ b/app/Http/Middleware/RoomApiTokenMiddleware.php @@ -0,0 +1,33 @@ +query('room_code', session('room_code')); + if ($roomCode) { + $roomSession = RoomSession::validToken($roomCode)->first(); + if (!$roomSession) { + session()->forget('room_code'); + return redirect()->route('welcome')->with('error', '房間不存在或狀態不可用'); + }else{ + session(['room_code' => $roomCode]); + $request->merge(['roomSession' => $roomSession]); // 可選:直接注入 request + } + } + return $next($request); + } +} diff --git a/app/Livewire/Pages/Home.php b/app/Livewire/Pages/Home.php index 01a6ff6..a44e74a 100644 --- a/app/Livewire/Pages/Home.php +++ b/app/Livewire/Pages/Home.php @@ -2,22 +2,33 @@ namespace App\Livewire\Pages; +use App\Models\RoomSession; use Livewire\Component; class Home extends Component -{ - public $roomCode; - +{ + public array $menus = []; public function mount() - { - // 先從 URL 取得 room_code,再存進 session - //session()->forget('room_code'); - $this->roomCode = request()->query('room_code', session('room_code', null)); - if ($this->roomCode) { - session(['room_code' => $this->roomCode]); + { + $this->menus = [ + ['route' => 'new-songs','image' => '手機點歌/首頁-新歌快報.png'], + ['route' => 'top-ranking','image' => '手機點歌/首頁-熱門排行.png'], + ['route' => 'search-song','image' => '手機點歌/首頁-歌名查詢.png'], + ]; + $roomMenus = [ + ['route' => 'clicked-song','image' => '手機點歌/首頁-已點歌曲.png'], + ['route' => 'sound-control','image' => '手機點歌/首頁-聲音控制.png',], + ['route' => 'love-message','image' => '手機點歌/首頁-真情告白.png',], + ]; + $roomCode = request()->query('room_code', session('room_code', null)); + if ($roomCode) { + $roomSession = RoomSession::validToken($roomCode)->first(); + if ($roomSession) { + session(['room_code' => $roomCode]); + $this->menus = array_merge($this->menus, $roomMenus); + } } } - public function render() { return view('livewire.pages.home'); diff --git a/app/Livewire/Pages/LoveMessage.php b/app/Livewire/Pages/LoveMessage.php index 03ba3c0..8756a3a 100644 --- a/app/Livewire/Pages/LoveMessage.php +++ b/app/Livewire/Pages/LoveMessage.php @@ -3,12 +3,21 @@ namespace App\Livewire\Pages; use Livewire\Component; +use App\Models\RoomSession; +use WireUi\Traits\WireUiActions; +use App\Services\TcpSocketClient; class LoveMessage extends Component { + use WireUiActions; protected $listeners = ['stickerSelected' => 'handleSticker']; + public $roomSession; public $message = ''; public $selectedSticker=null; + public function mount() + { + $this->roomSession = request()->roomSession; + } public function handleSticker($sticker) { diff --git a/app/Livewire/Pages/OrderedSongList.php b/app/Livewire/Pages/OrderedSongList.php index 63be556..403d839 100644 --- a/app/Livewire/Pages/OrderedSongList.php +++ b/app/Livewire/Pages/OrderedSongList.php @@ -9,7 +9,7 @@ use App\Models\OrderedSong; class OrderedSongList extends Component { - public ?string $roomSessionId = null; + public $roomSession; public EloquentCollection $playing ; public EloquentCollection $nextSong ; @@ -21,32 +21,22 @@ class OrderedSongList extends Component $this->playing = new EloquentCollection(); $this->nextSong = new EloquentCollection(); $this->finished = new EloquentCollection(); - $roomSession = $this->getRoomSession(session('room_code'))?->id ; - if ($roomSession) { - $this->roomSessionId = $roomSession->id; - $this->loadSongs($this->roomSessionId); - } - } - private function getRoomSession($apiToken): ?RoomSession - { - if (!$apiToken) return null; - return RoomSession::where('api_token', $apiToken) - ->whereIn('status', ['active', 'maintain']) - ->first(); + $this->roomSession = request()->roomSession; + $this->refreshSongs(); } public function refreshSongs() { - if ($this->roomSessionId) { - $this->loadSongs($this->roomSessionId); + $dbSession = $this->roomSession->refreshValid(); + if (!$dbSession) { + session()->forget('room_code'); + return $this->redirect(route('welcome'), navigate: true); } + $this->playing = OrderedSong::forSession($this->roomSession->id)->playing()->get(); + $this->nextSong = OrderedSong::forSession($this->roomSession->id)->nextSong()->get(); + $this->finished = OrderedSong::forSession($this->roomSession->id)->finished()->get(); } - protected function loadSongs(string $roomSessionId) - { - $this->playing = OrderedSong::forSession($roomSessionId)->playing()->get(); - $this->nextSong = OrderedSong::forSession($roomSessionId)->nextSong()->get(); - $this->finished = OrderedSong::forSession($roomSessionId)->finished()->get(); - } + public function render() { return view('livewire.pages.ordered-song-list', [ diff --git a/app/Livewire/Pages/SearchSong.php b/app/Livewire/Pages/SearchSong.php index feca84c..4d77af6 100644 --- a/app/Livewire/Pages/SearchSong.php +++ b/app/Livewire/Pages/SearchSong.php @@ -3,11 +3,15 @@ namespace App\Livewire\Pages; use Livewire\Component; +use App\Models\RoomSession; use App\Models\Artist; use App\Models\SongLibraryCache; +use WireUi\Traits\WireUiActions; class SearchSong extends Component { + use WireUiActions; + public $roomSession; public string $search = ''; public $searchCategory = ''; public $selectedLanguage = '國語'; // 預設語言 @@ -21,6 +25,13 @@ class SearchSong extends Component public function mount(string $searchCategory = '') { $this->searchCategory = $searchCategory; + $roomCode = request()->query('room_code', session('room_code', null)); + if ($roomCode) { + $this->roomSession = RoomSession::validToken($roomCode)->first(); + if ($this->roomSession) { + session(['room_code' => $roomCode]); + } + } } /** @@ -68,8 +79,10 @@ class SearchSong extends Component // TODO: 加入已點歌曲邏輯,例如: // auth()->user()->room->addSong($songId); - $this->dispatchBrowserEvent('notify', [ - 'message' => '已加入已點歌曲' + $this->notification()->send([ + 'icon' => 'success', + 'title' => $songId, + 'description' => '已加入已點歌曲', ]); } diff --git a/app/Livewire/Pages/SoundControl.php b/app/Livewire/Pages/SoundControl.php index e5a570f..aa57db8 100644 --- a/app/Livewire/Pages/SoundControl.php +++ b/app/Livewire/Pages/SoundControl.php @@ -2,37 +2,76 @@ namespace App\Livewire\Pages; +use App\Models\RoomSession; use Livewire\Component; +use App\Services\ApiClient; +use WireUi\Traits\WireUiActions; +use App\Services\TcpSocketClient; class SoundControl extends Component { + use WireUiActions; + public $roomSession; + public $buttons = [ - ['img'=>'音控-01.jpg', 'action'=>'pause'], - ['img'=>'音控-02.jpg', 'action'=>'volume_up'], - ['img'=>'音控-04.jpg', 'action'=>'mic_up'], - ['img'=>'音控-06.jpg', 'action'=>'mute'], - ['img'=>'音控-03.jpg', 'action'=>'volume_down'], - ['img'=>'音控-05.jpg', 'action'=>'mic_down'], - ['img'=>'音控-07.jpg', 'action'=>'original_song'], - ['img'=>'音控-08.jpg', 'action'=>'service'], - ['img'=>'音控-09.jpg', 'action'=>'replay'], - ['img'=>'音控-11.jpg', 'action'=>'male_key'], - ['img'=>'音控-12.jpg', 'action'=>'female_key'], - ['img'=>'音控-10.jpg', 'action'=>'cut'], - ['img'=>'音控-15.jpg', 'action'=>'lower_key'], - ['img'=>'音控-14.jpg', 'action'=>'standard_key'], - ['img'=>'音控-13.jpg', 'action'=>'raise_key'], + 'pause' =>['img'=>'音控-01.jpg','label' =>'暫停'], + 'volume_up' =>['img'=>'音控-02.jpg','label' =>'音量 ↑'], + 'volume_down' =>['img'=>'音控-04.jpg','label' =>'音量 ↓'], + 'mic_up' =>['img'=>'音控-06.jpg','label' =>'麥克風 ↑'], + 'mic_down' =>['img'=>'音控-03.jpg','label' =>'麥克風 ↓'], + 'mute' =>['img'=>'音控-05.jpg','label' =>'靜音'], + 'original_song' =>['img'=>'音控-07.jpg','label' =>'原唱'], + 'service' =>['img'=>'音控-08.jpg','label' =>'服務鈴'], + 'replay' =>['img'=>'音控-09.jpg','label' =>'重播'], + 'male_key' =>['img'=>'音控-11.jpg','label' =>'男調'], + 'female_key' =>['img'=>'音控-12.jpg','label' =>'女調'], + 'cut' =>['img'=>'音控-10.jpg','label' =>'切歌'], + 'lower_key' =>['img'=>'音控-15.jpg','label' =>'降調'], + 'standard_key' =>['img'=>'音控-14.jpg','label' =>'原調'], + 'raise_key' =>['img'=>'音控-13.jpg','label' =>'升調'], ]; - - public function sendVolumeControl(string $action) + public function mount() { - // 這裡可以加你的 API 或邏輯 - // 範例:發送到後台控制音量 - info("Sound control action: ".$action); - - $this->dispatchBrowserEvent('notify', [ - 'message' => "已執行操作: {$action}" - ]); + $this->roomSession = request()->roomSession; + } + public function sendControl(string $action) + { + $dbSession = $this->roomSession->refreshValid(); + if (!$dbSession) { + session()->forget('room_code'); + $this->notification()->send([ + 'icon' => 'error', + 'title' => '驗證失敗', + 'description' => 'Token 已失效,請重新登入', + ]); + return $this->redirect(route('welcome'), navigate: true); + }else{ + $room=$this->roomSession->room; + } + + $client = new TcpSocketClient($room->internal_ip, $room->port); + try { + $response = $client->send($room->name . "," . $action); + } catch (\Throwable $e) { + logger()->error('❌ TCP 傳送失敗: ' . $e->getMessage(), [ + 'room_id' => $room->id, + 'ip' => $room->internal_ip, + 'port' => $room->port, + ]); + } + $title = $this->buttons[$action]['label'] ?? $action; + $this->notification()->send( + (isset($response) && trim($response) === "OK") + ?[ + 'icon' => 'success', + 'title' => $title, + 'description' => '已執行操作', + ]:[ + 'icon' => 'error', + 'title' => $title, + 'description' => $data['message'] ?? '操作失敗', + ] + ); } public function render() diff --git a/app/Models/OrderedSong.php b/app/Models/OrderedSong.php index 552088c..ba7e00c 100644 --- a/app/Models/OrderedSong.php +++ b/app/Models/OrderedSong.php @@ -4,6 +4,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use App\Enums\OrderedSongStatus; /** * @OA\Schema( @@ -85,4 +86,33 @@ class OrderedSong extends Model } ]); } + public function scopeForSession($query, $roomSessionId) + { + return $query->where('room_session_id', $roomSessionId); + } + + public function scopePlaying($query) + { + return $query->where('status', OrderedSongStatus::Playing); + } + + public function scopeNextSong($query) + { + return $query->whereIn('status', [OrderedSongStatus::InsertPlayback, OrderedSongStatus::NotPlayed]) + ->orderByRaw("FIELD(status, ?, ?)", [ + OrderedSongStatus::InsertPlayback->value, + OrderedSongStatus::NotPlayed->value, + ]) + ->orderByRaw("CASE WHEN status=? THEN ordered_at END DESC", [OrderedSongStatus::InsertPlayback->value]) + ->orderByRaw("CASE WHEN status=? THEN ordered_at END ASC", [OrderedSongStatus::NotPlayed->value]); + } + + public function scopeFinished($query) + { + return $query->whereIn('status', [ + OrderedSongStatus::Played, + OrderedSongStatus::Skipped, + OrderedSongStatus::NoFile, + ])->orderByDesc('finished_at'); + } } diff --git a/app/Models/RoomSession.php b/app/Models/RoomSession.php index 7b17096..5fe541c 100644 --- a/app/Models/RoomSession.php +++ b/app/Models/RoomSession.php @@ -35,8 +35,22 @@ class RoomSession extends Model public const MODE_VIP = 'vip'; public const MODE_TEST = 'test'; + public function scopeValidToken($query, ?string $token) + { + return $query->with('room') + ->where('api_token', $token) + ->whereIn('status', ['active', 'maintain']); + } + public function refreshValid(): ?self + { + return self::validToken($this->api_token) + ->where('id', $this->id) + ->first(); + } + public function room() { return $this->belongsTo(Room::class); } + } diff --git a/bootstrap/app.php b/bootstrap/app.php index 38010d6..68ef8b5 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -14,6 +14,7 @@ return Application::configure(basePath: dirname(__DIR__)) ->withMiddleware(function (Middleware $middleware) { $middleware->alias([ 'api_token' => \App\Http\Middleware\ApiTokenMiddleware::class, + 'room_api_token' => \App\Http\Middleware\RoomApiTokenMiddleware::class, 'role' => \Spatie\Permission\Middleware\RoleMiddleware::class, 'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class, 'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class diff --git a/resources/views/livewire/app/love-message.blade.php b/resources/views/livewire/app/love-message.blade.php index 2be6155..62494bc 100644 --- a/resources/views/livewire/app/love-message.blade.php +++ b/resources/views/livewire/app/love-message.blade.php @@ -5,6 +5,6 @@ 超級巨星 Banner - + \ No newline at end of file diff --git a/resources/views/livewire/app/new-songs.blade.php b/resources/views/livewire/app/new-songs.blade.php index 11919e3..827f13f 100644 --- a/resources/views/livewire/app/new-songs.blade.php +++ b/resources/views/livewire/app/new-songs.blade.php @@ -5,6 +5,7 @@ 新歌快報 +
Wolf Fox Logo diff --git a/resources/views/livewire/app/search-song.blade.php b/resources/views/livewire/app/search-song.blade.php index 3351d52..e811a61 100644 --- a/resources/views/livewire/app/search-song.blade.php +++ b/resources/views/livewire/app/search-song.blade.php @@ -5,5 +5,6 @@ 歌名查詢
+ \ No newline at end of file diff --git a/resources/views/livewire/app/sound-control.blade.php b/resources/views/livewire/app/sound-control.blade.php index 7cb25e7..4d98406 100644 --- a/resources/views/livewire/app/sound-control.blade.php +++ b/resources/views/livewire/app/sound-control.blade.php @@ -5,6 +5,6 @@ 超級巨星 Banner - + \ No newline at end of file diff --git a/resources/views/livewire/app/top-ranking.blade.php b/resources/views/livewire/app/top-ranking.blade.php index c88557a..08ae96e 100644 --- a/resources/views/livewire/app/top-ranking.blade.php +++ b/resources/views/livewire/app/top-ranking.blade.php @@ -5,6 +5,7 @@ 熱門排行 +
Wolf Fox Logo diff --git a/resources/views/livewire/pages/home.blade.php b/resources/views/livewire/pages/home.blade.php index 613e683..932648d 100644 --- a/resources/views/livewire/pages/home.blade.php +++ b/resources/views/livewire/pages/home.blade.php @@ -1,15 +1,7 @@
- - - - - @if($roomCode) - - - - - - @endif + @foreach($menus as $menu) + + @endforeach
diff --git a/resources/views/livewire/pages/search-song.blade.php b/resources/views/livewire/pages/search-song.blade.php index 7c21ca9..5756e35 100644 --- a/resources/views/livewire/pages/search-song.blade.php +++ b/resources/views/livewire/pages/search-song.blade.php @@ -62,12 +62,14 @@ 編號 歌曲 - 操作 + @if($roomSession) + 操作 + @endif @forelse($songs as $song) - + {{ $song->song_id }}
@@ -75,7 +77,9 @@ {{ $song->str_artists_plus() }}
- 點歌 + @if($roomSession) + 點歌 + @endif @empty diff --git a/resources/views/livewire/pages/sound-control.blade.php b/resources/views/livewire/pages/sound-control.blade.php index f9e7422..c65407c 100644 --- a/resources/views/livewire/pages/sound-control.blade.php +++ b/resources/views/livewire/pages/sound-control.blade.php @@ -1,9 +1,10 @@
- @foreach($buttons as $btn) + @foreach($buttons as $key => $btn) @endforeach
diff --git a/routes/web.php b/routes/web.php index 6826488..1eaf296 100644 --- a/routes/web.php +++ b/routes/web.php @@ -11,9 +11,11 @@ Route::view('/welcome', 'livewire.app.welcome')->name('welcome'); Route::view('/new-songs', 'livewire.app.new-songs')->name('new-songs'); Route::view('/top-ranking', 'livewire.app.top-ranking')->name('top-ranking'); Route::view('/search-song', 'livewire.app.search-song')->name('search-song'); -Route::view('/clicked-song', 'livewire.app.clicked-song')->name('clicked-song'); -Route::view('/sound-control', 'livewire.app.sound-control')->name('sound-control'); -Route::view('/love-message', 'livewire.app.love-message')->name('love-message'); +Route::middleware('room_api_token')->group(function () { + Route::view('/clicked-song', 'livewire.app.clicked-song')->name('clicked-song'); + Route::view('/sound-control', 'livewire.app.sound-control')->name('sound-control'); + Route::view('/love-message', 'livewire.app.love-message')->name('love-message'); +}); Route::middleware(['auth'])->group(function () { // Profile diff --git a/開發手冊.ini b/開發手冊.ini index d66e286..dd305d2 100644 --- a/開發手冊.ini +++ b/開發手冊.ini @@ -89,4 +89,32 @@ php artisan migrate:rollback php artisan migrate php artisan transfer:sqlite sqlite/tempUser.sqlite --sync -php artisan queue:work --daemon --timeout=3600 --tries=1 --queue=default \ No newline at end of file +php artisan queue:work --daemon --timeout=3600 --tries=1 --queue=default + +npm install && npm run build + +https://ktvremote.test/?room_code=abc123 +http://192.168.12.14:9090/wnknwl1f3yy/windows.html +php artisan storage:link + +# 執行全部測試 +php artisan test +# 執行特定檔案 +php artisan test tests/Feature/InternalHtmlTest.php +# 執行特定測試方法 +php artisan test --filter test_internal_html_page + +# 執行全部測試 +vendor/bin/phpunit + +# 執行特定檔案 +vendor/bin/phpunit tests/Feature/InternalHtmlTest.php + +# 執行特定測試方法 +vendor/bin/phpunit --filter test_internal_html_page + + +02 2171 1168 89843 + + +