Files
star-cloud/docs/architecture_plan.md
sky121113 fc79148879 [FEAT] 實作角色權限分類、租戶角控管理與介面多語系優化
1. [FEAT] 權限劃分為「系統層級」與「客戶層級」,並在後端強制過濾跨權限分配。
2. [FEAT] 整合選單權限至主選單層級 (基本設定、權限設定),簡化角色管理 UI。
3. [STYLE] 側邊欄優化:補齊多語系翻譯,並為基本設定子選單增加視覺圖示。
4. [REFACTOR] 更新 RoleSeeder,將 tenant-admin 重新分類為客戶層級角色。
2026-03-17 16:53:28 +08:00

27 KiB
Raw Permalink Blame History

Star Cloud — 智能販賣機管理平台技術架構規劃

目標:為上千至上萬台智能販賣機建構集中式管理後台,承接所有機台透過 API 回寫的資料,並提供完整的營運管理介面。


一、系統架構全景

                        ┌──────────────────────────────────────────────┐
                        │            Star Cloud 管理後台                │
                        │     (Laravel 12 + Blade + Tailwind)          │
                        │  ┌──────────┐  ┌──────────┐  ┌───────────┐  │
                        │  │ 儀表板    │  │ 機台管理  │  │ 銷售分析   │  │
                        │  │ Dashboard │  │ Machines  │  │ Analytics  │  │
                        │  └──────────┘  └──────────┘  └───────────┘  │
                        │  ┌──────────┐  ┌──────────┐  ┌───────────┐  │
                        │  │ 倉庫管理  │  │ 會員系統  │  │ 遠端操控   │  │
                        │  │Warehouses │  │ Members   │  │  Remote    │  │
                        │  └──────────┘  └──────────┘  └───────────┘  │
                        └──────────┬───────────────┬───────────────────┘
                                   │  Web Routes   │
                                   │  (Blade SSR)  │
                        ┌──────────┴───────────────┴───────────────────┐
                        │              Laravel Application              │
                        │                                               │
                        │  Controllers → Services → Models → MySQL     │
                        │                                               │
                        └──────┬────────────────────┬──────────────────┘
                               │                    │
                    ┌──────────┴────┐       ┌───────┴───────┐
                    │  API Routes   │       │  Redis Queue  │
                    │  /api/v1/app  │       │  (異步處理)    │
                    └──────┬────────┘       └───────┬───────┘
                           │                        │
                    ┌──────┴────────┐       ┌───────┴───────┐
                    │ API Controller│──────►│   Jobs        │
                    │ (驗證 + 分派) │ dispatch│ (Worker 執行) │
                    └───────────────┘       └───────┬───────┘
                           ▲                        │
          ┌────────────────┤                        ▼
          │                │                 ┌──────────────┐
    ┌─────┴─────┐    ┌────┴─────┐           │   Services   │
    │ 販賣機 #1  │    │販賣機 #N │           │  (業務邏輯)   │
    │  B010      │    │  B600    │           └──────┬───────┘
    │  B600      │    │  B602    │                  │
    │  B602      │    │  ...     │                  ▼
    └────────────┘    └──────────┘           ┌──────────────┐
                                             │    MySQL     │
                                             │  (持久化)    │
                                             └──────────────┘

二、技術棧確認

層級 技術 說明
後端框架 PHP 8.5 / Laravel 12 單體架構Blade SSR
資料庫 MySQL 8.0 資料持久化
快取/隊列 Redis B010 心跳、B600 金流等高頻 API 必須異步
前端視圖 Blade + Tailwind CSS + Preline UI 極簡奢華風
前端互動 Alpine.js 輕量 DOM 互動
建置工具 Vite 前端資源編譯
開發環境 Laravel Sail (Docker) 本地開發
IoT 通訊 Redis Queue + Jobs 異步寫入,避免 API 直連 DB
認證 Laravel Sanctum 後台用 SessionAPI 用 Token

三、多租戶與權限架構 (Multi-Tenant RBAC)

為支援系統管理員(內部營運)與租戶(客戶公司及子帳號)的雙層管理模式,平台採用以下三層級權限隔離架構:

3.1 租戶隔離層 (Tenant Isolation)

  • 核心機制:導入 companies 資料表作為租戶實體,所有歸屬於客戶的資源(users, machines 等)均需綁定 company_id
  • 身份判定
    • 系統管理員 (內部)company_id = null,具備跨租戶檢視與管理機台、設定全局參數的最高權限。
    • 客戶端帳號 (租戶)company_id 有值,所有查詢(透過 Eloquent Global Scopes 或 Middleware自動注入 where('company_id', $user->company_id),達成資料物理隔離。

3.2 角色與權限層 (Roles & Permissions)

  • 採用 spatie/laravel-permission 套件並進行多租戶改造。
  • 多租戶角色 (Tenant-Aware Roles):擴充 roles 表,加入 company_id 欄位。
    • 系統預設角色 (company_id = null):如 Super Admin, System Support。
    • 客戶自建角色 (company_id = N):如客戶建立的「分區維修員」、「會計」,確保客戶只能看見與分配屬於自己公司的角色給員工。
  • 權限 (Permissions):定義系統最小操作單位(如 machine.view, user.create),供角色綁定。

3.3 流程控制與防禦 (Flow Control)

  • 介面隔離:依據身份動態隱藏/顯示系統級選單(例如「全站設定」、「合約管理」等多租戶不可見的功能)。
  • 越權防禦:客戶創建子帳號時,後端必定強制攔截並覆寫 user->company_id = Auth::user()->company_id,確保子帳號牢牢綁在自己的公司名下。同時限制其只能撈與分配自己公司名下的 Role。

四、資料庫完整設計

4.1 資料表總覽

分為 6 大領域,共 28 張表

graph LR
    subgraph 系統管理
        A1[users] --> A2[sessions]
        A1 --> A3[personal_access_tokens]
        A4[failed_jobs]
    end
    
    subgraph 機台領域
        B1[machines] --> B2[machine_slots]
        B1 --> B3[machine_logs]
        B1 --> B4[coin_inventories]
        B1 --> B5[timer_statuses]
        B1 --> B6[remote_commands]
        B1 --> B7[machine_heartbeats]
    end
    
    subgraph 商品領域
        C1[products]
        C2[product_categories]
    end
    
    subgraph 交易領域
        D1[orders] --> D2[order_items]
        D1 --> D3[invoices]
        D1 --> D4[dispense_records]
        D5[payment_types]
    end
    
    subgraph 會員領域
        E1[members] --> E2[social_accounts]
        E1 --> E3[member_wallets]
        E3 --> E4[wallet_transactions]
        E1 --> E5[member_points]
        E5 --> E6[point_transactions]
        E7[point_rules]
        E8[deposit_bonus_rules]
        E1 --> E9[membership_tiers]
        E1 --> E10[member_memberships]
        E1 --> E11[gift_definitions]
        E1 --> E12[member_gifts]
    end
    
    subgraph APP設定
        F1[app_configs]
    end

4.2 需新增的資料表Migration 設計)

以下為 API 分析後推導出的缺失表,搭配各 API 欄位產生對應。


👑 多租戶與權限基礎表 (RBAC & Tenant)

🆕 companies — 租戶/客戶主表

所有客戶端的資料邊界,用來實現資料物理隔離。

欄位 類型 說明
id BIGINT PK
name VARCHAR(255) 公司/組織名稱
tax_id VARCHAR(20) NULL 統一編號
contact_name VARCHAR(100) NULL 聯絡人姓名
contact_phone VARCHAR(50) NULL 聯絡電話
status TINYINT DEFAULT 1 狀態 (1:啟用, 0:停用)
valid_until DATE NULL 合約使用期限
timestamps
deleted_at TIMESTAMP NULL 軟刪除 (SoftDeletes)
🟡 users 表 — 需擴充欄位

將 Laravel 預設的使用者表與租戶綁定。

新增欄位 類型 說明
company_id BIGINT FK NULL companies (NULL 代表系統管理員)
status TINYINT DEFAULT 1 帳號狀態 (1:啟用, 0:停用)
🟡 roles 表 (Spatie 權限擴充)

Spatie 預設的 roles 表必須加上多租戶設計,確保留戶只能管理自己的角色。

新增/調整欄位 類型 說明
company_id BIGINT FK NULL companies (NULL 代表系統全域角色)
UNIQUE (name, guard_name, company_id) 原套件無 company_id,需修改為複合唯一鍵

機台設定與參數模型 (Configuration & Parameters)

為了提升機台的可維護性與金流設定的靈活性,系統引入了以下結構:

1. 機台型號設定表 (machine_models)

記錄系統支援的機台型號,供機台綁定基礎參數。

欄位 類型 說明
id BIGINT PK
name VARCHAR(255) 型號名稱 (例如: "B010-STD")
company_id BIGINT FK 所屬公司 (支援多租戶隔離)
creator_id / updater_id BIGINT FK 審計記錄 (建立者/修改者)
timestamps

2. 金流參數組合表 (payment_configs)

範本化設計:由公司建立支付參數組合範本,機台端選擇套用。

欄位 類型 說明
id BIGINT PK
company_id BIGINT FK 所屬公司
name VARCHAR(255) 組合名稱 (例如: "玉山+綠界組合A")
settings JSON 儲存各支付平台 API Key (Ecpay, Esun, Tappay, LinePay 等)
creator_id / updater_id BIGINT FK 審計記錄
timestamps

3. 機台主表擴充 (machines)

針對 machines 表擴充以下欄位以支援 IoT 通訊與進階營運設定:

類別 欄位 類型 說明
基礎識別 serial_no VARCHAR 機台序號 (API 唯一識別碼)
狀態監控 current_page VARCHAR 機台當前畫面停留頁面
door_status VARCHAR 門禁狀態 (open/closed)
temperature DECIMAL 機器溫度 (來自硬體回傳)
金流設定 payment_config_id BIGINT FK 關聯金流參數樣本 (payment_configs)
card_reader_seconds INT 刷卡機秒數 (預設 30)
card_reader_checkout_time_1 / 2 TIME 卡機結帳清機時間
payment_buffer_seconds INT 金流回傳緩衝時間 (預設 5)
硬體與營運 machine_model_id BIGINT FK 關聯機台型號 (machine_models)
heating_start / end_time TIME 加熱自動排程開啟與關閉時間
card_reader_no / key_no VARCHAR 刷卡機編號與鑰匙編號
invoice_status TINYINT 發票狀態 (0:不開, 1:預設捐, 2:預設不捐)
welcome_gift_enabled BOOLEAN 來店禮開關
member_system_enabled BOOLEAN 會員系統開關
is_spring_slot_1_10 ~ 60 BOOLEAN 貨道類型標記 (0=履帶, 1=彈簧)
審計權限 company_id BIGINT FK 關聯所屬公司 (租戶隔離)
creator_id / updater_id BIGINT FK 建立者與最後修改者 ID

🆕 products — 商品資料

欄位 類型 說明
id BIGINT PK
name VARCHAR(255) 商品名稱 (預設語系)
name_dictionary_key VARCHAR(100) NULL 多語系字典鍵值 (對應 translations 表)
sku VARCHAR(100) UNIQUE 商品編號
price DECIMAL(10,2) 售價
cost DECIMAL(10,2) NULL 成本
category_id BIGINT FK NULL → product_categories
image VARCHAR NULL 圖片 URL
barcode VARCHAR NULL 條碼
is_timer_product BOOLEAN 是否為計時型商品
is_active BOOLEAN 是否上架
timestamps

🌐 多語系支援表 (i18n)

為了支援後台介面與 APP 端顯示的多語系內容(如商品名稱、分類名稱、系統提示),統一採用字典表架構。

🆕 translations — 多語系字典表
欄位 類型 說明
id BIGINT PK
group VARCHAR(50) 分組 (例如: product, category, system)
key VARCHAR(100) 字典鍵值 (例如: prod_name_001)
locale VARCHAR(10) 語系代碼 (例如: zh_TW, en_US, ja_JP)
value TEXT 翻譯內容
timestamps
UNIQUE (group, key, locale) 確保同一組鍵值在同一語系下唯一

多語系實作策略

  1. 靜態文案:後台介面的靜態文字直接使用 Laravel 內建的 lang/ 目錄 (JSON 或 PHP 陣列)。
  2. 動態資料:資料庫內容(如商品名稱 products.name)保留預設語系供後台快速搜尋。同時新增 name_dictionary_key。當對接外部 APP 或前台需要多語系時,透過 API 關聯 translations 表,回傳當前語系對應的 value

🆕 machine_slots — 機台貨道

欄位 類型 說明 來源
id BIGINT PK
machine_id BIGINT FK → machines B017, B055, B710
slot_no VARCHAR(20) 貨道編號 B017 tid, B710 cid
product_id BIGINT FK NULL → products B710 pid
stock INT DEFAULT 0 當前庫存 B017 num
max_stock INT DEFAULT 0 最大容量
is_active BOOLEAN 是否啟用
timestamps
UNIQUE (machine_id, slot_no) 複合唯一鍵

🆕 orders — 訂單主表

欄位 類型 說明 來源
id BIGINT PK
flow_id VARCHAR UNIQUE Cloud 金流 ID B600 response
order_no VARCHAR APP 訂單號 B600 req9
machine_id BIGINT FK → machines B600 req2
member_id BIGINT FK NULL → members B650 關聯
payment_type SMALLINT 金流類型碼 B600 req3
total_amount DECIMAL(10,2) 消費金額 B600 req7
change_amount DECIMAL(10,2) 找零 B600 req13
points_used INT DEFAULT 0 使用點數 B600 req14
original_amount DECIMAL(10,2) NULL 折扣前金額 B602 req15
payment_status TINYINT 0:失敗/1:成功 B600 req12
payment_request TEXT NULL 金流送出 data B600 req4
payment_response TEXT NULL 金流回傳 data B600 req5
member_barcode VARCHAR NULL 會員條碼 B600 req10
invoice_info VARCHAR NULL 發票歸戶 B600 req8
machine_time DATETIME NULL 機台時間 B600 req11
timestamps
INDEX (machine_id, created_at) 查詢最佳化
INDEX (payment_type) 金流類型篩選

🆕 order_items — 訂單明細

欄位 類型 說明 來源
id BIGINT PK
order_id BIGINT FK → orders
product_id BIGINT FK → products B600 req16.pid
quantity INT 數量 B600 req16.num
unit_price DECIMAL(10,2) 單價 B600 req16.amount
subtotal DECIMAL(10,2) 小計 計算
timestamps

🆕 invoices — 發票紀錄

欄位 類型 說明 來源
id BIGINT PK
order_id BIGINT FK → ordersvia flow_id
flow_id VARCHAR 金流 ID B601 req3
machine_id BIGINT FK → machines B601 req2
rtn_code VARCHAR NULL 回傳碼 B601 req4
rtn_msg VARCHAR NULL 回傳訊息 B601 req5
invoice_no VARCHAR NULL 發票號碼 B601 req6
invoice_date DATE NULL 發票日期 B601 req7
random_number VARCHAR NULL 隨機碼 B601 req8
love_code VARCHAR NULL 愛心碼 B601 req9
timestamps

🆕 dispense_records — 出貨紀錄

欄位 類型 說明 來源
id BIGINT PK
order_id BIGINT FK NULL → orders
flow_id VARCHAR NULL 金流 ID B602 req3
machine_id BIGINT FK → machines B602 req2
product_id VARCHAR 商品 ID B602 req5
slot_no VARCHAR 貨道編號 B602 req6
amount DECIMAL(10,2) 消費金額 B602 req7
remaining_stock INT NULL 剩餘庫存 B602 req9
dispense_status TINYINT 0:成功/1:失敗 B602 req13
member_barcode VARCHAR NULL 會員條碼 B602 req10
machine_time DATETIME NULL 機台時間 B602 req11
points_used INT DEFAULT 0 使用點數 B602 req16
timestamps

🆕 remote_commands — 遠端指令佇列

欄位 類型 說明 來源
id BIGINT PK
machine_id BIGINT FK → machines
command_type VARCHAR(50) 指令類型 (reboot, lock, stock_update, dispense...) B010, B017, B055
status ENUM pending/sent/success/failed
payload JSON NULL 指令參數 (如: {"slot_no": "A01", "num": 50})
ttl INT DEFAULT 60 指令失效秒數 (尤其針對遠端出貨)
executed_at TIMESTAMP NULL 執行時間
timestamps

對應 B010 遠端指令reboot, lock, unlock, checkout, dispense 等)以及 B055 遠端出貨。


🆕 coin_inventories — 零錢機庫存

欄位 類型 說明 來源
id BIGINT PK
machine_id BIGINT FK → machines B220 machine
value_1 INT DEFAULT 0 1 元 B220
value_5 INT DEFAULT 0 5 元 B220
value_10 INT DEFAULT 0 10 元 B220
value_50 INT DEFAULT 0 50 元 B220
value_100 INT DEFAULT 0 100 元 B220
value_500 INT DEFAULT 0 500 元 B220
value_1000 INT DEFAULT 0 1000 元 B220
operator VARCHAR NULL 操作人 (0=消費者) B220 account
timestamps

🆕 timer_statuses — 計時器狀態

欄位 類型 說明 來源
id BIGINT PK
machine_id BIGINT FK → machines B710 req2
slot_no VARCHAR 貨道 ID B710 cid
product_id BIGINT FK NULL → products (0=未設定) B710 pid
status TINYINT 0:未啟用/1:使用中/2:異常 B710 status
remaining_seconds INT 剩餘秒數 B710 num
timestamps

🆕 payment_types — 金流類型參照

欄位 類型 說明
id BIGINT PK
code SMALLINT UNIQUE 金流代碼 (1,2,3...120)
name VARCHAR 中文名稱
category VARCHAR 大分類
is_active BOOLEAN 是否啟用
timestamps

五、IoT API 路由設計

5.1 路由結構

// routes/api.php
Route::prefix('v1/app')->middleware(['throttle:iot'])->group(function () {
    
    // B010 - 機台狀態上傳 & 指令撈回
    Route::post('/machine/status', [MachineStatusController::class, 'heartbeat']);
    
    // B017 - 遠端撈庫存
    Route::post('/machine/reload', [MachineStockController::class, 'reload']);
    
    // B055 - 遠端出貨
    Route::post('/machine/dispense', [DispenseController::class, 'getCommands']);
    Route::put('/machine/dispense', [DispenseController::class, 'updateStatus']);
    
    // B220 - 零錢機庫存
    Route::post('/coin/inventory', [CoinInventoryController::class, 'store']);
    
    // B600 - 消費金流
    Route::post('/transaction', [TransactionController::class, 'store']);
    
    // B601 - 發票回傳
    Route::post('/invoice', [InvoiceController::class, 'store']);
    
    // B602 - 出貨回傳
    Route::post('/dispense-record', [DispenseRecordController::class, 'store']);
    
    // B650 - 驗證會員
    Route::post('/member/verify', [MemberVerifyController::class, 'verify']);
    
    // B710 - 計時器狀態
    Route::post('/timer/status', [TimerStatusController::class, 'sync']);
});

5.2 處理管線

Important

遵循 IoT 通訊技能規範:嚴禁 API Controller 直接寫入 DB。

API 處理方式 原因
B010 異步 (Redis Queue) 最高頻,每台數秒一次
B017 同步 (直接回應) 低頻,需即時回傳庫存數據
B055 GET 同步 需即時回傳指令列表
B055 PUT 異步 出貨結果可背景處理
B220 異步 庫存變動日誌可背景寫入
B600 異步 高頻交易,核心數據
B601 異步 發票資訊可背景寫入
B602 異步 出貨紀錄可背景寫入
B650 同步 需即時回傳驗證結果與折抵金額
B710 異步 計時器狀態批量更新

六、後台模組 ↔ 資料表對應

後台模組web.php 對應資料表 資料來源 API
1. 儀表板 Dashboard orders, machines, dispense_records 統計彙整
2. 會員管理 Members members, social_accounts, member_wallets... B650
3. 機台管理 Machines machines, machine_logs, machine_slots B010, B017
4. APP 管理 App app_configs, timer_statuses B710
5. 倉庫管理 Warehouses products, machine_slots B017, B602
6. 銷售管理 Sales orders, order_items, dispense_records B600, B602
7. 分析管理 Analysis orders, dispense_records, coin_inventories 全部
8. 稽核管理 Audit orders, dispense_records B600, B602
9. 資料設定 DataConfig products, payment_types, users
10. 遠端管理 Remote remote_commands B010, B055
11. Line 管理 members (Line 綁定)
12. 預約系統 Reservation (未來擴充)
13. 特殊權限 SpecialPerm machines, app_configs
14. 權限設定 Permission users, roles

七、效能預估與應對

10,000 台機台的流量預估

API 頻率 每秒請求量 (QPS) 每日資料量
B010 心跳 每 5 秒/台 ~2,000 ~173M 筆
B600 金流 每筆交易 ~10-50 ~50K-200K 筆
B602 出貨 每筆交易 ~10-50 ~50K-200K 筆
B710 計時器 每 10 秒/台 ~1,000 ~86M 筆

應對策略

策略 實作方式
Redis Queue 異步寫入 所有高頻 API 進 QueueWorker 批量寫入
心跳資料瘦身 B010 不逐筆寫 DB僅更新 machines 表的即時欄位 + 異常時寫 log
資料分區 ordersdispense_records 按月份分區
索引策略 所有查詢欄位建立適當 INDEXmachine_id + created_at 複合索引)
速率限制 單台機台 60 req/min全站 100,000 req/min
快取 機台列表、商品列表等低變動資料用 Redis Cache

八、實作路線圖與預估時程

Note

為了保持規劃書的簡潔,詳細的開發時程、甘特圖、子選單拆解與 Phase 任務清單已獨立至專屬文件。

👉 查看詳細開發時程規劃 (development_schedule.md)

本專案預計總開發時程為 17 ~ 18 週 (約 85 個工作天),分為三個主要階段:

  1. Phase 1 (5天):基礎建設與 IoT API 串接。
  2. Phase 2 (50天):後台核心營運管理介面 (含遠端與倉庫管理)。
  3. Phase 3 (30天):進階分析報表與垂直功能模組。

九、目錄結構建議

app/
├── Http/Controllers/
│   ├── Admin/              ← 後台管理(已存在 14 模組)
│   └── Api/V1/
│       ├── App/            ← 機台 IoT API新增
│       │   ├── MachineStatusController.php   (B010)
│       │   ├── MachineStockController.php    (B017)
│       │   ├── DispenseController.php        (B055)
│       │   ├── CoinInventoryController.php   (B220)
│       │   ├── TransactionController.php     (B600)
│       │   ├── InvoiceController.php         (B601)
│       │   ├── DispenseRecordController.php  (B602)
│       │   ├── MemberVerifyController.php    (B650)
│       │   └── TimerStatusController.php     (B710)
│       ├── MemberController.php              ← 會員 API已存在
│       └── MachineController.php             ← 機台日誌(已存在)
├── Jobs/
│   └── Machine/            ← IoT 異步任務(新增)
│       ├── ProcessHeartbeat.php
│       ├── ProcessTransaction.php
│       ├── ProcessDispenseRecord.php
│       ├── ProcessInvoice.php
│       ├── ProcessCoinInventory.php
│       └── ProcessTimerStatus.php
├── Services/
│   └── Machine/            ← 業務邏輯(新增)
│       ├── HeartbeatService.php
│       ├── TransactionService.php
│       ├── DispenseService.php
│       └── MemberVerifyService.php
└── Models/
    ├── Machine/            ← 已存在
    │   ├── Machine.php
    │   └── MachineLog.php
    ├── Transaction/        ← 新增
    │   ├── Order.php
    │   ├── OrderItem.php
    │   ├── Invoice.php
    │   ├── DispenseRecord.php
    │   └── PaymentType.php
    ├── Product/            ← 新增
    │   ├── Product.php
    │   └── ProductCategory.php
    └── Member/             ← 已存在