---
title: 軟體設計文件(SDD)
---
# 軟體設計文件(SDD)
## 專案資訊
- **專案名稱**:早一點
- **撰寫日期**:2025/11/24
- **發展者**:
01157128 蔡翊宣
01257116 康慈軒
01257126 呂亭儀
01257153 鄭尹宣
01257161 李永聖
---
## 版次變更記錄
| 版次 | 變更項目 | 變更日期 |
|------|----------|----------|
| 0.1 |初版 |2025/11/24|
| 0.2 |使用者畫面設計 |2025/11/25|
| 0.3 |新增C4 Model圖|2025/12/23|
| 1.0 |最終版 |2026/01/03|
---
## 目錄
1. [系統模型與架構 (System Model / System Architecture)](#section1)
2. [介面需求與設計 (Interface Requirement and Design)](#section2)
3. [流程設計 (Process Design)](#section3)
4. [使用者畫面設計 (User Interface Design)](#section4)
5. [資料設計 (Data Design)](#section5)
6. [類別圖設計 (Class Diagram)](#section6)
7. [實作方案 (Implementation Languages and Platforms)](#section7)
8. [設計議題 (Design Issue)](#section8)
---
## <span id="section1">1. 系統模型與架構 (System Model / System Architecture)</span>



---
## <span id="section2">2. 介面需求與設計 (Interface Requirement and Design)</span>
- Store
| 介面名稱 | 介面提供者 | 介面使用者 | 連結方式 | 輸入資料 | 輸出資料 | 介面描述 |
|---------|------------|------------|----------|----------|----------|-----------|
| 創建新店家 | Backend - StoreController | Frontend - StoreSettingView | HTTP POST /api/stores | Header (Authorization), JSON (Store 物件) | JSON (Store 物件) | 驗證使用者 Token,將新的店家資訊寫入資料庫,並將當前使用者設為擁有者 Owner。 |
| 獲取所有店家 | Backend - StoreController | Frontend - StoreListView | HTTP GET /api/stores | 無 | JSON (List<Store>) | 從資料庫中檢索並回傳所有已註冊且狀態為 Active 的店家列表。 |
| 獲取店家詳情 | Backend - StoreController | Frontend - StoreDetailView | HTTP GET /api/stores/{storeId} | Path (storeId) | JSON (Store 物件) | 根據提供的 Store ID 查詢並回傳該店家的詳細資訊(包含菜單)。 |
| 更新店家菜單 | Backend - StoreController | Frontend - StoreManagementView | HTTP PUT /api/stores/{id}/menu | Path (id), Header (Authorization), JSON (List<MenuItem>) | JSON (Store 物件) 或 錯誤訊息 | 驗證店家擁有者權限,更新指定店家的菜單項目(若項目無 ID 則自動產生 ObjectId)。 |
| 搜尋店內商品 | Backend - StoreController | Frontend - StoreDetailView | HTTP GET /api/stores/{storeId}/search-product | Path (storeId), Query Param (keyword) | JSON (List<MenuItem>) | 在特定店家根據關鍵字搜尋菜單項目,回傳名稱包含關鍵字的商品列表。 |
---
- User
| 介面名稱 | 介面提供者 | 介面使用者 | 連結方式 | 輸入資料 | 輸出資料 | 介面描述|
| ------- | -------- | --------- | ---- | ----- | ---- | ----- |
| 使用者註冊 | Backend - UserController | Frontend - RegisterView | HTTP POST | JSON (account, password, role, email) | JSON (User 物件) | 接收使用者資訊,檢查帳號是否重複,若無重複就寫入資料庫 |
| 使用者登入 | Backend - UserController | Frontend - LoginView | HTTP POST | JSON (account, password) | JSON (User 物件) | 接收使用者帳號密碼,若與資料庫資料相同就登入 |
| 回傳使用者資訊 | Backend - UserController | Frontend - HomeView | HTTP GET | URL Path Variable: userId | JSON (User 物件) | 根據網址中的 userId 回傳該使用者的資訊 |
| 修改使用者資訊 | Backend - UserController | Frontend - HomeView | HTTP PATCH | URL Path Variable: userId + JSON(password, nickname, photo, phone, email) | JSON (User 物件) | 根據網址中的 userId 和 傳入的 JSON 資料來修改該使用者資訊 |
| 刪除使用者 | Backend - UserController | Frontend - HomeView | HTTP DELETE | URL Path Variable: userId | String(使用者 ID: userId 已成功刪除) | 根據網址中的 userId 來刪除該使用者 |
| 將店家加入收藏 | Backend - UserController | Frontend - ShopView or FavoriteView | HTTP POST | URL Path Variable: userId, storeId | JSON (User 物件) | 根據網址中的 userId 和 storeId ,將店家加入該使用者的收藏 |
| 將店家移除收藏 | Backend - UserController | Frontend - ShopView or FavoriteView | HTTP DELETE | URL Path Variable: userId, storeId | JSON (User 物件) | 根據網址中的 userId 和 storeId ,將店家移除該使用者的收藏 |
| 回傳收藏店家列表 | Backend - UserController | Frontend - FavoriteView | HTTP GET | URL Path Variable: userId | JSON (List<String\>) | 根據網址中的 userId ,回傳該使用者的收藏店家之 storeId |
| 將商品加入收藏 | Backend - UserController | Frontend - ShopView or FavoriteView | HTTP POST | URL Path Variable: userId, storeId, itemId | JSON (User 物件) | 根據網址中的 userId 、 storeId 和 itemId ,將商品加入該使用者的收藏 |
| 將商品移除收藏 | Backend - UserController | Frontend - ShopView or FavoriteView | HTTP DELETE | URL Path Variable: userId, storeId, itemId | JSON (User 物件) | 根據網址中的 userId 、 storeId 和 itemId ,將商品移除該使用者的收藏 |
| 回傳收藏商品列表 | Backend - UserController | Frontend - ShopView or FavoriteView | HTTP GET | URL Path Variable: userId, storeId, itemId | JSON (List<FavorItem 物件>) | 根據網址中的 userId ,回傳該使用者的收藏商品(FavorItem 物件) |
| 新增自訂義組合 | Backend - UserController | Frontend - ShopView or FavoriteView | HTTP POST | URL Path Variable: userId + JSON(comboName) | JSON (User 物件) | 根據網址中的 userId ,為該使用者的新增名稱為 comboName 的自訂義組合(內容為空) |
| 修改自訂義組合名稱 | Backend - UserController | Frontend - ShopView or FavoriteView | HTTP PATCH | URL Path Variable: userId, comboId + JSON(comboName) | JSON (User 物件) | 根據網址中的 userId ,為該使用者修改 ID 為 comboId 的自訂義組合名稱,修改為 comboName |
| 刪除自訂義組合 | Backend - UserController | Frontend - ShopView or FavoriteView | HTTP DELETE | URL Path Variable: userId, comboId | JSON (User 物件) | 根據網址中的 userId ,為該使用者刪除 ID 為 comboId 的自訂義組合 |
| 將商品加入自訂義組合 | Backend - UserController | Frontend - ShopView or FavoriteView | HTTP POST | URL Path Variable: userId, comboId, storeId, itemId | JSON (User 物件) | 根據網址中的 userId ,為該使用者將商品加入 ID 為 comboId 的自訂義組合(同一個自訂義組合內的商品不能跨店家) |
| 將商品移除自訂義組合 | Backend - UserController | Frontend - ShopView or FavoriteView | HTTP DELETE | URL Path Variable: userId, comboId, storeId, itemId | JSON (User 物件) | 根據網址中的 userId ,為該使用者將商品移除 ID 為 comboId 的自訂義組合 |
| 回傳自訂義組合列表 | Backend - UserController | Frontend - ShopView or FavoriteView | HTTP GET | URL Path Variable: userId | JSON (List<CustomCombo 物件>) | 根據網址中的 userId ,回傳該使用者的自訂義組合列表 |
| 回傳特定自訂義組合內容 | Backend - UserController | Frontend - ShopView or FavoriteView | HTTP GET | URL Path Variable: userId, comboId | JSON (CustomCombo 物件) | 根據網址中的 userId 和 comboId ,回傳該使用者的 ID 為 comboId 的自訂義組合內容 |
---
- Cart
| 介面名稱 | 介面提供者 | 介面使用者 | 連結方式 | 輸入資料 | 輸出資料 | 介面描述|
| ------- | -------- | --------- | ---- | ----- | ---- | ----- |
| 取得購物車內容 | Backend - CartController | Frontend - CartView, ShopView | HTTP GET | URL Path Variable: userId | JSON (Cart 物件) | 根據 User ID 取得該使用者的購物車內容,包含店家 ID、商品列表與總金額。若無購物車則回傳空車 |
| 加入商品至購物車 | Backend - CartController | Frontend - CartView, ShopView | HTTP POST | URL Path Variable: userId + JSON (CartItem 物件) + Request Param: storeId | JSON (Cart 物件) | 將商品加入購物車。若車內已有不同店家的商品,則回傳錯誤(禁止跨店點餐),若商品已存在則增加數量 |
| 移除購物車商品 | Backend - CartController | Frontend - CartView, ShopView | HTTP DELETE | URL Path Variable: userId, itemId | JSON (Cart 物件) | 根據 Item ID 將特定商品從購物車中移除,並重新計算總金額。若購物車清空,則重置店家鎖定 |
| 清空購物車 | Backend - CartController | Frontend - CartView, ShopView | HTTP DELETE | URL Path Variable: userId | String(使用者 ID 為:userId的購物車已清空) | 移除購物車內所有商品,並重置 storeId 為 null |
---
- Order
| 介面名稱 | 介面提供者 | 介面使用者 | 連結方式 | 輸入資料 | 輸出資料 | 介面描述|
| ------- | -------- | --------- | ---- | ----- | ---- | ----- |
| 建立新訂單 | Backend - OrderController | Frontend - CheckoutView | HTTP POST | JSON (storeId, customerId, items, totalAmount, orderType, dineInDetail/takeoutDetail, paymentMethod) | JSON (Order 物件) | 接收前端傳來的訂單詳細資訊,將資料寫入 MongoDB 資料庫,並回傳包含自動生成 `_id` 與 `createAt` 的完整訂單物件。 |
| 查詢顧客歷史訂單 | Backend - OrderController | Frontend - OrderHistoryView | HTTP GET | URL Path Variable: customerId | JSON (List<Order>) | 根據網址中的 `customerId`,回傳該名顧客過去所有的訂單列表(通常按時間倒序排列)。 |
| 查詢單筆訂單詳情 | Backend - OrderController | Frontend - OrderDetailView | HTTP GET | URL Path Variable: orderId | JSON (Order 物件) | 根據網址中的 `orderId`,回傳該筆訂單的完整資訊(包含訂單項目、金額、狀態等)。 |
| 查詢店家所有訂單 | Backend - OrderController | Frontend - MerchantOrderView | HTTP GET | URL Path Variable: storeId | JSON (List<Order>) | 根據網址中的 `storeId`,回傳該店家收到的所有訂單列表。 |
| 更新訂單狀態 | Backend - OrderController | Frontend - MerchantOrderView | HTTP PATCH | URL Path Variable: orderId + JSON (state) | JSON (Order 物件) | 商家操作接單或完成餐點時呼叫。接收新的狀態字串(如 '已接單', '已完成'),更新資料庫並回傳更新後的訂單。 |
| 取消訂單 | Backend - OrderController | Frontend - OrderHistoryView | HTTP PATCH | URL Path Variable: orderId | JSON (Order 物件) | 顧客或商家取消訂單。後端需驗證訂單狀態是否允許取消(例如:只有 '已送出' 狀態可取消),將狀態更新為 '已取消'。 |
---
## <span id="section3">3. 流程設計 (Process Design)</span>
<!-- <span style="color:red">可用 UML 之 Activity diagram / State Transition diagram 或一般的程式流程圖描述所開發的系統流程,目的是運用流程圖或狀態圖設計整個系統的完整運作。 此部分應是操作概念的細部設計。</span>
(於此處插入主要流程之 Activity diagram、State diagram 或流程圖) -->
### 3.1 Store
- 首頁顯示流程

- 查看商家流程

- 商家建立與設定流程

- 商家的菜單管理流程

### 3.2 User
- 註冊

- 登入

- 加入收藏

### 3.3 Cart
- 查詢購物車

- 加入購物車

- 移除購物車

### 3.4 Order
- 建立訂單流程圖

---
## <span id="section4">4. 使用者畫面設計 (User Interface Design)</span>
1.登入頁面

2.註冊頁面

3.顧客主頁面

4.使用者資訊

5.商家頁面

6.商品頁面

7.購物車頁面

8.訂單管理頁面

9.收藏頁面

10.商家設定頁面

---
## <span id="section5">5. 資料設計 (Data Design)</span>
### 5.1 資料庫 Schema
<table style="width: 100%; border-collapse: collapse;">
<tr style="background-color: #4472C4; color: white; text-align: center; font-weight: bold;">
<td colspan="2" style="border: 1px solid black; padding: 8px;">名稱</td>
<td colspan="2" style="border: 1px solid black; padding: 8px;">描述</td>
</tr>
<tr style="text-align: center;">
<td colspan="2" style="border: 1px solid black; padding: 8px;">store</td>
<td colspan="2" style="border: 1px solid black; padding: 8px;">店家的設定(包括菜單、資訊等)</td>
</tr>
<tr style="background-color: #4472C4; color: white; text-align: center; font-weight: bold;">
<td style="border: 1px solid black; padding: 8px; width: 20%;">屬性描述</td>
<td style="border: 1px solid black; padding: 8px; width: 20%;">屬性名稱</td>
<td style="border: 1px solid black; padding: 8px; width: 20%;">屬性型態</td>
<td style="border: 1px solid black; padding: 8px; width: 40%;">屬性設定</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">店家 ID</td>
<td style="border: 1px solid black; padding: 8px;">_id</td>
<td style="border: 1px solid black; padding: 8px;">ObjectId</td>
<td style="border: 1px solid black; padding: 8px;">required: true, PK<br>unique: true</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">店家名稱</td>
<td style="border: 1px solid black; padding: 8px;">name</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">required: true</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">店家分類</td>
<td style="border: 1px solid black; padding: 8px;">category</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">required: true</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">店家簡介</td>
<td style="border: 1px solid black; padding: 8px;">description</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">default: ""</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">擁有者 ID</td>
<td style="border: 1px solid black; padding: 8px;">ownerId</td>
<td style="border: 1px solid black; padding: 8px;">ObjectId</td>
<td style="border: 1px solid black; padding: 8px;">required: true</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">聯絡電話</td>
<td style="border: 1px solid black; padding: 8px;">phone</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">default: ""</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">聯絡信箱</td>
<td style="border: 1px solid black; padding: 8px;">email</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">default: ""</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">店家地址</td>
<td style="border: 1px solid black; padding: 8px;">address</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">required: true</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">經緯度座標</td>
<td style="border: 1px solid black; padding: 8px;">coordinates</td>
<td style="border: 1px solid black; padding: 8px;">Array<Number></td>
<td style="border: 1px solid black; padding: 8px;">required: true(格式:[經度, 緯度])</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">營業時間</td>
<td style="border: 1px solid black; padding: 8px;">businessHours</td>
<td style="border: 1px solid black; padding: 8px;">{
day: String,
start: String,
close: String,
note: String
}[]
</td>
<td style="border: 1px solid black; padding: 8px;">default: []</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">菜單</td>
<td style="border: 1px solid black; padding: 8px;">menu</td>
<td style="border: 1px solid black; padding: 8px;">{
_id: ObjectId,
itemName: String,
category: String,
price: Number,
description: String,
imgUrl: String,
isAvailable: Boolean,
tag: String
}[]
</td>
<td style="border: 1px solid black; padding: 8px;">default: []</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">店家狀態</td>
<td style="border: 1px solid black; padding: 8px;">isActive</td>
<td style="border: 1px solid black; padding: 8px;">Boolean</td>
<td style="border: 1px solid black; padding: 8px;">default: true</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">更新時間</td>
<td style="border: 1px solid black; padding: 8px;">updateAt</td>
<td style="border: 1px solid black; padding: 8px;">Date</td>
<td style="border: 1px solid black; padding: 8px;">default: 現在時間</td>
</tr>
</table>
---
<table style="width: 100%; border-collapse: collapse;">
<tr style="background-color: #4472C4; color: white; text-align: center; font-weight: bold;">
<td colspan="2" style="border: 1px solid black; padding: 8px;">名稱</td>
<td colspan="2" style="border: 1px solid black; padding: 8px;">描述</td>
</tr>
<tr style="text-align: center;">
<td colspan="2" style="border: 1px solid black; padding: 8px;">user</td>
<td colspan="2" style="border: 1px solid black; padding: 8px;">使用者的設定(包括帳密、偏好等)</td>
</tr>
<tr style="background-color: #4472C4; color: white; text-align: center; font-weight: bold;">
<td style="border: 1px solid black; padding: 8px; width: 20%;">屬性描述</td>
<td style="border: 1px solid black; padding: 8px; width: 20%;">屬性名稱</td>
<td style="border: 1px solid black; padding: 8px; width: 20%;">屬性型態</td>
<td style="border: 1px solid black; padding: 8px; width: 40%;">屬性設定</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">使用者 ID</td>
<td style="border: 1px solid black; padding: 8px;">_id</td>
<td style="border: 1px solid black; padding: 8px;">ObjectId</td>
<td style="border: 1px solid black; padding: 8px;">required: true, PK<br>unique: true</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">使用者帳號</td>
<td style="border: 1px solid black; padding: 8px;">account</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">required: true, <br>unique: true</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">使用者密碼</td>
<td style="border: 1px solid black; padding: 8px;">password</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">required: true</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">角色</td>
<td style="border: 1px solid black; padding: 8px;">role</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">required: true, <br>enum: [owner, buyer, admin]</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">大頭貼</td>
<td style="border: 1px solid black; padding: 8px;">photo</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">default: ""</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">電話</td>
<td style="border: 1px solid black; padding: 8px;">phone</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">default: ""</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">電子郵件</td>
<td style="border: 1px solid black; padding: 8px;">email</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">required: true, <br>unique: true</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">收藏店家</td>
<td style="border: 1px solid black; padding: 8px;">favorStores</td>
<td style="border: 1px solid black; padding: 8px;">Array<ObjectId></td>
<td style="border: 1px solid black; padding: 8px;">default: [], <br>ref: 'store'</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">收藏商品</td>
<td style="border: 1px solid black; padding: 8px;">favorItems</td>
<td style="border: 1px solid black; padding: 8px;">{
storeId: ObjectId,
itemId: ObjectId
}[]</td>
<td style="border: 1px solid black; padding: 8px;">default: [], <br>ref: 'store'</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">自訂義組合</td>
<td style="border: 1px solid black; padding: 8px;">customCombos</td>
<td style="border: 1px solid black; padding: 8px;">{
comboId: ObjectId,
comboName: String,
storeId: ObjectId,
{
storeId: ObjectId,
itemId: ObjectId
}[]
}[]</td>
<td style="border: 1px solid black; padding: 8px;">default: [], <br>ref: 'store'</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">更新時間</td>
<td style="border: 1px solid black; padding: 8px;">updateAt</td>
<td style="border: 1px solid black; padding: 8px;">Date</td>
<td style="border: 1px solid black; padding: 8px;">default: 現在時間</td>
</tr>
</table>
---
<table style="width: 100%; border-collapse: collapse;">
<tr style="background-color: #4472C4; color: white; text-align: center; font-weight: bold;">
<td colspan="2" style="border: 1px solid black; padding: 8px;">名稱</td>
<td colspan="2" style="border: 1px solid black; padding: 8px;">描述</td>
</tr>
<tr style="text-align: center;">
<td colspan="2" style="border: 1px solid black; padding: 8px;">cart</td>
<td colspan="2" style="border: 1px solid black; padding: 8px;">顧客在某店的購物車資訊(包含商品、價錢等)</td>
</tr>
<tr style="background-color: #4472C4; color: white; text-align: center; font-weight: bold;">
<td style="border: 1px solid black; padding: 8px; width: 20%;">屬性描述</td>
<td style="border: 1px solid black; padding: 8px; width: 20%;">屬性名稱</td>
<td style="border: 1px solid black; padding: 8px; width: 20%;">屬性型態</td>
<td style="border: 1px solid black; padding: 8px; width: 40%;">屬性設定</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">購物車 ID</td>
<td style="border: 1px solid black; padding: 8px;">_id</td>
<td style="border: 1px solid black; padding: 8px;">ObjectId</td>
<td style="border: 1px solid black; padding: 8px;">required: true, PK, <br>unique: true</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">使用者 ID</td>
<td style="border: 1px solid black; padding: 8px;">userId</td>
<td style="border: 1px solid black; padding: 8px;">ObjectId</td>
<td style="border: 1px solid black; padding: 8px;">required: true, <br>Indexed</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">店家 ID</td>
<td style="border: 1px solid black; padding: 8px;">storeId</td>
<td style="border: 1px solid black; padding: 8px;">ObjectId</td>
<td style="border: 1px solid black; padding: 8px;">required: true</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">購物車內容</td>
<td style="border: 1px solid black; padding: 8px;">items</td>
<td style="border: 1px solid black; padding: 8px;">{
itemId: ObjectId,
itemName: String,
price: Number,
quantity: Number,
subtotal: Number,
description: String
}[]</td>
<td style="border: 1px solid black; padding: 8px;">default: []</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">總金額</td>
<td style="border: 1px solid black; padding: 8px;">totalPrice</td>
<td style="border: 1px solid black; padding: 8px;">Number</td>
<td style="border: 1px solid black; padding: 8px;">default: 0</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">更新時間</td>
<td style="border: 1px solid black; padding: 8px;">updateAt</td>
<td style="border: 1px solid black; padding: 8px;">Date</td>
<td style="border: 1px solid black; padding: 8px;">default: 現在時間</td>
</tr>
</table>
---
<table style="width: 100%; border-collapse: collapse;">
<tr style="background-color: #4472C4; color: white; text-align: center; font-weight: bold;">
<td colspan="2" style="border: 1px solid black; padding: 8px;">名稱</td>
<td colspan="2" style="border: 1px solid black; padding: 8px;">描述</td>
</tr>
<tr style="text-align: center;">
<td colspan="2" style="border: 1px solid black; padding: 8px;">order</td>
<td colspan="2" style="border: 1px solid black; padding: 8px;">顧客提交的訂單(訂單詳情、訂單狀態等)</td>
</tr>
<tr style="background-color: #4472C4; color: white; text-align: center; font-weight: bold;">
<td style="border: 1px solid black; padding: 8px; width: 20%;">屬性描述</td>
<td style="border: 1px solid black; padding: 8px; width: 20%;">屬性名稱</td>
<td style="border: 1px solid black; padding: 8px; width: 20%;">屬性型態</td>
<td style="border: 1px solid black; padding: 8px; width: 40%;">屬性設定</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">訂單 ID</td>
<td style="border: 1px solid black; padding: 8px;">_id</td>
<td style="border: 1px solid black; padding: 8px;">ObjectId</td>
<td style="border: 1px solid black; padding: 8px;">required: true, PK <br>unique: true
</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">店家 ID</td>
<td style="border: 1px solid black; padding: 8px;">storeId</td>
<td style="border: 1px solid black; padding: 8px;">ObjectId</td>
<td style="border: 1px solid black; padding: 8px;">required: true, <br>ref: 'store'</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">顧客 ID</td>
<td style="border: 1px solid black; padding: 8px;">customerId</td>
<td style="border: 1px solid black; padding: 8px;">ObjectId</td>
<td style="border: 1px solid black; padding: 8px;">ref: 'user'</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">顧客電話號碼</td>
<td style="border: 1px solid black; padding: 8px;">customerPhone</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">(未登入顧客必填)</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">訂單類型</td>
<td style="border: 1px solid black; padding: 8px;">orderType</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">required: true, <br>enum: ['內用', '外帶']</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">內用詳情</td>
<td style="border: 1px solid black; padding: 8px;">dineInDetail</td>
<td style="border: 1px solid black; padding: 8px;">{
tableNumber: String
}</td>
<td style="border: 1px solid black; padding: 8px;">(當 orderType 為'內用'時需要)</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">外帶詳情</td>
<td style="border: 1px solid black; padding: 8px;">takeoutDetail</td>
<td style="border: 1px solid black; padding: 8px;">{
takeoutTime: Date
}</td>
<td style="border: 1px solid black; padding: 8px;">(當 orderType 為'外帶'時需要)</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">訂單項目</td>
<td style="border: 1px solid black; padding: 8px;">items</td>
<td style="border: 1px solid black; padding: 8px;">{
menuItemId: ObjectId,
itemName: String,
quantity: Number,
unitPrice: Number,
customization: [String],
itemSubTotal: Number
}[]</td>
<td style="border: 1px solid black; padding: 8px;">default = []</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">訂單總金額</td>
<td style="border: 1px solid black; padding: 8px;">totalAmount</td>
<td style="border: 1px solid black; padding: 8px;">Number</td>
<td style="border: 1px solid black; padding: 8px;">requied: true</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">顧客備註</td>
<td style="border: 1px solid black; padding: 8px;">remarks</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">default: ''</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">支付方式</td>
<td style="border: 1px solid black; padding: 8px;">paymentMethod</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">requied: true, <br>enum: ['店內付款']</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">訂單狀態</td>
<td style="border: 1px solid black; padding: 8px;">state</td>
<td style="border: 1px solid black; padding: 8px;">String</td>
<td style="border: 1px solid black; padding: 8px;">required: true, <br>default: '已送出訂單', <br>enum: ['已送出訂單', '已接單', '準備中', '已完成', '顧客已取餐']</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">訂單成立時間</td>
<td style="border: 1px solid black; padding: 8px;">createAt</td>
<td style="border: 1px solid black; padding: 8px;">Date</td>
<td style="border: 1px solid black; padding: 8px;">default: 現在時間</td>
</tr>
<tr>
<td style="border: 1px solid black; padding: 8px;">訂單更新時間</td>
<td style="border: 1px solid black; padding: 8px;">updateAt</td>
<td style="border: 1px solid black; padding: 8px;">Date</td>
<td style="border: 1px solid black; padding: 8px;">default: 現在時間</td>
</tr>
</table>
---
### 5.2 檔案結構
```
backend
└── src
└── main
├── java
│ └── Team5
│ └── example
│ └── breakfast_ordering
│ │
│ ├── config
│ │ └── WebSocketConfig.java // 建立後端與前端之間的通訊通道,推播通知使用
│ │
│ ├── controller // 負責接收前端 API 請求
│ │ ├── UserController.java // 處理使用者註冊、登入、收藏、自定義組合
│ │ ├── StoreController.java // 處理店家瀏覽、菜單、搜尋商品
│ │ ├── CartController.java // 處理購物車邏輯
│ │ ├── OrderController.java // 處理訂單邏輯
│ │ └── WebConfig.java // 處理 CORS 跨域設定
│ │
│ ├── model // 定義資料庫結構
│ │ ├── User.java // 使用者資料
│ │ ├── Store.java // 店家與菜單資料
│ │ ├── Cart.java // 購物車資料
│ │ └── Order.java // 訂單資料
│ │
│ ├── repository // 負責與 MongoDB 溝通
│ │ ├── UserRepository.java // User 資料庫操作介面
│ │ ├── StoreRepository.java // Store 資料庫操作介面
│ │ ├── CartRepository.java // Cart 資料庫操作介面
│ │ └── OrderRepository.java // Order 資料庫操作介面
│ │
│ ├── security
│ │ ├── CustomOAuth2UserService.java // 負責處理 Google 登入後的資料同步邏輯
│ │ └── OrderRepository.java // 整個後端的安全防護網,規定誰可以進入哪裡
│ │
│ └── BackendApplication.java // Spring Boot 程式入口
│
└── resources
└── application.properties // MongoDB 連線字串、Port 設定
frontend
├── src
│ ├── assets // 需編譯的靜態資源
│ │ └── logo.png
│ │
│ ├── components // 可重複使用的 UI 元件 (非完整頁面)
│ │ └── MenuItem.vue // 例如:顯示單一菜單項目的卡片
│ │
│ ├── data // 存放測試用的 JSON 資料
│ │ ├── menu.json
│ │ └── StoreData.json
│ │
│ ├── models // 前端資料結構定義 (Class/Interface)
│ │ └── store.model.js // 定義店家資料的格式
│ │
│ ├── router // 設定網址 (URL) 與頁面 (View) 的對應關係
│ │ └── index.js // Vue Router 設定檔
│ │
│ ├── store // 全域資料狀態 (Vuex),負責與後端 API 溝通
│ │ ├── index.js // Vuex 主設定檔
│ │ ├── cart.js // 購物車狀態 (對應後端 CartController)
│ │ ├── shops.js // 店家列表狀態 (對應後端 StoreController)
│ │ └── user.js // 使用者登入狀態 (對應後端 UserController)
│ │
│ ├── viewmodels // 處理視圖邏輯 (MVVM 模式),分離 UI 與資料處理
│ │ └── storeSettingViewModel.js
│ │
│ ├── views // 完整的網頁頁面
│ │ ├── CartView.vue // 購物車頁面
│ │ ├── FavoriteView.vue // 收藏頁面
│ │ ├── HomeView.vue // 顧客首頁
│ │ ├── LoginView.vue // 登入頁面
│ │ ├── RegisterView.vue // 註冊頁面
│ │ ├── ShopView.vue // 店家頁面
│ │ └── StoreSetting.vue // 店家設定頁面
│ │
│ ├── App.vue // 根組件 (Root Component)
│ └── main.js // 程式啟動點,掛載 Vue 實例
│
├── .browserslistrc // 瀏覽器相容性設定
├── .eslintrc.js // 程式碼風格檢查設定
├── babel.config.js // ES6+ 語法轉譯設定
└── package.json // 專案依賴套件管理
```
<!-- ### 5.3 XML / JSON 結構
(於此處描述重要的 XML / JSON 結構範例) -->
---
## <span id="section6">6. 類別圖設計 (Class Diagram)</span>
<!-- <span style="color:red">發展 UML 之 class diagram(可對應 C4 model 中的 Code diagram),明確設計系統中包含哪些類別,以及這些類別之間的關係。 若遇到非一般物件類別或特殊物件類別,如 HTML、JavaScript、JSP、Servlet 等,可用 stereotype 表達,如 <<JavaScript>>、<<Servlet>>。 可把重點放在定義程式結構:訂出所有類別名稱、拉出所有類別關係,再加入重要的 attribute/operation 即可。而特殊的類別職責設計可加上文字說明。</span>
(於此處插入 UML 類別圖,並可補充文字說明) -->
```mermaid
classDiagram
%% ==========================
%% 1. 資料模型 (Models)
%% ==========================
class User {
-String id
-String account
-String password
-String email
-String nickname
-String photo
-String phone
-List~String~ favorStores
-List~FavorItem~ favorItems
-List~CustomCombo~ customCombos
}
class FavorItem {
-String storeId
-String itemId
}
class CustomCombo {
-String comboId
-String comboName
-List~FavorItem~ items
}
class Cart {
-String userId
-String storeId
-List~CartItem~ items
-Integer totalAmount
}
class CartItem {
-String itemId
-String itemName
-Integer quantity
-Integer Price
-Integer subtotal
-String description
}
class Store {
-String id
-String ownerId
-String name
-String address
-String description
-Boolean isActive
-List~MenuItem~ menu
}
class MenuItem {
-String id
-String itemName
-Integer price
-Boolean isAvailable
}
class Order {
-String id
-String storeId
-String customerId
-String orderType
-Integer totalAmount
-String state
-Date createAt
-List~OrderItem~ items
-DineInDetail dineInDetail
-TakeoutDetail takeoutDetail
-String paymentMethod
}
class OrderItem {
-String menuItemId
-String itemName
-Integer quantity
-Integer unitPrice
-List~String~ customization
}
%% ==========================
%% 2. 控制層 (Controllers)
%% ==========================
class UserController {
+register(userJson)
+login(account, password)
+getUser(userId)
+updateUser(userId, updateJson)
+deleteUser(userId)
+addFavoriteStore(userId, storeId)
+removeFavoriteStore(userId, storeId)
+getFavoriteStores(userId)
+addFavoriteItem(userId, storeId, itemId)
+removeFavoriteItem(userId, storeId, itemId)
+getFavoriteItems(userId, storeId, itemId)
+addCustomCombo(userId, comboJson)
+updateCustomComboName(userId, comboId, comboJson)
+deleteCustomCombo(userId, comboId)
+addItemToCustomCombo(userId, comboId, storeId, itemId)
+removeItemFromCustomCombo(userId, comboId, storeId, itemId)
+getCustomCombos(userId)
+getCustomCombo(userId, comboId)
}
class CartController {
+getCart(userId)
+addItemToCart(userId, cartItem)
+removeItemFromCart(userId, itemId)
+clearCart(userId)
}
class StoreController {
+createStore(storeJson)
+getAllStores()
+getStoreDetail(storeId)
+updateStoreMenu(id, menuItems)
+searchStoreProducts(storeId, keyword)
}
class OrderController {
+createOrder(orderJson)
+getCustomerHistory(customerId)
+getOrderDetail(orderId)
+getStoreOrders(storeId)
+updateOrderState(orderId, state)
+cancelOrder(orderId)
}
%% ==========================
%% 3. 服務層 (Services)
%% ==========================
class StoreService {
+createNewStore(store)
+findAllActiveStores()
+findStoreById(storeId)
+updateMenu(storeId, menu)
+searchProducts(storeId, keyword)
}
class OrderService {
+createOrder(order)
+getOrdersByCustomer(customerId)
+getOrdersByStore(storeId)
+getOrderById(orderId)
+updateStatus(orderId, status)
}
%% ==========================
%% 4. 儲存庫 (Repositories)
%% ==========================
class UserRepository {
<<interface>>
+findByAccount(account)
+existsByAccount(account)
}
class CartRepository {
<<interface>>
+findByUserId(userId)
}
class StoreRepository {
<<interface>>
+findByIsActiveTrue()
+findByNameContaining(keyword)
}
class OrderRepository {
<<interface>>
+findByCustomerId(customerId)
+findByStoreId(storeId)
}
%% ==========================
%% 5. 關係連線 (Relationships)
%% ==========================
%% Controller 依賴 Service
StoreController ..> StoreService
OrderController ..> OrderService
%% Service 依賴 Repository
UserController ..> UserRepository
CartController ..> CartRepository
StoreService ..> StoreRepository
OrderService ..> OrderRepository
%% Repository 操作 Model
UserRepository ..> User
CartRepository ..> Cart
StoreRepository ..> Store
OrderRepository ..> Order
%% Model 內部的組合關係 (Composition)
User *-- FavorItem
User *-- CustomCombo
Cart *-- CartItem
Store *-- MenuItem
Order *-- OrderItem
%% 模組間的關聯 (Association via IDs)
Cart --> User : "userId"
Cart --> Store : "storeId"
Order --> User : "customerId"
Order --> Store : "storeId"
FavorItem --> Store : "storeId"
```
---
## <span id="section7">7. 實作方案 (Implementation Languages and Platforms)</span>
- 平台:
- 網站(可支援電腦與手機)
- 前端:
- 程式語言:HTML5 + CSS3 + JavaScript
- 框架:Vue 3
- 後端:
- 程式語言:Java
- 框架:Spring Boot
- 資料庫操作:MongoDB
- 主要函式庫與服務:
- 前端:
- Fetch API:向後端發送 HTTP 請求(登入、註冊、更新資料等)
- Vuex:管理前端狀態
- 後端:
- Spring Boot Web:提供 RESTful API,負責前後端通訊
- Spring Data MongoDB:與 MongoDB 連接,負責資料存取
- Spring Validation:處理資料驗證(例如 電子信箱格式、電話格式)
- Spring Security(若需要 JWT):管理登入驗證、授權
- bcrypt(Spring Security BCryptPasswordEncoder):
用於使用者密碼加密
- 資料庫:
- MongoDB:儲存使用者、店家、商品、訂單等資料
- MongoRepository:簡化 CRUD 操作
- 第三方服務:Cloud Storage、Email API、OAuth 認證(Google、Facebook 登入)
- 部署方式:
- 雲端 PaaS
---
## <span id="section8">8. 設計議題 (Design Issue)</span>
### 議題 1:後端 Controller 的職責劃分與 API 設計風格
- 議題內容:
- 在設計後端 API 架構時,我們面臨了如何劃分 Controller 職責的選擇。這個決策將影響到程式碼的可維護性以及前後端對接的模式。
- 可能解決方案:
- 以「功能」為中心:一個主要頁面或功能模組對應一個 Controller
- 以「資料庫 Model」 為中心:一個資料庫 Collection 對應一個 Controller
- 最後解決方案與理由:
- 最後選擇採用以「資料庫 Model」 為中心,此方案優點如下:
- 方便 debug:以 Model 為中心的寫法會寫出很多基本函式,如果有問題會比較好追(錯誤來源單純)
- 彈性較大:如上所述,會有很多基本函式拼湊成前端可用的功能
- 降低邏輯耦合:避免多個功能寫出來的程式碼中會有重複、相近的程式碼
<!-- ### 議題 2:________
- 議題內容:
- 可能解決方案:
- 最後解決方案與理由: -->