Skip to content

About

餐飲進銷存管理系統,React 19 + TypeScript 開發,整合 TanStack Query 與 n8n 資料自動化流程(開發中) 連鎖餐飲進銷存管理系統|React 19 + TypeScript 前端,多門市角色權限、JWT 登入、需求預測與叫貨規則,附線上 Demo(帳密 demo1234)

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

🍜 智慧餐飲進銷存管理系統

CI Deploy Demo

🔗 線上 Demo:https://atayallin.github.io/restaurant-inventory-system-React/(免安裝,打開即可操作)

儀表板畫面

一套為餐飲業打造的進銷存管理系統,整合 AI 自動化功能,協助店家掌握庫存、銷售與營運狀況。本專案為求職作品集,展示前端工程能力與 AI 自動化整合經驗。


📌 專案簡介

傳統餐飲業進銷存管理仰賴人工記帳與經驗判斷,容易發生庫存短缺、食材過期、營運數據不透明等問題。本系統透過網頁介面數位化管理商品、食材、採購與銷售流程,並串接 AI 服務,提供補貨建議與自然語言查詢功能,讓店家用口語問題就能快速掌握營運狀況。

GitHub:AtayalLin/restaurant-inventory-system-React


🛠 技術棧

分類 技術
前端框架 React 19 + TypeScript + Vite
資料狀態管理 TanStack Query v5
樣式 Tailwind CSS v4
後端 Spring Boot 3.5 + MySQL API(另一個 repo);開發期也可用 json-server,線上 Demo 用 MSW
測試 Vitest+MSW(單元/整合)、Playwright(端對端)
CI/CD GitHub Actions(lint、測試、型別檢查、建置、自動部署 GitHub Pages)
AI 自動化 n8n(Docker 自架)+ NVIDIA NIM
圖表 Recharts

✨ 核心功能

進銷存管理(CRUD)

  • 商品管理、食材庫存管理、採購進貨、銷售管理四大模組
  • Zod 表單驗證、Mock/真實 API 雙模式切換

儀表板

  • 今日/本週/本月營收統計、平均客單價
  • 低庫存清單、即期食材警告
  • 商品毛利率排行、近 7 天銷售趨勢圖
  • 30 秒自動輪詢更新

AI 商品文案生成

  • 串接 NVIDIA NIM,依商品資訊自動生成行銷描述文案(Streaming API)

AI 補貨建議(Agent A)

  • 依低庫存食材清單,透過 n8n workflow 呼叫 AI 生成補貨建議
  • 架構:Webhook → Code(組 prompt) → HTTP Request(呼叫 NVIDIA NIM) → Respond to Webhook

智慧查詢助理(Agent B)

  • 使用者可用自然語言提問(例如「本週營收多少」),系統回傳 AI 組織好的口語化回答
  • 架構決策:後端為 json-server(非 SQL),不適合走業界常見的 NL2SQL 路線,改採「意圖分類 + 語意層」架構(概念類似 Snowflake Cortex Analyst)——AI 只負責「判斷問題屬於哪種預定義意圖」與「將運算結果組織成自然語言」,實際數字運算交由寫死的 Code 節點邏輯處理,避免 AI 算錯數字
  • 目前支援意圖:
    • REVENUE_COMPARISON:本週/本月營收,與上期比較(含成長率計算)
    • TOP_SELLING_PRODUCTS:銷售排行榜(規劃中)
    • GENERAL_INVENTORY_QA:其他進銷存相關問答(規劃中)
    • UNKNOWN:無法歸類的問題,回覆固定引導文字
  • n8n workflow 節點:Webhook → Code(意圖判斷prompt) → HTTP Request(AI判斷意圖) → Code(解析意圖+撈資料運算+組回答prompt) → HTTP Request(AI生成回答) → Respond to Webhook

關鍵技術決策:n8n 節點間傳遞資料時,複雜的內嵌 {{ }} 表達式容易解析失敗(Fixed/Expression 模式切換不穩定),改用獨立 Code 節點先組好純文字 prompt,再由 HTTP Request 節點以 {{ JSON.stringify({...}) }} 整包表達式送出,大幅提升穩定性。


🚀 本機啟動方式

第一次啟動前:複製環境變數範本,填入自己的 NVIDIA API 金鑰

cp .env.example .env
# 編輯 .env,填入 NVIDIA_API_KEY=nvapi-...

🔒 金鑰不會進入前端:NVIDIA_API_KEY 沒有 VITE_ 前綴,只由 vite.config.ts 的 proxy 在開發伺服器端加到請求標頭,前端程式碼與 npm run build 的打包檔都不會出現金鑰。 API 與 n8n 網址統一在 src/lib/config.ts 管理,可用 .env 覆寫,未來換成 Spring Boot 後端只需修改 API_PROXY_TARGET。

需要同時開啟三個終端機:

# 終端機 1:前端開發伺服器
npm run dev          # http://localhost:5173

# 終端機 2:json-server mock 後端
npm run server        # http://localhost:3001

# 終端機 3:n8n 自動化服務(Docker Desktop 需先手動開啟)
docker start n8n       # http://localhost:5678

環境注意事項:

  • n8n 容器內部呼叫宿主機 json-server,必須使用 http://host.docker.internal:3001,不能用 localhost:3001(容器內部的 localhost 指向自己)
  • n8n 容器為永久容器(未加 --rm),workflow JSON 備份存放於 git 專案目錄外的 React-Frontendproject/n8n-workflows/,避免明文金鑰被 commit

🌐 線上 Demo 的做法

線上 Demo 部署在 GitHub Pages,那裡只能放靜態檔案,沒有 json-server、n8n,也不能放 API 金鑰。所以 Demo 版用 MSW(Mock Service Worker) 在瀏覽器裡攔截請求、模擬後端:

  • 前端程式碼不用為了 Demo 修改:hooks 照樣呼叫 /api/products、n8n webhook 與 /nvidia-api,由 Service Worker 在請求送出前接手回應(src/mocks/handlers.ts)。
  • 資料以今天為基準產生:src/mocks/seed.ts 動態產生近 60 天的訂單,任何時候打開,「今日營收」「近 7 天」都有數字。用固定種子的亂數,同一天看到的資料一致。
  • 操作會保留:新增商品、出餐扣庫存等變更存在訪客自己瀏覽器的 localStorage,重新整理後仍在;頁首可「重設資料」。
  • AI 功能改為範例回應:查詢助理的數字一樣由程式從資料計算(與正式版「程式算數字、AI 只負責講話」的設計相同),補貨建議與菜單文案則用範本產生,並在回答中標示為 Demo 範例。
  • 一般版本不含模擬程式碼:只有 vite build --mode demo 才會載入 src/mocks。
npm run dev:demo     # 本機用 Demo 模式開發(不需要 json-server 與 n8n)
npm run build:demo   # 建置 Demo 版到 dist/

部署:push 到 main 後,.github/workflows/deploy-demo.yml 會自動建置並部署。第一次使用前,到 repo 的 Settings → Pages → Build and deployment → Source 選「GitHub Actions」。

CI:每次 push 與 Pull Request,.github/workflows/ci.yml 會跑 ESLint、單元測試、TypeScript 型別檢查、一般版與 Demo 版建置,以及 Playwright 端對端測試。

🧪 測試

npm test            # 單元/整合測試(Vitest)
npm run test:watch  # 開發時監看模式
npm run test:e2e    # 端對端測試(Playwright,會自動建置並啟動 Demo 版)

測試直接沿用 Demo 的 MSW 模擬後端(src/test/setup.ts),不需要啟動 json-server,涵蓋:

  • 金額計算:營業稅 5% 無條件捨去、總計、成長率(上期為 0 時不顯示百分比)
  • 出餐扣庫存:依配方扣減、同食材跨商品合併計算、庫存不足時全部不扣
  • 樂觀鎖:採購收貨增加庫存並遞增版本號、扣成負數時拒絕
  • 登入:後端登入、json-server 退回本機帳號、請求自動帶 token、401 通知登出、403 不重試
  • 角色權限:三種角色的權限矩陣、門市篩選(舊資料歸預設門市)、跨店營收只算期間內已完成訂單
  • 叫貨規則:最低訂購量、供應商不符、重複食材、叫貨日提醒、由預估產生採購草稿
  • 需求預測:只取過去 4 個同星期的已完成訂單、資料不足時以實際週數平均、已下架商品排除、叫貨量進位
  • 查詢助理:意圖判斷、排行榜排序、補貨建議數量

測試固定「現在時間」,結果不會隨日期改變。

端對端測試(e2e/):用 Playwright 開真的瀏覽器操作 Demo 版,驗證使用者實際會走的流程:

  • 未登入導向登入頁、錯誤密碼提示、員工看不到也進不去採購頁、總部切換門市與登出
  • 店長點餐 → 備餐 → 出餐,確認牛腱肉庫存依配方減少 200g;折扣超過應付金額無法建立
  • 總部從明日預估一鍵叫貨、低於最低訂購量被擋下、建立成功、開啟列印版面

CI 另外跑一個 e2e 工作;失敗時會上傳含截圖與操作紀錄的報告。


👥 多門市與角色權限

參考連鎖餐飲「總部統一菜單、中央廚房統一進貨、門市負責銷售」的分工。登入頁提供三個示範帳號(密碼皆為 demo1234),點一下即可體驗不同角色看到的畫面。

功能 總部 owner 店長 manager(左營店) 員工 staff(左營店)
切換門市/全部門市、跨店營收比較 ✅ — —
菜單商品新增、編輯 ✅ 唯讀 唯讀
食材庫存編輯 ✅ ✅ 唯讀
採購進貨 ✅ 可操作 唯讀 無法進入
點餐、備餐、出餐 ✅ ✅ ✅
退款 ✅ ✅ —
  • 權限規則集中在 src/lib/permissions.ts(純函式,有單元測試),頁面用 useSession().can('...') 判斷。
  • 路由守衛 RequirePermission:直接輸入網址也會被擋下,不只是隱藏側欄選項。
  • JWT 登入:連到 Spring Boot 後端時,登入取得 JWT,所有 /api 請求由 src/lib/auth.ts 的 apiFetch 自動帶上 token;token 過期(401)自動登出回登入頁。前端權限只負責介面顯示,真正擋下請求的是後端(沒有權限回 403)。
  • 連到 json-server(沒有登入 API)時,改用本機示範帳號登入,方便前端單獨開發。
  • 出餐、收貨呼叫後端 POST /salesOrders/{id}/complete、/purchaseOrders/{id}/receive,由後端在同一筆交易內完成;連到 json-server(沒有這些端點)時自動改用前端逐步處理(src/lib/orderActions.ts)。
  • 銷售訂單屬於門市(storeId);食材為中央廚房統一庫存,所以「明日預估」用全部門市的銷售來推估。

🧾 叫貨規則

參考 iCHEF 門市訂貨管理的做法,規則集中在 src/lib/orderRules.ts(純函式,有單元測試):

規則 處理方式
低於食材的最低訂購量 阻擋送出,欄位下方顯示最低量;後端同樣檢查(422 BELOW_MIN_ORDER),繞過前端也擋得住
食材不是這家供應商供應 選單只列出該供應商的食材;送出時再檢查一次
同一食材重複多列 阻擋,請合併成一列
今天不是供應商的叫貨日 只提醒、不阻擋,並告知下次叫貨日(臨時追加叫貨在實務上很常見)
  • 明日預估 → 一鍵叫貨:儀表板「明日預估」依供應商分組,產生採購單草稿(已補到最低訂購量、帶入單位成本),總部點一下即可建立。
  • 列印採購單:/purchase/:id/print 以 A4 版面呈現供應商資訊、品項、合計與簽核欄,可直接列印或存成 PDF。

⚡ 效能優化

以 Lighthouse 12(預設的行動裝置模擬)量測線上 Demo 的登入頁,優化前後比較:

指標 優化前 優化後
Lighthouse 效能 86 94
Lighthouse 無障礙 94 100
First Contentful Paint 2.8 s 2.2 s
Largest Contentful Paint 3.4 s 2.7 s
Total Blocking Time 130 ms 50 ms
登入頁下載的 JS(Demo 版,含模擬後端) 1,259 KB 723 KB
一般版主程式包 848 KB 290 KB

做法:

  • 路由延遲載入:各頁面改用 React.lazy 拆成獨立檔案(src/routes/pages.ts),登入頁不再下載儀表板、圖表等用不到的程式。
  • 圖表獨立載入:營收趨勢圖用到的 Recharts 約占儀表板程式的九成,改為延遲載入;統計卡先顯示,圖表隨後補上。
  • 空閒時預先下載:登入後利用 requestIdleCallback 預先下載常用頁面,切換頁面時通常已下載完成,不會看到載入畫面。
  • 移除未使用的套件:mammoth、pdfjs-dist、react-markdown、axios 沒有被使用,從依賴中移除。
  • 對比度:次要文字色從 #6B7480 調為 #5E6672,在白底與頁面灰底都達到 WCAG AA(≥ 4.5:1)。

開發規劃樹狀圖

圖例:✅ 已完成 🔄 開發中/待驗證 🔲 待辦

智慧餐飲進銷存管理系統
│
├── 核心進銷存模組 ✅
│   ├── 商品管理
│   ├── 食材庫存
│   ├── 採購進貨
│   └── 銷售管理  
│
├── Dashboard 儀表板 ✅
│   ├── 營收統計 / 近7天趨勢
│   ├── 低庫存清單
│   ├── 即期食材警告
│   ├── 明日預估:同星期平均銷量 → 依配方換算食材用量 → 建議叫貨量
│   └── 商品毛利率排行
│
├── AI 自動化功能(n8n + NVIDIA NIM)
│   ├── Agent A:AI 補貨建議 ✅
│   │
│   └── Agent B:智慧查詢助理
│       ├── 架構:Tool-Calling 語意層
│       │   (AI 選工具 → 固定邏輯運算 → AI 組回答)
│       │
│       ├── REVENUE_COMPARISON ✅
│       ├── TOP_SELLING_PRODUCTS 🔄(驗證中)
│       ├── UNKNOWN ✅
│       ├── GENERAL_INVENTORY_QA 🔲
│       │   ├── getInventoryStatus 🔲
│       │   └── getProductSales 🔲
│       │
│       ├── 進階 KPI 工具(業界標準指標)
│       │   ├── getFoodCostPercentage 🔲
│       │   ├── getABCAnalysis 🔲
│       │   └── getInventoryTurnover 🔲(待 stockLogs 資料累積)
│       │
│       └── Tool-Calling 架構遷移 🔲
│           (由 Intent 分類 + if/else 改為治理過的工具選擇)
│
├── 資料完整性 ✅
│   └── stockLogs 寫入邏輯
│       ├── 銷售完成 → 依 recipe 扣減 ingredient.currentStock
│       └── 採購到貨 → 加回 ingredient.currentStock
│
├── 工程品質
│   ├── 共用 UI 元件與設計 token ✅
│   ├── GitHub Actions CI(lint、型別檢查、建置)✅
│   ├── 線上 Demo(MSW + GitHub Pages)✅
│   ├── Vitest 單元/整合測試(已加入 CI)✅
│   ├── Playwright 端對端測試(已加入 CI)✅
│   └── 效能優化:路由延遲載入、Lighthouse 效能 94/無障礙 100 ✅
│
└── 對標業界的下一階段(依序進行)
    ├── ① Vitest 單元測試並加入 CI ✅
    ├── ② 需求預測+叫貨建議 ✅
    │   (近 4 週同星期平均銷量 → 依配方換算食材用量 → AI 只負責解釋)
    ├── ③ 多門市切換+角色權限(總部/店長/員工)✅
    ├── ④ 叫貨規則:最低訂購量、供應商叫貨日、明日預估一鍵叫貨、列印採購單 ✅
    └── ⑤ Spring Boot + MySQL 後端、JWT 登入與權限 ✅

業界需求對照(2026/10 調查)

開發方向參考台灣大型餐飲集團與餐飲 POS 業者的公開資料,確認專案功能對應真實需求,而不是只做 CRUD 練習。

業界需求 業界案例 本專案對應
成本毛利管理、供需協同 王品與鼎新數智建置新一代資訊核心平台,聚焦供需協同、門店營運、成本毛利管理,為多品牌擴張打基礎 ✅ 配方扣庫存、商品毛利率排行
總部多門市管理 iCHEF 總部系統:跨店菜單統一、跨店業績與商品排行、跨店會員,規劃 API 對接 ERP ✅ ③ 門市切換、跨店營收比較、總部統一菜單、角色權限
門市叫貨流程 iCHEF 門市訂貨管理:依供應商規則訂貨、缺貨自動阻擋、產生撿貨出貨單 ✅ ④ 最低訂購量阻擋(前後端)、叫貨日提醒、依預估一鍵產生採購單、列印採購單
AI 預測客流與銷售 iCHEF 與 Google 等推出連鎖 AI 助手「春嬌」,整合 POS、會員、庫存資料預測客流與銷售 ✅ ② 明日預估(同星期平均)+叫貨建議
AI 報表解讀 iCHEF AI 報表解讀讓每週分析從約 2 小時縮短到 5 分鐘 ✅ 自然語言查詢助理(程式算數字、AI 組句)
AI 中央廚房 高雄「大南方新矽谷」AI 中央廚房補助:產線智慧化、數位食譜 ⚪ 暫不實作,列為延伸方向

設計呼應:「春嬌」的使用者回饋指出,整合後的分析比自行把資料丟給語言模型處理更穩定,與本專案 Agent B「數字由程式計算、AI 只負責說明」的設計方向一致。

🎨 設計系統

色彩 Hex 用途
主色(深藍) #1E3A5F Sidebar、標題
強調色 #1E40AF 主要按鈕、連結
成功色 #16A34A 庫存正常 Badge
警告色 #D97706 庫存不足 Badge
危險色 #DC2626 刪除按鈕、錯誤訊息
背景色 #F8FAFC 頁面背景

n8n Webhook 測試方式

這個 workflow 有兩種測試模式,網址不同、使用方式也不同,不要混用。

模式一:測試模式(Test Mode)— 用於單步除錯

適合情境:想在 n8n 編輯器裡即時看到每個節點的輸入/輸出資料,方便除錯。

步驟:

  1. 開啟 workflow,點擊 Webhook 節點
  2. 點擊橘色按鈕 「Listen for test event」
  3. 畫面會顯示:Listening for test event Make a POST request to: http://localhost:5678/webhook-test/query-assistant
  4. 在終端機執行(注意路徑是 webhook-test):
   curl -X POST http://localhost:5678/webhook-test/query-assistant \
     -H "Content-Type: application/json; charset=utf-8" \
     --data-binary @payload.json
  1. 送出後,編輯器會自動跳轉並顯示執行結果,可以逐一點開每個節點查看資料流

注意事項:

  • 測試模式用一次就會失效,每次測試前都要重新點「Listen for test event」
  • 只適合開發階段除錯用,不適合長時間對外提供服務

模式二:正式模式(Production Mode)— 用於實際呼叫

適合情境:workflow 已經開發完成,要讓外部系統(例如前端)呼叫。

步驟:

  1. 開啟 workflow,右上角點擊 「Publish」 讓 workflow 進入 Published 狀態
  2. 不需要點開任何節點、不需要等待監聽畫面
  3. 直接在終端機執行(注意路徑是 webhook,沒有 -test):
   curl -X POST http://localhost:5678/webhook/query-assistant \
     -H "Content-Type: application/json; charset=utf-8" \
     --data-binary @payload.json
  1. 這條路徑會持續監聽,可以無限次呼叫,不需要每次重新啟動

注意事項:

  • 修改任何節點內容後,必須重新按一次 Publish,正式路徑才會套用新版本
  • 忘記重新 Publish 的話,webhook 路徑跑的還是修改前的舊邏輯

常見錯誤對照

症狀 原因
節點卡在「Executing previous nodes...」轉圈不動 打了正式網址(webhook),但編輯器還停在「Listening for test event」等測試事件;或反過來打了測試網址但沒點「Listen for test event」
Received request for unknown webhook 呼叫的路徑跟目前監聽的模式(test / production)對不上
改了程式碼但結果沒變 忘記重新 Publish

📝 開發紀錄

已解決的技術問題

問題 原因 解法
API 回傳 304 + 空 body 瀏覽器快取標頭 If-None-Match 加入 Cache-Control: no-cache
TanStack Query 無限重試 res.json() 解析空 body 拋出錯誤 統一使用 fetchJSON helper
Zod v4 + z.coerce.number() 型別錯誤 v4 型別推導改變 改用 z.number() + valueAsNumber
NVIDIA API Key 洩漏風險 .env / workflow 備份檔可能被 commit .gitignore 排除、workflow 備份存放於 git 專案目錄外
Vite Proxy 未生效 設定後未重啟 Vite 修改 vite.config.ts 後需重啟
json-server 併發寫入崩潰 Promise.all 平行寫入衝突 改用 for...of 序列寫入
n8n HTTP Request Header 錯誤 Name/Value 欄位對調 確認 Authorization / Content-Type 各自獨立填入正確欄位
n8n 表達式 {{ }} 未被解析,AI 收到字面文字 複雜運算式塞在 Fixed 模式 JSON 字串中不穩定 新增 Code 節點預先組好 prompt,HTTP Request 改用 Expression 模式 {{ JSON.stringify({...}) }}
Docker 容器重啟後 n8n 工作流程消失 Windows + Git Bash 下 ~/.n8n volume 路徑未正確持久化 不使用 --rm、養成手動 Export workflow JSON 備份至 git 專案目錄外的習慣
n8n 容器連 json-server 連線被拒 容器內 localhost 指向容器自身,非宿主機 改用 host.docker.internal:3001
NVIDIA NIM 70b 模型頻繁 504 逾時 免費層級對大模型限制較嚴格 改用 meta/llama-3.1-8b-instruct,穩定性明顯提升
前端瀏覽器分頁與 n8n 失去即時連線,節點執行卡住無回應 WebSocket 連線在容器重啟後未正確恢復 關閉分頁重新連線,嚴重時重啟整個瀏覽器;同時為 n8n 的 HTTP Request 節點加上 timeout 設定,避免卡住無限等待
小型 AI 模型在 UNKNOWN 分類下偶爾不遵循固定回覆指令 llama-3.1-8b 對簡短指令的遵循度較低 明確要求「請用繁體中文回覆」並提供具體範例句子,降低模型自由發揮的機率

📍 開發進度快照(供接續開發參考)

最後更新:2026-07-13

已完成並驗證:

  • 進銷存 CRUD 四大模組
  • AI 商品文案生成
  • Dashboard 儀表板(含 isError 處理、效能優化)
  • Agent A:AI 補貨建議(已上線)
  • Agent B:REVENUE_COMPARISON 意圖,六節點完整串接(Webhook → 意圖分類 → AI判斷 → 資料運算 → AI生成回答 → 回傳),已整合進 Dashboard.tsx 前端 UI 並測試成功

下次接續開發的下一步:

  1. 新增 GENERAL_INVENTORY_QA 意圖的資料撈取 Code 節點(撈取 products + ingredients 摘要,作為 AI 回答的上下文依據)
  2. 實作 TOP_SELLING_PRODUCTS 意圖(統計 salesOrders 中各商品銷售數量並排序)
  3. 將查詢助理 UI 從 Dashboard 內嵌小卡片,視時間評估是否獨立成專屬頁面/聊天框元件
  4. 技術債:API 端點目前寫死在各 hook 檔案,應改用 .env 環境變數管理(已完成:集中於 src/lib/config.ts)

👨‍💻 開發者

林家齊 Ataya Lin


📄 授權

MIT License

About

餐飲進銷存管理系統,React 19 + TypeScript 開發,整合 TanStack Query 與 n8n 資料自動化流程(開發中) 連鎖餐飲進銷存管理系統|React 19 + TypeScript 前端,多門市角色權限、JWT 登入、需求預測與叫貨規則,附線上 Demo(帳密 demo1234)

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages