diff --git a/app/Http/Controllers/Admin/RemoteController.php b/app/Http/Controllers/Admin/RemoteController.php index e35ede9..bbec9c6 100644 --- a/app/Http/Controllers/Admin/RemoteController.php +++ b/app/Http/Controllers/Admin/RemoteController.php @@ -4,22 +4,78 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; use Illuminate\Http\Request; +use App\Models\Machine\Machine; +use App\Models\Machine\RemoteCommand; +use Illuminate\Support\Facades\Auth; class RemoteController extends Controller { - // 機台庫存 + /** + * 遠端管理指揮中心 + */ + public function index(Request $request) + { + $machines = Machine::withCount(['slots'])->orderBy('name')->get(); + $selectedMachine = null; + + if ($request->has('machine_id')) { + $selectedMachine = Machine::with(['slots.product', 'commands' => function($query) { + $query->latest()->limit(10); + }])->find($request->machine_id); + } + + return view('admin.remote.index', [ + 'machines' => $machines, + 'selectedMachine' => $selectedMachine, + ]); + } + + /** + * 儲存遠端指令 + */ + public function storeCommand(Request $request) + { + $validated = $request->validate([ + 'machine_id' => 'required|exists:machines,id', + 'command_type' => 'required|string|in:reboot,reboot_card,checkout,lock,unlock,change,dispense', + 'amount' => 'nullable|integer|min:0', + 'slot_no' => 'nullable|string', + 'note' => 'nullable|string|max:255', + ]); + + $payload = []; + if ($validated['command_type'] === 'change') { + $payload['amount'] = $validated['amount']; + } elseif ($validated['command_type'] === 'dispense') { + $payload['slot_no'] = $validated['slot_no']; + } + + RemoteCommand::create([ + 'machine_id' => $validated['machine_id'], + 'command_type' => $validated['command_type'], + 'payload' => $payload, + 'status' => 'pending', + 'note' => $validated['note'] ?? null, + ]); + + return redirect()->back()->with('success', __('Command has been queued successfully.')); + } + + /** + * 機台庫存管理 (現有功能保留) + */ public function stock(Request $request) { - $machines = \App\Models\Machine\Machine::withCount([ + $machines = Machine::withCount([ 'slots as slots_count', 'slots as low_stock_count' => function ($query) { $query->where('stock', '<=', 5); } ])->orderBy('name')->get(); + $selectedMachine = null; - if ($request->has('machine_id')) { - $selectedMachine = \App\Models\Machine\Machine::find($request->machine_id); + $selectedMachine = Machine::with('slots.product')->find($request->machine_id); } return view('admin.remote.stock', [ @@ -27,58 +83,4 @@ class RemoteController extends Controller 'selectedMachine' => $selectedMachine, ]); } - - // 機台重啟 - public function restart() - { - return view('admin.placeholder', [ - 'title' => '遠端重啟機台', - 'description' => '遠端重啟機台系統', - ]); - } - - // 卡機重啟 - public function restartCardReader() - { - return view('admin.placeholder', [ - 'title' => '遠端重啟刷卡機', - 'description' => '遠端重啟刷卡機設備', - ]); - } - - // 遠端結帳 - public function checkout() - { - return view('admin.placeholder', [ - 'title' => '遠端結帳', - 'description' => '遠端執行結帳流程', - ]); - } - - // 遠端鎖定頁 - public function lock() - { - return view('admin.placeholder', [ - 'title' => '遠端鎖定頁', - 'description' => '遠端鎖定機台頁面', - ]); - } - - // 遠端找零 - public function change() - { - return view('admin.placeholder', [ - 'title' => '遠端找零', - 'description' => '遠端執行找零功能', - ]); - } - - // 遠端出貨 - public function dispense() - { - return view('admin.placeholder', [ - 'title' => '遠端出貨', - 'description' => '遠端控制商品出貨', - ]); - } } diff --git a/app/Http/Controllers/Api/V1/App/MachineController.php b/app/Http/Controllers/Api/V1/App/MachineController.php index b231310..51cbbd8 100644 --- a/app/Http/Controllers/Api/V1/App/MachineController.php +++ b/app/Http/Controllers/Api/V1/App/MachineController.php @@ -23,11 +23,58 @@ class MachineController extends Controller // 異步處理狀態更新 ProcessHeartbeat::dispatch($machine->serial_no, $data); + // 取出待處理指令 + $command = \App\Models\Machine\RemoteCommand::where('machine_id', $machine->id) + ->pending() + ->first(); + + $status = '49'; // 預設 49 (OK / No Command) + $message = 'OK'; + + if ($command) { + switch ($command->command_type) { + case 'reboot': + $status = '51'; + $message = 'reboot'; + break; + case 'reboot_card': + $status = '60'; + $message = 'reboot card machine'; + break; + case 'checkout': + $status = '61'; + $message = 'checkout'; + break; + case 'lock': + $status = '71'; + $message = 'lock'; + break; + case 'unlock': + $status = '70'; + $message = 'unlock'; + break; + case 'change': + $status = '82'; + $message = $command->payload['amount'] ?? '0'; + break; + case 'dispense': + $status = '85'; + $message = $command->payload['slot_no'] ?? ''; + break; + case 'reload_stock': + $status = '49'; + $message = 'reload B017'; + break; + } + // 標記為已發送 (sent) + $command->update(['status' => 'sent', 'executed_at' => now()]); + } + return response()->json([ 'success' => true, 'code' => 200, - 'message' => 'OK', - 'status' => '49' // 某些硬體可能需要的成功碼 + 'message' => $message, + 'status' => $status ], 202); // 202 Accepted } @@ -69,6 +116,8 @@ class MachineController extends Controller 'capacity' => $slot->capacity, 'price' => $slot->price, 'status' => $slot->status, + 'expiry_date' => $slot->expiry_date, + 'batch_no' => $slot->batch_no, ]; }) ]); diff --git a/app/Models/Machine/RemoteCommand.php b/app/Models/Machine/RemoteCommand.php index f9de2de..f70e66c 100644 --- a/app/Models/Machine/RemoteCommand.php +++ b/app/Models/Machine/RemoteCommand.php @@ -11,16 +11,15 @@ class RemoteCommand extends Model protected $fillable = [ 'machine_id', - 'command', + 'command_type', 'payload', 'status', - 'response_payload', + 'ttl', 'executed_at', ]; protected $casts = [ 'payload' => 'array', - 'response_payload' => 'array', 'executed_at' => 'datetime', ]; @@ -28,4 +27,12 @@ class RemoteCommand extends Model { return $this->belongsTo(Machine::class); } + + /** + * Scope for pending commands + */ + public function scopePending($query) + { + return $query->where('status', 'pending')->orderBy('created_at', 'asc'); + } } diff --git a/database/migrations/2026_04_01_164946_add_note_to_remote_commands_table.php b/database/migrations/2026_04_01_164946_add_note_to_remote_commands_table.php new file mode 100644 index 0000000..8c698ce --- /dev/null +++ b/database/migrations/2026_04_01_164946_add_note_to_remote_commands_table.php @@ -0,0 +1,28 @@ +string('note', 255)->nullable()->after('payload'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('remote_commands', function (Blueprint $table) { + $table->dropColumn('note'); + }); + } +}; diff --git a/resources/views/admin/remote/index.blade.php b/resources/views/admin/remote/index.blade.php new file mode 100644 index 0000000..a2067ee --- /dev/null +++ b/resources/views/admin/remote/index.blade.php @@ -0,0 +1,427 @@ +@extends('layouts.admin') + +@section('content') + + +
+ + + + + + +
+ + +@endsection diff --git a/resources/views/layouts/admin.blade.php b/resources/views/layouts/admin.blade.php index 2ee0e4d..f84a687 100644 --- a/resources/views/layouts/admin.blade.php +++ b/resources/views/layouts/admin.blade.php @@ -236,7 +236,7 @@ -
+