Skip to content

About

連鎖餐飲進銷存系統(Spring Boot 3.5 + MySQL 後端)|Spring Security JWT、交易扣庫存+樂觀鎖、ProblemDetail 錯誤格式|JUnit 測試+CI|Docker Compose 一鍵啟動

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

5 Commits

Folders and files

Repository files navigation

🍜 智慧餐飲進銷存管理系統 — 後端 API

CI

前端 React 專案 的 REST API,取代開發期使用的 json-server。

分類 技術
框架 Spring Boot 3.5、Java 21
資料庫 MySQL 8(測試使用 H2 記憶體資料庫)
資料存取 Spring Data JPA/Hibernate
驗證 Spring Security、JWT(OAuth2 Resource Server,HS256)
測試 JUnit 5、MockMvc、AssertJ
部署 Docker、Docker Compose、GitHub Actions CI

設計重點

  • 與前端零改動對接:網址與行為對齊 json-server(GET/POST/PUT/PATCH/DELETE /api/{資源}、字串 id、PATCH 只更新有傳的欄位、找不到回 404),前端只要改一行環境變數就能從 json-server 換過來。通用邏輯集中在 CrudController,每個資源只要宣告網址。
  • 出餐扣庫存放在後端交易中:POST /api/salesOrders/{id}/complete 用 @Transactional 包成一筆交易。依配方計算用量 → 先檢查所有食材 → 逐一扣減並寫異動紀錄;任何一步失敗就整筆還原,不會「扣了一半」。
  • 樂觀鎖防止超賣:扣庫存使用條件更新 UPDATE … WHERE id = ? AND version = ?。兩台收銀機同時出餐時,只有一邊會成功,另一邊得到 409 STOCK_CONFLICT,由資料庫保證一致性。
  • 叫貨規則在後端把關:建立或修改採購單時,任一品項低於食材的最低訂購量就回 422 BELOW_MIN_ORDER(CrudController.validate() 讓各資源加入自己的業務規則)。供應商可設定叫貨日(orderDays)。
  • 標準錯誤格式:使用 Problem Details(RFC 9457),例如 {"status":422,"title":"INSUFFICIENT_STOCK","detail":"庫存不足,無法完成訂單:麵條"}。
  • JWT 登入與角色權限:POST /api/auth/login 回傳有效 8 小時的 JWT,之後每個請求帶 Authorization: Bearer <token>。後端依角色擋下沒有權限的請求(前端只負責隱藏按鈕,真正的防線在後端)。密碼以 BCrypt 雜湊儲存;簽章金鑰從環境變數讀取,沒設定時啟動時隨機產生,金鑰不會出現在程式碼裡。
  • 密碼不進版控:連線資訊全部由環境變數提供,.env 已列入 .gitignore。

登入與權限

第一次啟動會建立三個示範帳號(密碼預設 demo1234,可用 DEMO_PASSWORD 改):

帳號 角色 可以做的事
owner 總部 全部功能
manager 店長・左營店 編輯食材、查看採購單、點餐出餐退款
staff 員工・左營店 點餐、出餐;其他唯讀,無法查看採購單
規則 總部 店長 員工
讀取一般資料 ✅ ✅ ✅
菜單商品新增/修改/刪除 ✅ 403 403
食材新增/修改/刪除 ✅ ✅ 403
查看採購單 ✅ ✅ 403
建立/修改採購單、收貨 ✅ 403 403
未登入 401 401 401

API 一覽

資源 網址
門市 /api/stores
商品分類 /api/productCategories
菜單商品(含配方) /api/products
食材 /api/ingredients
供應商 /api/suppliers
採購單 /api/purchaseOrders
銷售訂單 /api/salesOrders
庫存異動紀錄 /api/stockLogs
業務動作 說明
POST /api/auth/login 登入,回傳 JWT 與使用者資料
GET /api/auth/me 依 token 取得目前使用者
POST /api/salesOrders/{id}/complete 出餐完成:依配方扣庫存(僅限「準備中」)
POST /api/purchaseOrders/{id}/receive 採購收貨:增加庫存(僅限「已下單」)

啟動方式

方式一:Docker Compose(最簡單,不需要安裝 Java 與 MySQL)

cp .env.example .env        # 填入自訂的 DB_PASSWORD
docker compose up --build   # 啟動 MySQL(port 3307)+ API(port 8080)

方式二:IDE+本機 MySQL(開發用)

  1. 在 MySQL Workbench 確認本機 MySQL 已啟動(127.0.0.1:3306)。資料庫 ris 會自動建立。
  2. 設定環境變數 DB_PASSWORD 為你的 MySQL root 密碼。
    • Eclipse:Run Configurations → Environment;IntelliJ:Run/Debug Configurations → Environment variables
  3. 執行 RisApplication。第一次啟動會自動匯入 seed/db.json 範例資料(與前端同一份)。

Eclipse 使用者:本專案使用 Lombok,請先執行一次 java -jar lombok.jar 把 Lombok 裝進 Eclipse,否則 IDE 會顯示找不到 getter/setter(Maven 建置不受影響)。

連接前端

前端專案的 .env 加入:

API_PROXY_TARGET=http://localhost:8080/api

重新執行 npm run dev,前端的 /api/* 請求就會轉到這個後端。

測試

mvn verify

測試使用 H2 記憶體資料庫,不需要 MySQL。涵蓋:

  • CRUD 行為與 json-server 一致(自動產生 id、PATCH 部分更新、404、重複 id 回 409)
  • 出餐依配方扣庫存、寫入異動紀錄、版本號遞增
  • 任一食材不足時整筆不扣(交易還原)
  • 非「準備中」訂單不能出餐
  • 樂觀鎖:版本號不符時不更新
  • 採購收貨增加庫存
  • 採購單低於最低訂購量回 422,達到即可建立
  • API 錯誤回應格式(422 INSUFFICIENT_STOCK)
  • 登入成功/失敗、未登入 401、竄改 token 無效、各角色的 403 權限規則

後續規劃

  • 門市資料隔離:店長、員工只能讀寫自己門市的訂單(目前由前端篩選)
  • Flyway 管理資料表版本(目前開發階段使用 ddl-auto: update)
  • 前端「出餐完成」改呼叫後端 /complete 端點,取代前端逐筆呼叫
  • Testcontainers:CI 改用真的 MySQL 跑測試

About

連鎖餐飲進銷存系統(Spring Boot 3.5 + MySQL 後端)|Spring Security JWT、交易扣庫存+樂觀鎖、ProblemDetail 錯誤格式|JUnit 測試+CI|Docker Compose 一鍵啟動

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages