[FEAT] 完善 IoT API 規范化、機台管理介面優化與 B005 改為 GET
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 1m4s
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 1m4s
1. 將 B005 (廣告同步) 從 POST 改為 GET,符合 RESTful 規範。
2. 完善 B009 (庫存回報) 回應規格,加入業務代碼 (200 OK)。
3. API 文件 UI 優化:新增 Method Badge (方法標籤),並修正 JSON 中文/斜線轉義問題。
4. 機台管理介面優化:實作「唯讀庫存與效期」面板,並將日誌圖示改為「👁️」。
5. 標準化 ID 識別邏輯:資料表全面移除對 sku 的依賴,改以 id 為主、barcode 為輔。
6. 新增 Migration:正式移除 sku 欄位並同步 barcode 指向。
7. 更新多語系支援 (zh_TW, en, ja)。
This commit is contained in:
@@ -5,9 +5,11 @@ namespace App\Http\Controllers\Api\V1\App;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Machine\Machine;
|
||||
use App\Models\System\User;
|
||||
use App\Jobs\Machine\ProcessHeartbeat;
|
||||
use App\Jobs\Machine\ProcessTimerStatus;
|
||||
use App\Jobs\Machine\ProcessCoinInventory;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class MachineController extends Controller
|
||||
@@ -104,7 +106,7 @@ class MachineController extends Controller
|
||||
{
|
||||
$machine = $request->get('machine');
|
||||
$slots = $machine->slots()->with('product')->get();
|
||||
|
||||
|
||||
// 自動轉 Success: 若機台來撈 B017,代表之前的 reload_stock 指令已成功被機台響應
|
||||
\App\Models\Machine\RemoteCommand::where('machine_id', $machine->id)
|
||||
->where('command_type', 'reload_stock')
|
||||
@@ -203,9 +205,11 @@ class MachineController extends Controller
|
||||
$machine = $request->get('machine');
|
||||
|
||||
$advertisements = \App\Models\Machine\MachineAdvertisement::where('machine_id', $machine->id)
|
||||
->with(['advertisement' => function ($query) {
|
||||
$query->active();
|
||||
}])
|
||||
->with([
|
||||
'advertisement' => function ($query) {
|
||||
$query->active();
|
||||
}
|
||||
])
|
||||
->get()
|
||||
->filter(fn($ma) => $ma->advertisement !== null)
|
||||
->map(function ($ma) {
|
||||
@@ -232,7 +236,7 @@ class MachineController extends Controller
|
||||
't070v03' => (string) ($posIdMap[$ma->position] ?? '1'), // 位置數字改放這裡 (App 會讀這欄當 Flag)
|
||||
't070v04' => $ma->advertisement->url,
|
||||
't070v05' => (string) $ma->sort_order,
|
||||
'raw_pos_weight' => $posWeight[$ma->position] ?? 99,
|
||||
'raw_pos_weight' => $posWeight[$ma->position] ?? 99,
|
||||
'raw_sort' => (int) $ma->sort_order
|
||||
];
|
||||
})
|
||||
@@ -241,7 +245,7 @@ class MachineController extends Controller
|
||||
['raw_sort', 'asc']
|
||||
])
|
||||
->values()
|
||||
->map(function($item) {
|
||||
->map(function ($item) {
|
||||
unset($item['raw_pos_weight'], $item['raw_sort']);
|
||||
return $item;
|
||||
});
|
||||
@@ -260,10 +264,51 @@ class MachineController extends Controller
|
||||
{
|
||||
$machine = $request->get('machine');
|
||||
$payload = $request->all();
|
||||
$account = $payload['account'] ?? null;
|
||||
|
||||
// 映射舊版機台回傳格式 (Map legacy machine format)
|
||||
// t060v00 -> product_id, num -> stock, tid -> slot_no
|
||||
// 1. 驗證帳號是否存在 (驗證執行補貨的人員身分)
|
||||
$user = User::where('username', $account)
|
||||
->orWhere('email', $account)
|
||||
->first();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'code' => 403,
|
||||
'message' => 'Unauthorized: Account not found',
|
||||
'status' => ''
|
||||
], 403);
|
||||
}
|
||||
|
||||
// 2. 階層式權限驗證 (遵循 RBAC 多租戶規範)
|
||||
$isAuthorized = false;
|
||||
if ($user->isSystemAdmin()) {
|
||||
// [系統層]:系統管理員可異動所有機台
|
||||
$isAuthorized = true;
|
||||
} elseif ($user->is_admin) {
|
||||
// [公司層]:公司管理員需驗證此機台是否隸屬於該公司
|
||||
$isAuthorized = ($machine->company_id === $user->company_id);
|
||||
} else {
|
||||
// [人員層]:一般人員需檢查 machine_user 授權表
|
||||
$isAuthorized = $user->machines()->where('machine_id', $machine->id)->exists();
|
||||
}
|
||||
|
||||
if (!$isAuthorized) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'code' => 403,
|
||||
'message' => 'Unauthorized: Account not authorized for this machine',
|
||||
'status' => ''
|
||||
], 403);
|
||||
}
|
||||
|
||||
// 3. 映射舊版機台回傳格式 (Map legacy machine format)
|
||||
// 支持單個物件 data: {} 或 陣列 data: [{}] (Handle both single object and array)
|
||||
$legacyData = $payload['data'] ?? [];
|
||||
if (Arr::isAssoc($legacyData)) {
|
||||
$legacyData = [$legacyData];
|
||||
}
|
||||
|
||||
$mappedSlots = array_map(function ($item) {
|
||||
return [
|
||||
'slot_no' => $item['tid'] ?? null,
|
||||
|
||||
@@ -16,7 +16,6 @@ class Product extends Model
|
||||
'category_id',
|
||||
'name',
|
||||
'name_dictionary_key',
|
||||
'sku',
|
||||
'barcode',
|
||||
'spec',
|
||||
'manufacturer',
|
||||
|
||||
@@ -14,7 +14,7 @@ class OrderItem extends Model
|
||||
'order_id',
|
||||
'product_id',
|
||||
'product_name',
|
||||
'sku',
|
||||
'barcode',
|
||||
'price',
|
||||
'quantity',
|
||||
'subtotal',
|
||||
|
||||
@@ -67,11 +67,10 @@ class MachineService
|
||||
// 蒐集所有傳入的商品 ID (可能是 SKU 或 實際 ID)
|
||||
$productCodes = collect($slotsData)->pluck('product_id')->filter()->unique()->toArray();
|
||||
|
||||
// 批次查詢商品 (支援以 SKU 查詢,確保對應至資料庫 ID)
|
||||
$products = \App\Models\Product\Product::whereIn('sku', $productCodes)
|
||||
->orWhereIn('id', $productCodes)
|
||||
->get()
|
||||
->keyBy(fn($p) => $p->sku ?: $p->id);
|
||||
// 優先以 ID 查詢商品,若 ID 不存在則嘗試 Barcode (Prioritize ID lookup, fallback to Barcode)
|
||||
$products = \App\Models\Product\Product::whereIn('id', $productCodes)
|
||||
->orWhereIn('barcode', $productCodes)
|
||||
->get();
|
||||
|
||||
foreach ($slotsData as $slotData) {
|
||||
$slotNo = $slotData['slot_no'] ?? null;
|
||||
@@ -79,11 +78,13 @@ class MachineService
|
||||
|
||||
$existingSlot = $machine->slots()->where('slot_no', $slotNo)->first();
|
||||
|
||||
// 查找對應的實體 ID
|
||||
// 查找對應的實體 ID (支援 ID 與 Barcode 比對)
|
||||
$productCode = $slotData['product_id'] ?? null;
|
||||
$actualProductId = null;
|
||||
if ($productCode) {
|
||||
$actualProductId = $products->get($productCode)?->id;
|
||||
$actualProductId = $products->first(function ($p) use ($productCode) {
|
||||
return (string)$p->id === (string)$productCode || $p->barcode === (string)$productCode;
|
||||
})?->id;
|
||||
}
|
||||
|
||||
$updateData = [
|
||||
|
||||
@@ -42,7 +42,7 @@ class TransactionService
|
||||
$order->items()->create([
|
||||
'product_id' => $item['product_id'],
|
||||
'product_name' => $item['product_name'] ?? 'Unknown',
|
||||
'sku' => $item['sku'] ?? null,
|
||||
'barcode' => $item['barcode'] ?? null,
|
||||
'price' => $item['price'],
|
||||
'quantity' => $item['quantity'],
|
||||
'subtotal' => $item['price'] * $item['quantity'],
|
||||
|
||||
Reference in New Issue
Block a user