前端 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/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 |
採購收貨:增加庫存(僅限「已下單」) |
cp .env.example .env # 填入自訂的 DB_PASSWORD
docker compose up --build # 啟動 MySQL(port 3307)+ API(port 8080)- 在 MySQL Workbench 確認本機 MySQL 已啟動(127.0.0.1:3306)。資料庫
ris會自動建立。 - 設定環境變數
DB_PASSWORD為你的 MySQL root 密碼。- Eclipse:Run Configurations → Environment;IntelliJ:Run/Debug Configurations → Environment variables
- 執行
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 跑測試