[FEAT] 實作 B000 機台登入 API 並同步 API 規格文件
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:
2026-04-02 17:24:13 +08:00
parent e085058d63
commit 08b6c60d2e
10 changed files with 141 additions and 19 deletions

View File

@@ -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`
- 說明:交易完成後提交支付方式、金額、商品與出貨結果。

View File

@@ -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);
}

View 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'
]);
}
}

View File

@@ -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'
],
],

View File

@@ -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",

View File

@@ -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": "シリアル番号",

View File

@@ -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": "機台序號",

View File

@@ -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">

View File

@@ -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>

View File

@@ -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']);