KTVCentral/app/Http/Controllers/RoomControlController.php

300 lines
10 KiB
PHP
Raw Normal View History

<?php
namespace App\Http\Controllers;
use App\Http\Requests\SendRoomSwitchCommandRequest;
use App\Http\Requests\ReceiveRoomRegisterRequest;
use App\Http\Requests\ReceiveRoomStatusDataRequest;
use App\Services\TcpSocketClient;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use App\Services\MachineStatusForwarder;
2025-06-03 17:29:50 +08:00
use App\Services\ApiClient;
use App\Models\User;
use App\Models\Branch;
use App\Models\Room;
use App\Models\MachineStatus;
use App\Enums\RoomStatus;
use App\Http\Responses\ApiResponse;
use App\Http\Resources\RoomResource;
use Illuminate\Support\Facades\Log;
/**
* @OA\Tag(
* name="Auth",
* description="包廂控制"
* )
*/
class RoomControlController extends Controller
{
/**
* @OA\Post(
* path="/api/room/receiveRegister",
* summary="包廂註冊控制指令",
* description="依據傳入的 branch_id 與 room_name知道過來的設備來之於那個IP設備。",
* operationId="registerRoomCommand",
* tags={"Room Control"},
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(ref="#/components/schemas/ReceiveRoomRegisterRequest")
* ),
* @OA\Response(
* response=200,
* description="成功傳送指令並回傳 TCP 回應",
* @OA\JsonContent(
* allOf={
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(
* @OA\Property(property="data", ref="#/components/schemas/Room")
* )
* }
* )
* ),
* @OA\Response(
* response=401,
* description="Unauthorized",
* @OA\JsonContent(
* allOf={
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(
* @OA\Property(property="code", type="string", example="UNAUTHORIZED"),
* @OA\Property(property="message", type="string", example="Unauthorized"),
* @OA\Property(property="data", type="null")
* )
* }
* )
* ),
* @OA\Parameter(
* name="Accept",
* in="header",
* required=true,
* @OA\Schema(type="string", default="application/json")
* )
* )
*/
public function receiveRegister(ReceiveRoomRegisterRequest $request): JsonResponse
{
// 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;
}
2025-07-01 13:59:19 +08:00
$branch = Branch::first();
return ApiResponse::success([
'token' => $token,
'branch_name' => $branch->name,
]);
}
/**
* @OA\Post(
* path="/api/room/heartbeat",
* summary="包廂心跳封包指令",
* description="記錄設備連線狀況",
* operationId="heartbeatRoomCommand",
* tags={"Room Control"},
* security={{"Authorization":{}}},
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(ref="#/components/schemas/ReceiveRoomStatusDataRequest")
* ),
* @OA\Response(
* response=200,
* description="成功傳送指令並回傳 TCP 回應",
* @OA\JsonContent(
* allOf={
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(
* @OA\Property(property="data", ref="#/components/schemas/MachineStatus")
* )
* }
* )
* ),
* @OA\Response(
* response=401,
* description="Unauthorized",
* @OA\JsonContent(
* allOf={
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(
* @OA\Property(property="code", type="string", example="UNAUTHORIZED"),
* @OA\Property(property="message", type="string", example="Unauthorized"),
* @OA\Property(property="data", type="null")
* )
* }
* )
* ),
* @OA\Parameter(
* name="Accept",
* in="header",
* required=true,
* @OA\Schema(type="string", default="application/json")
* )
* )
*/
public function StatusReport(ReceiveRoomStatusDataRequest $request)
{
$validated = $request->validated();
$roomType = null;
$roomName = null;
2025-07-01 13:59:19 +08:00
$floor = null;
// 從 room_name例如 PC101, SVR01中擷取 type 與 name
if (preg_match('/^([A-Za-z]+)(\d+)$/', $validated['hostname'], $matches)) {
$roomType = strtolower($matches[1]); // 'PC' → 'pc'
$roomName = $matches[2]; // '101'
if($roomType =='svr'){
$floor = '0';
} else{
$floor = (int) substr($roomName, 0, 1);
}
}
$branch=Branch::where('name',$validated['branch_name'])->first();
2025-07-01 13:59:19 +08:00
$room = Room::firstOrNew([
'branch_id' => $branch->id,
'floor' => $floor,
'name' => $roomName,
'type' => $roomType,
]);
if ($room->exists && $room->internal_ip !== $validated['ip']) {
$validated['status'] = 'error';
} else {
$validated['status'] = 'online';
}
2025-07-02 18:36:34 +08:00
$room->internal_ip = $validated['ip'];
$room->port = 1000;
2025-07-01 13:59:19 +08:00
$room->is_online=1;
$room->touch(); // 更新 updated_at
$room->save();
$response = (
new MachineStatusForwarder(
$branch->external_ip ?? '',
'/api/room/receiveSwitch',
(new RoomResource($room))->toArray(request())
)
)->forward();
return ApiResponse::success([
'data' => MachineStatus::create($validated),
]);
}
/**
* @OA\Post(
* path="/api/room/sendSwitch",
* summary="送出包廂控制指令",
* description="依據傳入的 room_id 與 command透過 TCP 傳送對應指令給包廂電腦。",
* operationId="sendRoomSwitchCommand",
* tags={"Room Control"},
* security={{"Authorization":{}}},
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(ref="#/components/schemas/SendRoomSwitchCommandRequest")
* ),
* @OA\Response(
* response=200,
* description="成功傳送指令並回傳 TCP 回應",
* @OA\JsonContent(
* allOf={
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(
* @OA\Property(property="data", ref="#/components/schemas/Room")
* )
* }
* )
* ),
* @OA\Response(
* response=401,
* description="Unauthorized",
* @OA\JsonContent(
* allOf={
* @OA\Schema(ref="#/components/schemas/ApiResponse"),
* @OA\Schema(
* @OA\Property(property="code", type="string", example="UNAUTHORIZED"),
* @OA\Property(property="message", type="string", example="Unauthorized"),
* @OA\Property(property="data", type="null")
* )
* }
* )
* ),
* @OA\Parameter(
* name="Accept",
* in="header",
* required=true,
* @OA\Schema(type="string", default="application/json")
* )
* )
*/
public function sendSwitch(SendRoomSwitchCommandRequest $request): JsonResponse
{
$validated = $request->validated();
2025-05-28 11:43:38 +08:00
$branch = Branch::where('name',$validated['branch_name'])->first();
$roomType = null;
$roomName = null;
// 從 room_name例如 PC101, SVR01中擷取 type 與 name
if (preg_match('/^([A-Za-z]+)(\d+)$/', $validated['room_name'], $matches)) {
$roomType = strtolower($matches[1]); // 'PC' → 'pc'
$roomName = $matches[2]; // '101'
}
$room = Room::where('branch_id', $branch->id)
->where('name', $roomName)
->where('type', $roomType)
->first();
if (!$room) {
return ApiResponse::error('房間不存在');
}
// 檢查必要欄位是否缺失或狀態為錯誤
if (empty($room->internal_ip) || empty($room->port)) {
return ApiResponse::error('房間未設定 IP 或 Port');
}
2025-07-29 14:53:07 +08:00
$room->status=$validated['command'];
$room->started_at=$validated['started_at'];
$room->ended_at=$validated['ended_at'];
$room->save();
$suffix = substr($room->name, -3) ?: $room->name;
$signal = match ($validated['command']) {
'maintain' => 'O',
'active' => 'O',
'closed' => 'X',
'fire' => 'F',
default => 'X', // fallback 保險起見
};
$data = $suffix . "," . $signal;
//dd($data);
$client = new TcpSocketClient($room->internal_ip, $room->port);
try {
$response = $client->send($data);
} catch (\Throwable $e) {
logger()->error('❌ TCP 傳送失敗: ' . $e->getMessage(), [
'room_id' => $room->id,
'ip' => $room->internal_ip,
'port' => $room->port,
]);
}
2025-07-29 14:53:07 +08:00
2025-06-05 21:51:27 +08:00
$response = (new MachineStatusForwarder(
$branch->external_ip,
"/api/room/receiveSwitch",
(new RoomResource($room))->toArray(request())
2025-06-05 21:51:27 +08:00
))->forward();
return $validated['command']==='error' ? ApiResponse::error('機房控制失敗') : ApiResponse::success($room);
}
}