diff --git a/.agents/skills/api-technical-specs/SKILL.md b/.agents/skills/api-technical-specs/SKILL.md index 175f16e..d83428c 100644 --- a/.agents/skills/api-technical-specs/SKILL.md +++ b/.agents/skills/api-technical-specs/SKILL.md @@ -19,7 +19,29 @@ description: 本技能規範定義了 Star Cloud 系統中所有機台 (IoT) 與 ## 3. 機台核心 API (IoT Endpoints) -### 3.1 B010: 心跳上報與狀態同步 +### 3.1 B000: 機台本地管理員同步登入 +用於機台 Android 端維護人員登入與進入設定頁。此 API 無狀態,且為例外不強制檢查 Bearer Token 的端點。 + +- **URL**: `POST /api/v1/app/admin/login/B000` +- **Request Body:** +| 參數 | 類型 | 必填 | 說明 | 範例 | +| :--- | :--- | :--- | :--- | :--- | +| `machine` | String | 是 | 機台編號 (serial_no) | `M-001` | +| `Su_Account` | String | 是 | 系統管理員或公司管理員帳號 | `admin` | +| `Su_Password` | String | 是 | 密碼 | `password123` | +| `ip` | String | 否 | 用戶端 IP (相容舊版) | `192.168.1.100` | +| `type` | String | 否 | 裝置類型代碼 (相容舊版) | `2` | + +- **Response Body:** +> [!IMPORTANT] +> 為了相容 Java APP 現有邏輯,這裡嚴格規定成功必須回傳字串 `Success`。 +| 參數 | 類型 | 說明 | 範例 | +| :--- | :--- | :--- | :--- | +| `message` | String | 驗證結果 (`Success` 或 `Failed`) | `Success` | + +--- + +### 3.2 B010: 心跳上報與狀態同步 用於確認機台在線狀態、更新感測數據、提交事件日誌並獲取雲端指令。 - **URL**: `POST /api/v1/app/machine/status/B010` @@ -56,23 +78,20 @@ description: 本技能規範定義了 Star Cloud 系統中所有機台 (IoT) 與 **雲端指令代碼 (status):** - `49`: reload B017 (貨道同步) -- `50`: reload B005 (基礎參數) - `51`: reboot (重啟系統) - `60`: reboot card machine (刷卡機重啟) -- `61`: checkout (結帳) -- `70`: unlock (解鎖) / `71`: lock (鎖定) -- `72`: sellCode reload B023 (即期品) -- `75`: exp reload B026 (效期) -- `79`: read B050 (參數讀取) -- `81`: sync timer status (B710) -- `85`: reload B0552 (出貨腳本) +- `61`: checkout (觸發結帳) +- `70`: unlock (解鎖) +- `71`: lock (鎖定) +- `85`: reload B0552 (遠端出貨) +- `待定義`: change (遠端找零 - 註:指令中心有此功能,但目前 Java App 尚無對接對應的連動事件) --- -### 3.2 B017: 貨道與庫存同步 (規劃中) +### 3.3 B017: 貨道與庫存同步 (規劃中) - **URL**: `POST /api/v1/app/machine/reload_msg/B017` - 說明:當機台收到 B010 回應 `status: 49` 時,應呼叫此 API 同步最新貨道佈局。 -### 3.3 B600: 交易數據回傳 (規劃中) +### 3.4 B600: 交易數據回傳 (規劃中) - **URL**: `POST /api/v1/app/B600` - 說明:交易完成後提交支付方式、金額、商品與出貨結果。 diff --git a/app/Http/Controllers/Admin/RemoteController.php b/app/Http/Controllers/Admin/RemoteController.php index 21b3c21..e2ed289 100644 --- a/app/Http/Controllers/Admin/RemoteController.php +++ b/app/Http/Controllers/Admin/RemoteController.php @@ -23,7 +23,7 @@ class RemoteController extends Controller $selectedMachine = Machine::with(['slots.product', 'commands' => function($query) { $query->where('command_type', '!=', 'reload_stock') ->latest() - ->limit(10); + ->limit(5); }])->find($request->machine_id); } diff --git a/app/Http/Controllers/Api/V1/App/MachineAuthController.php b/app/Http/Controllers/Api/V1/App/MachineAuthController.php new file mode 100644 index 0000000..601e3dc --- /dev/null +++ b/app/Http/Controllers/Api/V1/App/MachineAuthController.php @@ -0,0 +1,92 @@ +validate([ + 'machine' => 'required|string', + 'Su_Account' => 'required|string', + 'Su_Password' => 'required|string', + 'ip' => 'nullable|string', + 'type' => 'nullable|string', + ]); + + // 2. 透過帳號尋找使用者 (允許使用 username 或 email) + $user = User::where('username', $validated['Su_Account']) + ->orWhere('email', $validated['Su_Account']) + ->first(); + + // 若無此帳號或密碼錯誤 + if (!$user || !Hash::check($validated['Su_Password'], $user->password)) { + Log::warning("B000 機台登入失敗: 帳密錯誤", [ + 'account' => $validated['Su_Account'], + 'machine' => $validated['machine'] + ]); + // 按現行設定,Java 端只認 Success 字串,其餘視為帳密錯誤 + return response()->json(['message' => 'Failed']); + } + + // 3. 取得機台物件 + // 因為此 API 無狀態 (沒有登入 session),為了避免被 global scope 擋住,直接取消所有 scope 來撈取 + $machine = Machine::withoutGlobalScopes()->where('serial_no', $validated['machine'])->first(); + + if (!$machine) { + Log::warning("B000 機台登入失敗: 伺服器找不到該機台", [ + 'machine_serial' => $validated['machine'] + ]); + return response()->json(['message' => 'Failed']); + } + + // 4. RBAC 權限驗證 (遵循多租戶與機台授權規範) + if ($user->isSystemAdmin()) { + // [系統管理員] : 擁有最高權限,可登入平台下轄所有機台,直接放行 + + } elseif ($user->is_admin) { + // [公司管理員] : 不需要檢查 machine_user 表,但【必須驗證】該機台是否隸屬於他的公司 + if ($machine->company_id !== $user->company_id) { + Log::warning("B000 機台登入失敗: 企圖越權登入其他公司的機台", [ + 'user_id' => $user->id, + 'user_company' => $user->company_id, + 'machine_company' => $machine->company_id + ]); + return response()->json(['message' => 'Forbidden']); + } + + } else { + // [一般租戶帳號] : (包括補貨員等)必須嚴格檢查該帳號有沒有被分配到這台機器的 machine_user 關聯授權 + if (!$user->machines()->where('machine_id', $machine->id)->exists()) { + Log::warning("B000 機台登入失敗: 該帳號沒有此機台的授權", [ + 'user_id' => $user->id, + 'machine_id' => $machine->id + ]); + return response()->json(['message' => 'Forbidden']); + } + } + + // 5. 驗證完美通過!回傳固定字串 Success 讓 Java 端放行 + Log::info("B000 機台登入成功", [ + 'account' => $user->username, + 'machine' => $machine->serial_no + ]); + + return response()->json([ + 'message' => 'Success' + ]); + } +} diff --git a/config/api-docs.php b/config/api-docs.php index 0072f78..0564612 100644 --- a/config/api-docs.php +++ b/config/api-docs.php @@ -91,11 +91,10 @@ return [ 'status' => [ 'type' => 'string', 'description' => '雲端指令代碼。對照表: -49: reload B017 (貨道同步), 50: reload B005 (基礎參數), 51: reboot (重啟) -60: reboot card machine (刷卡機重啟), 61: checkout (結帳) -70: unlock (解鎖), 71: lock (鎖定), 72: sellCode reload B023 (即期品) -75: exp reload B026 (效期), 79: read B050 (參數讀取) -81: sync timer status (B710), 85: reload B0552 (出貨腳本)', +49: reload B017 (貨道同步), 51: reboot (重啟系統) +60: reboot card machine (刷卡機重啟), 61: checkout (觸發結帳) +70: unlock (解鎖), 71: lock (鎖定), 85: reload B0552 (遠端出貨) +待定義: change (遠端找零 - 目前 Java App 尚無對接)', 'example' => '49' ], ], diff --git a/lang/en.json b/lang/en.json index 6140afe..7c912da 100644 --- a/lang/en.json +++ b/lang/en.json @@ -484,6 +484,7 @@ "PARTNER_KEY": "PARTNER_KEY", "PI_MERCHANT_ID": "PI_MERCHANT_ID", "PNG, JPG up to 2MB": "PNG, JPG up to 2MB", + "PNG, JPG, WEBP up to 10MB": "PNG, JPG, WEBP up to 10MB", "PS_MERCHANT_ID": "PS_MERCHANT_ID", "Page Lock Status": "Page Lock Status", "Parameters": "Parameters", @@ -645,6 +646,7 @@ "Selected": "Selected", "Selected Date": "Search Date", "Selection": "Selection", + "set": "set", "Serial & Version": "Serial & Version", "Serial NO": "SERIAL NO", "Serial No": "Serial No", diff --git a/lang/ja.json b/lang/ja.json index 9821926..811f2ae 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -493,6 +493,7 @@ "PARTNER_KEY": "PARTNER_KEY", "PI_MERCHANT_ID": "PI_MERCHANT_ID", "PNG, JPG up to 2MB": "2MB以下のPNG、JPG", + "PNG, JPG, WEBP up to 10MB": "PNG, JPG, WEBP 最大10MB", "PS_MERCHANT_ID": "PS_MERCHANT_ID", "Page Lock Status": "ページロック状態", "Parameters": "パラメータ", @@ -645,6 +646,7 @@ "Selected": "選択中", "Selected Date": "検索日", "Selection": "選択", + "set": "設定済み", "Serial & Version": "シリアルとバージョン", "Serial NO": "シリアル番号", "Serial No": "シリアル番号", diff --git a/lang/zh_TW.json b/lang/zh_TW.json index cb68500..472856c 100644 --- a/lang/zh_TW.json +++ b/lang/zh_TW.json @@ -539,6 +539,7 @@ "PARTNER_KEY": "PARTNER_KEY", "PI_MERCHANT_ID": "Pi 拍錢包 商店代號", "PNG, JPG up to 2MB": "支援 PNG, JPG (最大 2MB)", + "PNG, JPG, WEBP up to 10MB": "支援 PNG, JPG, WEBP 格式,且不超過 10MB", "PS_LEVEL": "PS_LEVEL", "PS_MERCHANT_ID": "全盈+Pay 商店代號", "Page Lock Status": "頁面鎖定狀態", @@ -735,6 +736,7 @@ "Selected": "已選擇", "Selected Date": "查詢日期", "Selection": "已選擇", + "set": "已設定", "Serial & Version": "序號與版本", "Serial NO": "機台序號", "Serial No": "機台序號", diff --git a/resources/views/admin/basic-settings/machines/edit.blade.php b/resources/views/admin/basic-settings/machines/edit.blade.php index 46bd89c..5130957 100644 --- a/resources/views/admin/basic-settings/machines/edit.blade.php +++ b/resources/views/admin/basic-settings/machines/edit.blade.php @@ -231,7 +231,7 @@ -

{{ __('Machine Photos') }}

+

{{ __('Machine Images') }}

diff --git a/resources/views/admin/basic-settings/machines/index.blade.php b/resources/views/admin/basic-settings/machines/index.blade.php index a23259a..7b27ae5 100644 --- a/resources/views/admin/basic-settings/machines/index.blade.php +++ b/resources/views/admin/basic-settings/machines/index.blade.php @@ -943,7 +943,7 @@

- {{ __('Optimized for display. Supported formats: JPG, PNG, WebP (Max 10MB).') }} + {{ __('PNG, JPG, WEBP up to 10MB') }}

diff --git a/routes/api.php b/routes/api.php index 8719c43..b96400d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -47,6 +47,12 @@ Route::prefix('v1')->middleware(['throttle:api'])->group(function () { |-------------------------------------------------------------------------- | 專門用於機台通訊,頻率較高,建議搭配異步處理。 */ + + // 機台管理員 B000 登入驗證 (由於此階段機台未帶 Token 無法通過 iot.auth) + Route::prefix('app')->group(function () { + Route::post('admin/login/B000', [\App\Http\Controllers\Api\V1\App\MachineAuthController::class, 'loginB000'])->middleware('throttle:30,1'); + }); + Route::prefix('app')->middleware(['iot.auth', 'throttle:100,1'])->group(function () { // 心跳與狀態 (B010, B017, B710, B220) Route::post('machine/status/B010', [App\Http\Controllers\Api\V1\App\MachineController::class, 'heartbeat']);