ありがとうございます。
ここから 第14段階:受講機能(LMS機能)設計 に進みます。
これまでの設計では、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | 教材管理(LCMS)
教師
↓
教材作成
↓
管理者承認
↓
公開
受講管理(LMS)
受講者
↓
コース受講
↓
学習進捗管理
|
という2つの領域に分けています。
今回は 受講者が学習するための機能 を設計します。
第14段階の対象機能
今回実装対象:
| 機能 |
概要 |
| 受講登録 |
受講者がコースを開始 |
| コース進捗 |
何%完了したか管理 |
| レッスン進捗 |
閲覧済み・完了管理 |
| 動画視聴履歴 |
視聴時間管理 |
| クイズ回答 |
回答履歴管理 |
| 修了判定 |
コース完了判定 |
| 学習ダッシュボード |
進捗表示 |
1. 受講関連テーブル構成
全体:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | User
|
|
Enrollment
|
|
Course
|
|
CourseSection
|
|
Lesson
|
|
Content
LearningProgress
QuizAnswerHistory
VideoWatchHistory
|
2. 受講登録(Enrollment)
役割
「誰が、どのコースを受講しているか」
を管理します。
テーブル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | CREATE TABLE enrollments (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
course_id BIGINT NOT NULL,
status VARCHAR(30) NOT NULL,
started_at DATETIME,
completed_at DATETIME,
created_at DATETIME NOT NULL,
UNIQUE(
user_id,
course_id
)
);
|
3. Enrollment Entity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 | @Entity
@Table(name="enrollments")
@Getter
@Setter
public class Enrollment {
@Id
@GeneratedValue(
strategy=GenerationType.IDENTITY
)
private Long id;
@ManyToOne(fetch=FetchType.LAZY)
private User user;
@ManyToOne(fetch=FetchType.LAZY)
private Course course;
@Enumerated(EnumType.STRING)
private EnrollmentStatus status;
private LocalDateTime startedAt;
private LocalDateTime completedAt;
private LocalDateTime createdAt;
}
|
4. 受講状態
| public enum EnrollmentStatus {
ACTIVE,
COMPLETED,
CANCELLED
}
|
5. 受講開始処理
URL:
| /student/course/{id}/start
|
Service:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 | @Service
@RequiredArgsConstructor
public class EnrollmentService {
private final EnrollmentRepository repository;
@Transactional
public void start(
User user,
Course course){
Enrollment enrollment =
new Enrollment();
enrollment.setUser(
user
);
enrollment.setCourse(
course
);
enrollment.setStatus(
EnrollmentStatus.ACTIVE
);
enrollment.setStartedAt(
LocalDateTime.now()
);
repository.save(
enrollment
);
}
}
|
6. レッスン進捗管理
目的
例えば:
| Java基礎
第1章
✓ Lesson1
✓ Lesson2
△ Lesson3
進捗 66%
|
を表示します。
7. LearningProgressテーブル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | CREATE TABLE learning_progress (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
lesson_id BIGINT NOT NULL,
status VARCHAR(30),
started_at DATETIME,
completed_at DATETIME,
UNIQUE(
user_id,
lesson_id
)
);
|
8. Progress Entity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 | @Entity
@Table(name="learning_progress")
@Getter
@Setter
public class LearningProgress {
@Id
@GeneratedValue(
strategy=GenerationType.IDENTITY
)
private Long id;
@ManyToOne(fetch=FetchType.LAZY)
private User user;
@ManyToOne(fetch=FetchType.LAZY)
private Lesson lesson;
@Enumerated(EnumType.STRING)
private ProgressStatus status;
private LocalDateTime startedAt;
private LocalDateTime completedAt;
}
|
9. ProgressStatus
| public enum ProgressStatus {
NOT_STARTED,
LEARNING,
COMPLETED
}
|
10. レッスン完了処理
受講者が「完了」ボタンを押します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 | @Transactional
public void completeLesson(
User user,
Long lessonId){
LearningProgress progress =
repository
.findByUserIdAndLessonId(
user.getId(),
lessonId
)
.orElseGet(
LearningProgress::new
);
progress.setUser(
user
);
progress.setLesson(
lessonRepository
.getReferenceById(
lessonId
)
);
progress.setStatus(
ProgressStatus.COMPLETED
);
progress.setCompletedAt(
LocalDateTime.now()
);
repository.save(
progress
);
}
|
11. コース進捗計算
計算例:
1
2
3
4
5
6
7
8
9
10
11
12
13 | 全Lesson数
10
完了Lesson数
7
進捗
70%
|
MyBatisで集計します。
CourseProgressDto
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | @Data
public class CourseProgressDto {
private Long courseId;
private String courseName;
private Integer totalLessons;
private Integer completedLessons;
public int getProgressRate(){
if(totalLessons==0){
return 0;
}
return
completedLessons
*100
/
totalLessons;
}
}
|
12. CourseProgressMapper
| @Mapper
public interface CourseProgressMapper {
CourseProgressDto findProgress(
Long userId,
Long courseId
);
}
|
13. SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 | <select id="findProgress"
resultType="CourseProgressDto">
SELECT
c.id course_id,
c.name course_name,
COUNT(l.id)
AS total_lessons,
COUNT(lp.id)
AS completed_lessons
FROM courses c
JOIN lessons l
ON l.course_id=c.id
LEFT JOIN learning_progress lp
ON lp.lesson_id=l.id
AND lp.user_id=#{userId}
WHERE c.id=#{courseId}
GROUP BY c.id
</select>
|
14. 動画視聴履歴
動画教材の場合、
単純な完了だけでは不足します。
例:
を保持します。
VideoWatchHistory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | CREATE TABLE video_watch_history (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT,
lesson_id BIGINT,
watched_seconds INT,
last_position INT,
updated_at DATETIME
);
|
Entity:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 | @Entity
@Getter
@Setter
public class VideoWatchHistory {
@Id
@GeneratedValue
private Long id;
@ManyToOne
private User user;
@ManyToOne
private Lesson lesson;
private Integer watchedSeconds;
private Integer lastPosition;
private LocalDateTime updatedAt;
}
|
15. クイズ回答履歴
設計
1
2
3
4
5
6
7
8
9
10
11
12
13 | |
QuizAnswerHistory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | CREATE TABLE quiz_answer_history (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT,
quiz_id BIGINT,
answer_correct BOOLEAN,
score INT,
answered_at DATETIME
);
|
16. 修了判定
条件例:
| 全Lesson完了
+
必須Quiz合格
↓
修了
|
CompletionService
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 | @Service
@RequiredArgsConstructor
public class CompletionService {
public boolean checkComplete(
Long userId,
Long courseId){
int lessonRate =
progressMapper
.completedRate(
userId,
courseId
);
int quizRate =
quizMapper
.scoreRate(
userId,
courseId
);
return
lessonRate==100
&&
quizRate>=80;
}
}
|
17. 受講者画面
コース一覧
表示:
学習画面
表示:
1
2
3
4
5
6
7
8
9
10
11
12
13 | Javaとは
----------------
動画
▶ 再生
概要
[完了]
|
18. メンター対応準備
今回は画面を作りませんが、
将来:
1
2
3
4
5
6
7
8
9
10
11
12
13 | Mentor
↓
Student
↓
Enrollment
↓
LearningProgress
|
を見るだけなので、
現在の設計で対応可能です。
追加予定:
| mentor_assignments
mentor_id
student_id
|
19. 第14段階終了時点の構成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | Course
|
|
Enrollment
|
Student
|
v
LearningProgress
|
+------------+------------+
VideoWatch QuizHistory
|
Completion
|
第14段階終了時点
実装できる機能:
✅ コース受講登録
✅ レッスン進捗管理
✅ 動画視聴履歴
✅ クイズ回答履歴
✅ コース進捗率計算
✅ 修了判定設計
✅ メンター拡張準備
次の 第15段階 では、教材システムの検索・管理機能を強化します。
内容:
-
教材ライブラリ画面完成
-
高度な検索
-
タグ管理
-
カテゴリ管理
-
教材再利用機能
-
お気に入り教材
-
教材評価・レビュー
-
ダッシュボード集計
へ進みます。