系統模型設計#

本文檔詳細說明水球軟體學院複刻專案的所有資料模型(Entity Models),包括核心課程系統和道館挑戰系統。

模型總覽#

系統共包含 16 個核心實體模型,分為以下模組:

模組Entity 數量狀態說明
🎓 基礎系統6✅ 已實作用戶、課程、學習進度
🏋️ 道館系統9✅ 已實作挑戰任務、作業批改、技能評級
📦 訂單系統1✅ 已實作課程購買訂單

🎓 基礎系統模型#

1. User(用戶)#

用途: 用戶帳號、認證系統(含 OAuth)與遊戲化系統

實體類: tw.waterballsa.academy.entity.User

欄位說明#

欄位類型約束說明
idLongPK, Auto用戶 ID
emailString(255)Unique, NotNull登入信箱
passwordString(255)加密密碼(OAuth 用戶可為空)
nameString(100)NotNull用戶名稱
authProviderEnumNotNull, Default: LOCAL認證方式 (LOCAL/GOOGLE)
googleIdString(255)Google OAuth ID
roleEnumNotNull, Default: STUDENT角色 (STUDENT/TEACHER)
levelIntegerDefault: 1當前等級(1-36)
expIntegerDefault: 0經驗值
titleString(50)Default: 初級工程師稱號
avatarUrlString(500)頭像 URL
nicknameString(100)暱稱
genderString(20)性別
professionString(100)職業
birthdayLocalDate生日
regionString(100)地區
githubUrlString(500)GitHub URL
createdAtTimestampAuto帳號創建時間
updatedAtTimestampAuto最後更新時間

核心業務邏輯#

// 經驗值增加時自動升級
public void addExp(int expToAdd) {
    this.exp += expToAdd;
    updateLevel();  // 自動重算等級
}

// 等級計算邏輯(基於固定門檻陣列)
private void updateLevel() {
    int[] thresholds = {0, 200, 500, 1500, 3000, 5000, ...};
    for (int i = thresholds.length - 1; i >= 0; i--) {
        if (this.exp >= thresholds[i]) {
            this.level = i + 1;
            break;
        }
    }
}

關聯關係#

  • 1:NMissionProgress(學習進度)
  • 1:NCourseEnrollment(購課記錄)
  • 1:NSubmission(作業提交)
  • 1:NUserSkill(技能等級)

索引#

  • UNIQUE INDEX on email
  • INDEX on googleId

2. Course(課程)#

用途: 課程基本資訊(名稱、價格、講師等)

實體類: tw.waterballsa.academy.entity.Course

欄位說明#

欄位類型約束說明
idLongPK, Auto課程 ID
nameString(200)NotNull課程名稱
descriptionText課程簡介
imageUrlString(500)課程封面圖
priceBigDecimal(10,2)課程價格
levelString(50)難度等級
instructorString(100)講師名稱
totalDurationInteger總時長(秒)
studentCountIntegerDefault: 0學生人數
categoryString(100)課程分類
hasFreePreviewBooleanDefault: true是否有免費預覽
enabledFeaturesListElementCollection啟用功能列表

關聯關係#

  • 1:NChapter(章節,級聯刪除)
  • 1:NCourseEnrollment(購課記錄)

業務規則#

  • 刪除課程時,所有章節和任務將被級聯刪除(CascadeType.ALL
  • enabledFeatures 儲存在關聯表 course_features

3. Chapter(章節)#

用途: 課程章節(組織課程結構)

實體類: tw.waterballsa.academy.entity.Chapter

欄位說明#

欄位類型約束說明
idLongPK, Auto章節 ID
courseIdLongFK, NotNull所屬課程
titleString(200)NotNull章節標題
orderIndexIntegerNotNull排序索引

關聯關係#

  • N:1Course(所屬課程)
  • 1:NMission(任務單元,級聯刪除)
  • 1:NGym(道館,級聯刪除)

業務規則#

  • orderIndex 決定章節顯示順序
  • 刪除章節時,所有任務和道館將被級聯刪除

4. Mission(任務單元)#

用途: 學習單元(影片/文章/問卷)

實體類: tw.waterballsa.academy.entity.Mission

相關枚舉: MissionType(VIDEO, ARTICLE, SURVEY)

欄位說明#

欄位類型約束說明
idLongPK, Auto任務 ID
chapterIdLongFK, NotNull所屬章節
titleString(200)NotNull任務標題
typeEnumNotNull類型(VIDEO/ARTICLE/SURVEY)
contentText文章內容
videoUrlString(500)影片 URL
expRewardIntegerDefault: 100經驗值獎勵
orderIndexInteger排序索引
isFreePreviewBooleanDefault: false是否免費預覽

關聯關係#

  • N:1Chapter(所屬章節)
  • 1:NMissionProgress(用戶進度)

業務規則#

  • 免費預覽規則isFreePreview=true 的任務無需購課即可觀看
  • 獎勵領取規則:進度達 80% 才可領取 exp 獎勵(參見 MissionService:130
  • 類型限制
    • VIDEO:通常有 videoUrl
    • ARTICLE/SURVEY:使用 content 欄位

5. MissionProgress(學習進度)#

用途: 用戶學習進度追蹤

實體類: tw.waterballsa.academy.entity.MissionProgress

欄位說明#

欄位類型約束說明
idLongPK, Auto進度 ID
userIdLongFK, NotNull用戶 ID
missionIdLongFK, NotNull任務 ID
progressIntegerDefault: 0完成百分比 (0-100)
completedBooleanDefault: false是否已完成
rewardClaimedBooleanDefault: false是否已領獎勵
lastWatchedPositionIntegerDefault: 0最後觀看位置(秒)
videoDurationInteger影片總時長(秒)
watchedTimeIntegerDefault: 0累積觀看時間(秒,防作弊用)
completedAtTimestamp完成時間
updatedAtTimestampAuto最後更新時間

唯一性約束#

@UniqueConstraint(columnNames = {"user_id", "mission_id"})

每個用戶對每個任務只有一條進度記錄。

核心業務邏輯#

// 更新進度(只能增加,不能減少)
public void updateProgress(int newProgress) {
    int cappedProgress = Math.min(100, newProgress);
    if (cappedProgress > this.progress) {
        this.progress = cappedProgress;
    }
    if (this.progress >= 100 && !this.completed) {
        this.completed = true;
        this.completedAt = LocalDateTime.now();
    }
    this.updatedAt = LocalDateTime.now();
}

關聯關係#

  • N:1User(用戶)
  • N:1Mission(任務)

6. CourseEnrollment(購課記錄)#

用途: 用戶購課記錄(含免費、付費、贈送)

實體類: tw.waterballsa.academy.entity.CourseEnrollment

相關枚舉:

  • EnrollmentType(FREE, PURCHASED, GIFTED)
  • PaymentStatus(PENDING, COMPLETED, CANCELLED)

欄位說明#

欄位類型約束說明
idLongPK, Auto報名 ID
userIdLongFK, NotNull用戶 ID
courseIdLongFK, NotNull課程 ID
typeEnumNotNull, Default: PURCHASED報名類型
statusEnumNotNull, Default: PENDING支付狀態
enrolledAtTimestampAuto報名時間
completedAtTimestamp完成時間

唯一性約束#

@UniqueConstraint(columnNames = {"user_id", "course_id"})

同一用戶不可重複購買同一課程。

業務規則#

  • 免費課程:自動設為 FREE 類型,無需支付
  • 付費課程:須完成付款流程(測試模式下自動通過)
  • 贈送課程:由管理員手動指定為 GIFTED

🏋️ 道館系統模型#

7. Gym(道館)#

用途: 道館(包含多個挑戰任務)

實體類: tw.waterballsa.academy.entity.Gym

相關枚舉:

  • GymCategory(WHITE, BLACK)
  • GymDifficulty(BEGINNER, INTERMEDIATE, ADVANCED, EXPERT, MASTER)

欄位說明#

欄位類型約束說明
idLongPK, Auto道館 ID
chapterIdLongFK, NotNull所屬章節
nameString(200)NotNull道館名稱
categoryEnumNotNull類別(白/黑帶)
difficultyEnumNotNull難度等級
descriptionText道館描述
imageUrlString(500)道館圖片
orderIndexInteger排序索引

關聯關係#

  • N:1Chapter(所屬章節)
  • 1:NGymPrerequisite(前置課程要求)
  • 1:NChallenge(挑戰任務)

8. GymPrerequisite(前置課程)#

用途: 道館的前置課程要求

實體類: tw.waterballsa.academy.entity.GymPrerequisite

欄位說明#

欄位類型約束說明
idLongPK, AutoID
gymIdLongFK, NotNull道館 ID
missionIdLongFK, NotNull前置任務 ID
requireRewardClaimedBooleanDefault: true是否需領取獎勵

業務規則#

  • 用戶必須完成所有前置任務才能挑戰道館
  • 如果 requireRewardClaimed=true,還需領取該任務的經驗值獎勵

9. Challenge(挑戰任務)#

用途: 挑戰任務(速戰速決/實戰演練)

實體類: tw.waterballsa.academy.entity.Challenge

相關枚舉: ChallengeType(INSTANT, PRACTICAL)

欄位說明#

欄位類型約束說明
idLongPK, Auto挑戰 ID
gymIdLongFK, NotNull所屬道館
titleString(200)NotNull挑戰標題
typeEnumNotNull類型(速戰/實戰)
descriptionText挑戰描述
repeatableBooleanDefault: false是否可重複挑戰

關聯關係#

  • N:1Gym(所屬道館)
  • 1:NChallengeField(提交欄位定義)
  • 1:NSubmission(學員提交)

業務規則#

  • 速戰速決(INSTANT):快速驗證概念理解
  • 實戰演練(PRACTICAL):完整專案實作
  • 重複挑戰repeatable=true 允許多次提交

10. ChallengeField(提交欄位定義)#

用途: 挑戰需要提交的檔案欄位

實體類: tw.waterballsa.academy.entity.ChallengeField

相關枚舉: FieldType(FILE, URL, TEXT, CODE)

欄位說明#

欄位類型約束說明
idLongPK, Auto欄位 ID
challengeIdLongFK, NotNull所屬挑戰
fieldKeyString(50)NotNull欄位鍵值
fieldNameString(100)NotNull欄位顯示名稱
fieldTypeEnumNotNull欄位類型
requiredBooleanDefault: true是否必填
orderIndexInteger顯示順序

業務規則#

  • 定義學員需要提交的內容(如:原始碼檔案、GitHub URL、說明文字)
  • 驗證提交時會檢查所有 required=true 的欄位

11. Submission(作業提交)#

用途: 學員的作業提交記錄

實體類: tw.waterballsa.academy.entity.Submission

相關枚舉: SubmissionStatus(PENDING, GRADING, GRADED, REJECTED)

欄位說明#

欄位類型約束說明
idLongPK, Auto提交 ID
challengeIdLongFK, NotNull挑戰 ID
userIdLongFK, NotNull用戶 ID
statusEnumNotNull提交狀態
submittedAtTimestampAuto提交時間
gradedAtTimestamp批改時間

關聯關係#

  • N:1Challenge(所屬挑戰)
  • N:1User(提交用戶)
  • 1:NSubmissionFile(提交檔案)
  • 1:1GradeResult(批改結果)

狀態流轉#

PENDING → GRADING → GRADED
              ↓
          REJECTED

12. SubmissionFile(提交檔案)#

用途: 提交的檔案(支援多檔案)

實體類: tw.waterballsa.academy.entity.SubmissionFile

欄位說明#

欄位類型約束說明
idLongPK, Auto檔案 ID
submissionIdLongFK, NotNull提交 ID
fieldKeyString(50)NotNull對應欄位鍵值
fileNameString(255)NotNull檔案名稱
fileUrlString(500)NotNull檔案 URL(S3等)
fileSizeLong檔案大小(bytes)
contentTypeString(100)MIME 類型

業務規則#

  • fieldKey 對應 ChallengeField.fieldKey
  • 支援一個欄位上傳多個檔案

13. GradeResult(批改結果)#

用途: 老師的批改結果

實體類: tw.waterballsa.academy.entity.GradeResult

欄位說明#

欄位類型約束說明
idLongPK, Auto結果 ID
submissionIdLongFK, Unique提交 ID
scoreInteger分數
feedbackText批改意見
gradedByLongFK批改教師 ID
gradedAtTimestampAuto批改時間

關聯關係#

  • 1:1Submission(對應提交)
  • N:1User(批改教師)
  • 1:NSkillRating(技能評級)

14. SkillRating(技能評級)#

用途: 技能評級(6 個類別 × 34 種級別)

實體類: tw.waterballsa.academy.entity.SkillRating

相關枚舉:

  • SkillCategory(熟悉設計模式的Form、區分結構與行為、游刃有餘的開發能力、需求結構化分析、抽象萃取能力、建立Well-Defined Context)
  • SkillLevel(F- 到 ACE,共 34 個等級:F-, F, F+, E-, E, E+, D-, D, D+, C-, C, C+, B-, B, B+, A-, A, A+, AA-, AA, AA+, AAA-, AAA, AAA+, S-, S, S+, SS-, SS, SS+, SSS-, SSS, SSS+, ACE)

欄位說明#

欄位類型約束說明
idLongPK, Auto評級 ID
gradeResultIdLongFK, NotNull批改結果 ID
categoryEnumNotNull技能類別
levelEnumNotNull等級評價
commentText評語

業務規則#

  • 每次批改可對多個技能類別進行評級
  • 評級會影響用戶的 UserSkill 紀錄

15. UserSkill(用戶技能等級)#

用途: 用戶的技能等級記錄

實體類: tw.waterballsa.academy.entity.UserSkill

欄位說明#

欄位類型約束說明
idLongPK, Auto記錄 ID
userIdLongFK, NotNull用戶 ID
categoryEnumNotNull技能類別
levelEnumNotNull當前等級
updatedAtTimestampAuto最後更新時間

唯一性約束#

@UniqueConstraint(columnNames = {"user_id", "category"})

業務規則#

  • 用戶每個技能類別只有一條記錄
  • level 為最近一次評級結果

📦 訂單系統模型#

16. Order(訂單)#

用途: 課程購買訂單

實體類: tw.waterballsa.academy.entity.Order

相關枚舉: OrderStatus(PENDING, PAID, CANCELLED, REFUNDED)

欄位說明#

欄位類型約束說明
idLongPK, Auto訂單 ID
userIdLongFK, NotNull用戶 ID
courseIdLongFK, NotNull課程 ID
amountBigDecimal(10,2)NotNull訂單金額
statusEnumNotNull訂單狀態
paymentMethodString(50)支付方式
transactionIdString(100)交易流水號
createdAtTimestampAuto創建時間
paidAtTimestamp支付時間

關聯關係#

  • N:1User(購買用戶)
  • N:1Course(購買課程)

🔗 完整 ER 關係圖#

erDiagram
    User ||--o{ MissionProgress : tracks
    User ||--o{ CourseEnrollment : enrolls
    User ||--o{ Submission : submits
    User ||--o{ UserSkill : has
    User ||--o{ Order : places

    Course ||--o{ Chapter : contains
    Course ||--o{ CourseEnrollment : enrolled_by
    Course ||--o{ Order : purchased_in

    Chapter ||--o{ Mission : contains
    Chapter ||--o{ Gym : contains

    Mission ||--o{ MissionProgress : tracked_by
    Mission ||--o{ GymPrerequisite : required_by

    Gym ||--o{ GymPrerequisite : requires
    Gym ||--o{ Challenge : contains

    Challenge ||--o{ ChallengeField : defines
    Challenge ||--o{ Submission : receives

    Submission ||--o{ SubmissionFile : includes
    Submission ||--|| GradeResult : has

    GradeResult ||--o{ SkillRating : rates

💡 設計原則#

1. 資料一致性#

  • 唯一性約束:防止重複記錄(如同一用戶重複購課)
  • 外鍵約束:確保參照完整性
  • 級聯刪除:避免孤兒資料(如刪除課程時自動刪除章節)

2. 效能優化#

  • 延遲載入:使用 FetchType.LAZY 避免 N+1 查詢
  • 索引策略:在常查詢欄位加索引(email, googleId 等)
  • 預設值:使用 @Builder.Default 減少空值判斷

3. 擴展性#

  • 枚舉管理:使用 Enum 管理有限狀態集合
  • 彈性欄位:預留擴展欄位(如 metadata JSON 欄位)
  • 關聯表設計:便於未來新增關係

4. 安全性#

  • 密碼加密:使用 BCrypt 加密儲存
  • 時間戳記@PrePersist@PreUpdate 自動管理
  • 權限檢查:業務邏輯分離到 Service 層

📚 相關文檔#


🔄 模型狀態總覽#

模組Entity狀態Release
基礎系統User, Course, Chapter, Mission, MissionProgress, CourseEnrollment✅ 已實作R1
道館系統Gym, GymPrerequisite, Challenge, ChallengeField✅ 已實作R2
作業系統Submission, SubmissionFile, GradeResult✅ 已實作R2
技能系統SkillRating, UserSkill✅ 已實作R2
訂單系統Order✅ 已實作R1