---
# System prepended metadata

title: 軟體設計文件（SDD）

---

---

title: 軟體設計文件（SDD）

---

# 軟體設計文件（SDD）

## 專案資訊

- 專案名稱：海大教室租借系統
- 撰寫日期：2025/11/23
- 發展者：吳致緯、黃星智、陳宥嘉、薛翔安、高翊誠

---

## 版次變更記錄

| 版次 | 變更項目 | 變更日期 |
| :---: | :--------: |  :--------:  |
| 0.1 |         初版        | 2025/11/23 |
| 0.2 | Iteration_2 後的修改 | 2025/12/01 |
| 0.3 | Iteration_3 前的修改 | 2025/12/19 |
| 0.4 |  |  |
| 0.5 |  |  |
| 1.0 |  |  |

---

## 目錄

1. [系統模型與架構 (System Model / System Architecture)](#1.-系統模型與架構-(System-Model-/-System-Architecture))  
2. [介面需求與設計 (Interface Requirement and Design)](#2.-介面需求與設計-(Interface-Requirement-and-Design))  
3. [流程設計 (Process Design)](#3.-流程設計-(Process-Design))  
4. [使用者畫面設計 (User Interface Design)](#4.-使用者畫面設計-(User-Interface-Design))  
5. [資料設計 (Data Design)](#5.-資料設計-(Data-Design))  
6. [類別圖設計 (Class Diagram)](#6.-類別圖設計-(Class-Diagram))  
7. [實作方案 (Implementation Languages and Platforms)](#7.-實作方案-(Implementation-Languages-and-Platforms))  
8. [設計議題 (Design Issue)](#8.-設計議題-(Design-Issue))  

---

## 1. 系統模型與架構 (System Model / System Architecture)

本系統前端語言/框架是以React（Vite + Tailwind CSS）進行畫面呈現、租借表單、租借查詢。

後端語言/框架則是以 Python + Django 進行帳號登入、租借邏輯、資料處理與寄信通知，Redis 用來發送驗證碼，Aiven 遠端資料庫儲存資料，並部署在 Zeabur。

- System Context Diagram

![SystemContext.drawio](https://hackmd.io/_uploads/HkVFl4d-Zl.png)

- Containers Diagram

![Containers.drawio](https://hackmd.io/_uploads/BkAAwhwmbg.png)

- Component Diagram

![Component.drawio](https://hackmd.io/_uploads/B1QaLnPm-e.png)

---

## 2. 介面需求與設計 (Interface Requirement and Design)

- Login.jsx

| 介面名稱 | 介面提供者 | 介面使用者 | 連結方式 | 輸入資料 | 輸出資料 | 介面描述 |
| :-----: | :-----: | :-----: | :-----: | :-----: | :-----: | :-----: |
| 使用者登入 API | Django 後端（Auth 模組） | 前端 Login.jsx | POST /api/auth/login/ | { account, password } | 成功：{ access, refresh, user }；失敗：錯誤訊息 | 供前端提交帳密，後端驗證後回傳 JWT Token 與使用者基本資訊。 |
| 使用者註冊 API | Django 後端（Auth 模組） | 前端 Login.jsx | POST /api/auth/register/ | { name, account, password } | 成功訊息或錯誤訊息 JSON | 前端傳送註冊資料給後端，後端建立新使用者，成功後前端切回登入頁面。 |
| 忘記密碼 API | Django 後端（Auth 模組） | 前端 Login.jsx | POST /api/auth/forgot/ | { account, verify_code, new_password, confirm_password } | 成功：{ message: "Password reset success" }；失敗：錯誤訊息 | 用於重設密碼。後端驗證帳號與驗證碼是否正確，若通過則更新密碼。前端收到成功訊息後返回登入頁面。 |

- ClassroomBooking.jsx

| 介面名稱 | 介面提供者 | 介面使用者 | 連結方式 | 輸入資料 | 輸出資料 | 介面描述 |
| :-----: | :------: | :------: | :----: | :-----: | :-----: | :----: |
| 取得大樓與教室清單 API | Django 後端（教室管理模組） | 前端 ClassroomBooking.jsx | **GET** `/api/buildings/` | 無 | `[ { code, name, rooms[] }, ... ]` | 提供前端載入所有可借用的大樓與底下的教室，用於左側樹狀清單顯示。 |
| 取得教室週曆時段 API | Django 後端（預約模組） | 前端 WeekCalendar 元件 | **GET** `/api/rooms/{room_code}/schedule/?week=YYYY-WW` | 路徑：`room_code`；查詢參數：`week`（週別） | `[ { day, start, end }, ... ]` | 查詢某間教室在指定週的被占用時段，供前端標示「已被預約 / 可預約」格子。 |
| 建立教室預約申請 API | Django 後端（預約模組） | 前端 ClassroomBooking.jsx（handleReserve） | **POST** `/api/reservations/` | `{ room_code, building_code, day_of_week, start_hour, end_hour }`（使用者從 JWT 判定） | 建立後的預約資料（含 `status: "待確認"`） | 使用者在週曆點選可預約時段後，送出預約申請，等待管理員審核。 |
| 取得我的預約歷史 API | Django 後端（預約模組） | 前端 HistoryPanel | **GET** `/api/reservations/my/` | JWT 使用者身分 | `[ { id, room_code, building_code, day, start, end, status, created_at }, ... ]` | 回傳登入使用者的所有預約紀錄，用於「我的教室預約歷史」頁面。 |
| 取消教室預約 API | Django 後端（預約模組） | 前端 HistoryPanel（取消預約按鈕） | **PATCH** `/api/reservations/{id}/cancel/` | 路徑參數：id（預約編號）；JWT 使用者身分 | 更新後的預約資料`（status: "已取消"）` | 使用者可取消「尚未開始」的預約；前端需先顯示確認訊息，成功後更新狀態並釋放該時段。 |
| 管理員取得待審核預約清單 API | Django 後端（預約模組，管理端） | 前端 RequestPanel | **GET** `/api/reservations/pending/` | 管理員身分（JWT） | `[ { id, room_code, building_code, day, start, end, status, created_at, user }, ... ]` | 管理員查看所有 `status = "待確認"` 的預約申請，用於審核。 |
| 管理員批准預約 API | Django 後端（預約模組） | 前端 RequestPanel（批准按鈕） | **PATCH** `/api/reservations/{id}/approve/` | 路徑參數：`id` | 更新後預約資料（`status: "已批准"`） | 管理員批准某筆預約，並將該時段加入占用清單。 |
| 管理員拒絕預約 API | Django 後端（預約模組） | 前端 RequestPanel（拒絕按鈕） | **PATCH** `/api/reservations/{id}/reject/` | 路徑參數：`id`；可選：`reason` | 更新後預約資料（`status: "已拒絕"`） | 管理員拒絕某筆預約，可附上拒絕理由。 |

- EditingClassroom.jsx


| 介面名稱 | 介面提供者 | 介面使用者 | 連結方式 | 輸入資料 | 輸出資料 | 介面描述 |
| :-----: | :------: | :------: | :-----: | :----: | :-----: | :----: |
| 取得教室清單 API | Django 後端（教室管理模組） | 前端 EditingClassroom.jsx | **GET** `/api/classrooms/?page_size=200` | JWT 管理員身分 | `{ results: [ { building, room_code, name, capacity, has_projector, has_whiteboard, has_network, has_mic }, ... ] }` | 載入系統中所有教室資料，供管理員編輯教室資訊與設備設定。 |
| 新增教室 API | Django 後端（教室管理模組） | 前端 EditingClassroom.jsx（handleCreate） | **POST** `/api/classrooms/` | `{ building, room_code, name, capacity, has_projector, has_whiteboard, has_network, has_mic }` | 新增後的教室資料 | 管理員新增一間教室，包含基本資訊與設備設定。 |
| 更新教室設定 API | Django 後端（教室管理模組） | 前端 EditingClassroom.jsx（handleSaveClassroom） | **PATCH** `/api/classrooms/{room_code}/` | `{ capacity, has_projector, has_whiteboard, has_network, has_mic }` | 更新後的教室資料 | 管理員修改既有教室的可容納人數與設備配置。 |
| 刪除教室 API | Django 後端（教室管理模組） | 前端 EditingClassroom.jsx（handleDelete） | **DELETE** `/api/classrooms/{room_code}/` | 路徑參數：`room_code` | 無（204 No Content） | 管理員刪除指定教室資料，刪除前需再次確認。 |

- BlacklistPage.jsx

| 介面名稱 | 介面提供者 | 介面使用者 | 連結方式 | 輸入資料 | 輸出資料 | 介面描述 |
| :-----: | :------: | :------: | :-----: | :----: | :-----: | :----: |
| 取得使用者清單與黑名單清單 API | Django 後端（黑名單模組，管理端） | 前端 Blacklist.jsx（fetchUsers） | **GET** `/api/blacklist/users/` | 管理員身分（JWT） | `{ normal_users: [ { id, username, email, first_name, last_name }, ... ], blacklisted_users: [ ... ] }` | 回傳所有使用者並分成正常使用者與黑名單使用者，供前端左右兩欄顯示並提供停權/恢復操作。 |
| 將使用者加入黑名單 API（停權） | Django 後端（黑名單模組） | 前端 Blacklist.jsx（handleBlockUser） | **POST** `/api/blacklist/ban/` | `{ user_id, reason }`（reason 可選） | `{ detail: "...", user_id, status }` 或更新後黑名單資料 | 管理員將指定使用者加入黑名單。前端會先跳出確認視窗，並可選填停權原因。成功後重新載入清單。 |
| 將使用者移出黑名單 API（恢復） | Django 後端（黑名單模組） | 前端 Blacklist.jsx（handleRestoreUser） | **POST** `/api/blacklist/unban/` | `{ user_id }` | `{ detail: "...", user_id, status }` 或更新後黑名單資料 | 管理員解除指定使用者的黑名單狀態。前端先跳出確認視窗，成功後重新載入清單。 |

- ProfilePage.jsx

| 介面名稱 | 介面提供者 | 介面使用者 | 連結方式 | 輸入資料 | 輸出資料 | 介面描述 |
| :-----: | :------: | :------: | :----: | :-----: | :-----: | :----: |
| 更新使用者名稱 API | Django 後端（Auth/個人資料模組） | 前端 ProfilePage.jsx（saveDisplayName） | **PATCH** `/api/auth/profile/`（或 `API_ENDPOINTS.change_name()`） | `{ name }`；JWT 使用者身分 | 更新後的使用者資料（或 `{ detail: "..." }`） | 使用者修改「使用者名稱」。成功後更新畫面並寫入 localStorage（name），讓其他頁面可同步顯示新名稱。 |
| 修改密碼 API | Django 後端（Auth/密碼模組） | 前端 ProfilePage.jsx（savePassword） | **POST** `/api/auth/change-password/`（或 `API_ENDPOINTS.change_password()`） | `{ current_password, new_password }`；JWT 使用者身分 | `{ detail: "..." }` 或狀態訊息 | 使用者修改密碼。需驗證舊密碼且新密碼需符合規範（前端至少 6 碼）。成功後要求重新登入。 |

---

## 3. 流程設計 (Process Design)

- 3.1：註冊流程

![註冊流程.drawio](https://hackmd.io/_uploads/rJuYTuv-Zg.png)

- 3.2：登入流程

![登入流程.drawio](https://hackmd.io/_uploads/B1jxJnzXZx.png)

- 3.3：忘記密碼流程

![忘記密碼流程.drawio](https://hackmd.io/_uploads/Syx06uwWZg.png)

- 3.4：查詢教室流程

![查詢教室流程.drawio](https://hackmd.io/_uploads/Skr1AdPb-x.png)

- 3.5：申請租借教室流程

![申請租借教室流程.drawio](https://hackmd.io/_uploads/SJ-gRdvbWg.png)

- 3.6：使用者取消租借教室

![取消租借.drawio](https://hackmd.io/_uploads/rkuTQpMX-g.png)

- 3.7：使用者修改使用者名稱

![修改使用者名稱.drawio](https://hackmd.io/_uploads/HkK0mpMXbe.png)

- 3.8：使用者修改密碼

![修改密碼.drawio](https://hackmd.io/_uploads/ByU14aG7We.png)

- 3.9：管理員新增教室流程

![管理員新增教室流程.drawio](https://hackmd.io/_uploads/ryqZAODZZg.png)

- 3.10：管理員刪除教室流程

![管理員刪除教室流程.drawio](https://hackmd.io/_uploads/SySGAOwbWx.png)

- 3.11：管理員編輯教室流程

![管理員編輯教室流程.drawio](https://hackmd.io/_uploads/rJaMRdPZZg.png)

- 3.12：管理員將一般使用者加入黑名單

![加入黑名單.drawio](https://hackmd.io/_uploads/rkreETfmZx.png)

- 3.13：管理員將黑名單裡的使用者移回一般使用者

![移出黑名單.drawio](https://hackmd.io/_uploads/H12lVTfmbx.png)

- 3.14：教室狀態流程

![教室狀態.drawio](https://hackmd.io/_uploads/SywWNaGQWe.png)

---

## 4. 使用者畫面設計 (User Interface Design)

- 主畫面

![主畫面](https://hackmd.io/_uploads/S1pxijf7-x.png)

- 註冊帳號

![註冊帳號](https://hackmd.io/_uploads/S1kNojfX-l.png)

- 登入畫面

![登入](https://hackmd.io/_uploads/rJ78isfQbg.png)

- 忘記密碼

![忘記密碼](https://hackmd.io/_uploads/BJVOooMX-g.png)

- 搜尋和租借教室

![搜尋和租借教室](https://hackmd.io/_uploads/Sk50jjzX-e.png)

- 預約歷史

![預約歷史](https://hackmd.io/_uploads/rku-nifQ-l.png)

- 修改個人資料

![個人資料](https://hackmd.io/_uploads/BJ1ahozmZl.png)

- 租借請求

![租借請求](https://hackmd.io/_uploads/SyRE3szXZe.png)


- 管理教室

![管理教室](https://hackmd.io/_uploads/HJzZowv-We.png)

- 黑名單管理

![黑名單](https://hackmd.io/_uploads/SkZ16sfX-x.png)

---

## 5. 資料設計 (Data Design)

### 5.1 資料庫 Schema 及XML / JSON 結構

#### user

| 欄位代碼          | 欄位名稱  | 說明        | 型態                |
| ------------- | ----- | --------- | ----------------- |
| `id`          | 使用者編號 | id   | Integer           |
| `username`    | 帳號    | 使用者登入帳號   | String            |
| `email`       | 電子郵件  | 使用者 Email | String            |
| `is_staff`    | 管理員旗標 | 是否為管理員    | Boolean           |
| `is_active`   | 啟用狀態  | 帳號是否啟用    | Boolean           |
| `date_joined` | 建立時間  | 帳號建立時間    | String  |

#### Validator

```json
  {
  "jsonSchema": {
    "bsonType": "object",
    "required": ["username", "email"],
    "properties": {
      "id": { "bsonType": "int" },
      "username": { "bsonType": "string" },
      "email": { "bsonType": "string" },
      "is_staff": { "bsonType": "bool" },
      "is_active": { "bsonType": "bool" },
      "date_joined": { "bsonType": "string" }
    }
  }
}
```

__________________________

#### rooms

| 欄位代碼                     | 欄位名稱   | 說明                                       | 型態                |
| ------------------------ | ------ | ---------------------------------------- | ----------------- |
| `id`                     | 教室編號   | id                                  | Integer           |
| `building`               | 大樓代碼   | CS / E1 / E2 / M / S           | String            |
| `room_code`              | 教室代碼   | 如 CS201、E1-204                           | String            |
| `name`                   | 教室名稱   | 顯示名稱                      | String            |
| `capacity`               | 容納人數   | 可容納之座位數                                  | Integer           |
| `room_type`              | 教室類型   | NORMAL / LAB / MEETING / LECTURE / OTHER | String            |
| `has_projector`          | 投影機    | 是否有投影機                                                     | Boolean           |
| `has_whiteboard`         | 白板     | 是否有白板                                    | Boolean           |
| `has_mic`                | 麥克風    | 是否有麥克風                                   | Boolean           |
| `has_internet`                | 網路    | 是否有網路                                   | Boolean           |
| `is_active`              | 是否啟用   | 是否啟用此教室                                  | Boolean           |
| `created_at`             | 建立時間   | 建立記錄時自動填入                                | String  |
| `updated_at`             | 最後更新時間 | 每次修改時自動更新                                | String  |

#### Validator

```json
{
  "jsonSchema": {
    "bsonType": "object",
    "required": [
      "building",
      "room_code",
      "capacity",
      "room_type"
    ],
    "properties": {
      "id": { "bsonType": "int" },
      "building": { "bsonType": "string" },
      "room_code": { "bsonType": "string" },
      "name": { "bsonType": "string" },
      "capacity": { "bsonType": "int" },
      "room_type": { "bsonType": "string" },

      "has_projector": { "bsonType": "bool" },
      "has_whiteboard": { "bsonType": "bool" },
      "has_mic": { "bsonType": "bool" },
      "has_internet": { "bsonType": "bool" },

      "is_active": { "bsonType": "bool" },
      "created_at": { "bsonType": "string" },
      "updated_at": { "bsonType": "string" }
    }
  }
}
```

_____________________________

#### reservation

| 欄位代碼         | 欄位名稱   | 說明                                        | 型態                |
| ------------ | ------ | ----------------------------------------- | ----------------- |
| `id`         | 預約編號   | 主鍵，自動遞增                                   | Integer           |
| `classroom`  | 教室 ID  | 外鍵，對應 `Classroom.id`                      | Integer           |
| `user`       | 使用者 ID | 外鍵，對應 `auth_user.id`                      | Integer           |
| `date`       | 預約日期   | 預約使用日期（yyyy-mm-dd）                        | Date / String     |
| `time_slot`  | 預約時段   | 例如  `"10:00-11:00"`              | String            |
| `reason`     | 預約原因   | 使用教室的目的說明（可留空）                            | String            |
| `status`     | 審核狀態   | pending / approved / rejected / cancelled | String            |
| `created_at` | 建立時間   | 建立預約的時間                                   | String  |

#### Validator

```json
{
  "jsonSchema": {
    "bsonType": "object",
    "required": [
      "classroom",
      "user",
      "date",
      "time_slot"
    ],
    "properties": {
      "id": { "bsonType": "int" },
      "classroom": { "bsonType": "int" },
      "user": { "bsonType": "int" },
      "date": { "bsonType": "string" },
      "time_slot": { "bsonType": "string" },
      "reason": { "bsonType": "string" },
      "status": { "bsonType": "string" },
      "created_at": { "bsonType": "string" }
    }
  }
}
```

___

### 5.2 檔案結構

```text
ntou-classroom-reservation/
│
├─ frontend/
│    │
│    ├─ App.jsx 
│    ├─ ClassroomBooking.jsx 
│    ├─ Login.jsx 
│    ├─ App.css 
│    ├─ ClassroomBooking.css 
│    └─ Login.css 
│    
 
├─ backend/
│   │
│   ├─ djangosetting/ 
│   │   ├─ init.py
│   │   ├─ asgi.py
│   │   ├─ settings.py
│   │   ├─ urls.py
│   │   └─ wsgi.py
│   │
│   ├─ accounts/
│   │   ├─ init.py
│   │   ├─ apps.py
│   │   ├─ models.py
│   │   ├─ serializers.py
│   │   ├─ services.py
│   │   ├─ views.py
│   │   ├─ urls.py
│   │   ├─ admin.py
│   │   ├─ tests.py
│   │
│   ├─ rooms/
│   │   ├─ init.py
│   │   ├─ apps.py
│   │   ├─ models.py
│   │   ├─ serializers.py
│   │   ├─ views.py
│   │   ├─ urls.py
│   │   ├─ admin.py
│   │   └─ tests.py
│   │
│   ├─ reservations/
│   │   ├─init.py
│   │   ├─ apps.py
│   │   ├─ models.py
│   │   ├─ serializers.py
│   │   ├─ views.py
│   │   ├─ urls.py
│   │   ├─ admin.py
│   │   └─ tests.py
│   │ 
│
└─ venv/
```

---

## 6. 類別圖設計 (Class Diagram)

![image](https://hackmd.io/_uploads/H1qnBy_7Wx.png)



#### AuthController：負責登入、註冊與取得目前登入使用者資訊。 

#### ClassroomController：提供教室查詢、單一教室資訊、列出所有教室。

#### ReservationController：負責新增、更新預約狀況並查詢預約紀錄。

---

## 7. 實作方案 (Implementation Languages and Platforms)

- 平台：網頁

- 前端技術與框架：JavaScript + React

- 後端技術與框架：Python + Django ，資料庫則是使用 Aiven，驗證碼使用 Redis

- 主要函式庫：React / DRF / SimpleJWT / cors-headers

- 服務：Vite Dev Server、Django API、JWT Auth

- 部署方式：在 GitHub Action 進行 CI / CD，並預計部署在 Zeabur

---

## 8. 設計議題 (Design Issue)

### 議題 1：不同作業系統（Windows / macOS）之間的開發環境問題

- 議題內容：
    
    使用不同作業系統（Windows 與 macOS）進行開發時，導致開發環境設定不一致。
    
    例如：Python venv 建立方式不同、Django 套件安裝版本差異、Node.js / npm 在 Mac 與 Windows 的安裝方式不同、MySQL / phpMyAdmin 在兩個平台上的配置不一致。
    
    造成專案無法在不同組員之間執行、版本不一致、啟動錯誤（import module error、環境變數問題等）。
  
- 可能解決方案：
    
    1. 每位組員自行建立本機環境（Local Setup），視 OS 調整設定 
    
    2. 使用虛擬環境與 requirements.txt / package.json 統一版本
  
- 最後解決方案與理由：
    
    解決方案：使用虛擬環境與 requirements.txt / package.json 統一版本
    
    理由：較簡單的方式來跨平台合作

### 議題 2：使用 GitHub 時發生程式碼衝突（Merge Conflict）問題

- 議題內容：
    
    使用 GitHub 協作專案時，多位組員同時修改相同檔案，導致 Merge Conflict，造成版本混亂、功能被覆蓋。
    
- 可能解決方案：
    
    1. 每個組員負責不同區域／檔案，避免同時修改同一檔案
    
    2. 發生衝突時手動比對與解決（使用 VS Code / GitHub Desktop）
    
- 最後解決方案與理由：
        
    解決方案：發生衝突時手動比對與解決（使用 VS Code / GitHub Desktop）
    
    理由：每周開會時解決衝突就好了

### 議題 3：部署專案至 Zeabur 時發生環境與設定錯誤問題

- 議題內容：
    
    在將專案部署到 Zeabur 時，因為本地端與雲端環境設定不同（如環境變數未設定、API 位址錯誤、Build 失敗等），導致專案無法正常啟動或功能異常，影響系統對外服務。
    
- 可能解決方案：
    
    1. 部署前先統一並確認本地與雲端的環境設定（如 .env、API Base URL、Port 設定）
    
    2. 發生錯誤時，透過 Zeabur 提供的 Log 與錯誤訊息進行除錯，逐一修正設定問題
    
- 最後解決方案與理由：
        
    解決方案：發生問題時，透過 Zeabur 的部署紀錄與系統 Log 手動檢查並修正環境變數與設定錯誤
    
    理由：部署問題多半與環境設定相關，透過實際查看 Log 能快速定位錯誤原因，並在每次部署時即時修正，確保系統能順利上線

---
