diff --git a/.DS_Store b/.DS_Store index cf959a3..15a302c 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/app/Enums/RoomStatus.php b/app/Enums/RoomStatus.php index 74b27ec..72ae246 100644 --- a/app/Enums/RoomStatus.php +++ b/app/Enums/RoomStatus.php @@ -8,7 +8,7 @@ use App\Enums\Traits\HasLabels; * @OA\Schema( * schema="RoomStatus", * type="string", - * enum={"active", "closed", "fire", "error", "maintenance"}, + * enum={"active", "closed", "fire", "maintenance"}, * example="error" * ) */ @@ -19,7 +19,6 @@ enum RoomStatus: string { case Closed = 'closed'; case Fire ='fire'; case Error = 'error'; - case Maintenance = 'maintenance'; // 返回對應的顯示文字 public function labels(): string @@ -29,7 +28,6 @@ enum RoomStatus: string { self::Closed => __('enums.room.status.Closed'), self::Fire => __('enums.room.status.Fire'), self::Error => __('enums.room.status.Error'), - self::Maintenance => __('enums.room.status.Maintenance'), }; } } diff --git a/app/Http/Controllers/RoomControlController.php b/app/Http/Controllers/RoomControlController.php index 9387c47..40693d8 100644 --- a/app/Http/Controllers/RoomControlController.php +++ b/app/Http/Controllers/RoomControlController.php @@ -4,11 +4,13 @@ namespace App\Http\Controllers; use App\Http\Requests\SendRoomSwitchCommandRequest; use App\Http\Requests\ReceiveRoomRegisterRequest; +use App\Http\Requests\ReceiveRoomStatusRequest; use App\Services\TcpSocketClient; use Illuminate\Http\JsonResponse; - +use Illuminate\Support\Facades\Auth; use App\Models\Room; use App\Enums\RoomStatus; +use App\Http\Responses\ApiResponse; /** * @OA\Tag( @@ -25,7 +27,6 @@ class RoomControlController extends Controller * description="依據傳入的 branch_id 與 room_name,知道過來的設備來之於那個IP設備。", * operationId="registerRoomCommand", * tags={"Room Control"}, - * security={{"Authorization":{}}}, * @OA\RequestBody( * required=true, * @OA\JsonContent(ref="#/components/schemas/ReceiveRoomRegisterRequest") @@ -66,19 +67,61 @@ class RoomControlController extends Controller */ public function receiveRegister(ReceiveRoomRegisterRequest $request): JsonResponse { - $validated = $request->validated(); - - $room= Room::where([['branch_id',$validated['branch_id']],['name',$validated['room_name']]])->first(); - if (!$room) { - return \App\Http\Responses\ApiResponse::error("'找不到對應包廂'"); - } - $room->internal_ip = $validated['room_ip']; - $room->port =1000; - $room->touch(); - $room->status=RoomStatus::Closed; - $room->save(); - return \App\Http\Responses\ApiResponse::success($room); + // 1. 驗證帳密(登入用) + $credentials = $request->only('email', 'password'); + if (!Auth::attempt($credentials)) { + return ApiResponse::unauthorized(); + } + + // 2. 取得登入使用者 + $user = Auth::user(); + + // 3. 產生或取得 Token + if (empty($user->api_plain_token)) { + $token = $user->createToken('pc-heartbeat')->plainTextToken; + $user->api_plain_token = $token; + $user->save(); + } else { + $token = $user->api_plain_token; + } + + // 4. 驗證其他註冊欄位 + $validated = $request->validated(); // branch_id, room_name, room_ip + + // 5. 找出對應包廂 + $room = Room::where('branch_id', $validated['branch_id']) + ->where('name', $validated['room_name']) + ->first(); + + if (!$room) { + return ApiResponse::error('找不到對應包廂'); + } + + // 6. 更新包廂資訊 + $room->internal_ip = $request->ip(); + $room->port = 1000; // 預設值 + $room->is_online =1; + $room->status = RoomStatus::Closed; + $room->touch(); // 更新 updated_at + $room->save(); + + // 7. 回傳 token 與包廂資料 + return ApiResponse::success([ + 'token' => $token, + 'room' => $room, + ]); + } + public function StatusReport(ReceiveRoomStatusRequest $request) + { + $data = $request->validate([ + 'hostname' => 'required|string', + 'ip' => 'required|string', + 'cpu' => 'nullable|numeric', + 'memory' => 'nullable|numeric', + 'disk' => 'nullable|numeric', + 'status' => 'required|string', + ]); } /** * @OA\Post( @@ -129,14 +172,36 @@ class RoomControlController extends Controller public function sendSwitch(SendRoomSwitchCommandRequest $request): JsonResponse { $validated = $request->validated(); - $room= Room::where([['branch_id',$validated['branch_id']],['name',$validated['room_name']]])->first(); - $ip = $room->internal_ip; - $port = $room->port; - $data=(substr($room->name, -3) ?? $room->name).",".($validated['command']=='active' ? 'O':'X'); + $room = Room::where([ + ['branch_id', $validated['branch_id']], + ['name', $validated['room_name']], + ])->first(); + + if (!$room) { + return ApiResponse::error('房間不存在'); + } + + // 檢查必要欄位是否缺失或狀態為錯誤 + if (empty($room->internal_ip) || empty($room->port)) { + return ApiResponse::error('房間未設定 IP 或 Port'); + } + + if ($room->status === RoomStatus::Error) { + return ApiResponse::error('房間目前處於錯誤狀態,無法操作'); + } + + $suffix = substr($room->name, -3) ?: $room->name; + $signal = match ($validated['command']) { + 'active' => 'O', + 'closed' => 'X', + 'fire' => 'F', + default => 'X', // fallback 保險起見 + }; + $data = $suffix . "," . $signal; //dd($data); - $client = new TcpSocketClient($ip, $port); + $client = new TcpSocketClient($room->internal_ip, $room->port); try { $response = $client->send($data); @@ -145,13 +210,13 @@ class RoomControlController extends Controller $room->ended_at=$validated['ended_at']; $room->save(); - return \App\Http\Responses\ApiResponse::success($room); + return ApiResponse::success($room); } catch (\Throwable $e) { $room->status=RoomStatus::Error; $room->started_at=null; $room->ended_at=null; $room->save(); - return \App\Http\Responses\ApiResponse::error($e->getMessage()); + return ApiResponse::error($e->getMessage()); } } } diff --git a/app/Http/Requests/ReceiveRoomRegisterRequest.php b/app/Http/Requests/ReceiveRoomRegisterRequest.php index 13fb561..bf416fd 100644 --- a/app/Http/Requests/ReceiveRoomRegisterRequest.php +++ b/app/Http/Requests/ReceiveRoomRegisterRequest.php @@ -7,10 +7,11 @@ use Illuminate\Foundation\Http\FormRequest; /** * @OA\Schema( * schema="ReceiveRoomRegisterRequest", - * required={"branch_id", "room_id", "ip"}, - * @OA\Property(property="branch_id", type="integer", example="5"), - * @OA\Property(property="room_name", type="string", example="pc102"), - * @OA\Property(property="room_ip", type="string", example="192.168.x.x"), + * required={"branch_id", "room_name", "email" ,"password"}, + * @OA\Property(property="branch_id", type="integer", example="1"), + * @OA\Property(property="room_name", type="string", example="102"), + * @OA\Property(property="email", type="string", example="XX@gmail.com"), + * @OA\Property(property="password", type="string", example="XXX"), * ) */ class ReceiveRoomRegisterRequest extends ApiRequest @@ -25,7 +26,8 @@ class ReceiveRoomRegisterRequest extends ApiRequest return [ 'branch_id' => 'required|integer|exists:branches,id', 'room_name' => 'required|string', - 'room_ip' => 'nullable|ip', + 'email' => 'required|email', + 'password' => 'required', ]; } } diff --git a/app/Http/Requests/ReceiveRoomStatusRequest.php b/app/Http/Requests/ReceiveRoomStatusRequest.php new file mode 100644 index 0000000..81887f2 --- /dev/null +++ b/app/Http/Requests/ReceiveRoomStatusRequest.php @@ -0,0 +1,37 @@ +|string> + */ + public function rules(): array + { + return [ + 'hostname' => 'required|string', + 'ip' => 'required|string', + 'cpu' => 'nullable|numeric', + 'memory' => 'nullable|numeric', + 'disk' => 'nullable|numeric', + 'status' => 'required|string', + ]; + } +} diff --git a/app/Http/Requests/SendRoomSwitchCommandRequest.php b/app/Http/Requests/SendRoomSwitchCommandRequest.php index e002345..ba5a533 100644 --- a/app/Http/Requests/SendRoomSwitchCommandRequest.php +++ b/app/Http/Requests/SendRoomSwitchCommandRequest.php @@ -7,7 +7,7 @@ use Illuminate\Foundation\Http\FormRequest; /** * @OA\Schema( * schema="SendRoomSwitchCommandRequest", - * required={"room_id", "command"}, + * required={"branch_id", "room_name", "command"}, * @OA\Property(property="branch_id", type="integer", example="5"), * @OA\Property(property="room_name", type="string", example="pc102"), * @OA\Property(property="command", type="string", enum={"active", "closed", "fire", "maintenance"}, example="active"), diff --git a/app/Livewire/Admin/RoomDetailModal.php b/app/Livewire/Admin/RoomDetailModal.php index a7472fc..517ad59 100644 --- a/app/Livewire/Admin/RoomDetailModal.php +++ b/app/Livewire/Admin/RoomDetailModal.php @@ -5,12 +5,18 @@ namespace App\Livewire\Admin; use App\Models\Room; use Livewire\Component; +use App\Services\ApiClient; +use Illuminate\Support\Facades\Auth; class RoomDetailModal extends Component { - protected $listeners = ['openModal']; + protected $listeners = [ + 'openModal', 'closeModal', + 'startNotify', 'stopNotify', 'fireNotify', + 'openAccountNotify','closeAccountNotify' + ]; public $room; public bool $showModal = false; @@ -20,6 +26,69 @@ class RoomDetailModal extends Component $this->room = Room::find($roomId); $this->showModal = true; } + public function closeModal() + { + $this->showModal = false; + } + public function startNotify() + { + $data = $this->buildNotifyData('active', now(), null); + $this->send($data); + } + + public function stopNotify() + { + $data = $this->buildNotifyData('closed', null, null); + $this->send($data); + } + + public function fireNotify() + { + $data = $this->buildNotifyData('fire', null, null); + $this->send($data); + } + + public function openAccountNotify() + { + $data = $this->buildNotifyData('active', now(), null); + $this->send($data); + } + + public function closeAccountNotify() + { + $data = $this->buildNotifyData('closed', now(), null); + $this->send($data); + } + protected function buildNotifyData(string $command, $startedAt = null, $endedAt = null): array + { + return [ + 'branch_id' => $this->room->branch_id ?? 0, + 'room_name' => $this->room->name ?? '', + 'command' => $command, + 'started_at' => $startedAt ? $startedAt->toDateTimeString() : null, + 'ended_at' => $endedAt ? $endedAt->toDateTimeString() : null, + ]; + } + + function send(array $data){ + $user = Auth::user(); + $token = $user->api_plain_token ?? null; + + if (!$token) { + $this->addError('api', 'API token is missing.'); + return false; + } + + $apiClient = new ApiClient(); + $response = $apiClient->setToken($token)->post('/room/sendSwitch', $data); + if ($response->failed()) { + $this->addError('api', 'API request failed: ' . $response->body()); + return false; + } + + // 可以加入成功提示或事件 + return true; + } public function render() { diff --git a/app/Models/MachineStatus.php b/app/Models/MachineStatus.php new file mode 100644 index 0000000..6d26700 --- /dev/null +++ b/app/Models/MachineStatus.php @@ -0,0 +1,17 @@ + 'string', 'internal_ip' =>'string', 'port' => 'int', + 'is_online' => 'boolean', 'status' => \App\Enums\RoomStatus::class, 'started_at' => 'datetime', 'ended_at' => 'datetime', diff --git a/app/Services/ApiClient.php b/app/Services/ApiClient.php new file mode 100644 index 0000000..f231111 --- /dev/null +++ b/app/Services/ApiClient.php @@ -0,0 +1,41 @@ +baseUrl = config('services.room_api.base_url', 'https://ktv.test/api'); + $this->token = $token ?? config('services.room_api.token'); + } + public function setToken(string $token): self + { + $this->token = $token; + return $this; + } + + public function withDefaultHeaders(): \Illuminate\Http\Client\PendingRequest + { + return Http::withHeaders([ + 'Accept' => 'application/json', + 'Authorization' => 'Bearer ' . config('services.room_api.token'), + 'Content-Type' => 'application/json', + ]); + } + + public function post(string $endpoint, array $data = []) + { + return $this->withDefaultHeaders()->post($this->baseUrl . $endpoint, $data); + } + + public function get(string $endpoint, array $query = []) + { + return $this->withDefaultHeaders()->get($this->baseUrl . $endpoint, $query); + } + +} \ No newline at end of file diff --git a/database/migrations/2025_04_23_052656_create_FavoriteSongs_table.php b/database/migrations/2025_04_23_052656_create_FavoriteSongs_table.php index 84003eb..8ec5328 100644 --- a/database/migrations/2025_04_23_052656_create_FavoriteSongs_table.php +++ b/database/migrations/2025_04_23_052656_create_FavoriteSongs_table.php @@ -3,7 +3,6 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -use Illuminate\Support\Facades\DB; return new class extends Migration { @@ -18,13 +17,6 @@ return new class extends Migration $table->string('userPhone', 10); $table->timestamps(); }); - // 預塞一筆資料 - DB::table('FavoriteSongs')->insert([ - 'songNumber' => 999996, - 'userPhone' => '0912345678', - 'created_at' => now(), - 'updated_at' => now(), - ]); } /** diff --git a/database/migrations/2025_04_23_052656_create_user_song_table.php b/database/migrations/2025_04_23_052656_create_user_song_table.php new file mode 100644 index 0000000..d38cd40 --- /dev/null +++ b/database/migrations/2025_04_23_052656_create_user_song_table.php @@ -0,0 +1,34 @@ +unsignedBigInteger('song_id'); + $table->unsignedBigInteger('user_id'); + + $table->foreign('song_id')->references('id')->on('songs')->restrictOnDelete()->restrictOnUpdate(); + $table->foreign('user_id')->references('id')->on('users')->restrictOnDelete()->restrictOnUpdate(); + $table->primary(['song_id', 'user_id']); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_song'); + } +}; diff --git a/database/migrations/2025_05_06_055307_create_rooms_table.php b/database/migrations/2025_05_06_055307_create_rooms_table.php index 5e4a026..d4468c7 100644 --- a/database/migrations/2025_05_06_055307_create_rooms_table.php +++ b/database/migrations/2025_05_06_055307_create_rooms_table.php @@ -19,7 +19,8 @@ return new class extends Migration $table->string('name')->comment('包廂名稱'); $table->string('internal_ip')->nullable()->comment('內部 IP'); $table->unsignedSmallInteger('port')->nullable()->comment('通訊 Port'); - $table->enum('status', ['active', 'closed','fire', 'error', 'maintenance'])->default('error')->comment('狀態'); // :啟用中 / 已結束 + $table->tinyInteger('is_online')->default(0)->comment('連線狀態'); + $table->enum('status', ['active', 'closed','fire', 'error'])->default('error')->comment('狀態'); // :啟用中 / 已結束 $table->dateTime('started_at')->nullable()->comment('開始時間'); // $table->dateTime('ended_at')->nullable()->comment('結束時間'); // $table->timestamps(); diff --git a/database/migrations/2025_05_21_170205_create_machine_statuses_table.php b/database/migrations/2025_05_21_170205_create_machine_statuses_table.php new file mode 100644 index 0000000..4e21b81 --- /dev/null +++ b/database/migrations/2025_05_21_170205_create_machine_statuses_table.php @@ -0,0 +1,33 @@ +id(); + $table->string('hostname'); + $table->string('ip')->nullable(); + $table->decimal('cpu', 5, 2)->nullable(); + $table->decimal('memory', 5, 2)->nullable(); + $table->decimal('disk', 5, 2)->nullable(); + $table->string('status'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('machine_statuses'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 3098295..9fda0e0 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -16,6 +16,7 @@ class DatabaseSeeder extends Seeder $this->call([ PermissionTableSeeder::class, SongCategorySeeder::class, + FavoriteSongsSeeder::class, CreateAdminUserSeeder::class, ]); } diff --git a/database/seeders/FavoriteSongsSeeder.php b/database/seeders/FavoriteSongsSeeder.php new file mode 100644 index 0000000..e370bce --- /dev/null +++ b/database/seeders/FavoriteSongsSeeder.php @@ -0,0 +1,23 @@ +insert([ + 'songNumber' => 999996, + 'userPhone' => '0912345678', + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]); + } +} \ No newline at end of file diff --git a/resources/lang/zh-tw/enums.php b/resources/lang/zh-tw/enums.php index a8f1c8b..1d05487 100644 --- a/resources/lang/zh-tw/enums.php +++ b/resources/lang/zh-tw/enums.php @@ -27,6 +27,5 @@ return [ 'room.status.Active' => '已占用', 'room.status.Closed' => '可用', 'room.status.Fire' => '火災', - 'room.status.Error' => '異常', - 'room.status.Maintenance' => '維修', + 'room.status.Error' => '維修', ]; \ No newline at end of file diff --git a/resources/views/components/room-card-svr.blade.php b/resources/views/components/room-card-svr.blade.php index 134097d..4b4711c 100644 --- a/resources/views/components/room-card-svr.blade.php +++ b/resources/views/components/room-card-svr.blade.php @@ -4,12 +4,18 @@ RoomStatus::Active->value => 'green-600', RoomStatus::Closed->value => 'gray-600', RoomStatus::Error->value => 'red-600', - RoomStatus::Maintenance->value => 'yellow-600', ]; @endphp