課程進度追蹤機制#
概述#
平台採用雙重追蹤機制來準確記錄學習進度,確保用戶真實觀看影片內容,而非透過快轉作弊。
兩種進度指標#
1. 播放位置(lastWatchedPosition)#
用途: 斷點續看
記錄用戶在影片中的位置(以秒為單位),用於下次進入時從該位置繼續播放。
範例:
- 30 分鐘影片,看到第 15 分鐘
- 系統記錄:
lastWatchedPosition = 900秒 - 下次打開影片,自動從第 15 分鐘開始播放
特性:
- ✅ 可以自由拖拉進度條
- ✅ 會即時更新位置
- ❌ 不影響課程完成度
2. 實際觀看時間(watchedTime)#
用途: 防作弊,計算課程完成度
記錄用戶真實觀看的累積時間,這是計算課程完成度的核心指標。
範例:
- 30 分鐘影片(1800 秒)
- 用戶實際觀看 1500 秒
- 課程完成度:1500 / 1800 = 83%
特性:
- ✅ 只有連續播放才會累積
- ✅ 重複觀看可以累積(直到 100%)
- ❌ 快轉跳躍不會增加觀看時間
- ❌ 拖拉進度條不會增加觀看時間
防作弊機制詳解#
運作原理#
系統每秒檢查一次影片播放狀態:
每 1 秒執行:
1. 記錄當前播放位置 A
2. 比較上一秒的位置 B
3. 計算時間差:A - B = 差值
判斷:
- 如果差值 = 0-2 秒 → ✅ 正常播放,累積觀看時間
- 如果差值 > 2 秒 → ❌ 偵測到跳躍,不累積觀看時間
- 如果差值 < 0 秒 → ❌ 向後拖拉,不累積觀看時間為什麼允許 2 秒誤差?
- 考慮網路緩衝
- 影片載入延遲
- 系統時鐘誤差
實際案例說明#
案例一:正常觀看#
小明的操作:
- 播放 30 分鐘影片(1800 秒)
- 從頭開始看到結束,中途暫停喝水 3 次
- 總播放時間:40 分鐘(包含暫停)
- 實際觀看時間:30 分鐘
系統記錄:
時間軸:
0秒 ► 播放開始
1秒 ► 檢查:1-0=1秒 ✅ 累積 +1秒 (總計: 1秒)
2秒 ► 檢查:2-1=1秒 ✅ 累積 +1秒 (總計: 2秒)
3秒 ► 檢查:3-2=1秒 ✅ 累積 +1秒 (總計: 3秒)
...
300秒 ► 用戶暫停喝水
...
320秒 ► 用戶繼續播放(暫停 20 秒不計時)
321秒 ► 檢查:321-320=1秒 ✅ 累積 +1秒 (總計: 301秒)
...
1800秒 ► 影片結束
最終:watchedTime = 1800秒 (100%)結果: ✅ 可以領取獎勵
案例二:嘗試快轉作弊#
小華的操作:
- 播放 30 分鐘影片(1800 秒)
- 看了 5 分鐘(300 秒)
- 直接拖拉進度條到最後
- 播放最後 10 秒
- 影片結束
系統記錄:
時間軸:
0秒 ► 播放開始
1秒 ► 檢查:1-0=1秒 ✅ 累積 +1秒 (總計: 1秒)
...
300秒 ► 檢查:300-299=1秒 ✅ 累積 +1秒 (總計: 300秒)
>>> 用戶拖拉進度條 <<<
1790秒 ► 檢查:1790-300=1490秒 ❌ 偵測到跳躍,不累積
⚠️ 警告:跳過 1490 秒(約 24.8 分鐘)
1791秒 ► 檢查:1791-1790=1秒 ✅ 累積 +1秒 (總計: 301秒)
...
1800秒 ► 影片結束
最終:watchedTime = 310秒 (17%)顯示訊息:
⚠️ 偵測到跳躍: 300秒 -> 1790秒 (跳過 1490秒,不計入觀看時間)結果: ❌ 進度只有 17%,無法領取獎勵(需要 100%)
案例三:重複觀看累積進度#
小李的觀看過程:
小李購買了「設計模式入門」課程,影片長度 30 分鐘(1800 秒)。
第一天:只看了前半段
操作:播放 0 → 900 秒,然後關閉瀏覽器
系統記錄:
- watchedTime: 900 秒
- progress: 900/1800 = 50%
- 可以領取獎勵?❌ 需要 100%第二天:想快速完成,嘗試快轉
操作:
1. 打開影片,自動從 900 秒繼續播放
2. 看了 100 秒(現在在 1000 秒位置)
3. 想說「剩下 800 秒太長了,直接拖到最後吧」
4. 拖拉進度條到 1790 秒
5. 播放最後 10 秒
系統記錄:
時間軸:
900秒 ► 繼續播放(上次停止的位置)
901秒 ► 檢查:901-900=1秒 ✅ 累積 +1秒 (總計: 901秒)
...
1000秒 ► 檢查:1000-999=1秒 ✅ 累積 +1秒 (總計: 1000秒)
>>> 拖拉到 1790 秒 <<<
1790秒 ► 檢查:1790-1000=790秒 ❌ 偵測到跳躍
⚠️ 警告:跳過 790 秒,不計入觀看時間
1791秒 ► 檢查:1791-1790=1秒 ✅ 累積 +1秒 (總計: 1001秒)
...
1800秒 ► 影片結束
最終:watchedTime = 1010秒 (56%)小李的想法: 「蛤?我明明看到最後了,怎麼才 56%?」
系統回應: 「您只實際觀看了 1010 秒(約 17 分鐘),還需要觀看 790 秒才能達到 100%。」
第三天:小李改變策略,重複觀看
操作:
1. 小李決定重新播放影片
2. 這次從頭開始(位置 0 秒)
3. 連續播放 800 秒(前 13 分鐘的內容)
4. 雖然這 800 秒已經看過,但系統仍會累積
系統記錄:
0秒 ► 播放開始(重新觀看)
1秒 ► 檢查:1-0=1秒 ✅ 累積 +1秒 (總計: 1011秒)
...
800秒 ► 檢查:800-799=1秒 ✅ 累積 +1秒 (總計: 1810秒)
最終:
- watchedTime = 1810秒
- 但影片總長度只有 1800 秒
- 系統限制最大值:Math.min(1810, 1800) = 1800秒
- progress: 1800/1800 = 100% ✅結果: ✅ 可以領取獎勵了!
小李學到的教訓:
- ❌ 快轉無法增加觀看時間
- ✅ 重複觀看可以累積進度
- ✅ 達到總時長後自動 100%
課程完成度計算#
公式#
課程完成度 = (實際觀看時間 / 影片總時長) × 100%
進度上限 = 100%(重複觀看不會超過)領取獎勵條件#
唯一條件: 課程完成度達到 100%
常見誤解:
- ❌ 「拖到最後就能領獎勵」→ 錯,必須實際觀看
- ❌ 「看到 80% 就能領」→ 錯,必須 100%
- ❌ 「暫停會影響進度」→ 錯,暫停不計時但不影響已累積的觀看時間
達到 100% 的方法#
方法一:連續播放(推薦)#
30 分鐘影片
↓
從頭播放到尾(1800 秒)
↓
watchedTime = 1800 秒
↓
progress = 100% ✅方法二:分段觀看#
第一天:看 0-900 秒(前 15 分鐘)
watchedTime = 900 秒 (50%)
第二天:看 900-1800 秒(後 15 分鐘)
watchedTime = 900 + 900 = 1800 秒 (100%) ✅方法三:重複觀看(如果之前快轉了)#
已累積:1000 秒 (56%)
還需要:1800 - 1000 = 800 秒
操作:重新播放影片前 800 秒
結果:1000 + 800 = 1800 秒 (100%) ✅進度顯示#
在影片播放頁面,你會看到兩種進度:
播放位置進度條#
影片進度條:▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░ 70%
↑ 當前播放位置用途: 顯示當前在影片的哪個位置
可以做:
- 拖拉到任意位置
- 快轉、倒轉
- 查看縮圖
不影響: 課程完成度
課程完成度#
課程完成度:56%
實際觀看:1010 秒 / 1800 秒用途: 決定能否領取獎勵
如何增加:
- ✅ 連續播放影片
- ✅ 重複觀看任意片段
- ❌ 無法透過快轉增加
控制台日誌(開發者模式)#
打開瀏覽器開發者工具(F12),可以看到詳細的追蹤日誌:
正常播放#
✓ 觀看時間累積: +1秒 (總計: 1秒)
✓ 觀看時間累積: +1秒 (總計: 2秒)
✓ 觀看時間累積: +1秒 (總計: 3秒)
📊 更新進度: 位置 17% | 實際觀看 17%快轉跳躍#
⚠️ 偵測到跳躍: 300秒 -> 1790秒 (跳過 1490秒,不計入觀看時間)恢復進度#
✅ 恢復觀看進度: 1010秒 (56%)常見問題#
Q1: 為什麼我看完影片了,但進度只有 50%?#
A: 您可能使用了快轉功能。系統只計算實際連續播放的時間,快轉跳過的部分不計入。
解決方法: 重新播放影片,或播放您快轉跳過的片段。
Q2: 我重複看同一段會累積進度嗎?#
A: 會!重複觀看任何片段都會累積實際觀看時間,直到達到影片總時長(100%)。
範例:
- 30 分鐘影片
- 第一次看前 20 分鐘(1200 秒)
- 第二次再看前 10 分鐘(600 秒)
- 總觀看時間:1200 + 600 = 1800 秒 = 100% ✅
Q3: 暫停會影響進度嗎?#
A: 不會。暫停只是停止播放,不會減少已累積的觀看時間。
範例:
- 看了 10 分鐘(600 秒)
- 暫停去上廁所 5 分鐘
- 繼續播放
- 觀看時間仍然是 600 秒(暫停期間不計時)
Q4: 可以用 1.5 倍速播放嗎?#
A: 可以!YouTube 播放器支援調整播放速度(0.5x - 2x)。
注意:
- 影片總時長仍以正常速度計算
- 例如:30 分鐘影片用 2 倍速播放,只需 15 分鐘
- 但系統仍會記錄為觀看 30 分鐘(因為內容確實看完了)
這是允許的嗎? ✅ 是的,因為您仍然在真實觀看內容,只是速度較快。
Q5: 我看到 80% 可以領獎勵嗎?#
A: 不行,必須達到 100% 才能領取獎勵。
Q6: 進度會過期嗎?#
A: 不會!已累積的觀看時間永久保存,隨時可以繼續累積。
範例:
- 今天看 10 分鐘(33%)
- 一個月後回來再看 20 分鐘(67%)
- 總進度:33% + 67% = 100% ✅
Q7: 關閉瀏覽器會丟失進度嗎?#
A: 不會。系統每 3 秒會自動儲存進度到伺服器。
最壞情況: 如果瀏覽器突然崩潰,最多只會丟失最後 3 秒的進度。
Q8: 為什麼要這麼嚴格?直接拖到最後不行嗎?#
A: 這是為了確保學習品質。課程內容是講師精心設計的,跳過內容會影響學習效果。
類比:
- 🏃♂️ 馬拉松:必須跑完全程,不能坐車到終點
- 📖 讀書:必須真正閱讀,不能只看封面和結尾
- 🎓 上課:必須實際出席,不能只簽到就走
平台目標: 培養真正的技術能力,而非只是收集證書。
設計理念#
為什麼選擇這種機制?#
1. 確保學習品質
- 學員必須真實觀看內容
- 講師的心血不會被浪費
- 證書代表真實的學習成果
2. 公平性
- 所有學員都以相同標準計算進度
- 排行榜和經驗值反映真實努力
- 防止作弊者獲得不公平優勢
3. 彈性與友善
- 允許分段學習(不必一次看完)
- 允許重複觀看(加深理解)
- 允許調整播放速度(配合個人習慣)
- 自動斷點續看(無縫學習體驗)
4. 透明度
- 控制台日誌清楚顯示追蹤過程
- 用戶可以理解為何進度是這個數字
- 沒有隱藏的規則
與其他平台的比較#
| 平台 | 進度計算方式 | 可以快轉作弊? |
|---|---|---|
| YouTube | 無強制追蹤 | ✅ 隨意快轉 |
| Coursera | 播放位置 | ❌ 有防作弊 |
| Udemy | 播放位置 | ✅ 可以快轉 |
| 水球學院 | 實際觀看時間 | ❌ 嚴格防作弊 |
技術細節(給技術人員)#
前端追蹤#
// 每秒檢查一次
setInterval(() => {
const currentTime = player.getCurrentTime();
const lastTime = lastPosition;
const timeDiff = currentTime - lastTime;
// 只有連續播放(誤差 ≤ 2 秒)才累積
if (timeDiff >= 0 && timeDiff <= 2) {
watchedTime += timeDiff;
} else if (timeDiff > 2) {
console.warn('偵測到跳躍,不計入觀看時間');
}
lastPosition = currentTime;
}, 1000);後端驗證#
// 優先使用實際觀看時間計算進度
if (watchedTime != null && watchedTime > 0) {
progressPercent = (duration > 0)
? (int) ((watchedTime * 100.0) / duration)
: 0;
} else {
// 向後兼容:沒有 watchedTime 時使用播放位置
progressPercent = (duration > 0)
? (int) ((watchedPosition * 100.0) / duration)
: 0;
}
// 限制最大累積時間 = 影片總長度
int cappedWatchedTime = Math.min(watchedTime, duration);
missionProgress.setWatchedTime(cappedWatchedTime);資料庫欄位#
CREATE TABLE mission_progress (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
mission_id BIGINT NOT NULL,
progress INTEGER NOT NULL, -- 百分比 (0-100)
completed BOOLEAN NOT NULL, -- 是否完成
reward_claimed BOOLEAN NOT NULL, -- 是否領取獎勵
last_watched_position INTEGER, -- 最後播放位置(秒)
video_duration INTEGER, -- 影片總長度(秒)
watched_time INTEGER, -- 實際累積觀看時間(秒)
completed_at TIMESTAMP,
updated_at TIMESTAMP
);總結#
水球軟體學院的進度追蹤系統採用實際觀看時間作為核心指標,確保:
✅ 學習品質:學員真實觀看課程內容 ✅ 防止作弊:無法透過快轉完成課程 ✅ 學習彈性:可以分段學習、重複觀看 ✅ 公平競爭:所有學員使用相同標準 ✅ 用戶友善:自動儲存、斷點續看
雖然這讓「快速完成課程」變得不可能,但這正是我們的目標:培養真正的技術能力,而非只是收集證書。