[FEAT] 實作 B000 機台登入 API 並同步 API 規格文件
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 45s
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 45s
1. 於 routes/api.php 新增 B000 登入驗證路由,並設定速率限制。 2. 更新 .agents/skills/api-technical-specs/SKILL.md,補上 B000 規格並根據 Java App 實作精簡 B010 指令碼。 3. 同步更新 config/api-docs.php 中的 API 說明文件。 4. 調整 RemoteController 指令歷史紀錄上限為 5 筆以優化效能。 5. 統一機台編輯頁面之圖片上傳文字說明與標題。 6. 補齊多語系翻譯檔案 (zh_TW, en, ja) 出現的新字串。
This commit is contained in:
@@ -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`
|
||||
- 說明:交易完成後提交支付方式、金額、商品與出貨結果。
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
92
app/Http/Controllers/Api/V1/App/MachineAuthController.php
Normal file
92
app/Http/Controllers/Api/V1/App/MachineAuthController.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\App;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use App\Models\System\User;
|
||||
use App\Models\Machine\Machine;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class MachineAuthController extends Controller
|
||||
{
|
||||
/**
|
||||
* B000: 機台本機管理員/補貨員 離線同步登入驗證
|
||||
* 這個 API 僅用於機台的 Java App 登入畫面驗證帳密。不必進入 Queue。
|
||||
*/
|
||||
public function loginB000(Request $request): JsonResponse
|
||||
{
|
||||
// 1. 驗證欄位 (相容舊版 Java App 發送的 JSON 格式)
|
||||
$validated = $request->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'
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
],
|
||||
],
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "シリアル番号",
|
||||
|
||||
@@ -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": "機台序號",
|
||||
|
||||
@@ -231,7 +231,7 @@
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-black text-slate-800 dark:text-white tracking-tight">{{ __('Machine Photos') }}</h3>
|
||||
<h3 class="text-lg font-black text-slate-800 dark:text-white tracking-tight">{{ __('Machine Images') }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="space-y-8">
|
||||
|
||||
@@ -943,7 +943,7 @@
|
||||
</div>
|
||||
<p
|
||||
class="text-xs font-bold text-amber-700 dark:text-amber-300 leading-relaxed text-left flex-1">
|
||||
{{ __('Optimized for display. Supported formats: JPG, PNG, WebP (Max 10MB).') }}
|
||||
{{ __('PNG, JPG, WEBP up to 10MB') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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']);
|
||||
|
||||
Reference in New Issue
Block a user