orderBy('last_heartbeat_at', 'desc')->orderBy('id', 'desc'); if ($request->filled('search') && $request->input('tab') === 'list') { $search = $request->input('search'); $machineQuery->where(function ($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhere('serial_no', 'like', "%{$search}%"); }); } $machines = $machineQuery->paginate($request->input('per_page', 10), ['*'], 'machine_page'); // --- 2. 歷史紀錄處理 (Operation Records Tab) --- $historyQuery = RemoteCommand::where('command_type', '!=', 'reload_stock') ->with(['machine', 'user']); if ($request->filled('search') && ($request->input('tab') === 'history' || !$request->has('tab'))) { $search = $request->input('search'); $historyQuery->where(function ($q) use ($search) { $q->whereHas('machine', function ($mq) use ($search) { $mq->where('name', 'like', "%{$search}%") ->orWhere('serial_no', 'like', "%{$search}%"); })->orWhereHas('user', function ($uq) use ($search) { $uq->where('name', 'like', "%{$search}%"); }); }); } // 時間區間過濾 (created_at) if ($request->filled('start_date') || $request->filled('end_date')) { try { if ($request->filled('start_date')) { $start = \Illuminate\Support\Carbon::parse($request->input('start_date')); $historyQuery->where('created_at', '>=', $start); } if ($request->filled('end_date')) { $end = \Illuminate\Support\Carbon::parse($request->input('end_date')); $historyQuery->where('created_at', '<=', $end); } } catch (\Exception $e) { // 忽略解析錯誤 } } // 指令類型過濾 if ($request->filled('command_type')) { $historyQuery->where('command_type', $request->input('command_type')); } // 狀態過濾 if ($request->filled('status')) { $historyQuery->where('status', $request->input('status')); } $history = $historyQuery->latest()->paginate($request->input('per_page', 10), ['*'], 'history_page'); // --- 3. 特定機台詳情處理 --- if ($request->has('machine_id')) { $selectedMachine = Machine::with([ 'slots.product', 'commands' => function ($query) { $query->where('command_type', '!=', 'reload_stock') ->latest() ->limit(5); } ])->find($request->machine_id); } // --- 4. AJAX 回應處理 --- if ($request->ajax()) { if ($request->has('tab')) { $tab = $request->input('tab'); $viewPath = $tab === 'list' ? 'admin.remote.partials.tab-machines-index' : 'admin.remote.partials.tab-history-index'; return response()->json([ 'success' => true, 'html' => view($viewPath, [ 'machines' => $machines, 'history' => $history, ])->render() ]); } return response()->json([ 'success' => true, 'machine' => $selectedMachine, 'commands' => $selectedMachine ? $selectedMachine->commands : [] ]); } return view('admin.remote.index', [ 'machines' => $machines, 'selectedMachine' => $selectedMachine, 'history' => $history, 'title' => __('Remote Command Center'), 'subtitle' => __('Execute maintenance and operational commands remotely') ]); } /** * 儲存遠端指令 */ 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']; } // 指令去重:將同機台、同類型的 pending 指令標記為「已取代」 RemoteCommand::where('machine_id', $validated['machine_id']) ->where('command_type', $validated['command_type']) ->where('status', 'pending') ->update([ 'status' => 'superseded', 'note' => __('Superseded by new command'), 'executed_at' => now(), ]); RemoteCommand::create([ 'machine_id' => $validated['machine_id'], 'user_id' => auth()->id(), 'command_type' => $validated['command_type'], 'payload' => $payload, 'status' => 'pending', 'note' => $validated['note'] ?? null, ]); session()->flash('success', __('Command has been queued successfully.')); if ($request->expectsJson()) { return response()->json([ 'success' => true, 'message' => __('Command has been queued successfully.') ]); } return redirect()->back(); } /** * 機台庫存管理 (現有功能保留) */ public function stock(Request $request) { // 1. 機台查詢與分頁 $machineQuery = Machine::withCount([ 'slots as slots_count', 'slots as low_stock_count' => function ($query) { $query->where('stock', '<=', 5); }, 'slots as expiring_soon_count' => function ($query) { $query->whereNotNull('expiry_date') ->where('expiry_date', '<=', now()->addDays(7)) ->where('expiry_date', '>=', now()->startOfDay()); } ]); if ($request->filled('machine_search')) { $ms = $request->input('machine_search'); $machineQuery->where(function ($q) use ($ms) { $q->where('name', 'like', "%{$ms}%") ->orWhere('serial_no', 'like', "%{$ms}%"); }); } $machines = $machineQuery->orderBy('last_heartbeat_at', 'desc') ->orderBy('id', 'desc') ->paginate($request->input('per_page', 10), ['*'], 'machine_page'); // 2. 歷史紀錄查詢與分頁 $historyQuery = RemoteCommand::with(['machine', 'user']); $historyQuery->where('command_type', 'reload_stock'); if ($request->filled('search')) { $search = $request->input('search'); $historyQuery->where(function ($q) use ($search) { $q->whereHas('machine', function ($mq) use ($search) { $mq->where('name', 'like', "%{$search}%") ->orWhere('serial_no', 'like', "%{$search}%"); })->orWhereHas('user', function ($uq) use ($search) { $uq->where('name', 'like', "%{$search}%"); }); }); } // 時間區間過濾 (created_at) if ($request->filled('start_date') || $request->filled('end_date')) { try { if ($request->filled('start_date')) { $start = \Illuminate\Support\Carbon::parse($request->input('start_date')); $historyQuery->where('created_at', '>=', $start); } if ($request->filled('end_date')) { $end = \Illuminate\Support\Carbon::parse($request->input('end_date')); $historyQuery->where('created_at', '<=', $end); } } catch (\Exception $e) { // 忽略解析錯誤 } } // 狀態過濾 if ($request->filled('status')) { $historyQuery->where('status', $request->input('status')); } $history = $historyQuery->latest()->paginate($request->input('per_page', 10), ['*'], 'history_page'); // 3. AJAX 回傳處理 if ($request->boolean('_ajax')) { $tab = $request->input('tab', 'history'); if ($tab === 'machines') { return response()->view('admin.remote.partials.tab-machines', [ 'machines' => $machines, ]); } return response()->view('admin.remote.partials.tab-history', [ 'history' => $history, ]); } $selectedMachine = null; if ($request->has('machine_id')) { $selectedMachine = Machine::with([ 'slots.product', 'commands' => function ($query) { $query->where('command_type', 'reload_stock') ->latest() ->limit(50); } ])->find($request->machine_id); } return view('admin.remote.stock', [ 'machines' => $machines, 'selectedMachine' => $selectedMachine, 'history' => $history, 'title' => __('Stock & Expiry Management'), 'subtitle' => __('Real-time monitoring and adjustment of cargo lane inventory and expiration dates') ]); } }