From 40aec884b084d2780fcb7863f8981114bc88b5d4 Mon Sep 17 00:00:00 2001 From: sky121113 Date: Wed, 22 Apr 2026 17:02:24 +0800 Subject: [PATCH] =?UTF-8?q?[REFACTOR]=20=E6=BA=AB=E5=BA=A6=E5=9B=9E?= =?UTF-8?q?=E5=A0=B1=E9=82=8F=E8=BC=AF=E5=84=AA=E5=8C=96=E8=88=87=20MQTT?= =?UTF-8?q?=20=E8=A6=8F=E6=A0=BC=E6=A8=99=E6=BA=96=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 優化 ProcessHeartbeat 溫度處理邏輯,改為「變動即記錄」並標準化為整數型態。 2. 在 Machine Model 中新增 temperature 欄位的 integer cast。 3. 更新 MQTT 與 API 技術規格技能文件 (SKILL.md),同步最新的溫度記錄規則。 4. 更新 config/api-docs.php,明確標註心跳上報之溫度規格與日誌規則。 5. 移除 routes/api.php 中已廢棄之 B600 與機台日誌 REST API 路由。 6. 清理 docs 目錄,將初期計畫文件遷移至 docs/plan/。 --- .agents/skills/api-technical-specs/SKILL.md | 1 + .../skills/mqtt-communication-specs/SKILL.md | 38 ++++-- .../Api/V1/App/TransactionController.php | 66 ----------- .../Controllers/Api/V1/MachineController.php | 39 ------- app/Jobs/Machine/ProcessHeartbeat.php | 14 ++- app/Jobs/Machine/ProcessMachineLog.php | 46 -------- app/Jobs/Transaction/ProcessTransaction.php | 39 ------- app/Models/Machine/Machine.php | 1 + .../Transaction/TransactionService.php | 1 + config/api-docs.php | 6 +- docs/api/iot-spec.md | 108 ------------------ docs/b017_technical_spec.md | 71 ------------ docs/b055_technical_spec.md | 67 ----------- docs/b220_technical_spec.md | 60 ---------- docs/b600_technical_spec.md | 62 ---------- docs/b601_technical_spec.md | 49 -------- docs/b602_technical_spec.md | 57 --------- docs/b650_technical_spec.md | 46 -------- docs/b710_technical_spec.md | 49 -------- docs/{ => plan}/api_analysis.md | 0 docs/{ => plan}/architecture_plan.md | 0 docs/{ => plan}/development_schedule.md | 0 docs/{ => plan}/future_todo.md | 0 docs/{ => plan}/members.md | 0 docs/{ => plan}/mqtt-implementation-plan.md | 0 docs/{ => plan}/rbac_architecture.md | 0 routes/api.php | 5 - 27 files changed, 40 insertions(+), 785 deletions(-) delete mode 100644 app/Http/Controllers/Api/V1/App/TransactionController.php delete mode 100644 app/Http/Controllers/Api/V1/MachineController.php delete mode 100644 app/Jobs/Machine/ProcessMachineLog.php delete mode 100644 app/Jobs/Transaction/ProcessTransaction.php delete mode 100644 docs/api/iot-spec.md delete mode 100644 docs/b017_technical_spec.md delete mode 100644 docs/b055_technical_spec.md delete mode 100644 docs/b220_technical_spec.md delete mode 100644 docs/b600_technical_spec.md delete mode 100644 docs/b601_technical_spec.md delete mode 100644 docs/b602_technical_spec.md delete mode 100644 docs/b650_technical_spec.md delete mode 100644 docs/b710_technical_spec.md rename docs/{ => plan}/api_analysis.md (100%) rename docs/{ => plan}/architecture_plan.md (100%) rename docs/{ => plan}/development_schedule.md (100%) rename docs/{ => plan}/future_todo.md (100%) rename docs/{ => plan}/members.md (100%) rename docs/{ => plan}/mqtt-implementation-plan.md (100%) rename docs/{ => plan}/rbac_architecture.md (100%) diff --git a/.agents/skills/api-technical-specs/SKILL.md b/.agents/skills/api-technical-specs/SKILL.md index e1e21f8..b7c6d74 100644 --- a/.agents/skills/api-technical-specs/SKILL.md +++ b/.agents/skills/api-technical-specs/SKILL.md @@ -153,6 +153,7 @@ description: 本技能規範定義了 Star Cloud 系統中所有機台 (IoT) 與 ### 3.12 B010: [DEPRECATED / REMOVED] 心跳上報與狀態同步 > [!WARNING] > **此 REST API 已廢棄並移除**。機台心跳上報與指令下發已全面遷移至 **MQTT** 架構。 +> (註:溫度數據已標準化為 **Integer**,且雲端採「變動即記錄」規則)。 > 詳細格式請參閱:[MQTT 通訊規範 - B010 心跳上報](file:///home/mama/projects/star-cloud/.agents/skills/mqtt-communication-specs/SKILL.md#31-心跳上報-b010) --- diff --git a/.agents/skills/mqtt-communication-specs/SKILL.md b/.agents/skills/mqtt-communication-specs/SKILL.md index b509886..a658a25 100644 --- a/.agents/skills/mqtt-communication-specs/SKILL.md +++ b/.agents/skills/mqtt-communication-specs/SKILL.md @@ -123,7 +123,8 @@ Mqtt3Client client = MqttClient.builder() ``` > [!NOTE] -> 為了降低傳輸開銷,目前僅要求上報 **韌體版本** 與 **溫度**。其餘狀態如連線/斷線已透過 LWT 與 $SYS 事件處理。 +> 為了降低傳輸開銷,目前僅要求上報 **韌體版本** 與 **溫度** (整數型態)。其餘狀態如連線/斷線已透過 LWT 與 $SYS 事件處理。 +> 雲端對於溫度的日誌記錄規則:**只要有變動 (current !== last) 即寫入日誌**,並維持每 4 小時一次的保底記錄。 ### 3.2 異常與錯誤上報 (B013) 當機台發生硬體故障(如卡貨、通訊中斷)時,發布至此 Topic。 @@ -158,10 +159,19 @@ Mqtt3Client client = MqttClient.builder() ```json { "action": "create", - "payment_type": "credit_card", - "amount": 25.0, - "slot_no": 3, - "product_id": "5" + "flow_id": "T202604140001", + "payment_type": 1, + "total_amount": 25.0, + "discount_amount": 0.0, + "pay_amount": 25.0, + "items": [ + { + "product_id": "5", + "product_name": "Coca Cola", + "price": 25.0, + "quantity": 1 + } + ] } ``` @@ -169,11 +179,12 @@ Mqtt3Client client = MqttClient.builder() ```json { "action": "invoice", + "flow_id": "T202604140001", "invoice_no": "AB12345678", - "transaction_ref": "T202604140001", "invoice_date": "2026-04-22", - "buyer_tax_id": "", - "amount": 25.0 + "amount": 25.0, + "random_no": "1234", + "love_code": "" } ``` @@ -181,10 +192,13 @@ Mqtt3Client client = MqttClient.builder() ```json { "action": "dispense", - "slot_no": 3, - "result": "success", - "transaction_ref": "T202604140001", - "remaining_stock": 9 + "flow_id": "T202604140001", + "slot_no": "3", + "product_id": "5", + "amount": 25.0, + "dispense_status": 1, + "remaining_stock": 9, + "machine_time": "2026-04-22 14:30:00" } ``` diff --git a/app/Http/Controllers/Api/V1/App/TransactionController.php b/app/Http/Controllers/Api/V1/App/TransactionController.php deleted file mode 100644 index 3da1dbd..0000000 --- a/app/Http/Controllers/Api/V1/App/TransactionController.php +++ /dev/null @@ -1,66 +0,0 @@ -get('machine'); - $data = $request->except(['machine', 'key']); // 排除 Middleware 注入物件 - $data['serial_no'] = $machine->serial_no; - - ProcessTransaction::dispatch($data); - - return response()->json([ - 'success' => true, - 'code' => 200, - 'message' => 'Accepted' - ], 202); - } - - /** - * B601: Record Invoice (Asynchronous) - */ - public function recordInvoice(Request $request) - { - $machine = $request->get('machine'); - $data = $request->except(['machine', 'key']); // 排除 Middleware 注入物件 - $data['serial_no'] = $machine->serial_no; - - ProcessInvoice::dispatch($data); - - return response()->json([ - 'success' => true, - 'code' => 200, - 'message' => 'Accepted' - ], 202); - } - - /** - * B602: Record Dispense Result (Asynchronous) - */ - public function recordDispense(Request $request) - { - $machine = $request->get('machine'); - $data = $request->all(); - $data['serial_no'] = $machine->serial_no; - - ProcessDispenseRecord::dispatch($data); - - return response()->json([ - 'success' => true, - 'code' => 200, - 'message' => 'Accepted' - ], 202); - } -} diff --git a/app/Http/Controllers/Api/V1/MachineController.php b/app/Http/Controllers/Api/V1/MachineController.php deleted file mode 100644 index 643fb37..0000000 --- a/app/Http/Controllers/Api/V1/MachineController.php +++ /dev/null @@ -1,39 +0,0 @@ -all(), [ - 'level' => 'required|string|in:info,warning,error', - 'message' => 'required|string', - 'context' => 'nullable|array', - ]); - - if ($validator->fails()) { - return $this->errorResponse('Validation error', 422, $validator->errors()); - } - - // 檢查機台是否存在 - if (!Machine::where('id', $id)->exists()) { - return $this->errorResponse('Machine not found', 404); - } - - // 丟入隊列進行異步處理,回傳 202 Accepted - ProcessMachineLog::dispatch($id, $request->only(['level', 'message', 'context'])); - - return $this->successResponse([], 'Log accepted. Processing asynchronously.', 202); - } -} diff --git a/app/Jobs/Machine/ProcessHeartbeat.php b/app/Jobs/Machine/ProcessHeartbeat.php index 60207ba..c966624 100644 --- a/app/Jobs/Machine/ProcessHeartbeat.php +++ b/app/Jobs/Machine/ProcessHeartbeat.php @@ -66,9 +66,10 @@ class ProcessHeartbeat implements ShouldQueue $newState['firmware_version'] = $fv; } - // 2. 溫度更新 (智慧過濾日誌) + // 2. 溫度更新 (只要有變動就紀錄日誌) if (isset($this->payload['temperature'])) { - $temp = $this->payload['temperature']; + $temp = (int) $this->payload['temperature']; + Log::debug("ProcessHeartbeat: Temperature reported for {$this->serialNo}: {$temp}"); $updateData['temperature'] = $temp; // 讀取最後一次記錄日誌的溫度與時間 @@ -79,14 +80,15 @@ class ProcessHeartbeat implements ShouldQueue if (!$lastLogEntry) { $shouldLog = true; } else { - $lastValue = (float) $lastLogEntry['value']; + $lastValue = (int) $lastLogEntry['value']; $lastAt = \Carbon\Carbon::parse($lastLogEntry['at']); - // 條件 A: 變動超過 3°C - if (abs($temp - $lastValue) >= 3.0) { + // 條件 A: 只要溫度與上次記錄不同就記錄 (整數比對) + if ($temp !== $lastValue) { + Log::debug("ProcessHeartbeat: Temperature changed from {$lastValue} to {$temp}. Triggering log."); $shouldLog = true; } - // 條件 B: 距離上次日誌超過 4 小時 + // 條件 B: 距離上次日誌超過 4 小時 (保底機制) if ($lastAt->diffInHours(now()) >= 4) { $shouldLog = true; } diff --git a/app/Jobs/Machine/ProcessMachineLog.php b/app/Jobs/Machine/ProcessMachineLog.php deleted file mode 100644 index b1905e8..0000000 --- a/app/Jobs/Machine/ProcessMachineLog.php +++ /dev/null @@ -1,46 +0,0 @@ -machineId = $machineId; - $this->logData = $logData; - } - - public function getMachineId(): int - { - return $this->machineId; - } - - public function handle(MachineService $service): void - { - try { - $service->recordLog($this->machineId, $this->logData); - } catch (\Exception $e) { - Log::error("Failed to process machine log for machine {$this->machineId}: " . $e->getMessage()); - } - } -} diff --git a/app/Jobs/Transaction/ProcessTransaction.php b/app/Jobs/Transaction/ProcessTransaction.php deleted file mode 100644 index 235e528..0000000 --- a/app/Jobs/Transaction/ProcessTransaction.php +++ /dev/null @@ -1,39 +0,0 @@ -data = $data; - } - - /** - * Execute the job. - */ - public function handle(TransactionService $transactionService): void - { - try { - $transactionService->processTransaction($this->data); - } catch (\Exception $e) { - Log::error("Failed to process transaction: " . $e->getMessage()); - throw $e; - } - } -} diff --git a/app/Models/Machine/Machine.php b/app/Models/Machine/Machine.php index 0b5b849..409b7c6 100644 --- a/app/Models/Machine/Machine.php +++ b/app/Models/Machine/Machine.php @@ -157,6 +157,7 @@ class Machine extends Model protected $casts = [ 'last_heartbeat_at' => 'datetime', + 'temperature' => 'integer', 'welcome_gift_enabled' => 'boolean', 'is_spring_slot_1_10' => 'boolean', 'is_spring_slot_11_20' => 'boolean', diff --git a/app/Services/Transaction/TransactionService.php b/app/Services/Transaction/TransactionService.php index 206fa70..28c7d00 100644 --- a/app/Services/Transaction/TransactionService.php +++ b/app/Services/Transaction/TransactionService.php @@ -112,6 +112,7 @@ class TransactionService 'slot_no' => $data['slot_no'] ?? 'unknown', 'product_id' => $data['product_id'] ?? null, 'amount' => $data['amount'] ?? 0, + 'remaining_stock' => $data['remaining_stock'] ?? null, 'dispense_status' => $data['dispense_status'] ?? 0, 'machine_time' => $data['machine_time'] ?? now(), ]); diff --git a/config/api-docs.php b/config/api-docs.php index 0ab9d0d..31b9196 100644 --- a/config/api-docs.php +++ b/config/api-docs.php @@ -427,13 +427,13 @@ return [ 'action' => 'PUB', 'topic' => 'machine/{serial_no}/heartbeat', 'qos' => 0, - 'description' => '機台每分鐘固定上報的狀態大包裝。整合了溫度、韌體版本與機台系統級日誌。', + 'description' => '機台每分鐘固定上報的狀態大包裝。整合了溫度與韌體版本。雲端記錄規則:溫度只要有變動 (current !== last) 即記錄日誌。', 'payload_parameters' => [ - 'temperature' => ['type' => 'number', 'description' => '目前機台溫度'], + 'temperature' => ['type' => 'integer', 'description' => '目前機台溫度'], 'firmware_version' => ['type' => 'string', 'description' => 'APP 韌體版本'], ], 'payload_example' => [ - 'temperature' => 20.5, + 'temperature' => 20, 'firmware_version' => '2.1.6' ], ], diff --git a/docs/api/iot-spec.md b/docs/api/iot-spec.md deleted file mode 100644 index aff8e22..0000000 --- a/docs/api/iot-spec.md +++ /dev/null @@ -1,108 +0,0 @@ -# IoT API 測試與對接文件 (IoT API Testing & Documentation) - -本文件紀錄 Star Cloud IoT API 的測試紀錄與對接規格,供後續開發與測試追蹤。 - ---- - -## 🔐 B000: 維運人員登入認證 (Technician Login) -機台引導階段 (Provisioning) 的第一步,用於核發臨時身份 Token 以便後續下載敏感設定。 - -### 1. API 資訊 -- **Endpoint**: `POST /api/v1/app/admin/login/B000` -- **認證方式**: 無 (需傳入 `username`, `password`, `machine`) -- **回應內容**: `token` (Sanctum Token) - -### 2. 回應範例 -```json -{ - "message": "Success", - "token": "3|abcdef1234567890..." -} -``` - ---- - -## 🔑 B014: 機台參數與金鑰下載 (Config Download) -下載機台運作所需的支付金鑰、電子發票設定與正式通訊 Token。 - -### 1. API 資訊 -- **Endpoint**: `POST /api/v1/app/machine/setting/B014` -- **認證方式**: **Bearer Token** (需帶上 B000 取得的 Token) -- **Header**: `Authorization: Bearer {token}` - -### 2. 請求參數 -- `machine`: 機台序號 (Serial No) - -### 3. 回應規格 (欄位映射) -| 欄位 | 說明 | 來源範例 | -| :--- | :--- | :--- | -| `t050v01` | 機台序號 | `SN2026041301` | -| `api_token` | **機台正式 Token** | 後續 B010/B600 認證用 | -| `t050v41` | 玉山特店編號 | `ESUN_STORE_ID` | -| `t050v43` | 玉山 Hash Key | `ESUN_HASH` | -| `t050v34` | 發票特店 ID | `INV_MID` | -| `TP_APP_ID` | 趨勢支付 AppID | `TP_APP_ID` | - -### 4. 回應範例 (JSON) -```json -{ - "success": true, - "code": 200, - "data": [ - { - "t050v01": "SN2026041301", - "api_token": "mac_token_...", - "t050v41": "8081234567", - "t050v42": "9001", - "t050v43": "password123", - "t050v34": "2000132", - "TP_APP_ID": "GREEN_001", - "...": "..." - } - ] -} -``` - ---- - - -## 🔵 B600: 交易紀錄上報 (Transaction Record) -當機台端完成交易(收款或扣點成功)後上傳。 - -### 1. API 資訊 -- **Endpoint**: `POST /api/v1/app/B600` -- **認證方式**: Bearer Token -- **處理方式**: 異步處理 (Redis Queue)。 - -### 2. 請求範例 (JSON) -```json -{ - "flow_id": "F123456789", - "total_amount": 100, - "pay_amount": 100, - "payment_type": 1, - "items": [ - { - "product_id": 1, - "product_name": "Test Product", - "price": 50, - "quantity": 2 - } - ] -} -``` - ---- - -## 📝 測試紀錄 (Test Logs) - -### 2026-03-16: B600 交易功能測試 -- **測試機台**: `SN001` -- **測試方式**: `curl` 命令模擬 -- **驗證項目**: - - [ ] HTTP 回傳 `202` - - [ ] 資料庫 `orders` 產生紀錄 - - [ ] 資料庫 `order_items` 產生紀錄 -- **結果**: 準備中... - - diff --git a/docs/b017_technical_spec.md b/docs/b017_technical_spec.md deleted file mode 100644 index 4586ada..0000000 --- a/docs/b017_technical_spec.md +++ /dev/null @@ -1,71 +0,0 @@ -# B017 API (遠端改庫存) 技術規範與執行指南 - -本文件記錄 B017 API 的業務邏輯與技術執行細節,用於指導後端管理系統遠端調整機台庫存的實作。 - ---- - -## 1. 業務情境與觸發機制 (Trigger) - -### 1.1 觸發流程 -1. **管理行為**:管理員在後台管理介面手動修改特定機台的貨道庫存數量。 -2. **指令預備**:後端系統將異動請求寫入 `remote_commands` 表 (type: `stock_update`, payload 含貨道編號與數量),並在該機台的 B010 (心跳) 回應中將 `status` 設為 `49` (reload B017)。 -3. **機台拉取**:機台收到心跳回應中的 `49` 代碼後,主動呼叫 B017 API (`/api/app/machine/reload_msg/{workid}`)。 -4. **數據同步**:機台獲取最新庫存數據並更新本地狀態。 - -### 1.2 更新範圍 -* **全量更新 (Full Sync)**:B017 API 回傳該機台**所有貨道**的最新庫存數量,而非僅回傳變動部分。這有助於機台軟體進行「最終狀態一致性」的自我校正。 - ---- - -## 2. 資料一致性與日誌 (Consistency & Audit) - -### 2.1 更新確認機制 -* **隱式成功 (Implicit Success)**:機台更新本地庫存後不需要回傳明確的 B017 確認 API。 -* **驗證方式**:系統將以機台下次呼叫 B010 回報的狀態或機台後續的正常銷售紀錄,視為前次更新成功。 - -### 2.2 管理員異動日誌 (Audit Log) -* **強制要求**:每次後台發起遠端改庫存操作,必須記錄於 `activity_logs` 或特定的 `stock_adjustment_logs`。 -* *紀錄內容*:管理者 ID、執行時間、機台 ID、調整前後的貨道庫存對照。 - -### 2.3 庫存衝突處理 -* *情境*:若雲端下發庫存更新時,機台現場同時發生實體銷售。 -* *處理原則*:**詳見下方待確認事項**。 - ---- - -## 3. 執行面優化 (Execution Strategy) - -### 3.1 Redis 緩衝與效能 -* **快取策略**:機台的貨道結構與庫存數據應快取於 Redis 中。當管理員調整庫存時,同步更新 Redis 與 MySQL。 -* **讀取優化**:B017 API 應直接從 Redis 讀取數據回傳給機台,以降低 MySQL 的查詢壓力。 - ---- - -## 4. 欄位定義與對照 - -| B017 欄位 | 資料庫對應 | 說明 | -|----------|------------|------| -| `channelid` | `machine_slots.slot_no` | 貨道編號 | -| `stock` | `machine_slots.stock` | 該貨道的最新庫存數量 | - ---- - -## 5. 開發建議 -* 建立一個專屬的 `StockService` 來處理 Redis 與 DB 的同步,以及異動日誌的寫入。 -* 在 `B010` 的回應邏輯中,檢查 `remote_commands` 是否有 pending 的庫存異動指令,以動態切換 `status: 49`。 - ---- - -## 6. 庫存衝突處理原則 - -**以機台實際出貨為最終依據(最終一致性策略)**。 - -* **情境**:雲端下發庫存更新(如設為 50),同時機台現場發生實體銷售(賣出 2 個)。 -* **處理原則**:機台在本地執行 `50 - 已出貨量 = 48`,並透過後續的 B602 出貨紀錄上報給雲端,雲端以此為最終庫存數字。 -* **理由**:機台掌握實際出貨資訊,強制以雲端覆蓋將導致帳面庫存與實際庫存不符,引發空貨道但系統顯示有庫存的嚴重問題。 - ---- - -## 7. 待確認事項 (已結案) - -所有關鍵決策已於 2026-03-12 完成確認。 diff --git a/docs/b055_technical_spec.md b/docs/b055_technical_spec.md deleted file mode 100644 index 184fd40..0000000 --- a/docs/b055_technical_spec.md +++ /dev/null @@ -1,67 +0,0 @@ -# B055 API (遠端出貨) 技術規範與執行指南 - -本文件定義 B055 API 的技術實作規範,用於處理從雲端發起的機台遠端出貨指令及其結果回饋。 - ---- - -## 1. 業務流程與通訊機制 - -### 1.1 指令下發流程 -1. **指令生成**:Cloud 後端(管理者)在 `remote_commands` 表中建立一筆類型為 `dispense`、狀態為 `pending` 的指令。 -2. **心跳通知**:在該機台的 B010 回應中,將 `status` 設為 `85` (reload B055)。 -3. **機台撈取 (POST)**:機台呼叫 B055 POST API (`/api/app/machine/dispense/{workid}`) 取得詳細出貨參數(貨道 ID、指令 ID)。 -4. **執行與回報 (PUT)**:機台嘗試出貨後,呼叫 B055 PUT API 回報出貨結果及剩餘庫存。 - -### 1.2 數據關聯 (Data Linking) -* **出貨紀錄整合**:遠端出貨成功後,系統必須在 `dispense_records` 表中同步存入一筆紀錄,並將 `source` 標記為 `remote`(或對應識別),確保後台報表包含所有實體出貨數據。 - ---- - -## 2. 指令資料定義:使用全域 `remote_commands` - -遠端出貨指令統一存放在 `remote_commands` 表中,B055 的特定參數將存放於 `payload` (JSON) 欄位。 - -| 欄位 | B055 對應說明 | 備註 | -|------|--------------|------| -| `id` | 執行命令 ID | 用於追蹤單次指令 (POST 回傳) | -| `payload->slot_no` | 貨道 ID | 指派機台出貨的通道 | -| `status` | 執行狀態 | pending/sent/success/failed | -| `ttl` | 指令效期 | 預設 60 秒 | - ---- - -## 3. 執行面優化 (Execution Strategy) - -* **Redis 隊列**:由於遠端出貨屬於高優先權操作,待執行指令應快取於 Redis 中,以確保機台呼叫 POST 時能即時回應。 -* **異步處理**:機台 PUT 回報結果後,系統透過隊列更新 DB 狀態,並根據結果觸發後續邏輯(如扣除庫存、異動日誌)。 - ---- - -## 4. 欄位定義與對照 - -| B055 欄位 | 資料庫對應 | 說明 | -|----------|------------|------| -| `command_id` | `remote_dispense_commands.id` | 指令唯一識別碼 | -| `slot_no` | `machine_slots.slot_no` | 目標貨道編號 | - ---- - -## 5. 業務邏輯細節建議 - -### 5.1 指令效期 (TTL) -* **建議設定**:**60 秒 (1 分鐘)**。 -* **理由**:由於遠端出貨指令由管理員手動發起(通常用於現場故障排除或客訴處理),若機台因網路問題超過一分鐘未拉取指令,該次操作的情境通常已失效,應自動失效以防止後續無預警出貨。 - -### 5.2 失敗處理 -* **機制**:機台回報出貨失敗時,系統僅需記錄結果並在後台顯示異常狀態。 -* **處理原則**:考量目前僅供管理員手動發起,**不需執行自動退款**,由管理員依據日誌進行後續手動處理即可。 - -### 5.3 安全與權限 -* **驗證機制**:維持 `api_token` 驗證,不需額外的加密簽名。 -* **權限控制**:建議在 RBAC 權限系統中,限制僅「高級管理員」或「維修主管」具備發起遠端出貨的權限。 - ---- - -## 6. 待確認事項 (已結案) - -所有關鍵決策已於 2026-03-12 完成確認。 diff --git a/docs/b220_technical_spec.md b/docs/b220_technical_spec.md deleted file mode 100644 index 1163227..0000000 --- a/docs/b220_technical_spec.md +++ /dev/null @@ -1,60 +0,0 @@ -# B220 API (零錢機庫存變動) 技術規範與執行指南 - -本文件定義 B220 API 的技術實作規範,用於監控機台內的硬幣與鈔票庫存數量,並提供補幣紀錄與找零水位告警功能。 - ---- - -## 1. 業務情境與數據特性 - -### 1.1 數據採集 -* **回報內容**:機台定期回報各面額(1, 5, 10, 50, 100, 500, 1000 元)的**當前累積總量 (Total Amount)**。 -* **操作識別**: - * `account = 0`:消費者交易引起的庫存變動。 - * `account != 0`:管理員或補貨員進行手動補幣或清空錢箱的操作。 - -### 1.2 報表與對帳 -* **零錢庫存分析**:系統必須保留所有變動紀錄,以提供後台「分析報表 > 零錢庫存分析」選單顯示歷史庫存走勢與補幣紀錄。 - ---- - -## 2. 執行面優化與告警 (Execution & Alerts) - -### 2.1 數據處理頻率 -* **回報時機**:機台在**每次交易完成後**(包含消費者找零或管理員補幣)即時呼叫 B220 API 回報最新庫存。 -* **快取策略**:機台各面額的最新總量應快取於 Redis 或更新至 `machines` 表的擴充欄位,供後台儀表板即時監看。 -* **寫入過濾**:為節省儲存空間,僅在資料數值有實際變動時才寫入 `coin_inventory_logs`。 - -### 2.2 找零不足告警 (Low Inventory Warning) - -當特定面額低於預設的水位閾值時,系統將觸發告警。 - -#### **建議水位告警計畫 (Threshold Plan)** -| 面額 | 告警閾值 (枚數) | 說明 | -|------|----------------|------| -| 1 元 | < 50 | 找零頻率高,需預留較快補幣空間 | -| 5 元 | < 40 | | -| 10 元| < 40 | 主要找零面額,需重點監控 | -| 50 元| < 20 | | -| 100 元以上 | — | 通常不作為找零面額,僅供庫存監控 | - -* **通知管道**: - 1. **後台儀表板**:在機台管理介面顯示顯眼的「紅字告警」。 - 2. **系統推播**:即時發送通知給相關營運或補貨人員。 - ---- - -## 3. 資料庫結構:`coin_inventory_logs` - -| 欄位 | 說明 | 備註 | -|------|------|------| -| `machine_id` | 機台關聯 ID | | -| `value_x` | 各面額數量 | 包含 1, 5, 10, 50, 100, 500, 1000 | -| `account` | 操作人識別 | 0 為消費者,其餘為管理員 | -| `created_at` | 紀錄時間 | | - ---- - -## 4. 待確認事項 (已結案) - -所有關鍵決策已於 2026-03-12 完成確認並由系統規劃水位計畫。 - diff --git a/docs/b600_technical_spec.md b/docs/b600_technical_spec.md deleted file mode 100644 index 6c7c1af..0000000 --- a/docs/b600_technical_spec.md +++ /dev/null @@ -1,62 +0,0 @@ -# B600 API (消費金流回傳) 技術規範與執行指南 - -本文件定義 B600 API 的技術實作規範,這是系統處理金流交易、訂單生成與跨 API 關聯的核心。 - ---- - -## 1. 交易生命週期與 `flow_id` 串聯 - -為確保一筆交易數據的完整性,系統採用 `flow_id` 串聯三個連續上報的 API: - -1. **B600 (金流上報)**: - * 機台於支付成功後即時呼叫。 - * 雲端收到後產出專屬的 `flow_id` 並回傳給機台。 -2. **B601 (發票上報)**: - * 機台開立發票後,攜帶 B600 取得的 `flow_id` 呼叫。 - * 系統根據 `flow_id` 將發票資訊與 `orders` 表關聯。 -3. **B602 (出貨上報)**: - * 機台實體出貨後,攜帶 `flow_id` 呼叫。 - * 系統根據 `flow_id` 更新訂單的出貨狀態。 - ---- - -## 2. 金流類型與標準化 - -### 2.1 類型定義 -根據 B600 PDF 文件規範 (Req3),系統優先採用以下預定義類型進行處理與對帳。 - -### 2.2 防重複上報 (Idempotency) -為防止網路不穩導致機台重複呼叫 B600,系統必須使用機台產生的 `order_no (tid)` 結合 `machine_id` 作為唯一鍵值校驗。 - ---- - -## 3. 執行面優化:異步隊列 (Async Queue) - -在高併發環境下,為確保機台端不因等待資料庫寫入而時耗,B600 採以下流程: - -1. **即時回應**:API 接收請求後,將原始 JSON 派發至 **Redis Queue**。 -2. **背景處理**:由 `ProcessTransaction` 任務異步執行: - * 建立 `orders` 與 `order_items`。 - * 計算並異動會員點數/優惠券。 - * 同步更新儀表板統計數據。 -3. **異步回傳**:機台呼叫 API 後會收到 `202 Accepted`,`flow_id` 則透過回應檔同步回傳。 - ---- - -## 4. 數據完整性與異常處理 (Integrity & Exceptions) - -### 4.1 自動對帳機制 (Auto-Reconciliation) -* **機制**:系統後台需建立定期定時任務(Scheduled Task),比對同一 `flow_id` 下的以下數據: - - **B600 (收取金額)**:訂單總計金額。 - - **B602 (出貨總價值)**:機台實際出貨商品之總價值。 -* **異常標記**:若兩者金額不符(例如:付了 $50 但僅出貨 $0 或 $30),系統應自動將該訂單標記為 **「金額異常」**,並同步產出警示通知供營運人員手動稽核。 - -### 4.2 出貨失敗處理 (Dispense Failure) -* **原則**:若 B602 回報「出貨失敗」,系統**不觸發自動退款**程序。 -* **處理流程**:系統僅記錄失敗狀態並維持訂單為「待處理/異常」,後續由客服或管理員介入,根據實際情況進行人工退款、補發指令或現場處理。 - ---- - -## 5. 待確認事項 (已結案) - -所有關鍵決策已於 2026-03-12 完成確認。 diff --git a/docs/b601_technical_spec.md b/docs/b601_technical_spec.md deleted file mode 100644 index 4a92b19..0000000 --- a/docs/b601_technical_spec.md +++ /dev/null @@ -1,49 +0,0 @@ -# B601 API (發票資訊回傳) 技術規範與執行指南 - -本文件定義 B601 API 的技術實作規範,用於紀錄每筆訂單對應的電子發票資訊。 - ---- - -## 1. 業務流程與角色定義 - -### 1.1 系統角色 -* **機台端**:負責與電子發票系統對接、開立發票,並於取得發票號碼後將資訊回傳至雲端。 -* **雲端 (Star Cloud)**:僅負責**紀錄 (Logging)** 發票資訊,不涉及上傳傳財政部平台或寄送中獎通知。 - -### 1.2 串聯機制 -* **關聯 ID**:必須攜帶 B600 取得的 `flow_id`,以便將發票資訊正確掛載至對應的 `orders`。 - ---- - -## 2. 資料庫結構:`invoices` - -為符合台灣發票規範,除了原始 PDF 欄位外,本系統將擴充相關稅務欄位: - -| 欄位 | 說明 | 備註 | -|------|------|------| -| `order_id` | 訂單關聯 ID | 透過 `flow_id` 對應 | -| `invoice_no` | 發票號碼 | 例:AB-12345678 | -| `invoice_date` | 發票日期 | | -| `random_number` | 隨機碼 | 4 位數 | -| `love_code` | 愛心碼 | 捐贈碼 | -| `business_tax_id` | **公司統編** | 8 位數 | -| `carrier_id` | **載具編號** | 手機條碼或自然人憑證 | -| `carrier_type` | **載具類型** | | -| `status` | **發票狀態** | 1:有效, 0:作廢 | - ---- - -## 3. 發票作廢機制 (Invoice Voiding) - -* **需求確認**:當交易發生退款或特定失敗情境時,機台端需具備回報「發票作廢」的能力。 -* **API 實作**: - * **方法一**:呼叫 B601 POST 時帶入 `status = 0`。 - * **方法二**:新增 `/api/app/B601/void` 專屬端點(待後續細化)。 - ---- - -## 4. 待確認事項 (已結案) - -所有關於發票關聯邏輯與重試機制的技術決策已於 2026-03-12 完成確認: -1. **發票缺失標記**:系統不需針對 B600 與 B601 的到達時差進行「發票缺失」自動標記。 -2. **重試機制**:機台端呼叫失敗時不需額外的緩存與重傳策略(維持基本回報邏輯)。 diff --git a/docs/b602_technical_spec.md b/docs/b602_technical_spec.md deleted file mode 100644 index e92bb4d..0000000 --- a/docs/b602_technical_spec.md +++ /dev/null @@ -1,57 +0,0 @@ -# B602 API (出貨回傳) 技術規範與執行指南 - -本文件定義 B602 API 的技術實作規範,用於紀錄機台實體出貨結果,並作為訂單完結與庫存校準的依據。 - ---- - -## 1. 業務邏輯與交易完結 - -### 1.1 串聯機制 -* **關聯 ID**:必須攜帶 B600 取得的 `flow_id`,以確保出貨紀錄正確鏈結至對應的 `orders`。 - -### 1.2 多品項交易處理 (Multi-item Orders) -* **一對多關係**:系統支持一筆訂單(同一個 `flow_id`)對應多筆 `dispense_records`。 -* **上報方式**:機台每完成一個貨道之出貨即呼叫一次 B602。雲端後端將自動彙整所有關聯之出貨紀錄以計算該訂單之總體狀態。 - ---- - -## 2. 庫存一致性機制 (Stock Consistency) - -為確保雲端與機台端的物理庫存同步,系統採以下策略: - -1. **強制信任機制**:系統將「強制信任」機台在 B602 中回報的 `remaining_stock` (絕對值),並直接更新資料庫中的貨道庫存。 -2. **異常監控**:若機台回報的庫存與系統計算的預期值(原值 - 1)不符時,系統應於後台標記該次出貨為「庫存異動異常」,供管理員檢查是否發生卡貨或漏貨。 - ---- - -## 3. 資料庫結構:`dispense_records` - -| 欄位 | 說明 | 備註 | -|------|------|------| -| `order_id` | 訂單 ID | 透過 `flow_id` 關聯 | -| `product_id` | 商品 ID | 出貨之商品 | -| `slot_no` | 貨道編號 | 出貨之貨道 | -| `dispense_status` | 出貨狀態 | 0: 成功, 1: 失敗 | -| `remaining_stock` | 剩餘庫存 | 機台回報之絕對值 | -| `source` | 來源標記 | `local` (B602), `remote` (B055) | - ---- - -## 4. 數據完整性與異常處理 (Integrity & Exceptions) - -### 4.1 出貨超時監控 (Dispense Timeout) -* **閾值建議**:若系統收到 B600 (金流) 超過 **5 分鐘** 仍未收到任何對應之 B602 (出貨回報),雲端後台應自動將該訂單標記為 **「出貨超時」**。 -* **用途**:此狀態主要用於輔助對帳,標記可能發生網路中斷或機台異常卡死之交易,需由管理員介入核實。 - -### 4.2 出貨失敗處理 (Dispense Failure) -* **狀態遷移**:當 B602 回報 `dispense_status = 1` (失敗) 時,訂單狀態應更新為 **「出貨失敗」**。 -* **即時通知**:系統應立即產出 **「後台警示通知」** 並推播給營運人員,以便快速處理現場排礙或後續補償。 -* **補償與維護策略 (現階段)**: - - **點數/優惠券**:目前不採自動退回機制,維持異常狀態由管理員人工處理。 - - **營運維護**:目前暫不自動產出維護工單或推播給補貨員,僅紀錄於異常日誌並顯示於後台通知區。 - ---- - -## 5. 待確認事項 (已結案) - -所有關於出貨流程與超時判定之技術決策已於 2026-03-12 完成確認。 diff --git a/docs/b650_technical_spec.md b/docs/b650_technical_spec.md deleted file mode 100644 index ba4518c..0000000 --- a/docs/b650_technical_spec.md +++ /dev/null @@ -1,46 +0,0 @@ -# [暫緩執行] B650 API (會員/點數/取貨碼驗證) 技術規範與執行指南 - -本文件定義 B650 API 的技術實作規範,用於處理會員驗證、點數折抵、優惠券兌換、取貨碼驗證以及線上訂單線下結帳等多元功能。 - ---- - -## 1. 業務功能與驗證類型 (req6) - -根據 API 請求中的 `req6` 欄位,B650 提供以下驗證模式: - -| req6 | 說明 | 核心流程 | -|------|------|----------| -| `0` | 點擊商品驗證 | 驗證會員資格與點數餘額 | -| `1` | 會員純驗證 | 僅確認會員身份是否有效 | -| `2` | 會員兌換商品清單 | 撈取該會員可使用的兌換券或贈品清單 | -| `3` | 線上訂單線下結帳 | 驗證取貨碼或訂單編號 | -| `4` | 會員贈品 | 處理行銷活動贈品領取 | - ---- - -## 2. 執行面機制 (Execution Strategy) - -* **高可用性請求**:由於會員驗證通常發生在交易前夕,API 必須具備極速響應能力,建議與第三方系統(若有)採用高效能通訊協議。 -* **Response 狀態碼校驗**:系統必須嚴格遵守 PDF 定義的狀態碼(如 `101` 全額折抵, `104` 線上訂單驗證成功, `30` 會員使用中等)進行前端反饋。 - ---- - -## 3. 待確認事項 (To-be-confirmed) - -以下議題目前皆列為**待確認**,將於 Phase 2 或後續討論中釐清: - -### 3.1 會員系統架構 (Architecture) -1. **資料主體**:Star Cloud 是直接管理會員資料庫,還是僅作為轉發請求至第三方 CRM 的代理伺服器? - -### 3.2 安全與防雙重消費 (Security) -2. **鎖定機制**:是否需要在 Redis 實作「會員操作 Session」,防止同一帳號在多台機台同時扣點 (狀態碼 30)? -3. **Token 時效**:回傳之 `userToken` 的有效期與刷新機制。 - -### 3.3 線上/線下整合流程 -4. **訂單轉換**:驗證取貨碼成功後,雲端是否應自動產出對應的銷售紀錄單? - -### 3.4 扣點邏輯 -5. **預扣 vs 實扣**:點數與優惠券應於 B650 驗證時預先鎖定,還是待 B602 出貨成功後才正式扣除? - -### 3.5 界面展示需求 -6. **商品資訊**:當 `req6 = 2` 時,回傳的產品清單是否需要包含圖片或詳細描述供機台屏幕顯示? diff --git a/docs/b710_technical_spec.md b/docs/b710_technical_spec.md deleted file mode 100644 index 6b3181a..0000000 --- a/docs/b710_technical_spec.md +++ /dev/null @@ -1,49 +0,0 @@ -# [暫緩執行] B710 API (計時器狀態回傳) 技術規範與執行指南 - -本文件定義 B710 API 的技術實作規範,用於處理計時型設備(如洗衣服務、按摩服務)的剩餘時間監測與狀態回報。 - ---- - -## 1. 業務流程與資料流 - -### 1.1 狀態回報 -* **回報內容**:機台定期回報特定貨道 (product/timer) 的 `remaining_seconds` (剩餘秒數) 與目前狀態。 -* **資料用途**: - * **即時監控**:供管理後台儀表板顯示設備的使用進度。 - * **稼動分析**:紀錄設備的總租用時數與閒置率。 - ---- - -## 2. 執行面機制 (Execution Strategy) - -* **異步處理**:由於計時狀態可能高頻回報,系統應優先將數據更新至 **Redis 快取**,以確保前端查詢的高效能與資料庫的負擔平衡。 - ---- - -## 3. 資料庫結構:`timer_statuses` - -| 欄位 | 說明 | 備註 | -|------|------|------| -| `machine_id` | 機台 ID | | -| `product_id` | 產品/服務 ID | | -| `remaining_seconds` | 剩餘秒數 | | -| `status` | 運行狀態 | 1:運行中, 0:停止 | - ---- - -## 4. 待確認事項 (To-be-confirmed) - -以下議題目前皆列為**待確認**,將於後續開發階段釐清: - -### 4.1 回報頻率與快取 -1. **更新頻率**:確認機台呼叫 B710 的精確頻率,以及資料庫寫入的節流策略(例如:僅在關鍵變動或每分鐘寫入一次)。 - -### 4.2 遠端通知與異常 -2. **結束通知**:倒數結束時,雲端是否需主動對對消費者發送推播或系統訊息? -3. **斷線判定**:針對長期未回報的計時器,雲端應自動判定為結束還是異常? - -### 4.3 復歸機制 -4. **斷電補償**:機台意外斷電重啟後,是否需向雲端 B010 撈回最後的剩餘秒數以恢復服務? - -### 4.4 報表需求 -5. **稼動分析**:後台是否需要針對此數據計算「設備利用率」等績效指標。 diff --git a/docs/api_analysis.md b/docs/plan/api_analysis.md similarity index 100% rename from docs/api_analysis.md rename to docs/plan/api_analysis.md diff --git a/docs/architecture_plan.md b/docs/plan/architecture_plan.md similarity index 100% rename from docs/architecture_plan.md rename to docs/plan/architecture_plan.md diff --git a/docs/development_schedule.md b/docs/plan/development_schedule.md similarity index 100% rename from docs/development_schedule.md rename to docs/plan/development_schedule.md diff --git a/docs/future_todo.md b/docs/plan/future_todo.md similarity index 100% rename from docs/future_todo.md rename to docs/plan/future_todo.md diff --git a/docs/members.md b/docs/plan/members.md similarity index 100% rename from docs/members.md rename to docs/plan/members.md diff --git a/docs/mqtt-implementation-plan.md b/docs/plan/mqtt-implementation-plan.md similarity index 100% rename from docs/mqtt-implementation-plan.md rename to docs/plan/mqtt-implementation-plan.md diff --git a/docs/rbac_architecture.md b/docs/plan/rbac_architecture.md similarity index 100% rename from docs/rbac_architecture.md rename to docs/plan/rbac_architecture.md diff --git a/routes/api.php b/routes/api.php index 26f3d83..32322c6 100644 --- a/routes/api.php +++ b/routes/api.php @@ -77,11 +77,6 @@ Route::prefix('v1')->middleware(['throttle:api'])->group(function () { // 交易 (B600) — B601/B602 已遷移至 MQTT transaction Topic Route::post('machine/restock/B018', [App\Http\Controllers\Api\V1\App\MachineController::class, 'recordRestock']); - Route::post('B600', [App\Http\Controllers\Api\V1\App\TransactionController::class, 'store']); - }); - - Route::prefix('machines')->group(function () { - Route::post('/{id}/logs', [\App\Http\Controllers\Api\V1\MachineController::class, 'storeLog']); }); });