---
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

- Containers Diagram

- Component Diagram

---
## 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:註冊流程

- 3.2:登入流程

- 3.3:忘記密碼流程

- 3.4:查詢教室流程

- 3.5:申請租借教室流程

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

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

- 3.8:使用者修改密碼

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

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

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

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

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

- 3.14:教室狀態流程

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

- 註冊帳號

- 登入畫面

- 忘記密碼

- 搜尋和租借教室

- 預約歷史

- 修改個人資料

- 租借請求

- 管理教室

- 黑名單管理

---
## 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)

#### 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 能快速定位錯誤原因,並在每次部署時即時修正,確保系統能順利上線
---