Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ public OffsetPageResponse<CourseSummaryResponse> getMyCourses(
โ€” Chapter.status๋Š” ์ƒ์„ฑ/์ž ๊ธˆ ์ƒํƒœ์— ๊ฐ€๊น๊ณ , ํ•™์ƒ๋ณ„ ํ•™์Šต ์™„๋ฃŒ ์—ฌ๋ถ€๋Š”
lesson_progress.is_completed ์— ์ €์žฅ๋˜๋ฏ€๋กœ ์‚ฌ์šฉ์ž๋ณ„ ์ง„ํ–‰ ๊ธฐ๋ก์„ ๊ธฐ์ค€์œผ๋กœ ์ง‘๊ณ„ํ•œ๋‹ค.
*/
Map<String, Integer> studiedByCourseId =
countStudiedChaptersByCourseIds(
Map<String, Integer> completedByCourseId =
countCompletedChaptersByCourseIds(
userId,
coursePage.getContent().stream().map(Course::getId).toList());

Expand All @@ -160,7 +160,7 @@ public OffsetPageResponse<CourseSummaryResponse> getMyCourses(
.map(
course ->
courseMapper.toSummaryResponse(
course, studiedByCourseId.getOrDefault(course.getId(), 0)))
course, completedByCourseId.getOrDefault(course.getId(), 0)))
.toList();
OffsetPageResponse<CourseSummaryResponse> result =
OffsetPageResponse.of(content, coursePage.getTotalElements(), page, size);
Expand Down Expand Up @@ -189,7 +189,7 @@ public CourseDetailResponse getCourse(String userId, String courseId) {
2. ํ•™์Šต ๊ฐ•์˜ ์ˆ˜ ์ง‘๊ณ„ ํ›„ DTO ๋ณ€ํ™˜ (๋‹จ์ผ ์ฝ”์Šค)
*/
CourseDetailResponse result =
courseMapper.toDetailResponse(course, countStudiedChapters(userId, courseId));
courseMapper.toDetailResponse(course, countCompletedChapters(userId, courseId));

log.debug("[CourseService] getCourse() - END | courseId: {}", courseId);
return result;
Expand Down Expand Up @@ -683,7 +683,7 @@ public CourseDetailResponse updateTutoring(
*/
CourseDetailResponse result =
courseMapper.toDetailResponse(
course, countStudiedChapters(principal.getUserId(), courseId));
course, countCompletedChapters(principal.getUserId(), courseId));

log.debug("[CourseService] updateTutoring() - END | courseId: {}", courseId);
return result;
Expand Down Expand Up @@ -719,38 +719,38 @@ public void deleteTutoring(
// ========================= Private Helpers =========================

/**
* ์—ฌ๋Ÿฌ ๊ณผ์™ธ์˜ ์‚ฌ์šฉ์ž๋ณ„ ํ•™์Šต ๊ฐ•์˜(์ฑ•ํ„ฐ) ์ˆ˜๋ฅผ ํ•œ ๋ฒˆ์— ์ง‘๊ณ„ํ•œ๋‹ค (N+1 ๋ฐฉ์ง€).
* ์—ฌ๋Ÿฌ ๊ณผ์™ธ์˜ ์‚ฌ์šฉ์ž๋ณ„ ์™„๋ฃŒ(isCompleted=true) ๊ฐ•์˜(์ฑ•ํ„ฐ) ์ˆ˜๋ฅผ ํ•œ ๋ฒˆ์— ์ง‘๊ณ„ํ•œ๋‹ค (N+1 ๋ฐฉ์ง€).
*
* <p>ํ•™์ƒ์ด ์‹ค์ œ๋กœ ๋“ค์–ด๊ฐ„ ๊ฐ•์˜ ์ˆ˜๋Š” Chapter.status๊ฐ€ ์•„๋‹ˆ๋ผ LessonProgress๋กœ ๊ธฐ๋ก๋œ๋‹ค. ํ™ˆ/๋‚ด ๊ณผ์™ธ์˜
* ๊ฐ•์˜ ์ˆ˜ ํ‘œ์‹œ๋Š” ์™„๊ฐ• ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ํ•™์Šต์„ ์‹œ์ž‘ํ–ˆ๊ฑฐ๋‚˜ ์™„๋ฃŒํ•œ ์ฑ•ํ„ฐ ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
* <p>completedLessons ๋Š” ์ง„ํ–‰๋ฅ (progress, ์™„๋ฃŒ ๊ธฐ์ค€)๊ณผ ์ผ๊ด€๋ผ์•ผ ํ•˜๋ฏ€๋กœ ์ง„ํ–‰์ค‘(currentSlideIdx>0)์€ ์ œ์™ธํ•˜๊ณ 
* ์™„๋ฃŒํ•œ ์ฑ•ํ„ฐ๋งŒ ์„ผ๋‹ค. ๊ฐ•์˜๋ฅผ ์—ด๊ธฐ๋งŒ ํ•˜๊ณ  ๋๋‚ด์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ง„ํ–‰๋ฅ  0%์ธ๋ฐ 1๊ฐ• ์™„๋ฃŒ๋กœ ํ‘œ์‹œ๋˜๋˜ ๋ชจ์ˆœ์„ ๋ง‰๋Š”๋‹ค.
*
* @param userId ์‚ฌ์šฉ์ž ID
* @param courseIds ์ง‘๊ณ„ ๋Œ€์ƒ ๊ณผ์™ธ ID ๋ชฉ๋ก
* @return courseId โ†’ ํ•™์Šตํ•œ ์ฑ•ํ„ฐ ์ˆ˜ (0๊ฐœ์ธ ์ฝ”์Šค๋Š” ํ‚ค ์—†์Œ โ†’ ํ˜ธ์ถœ๋ถ€์—์„œ getOrDefault(0))
* @return courseId โ†’ ์™„๋ฃŒํ•œ ์ฑ•ํ„ฐ ์ˆ˜ (0๊ฐœ์ธ ์ฝ”์Šค๋Š” ํ‚ค ์—†์Œ โ†’ ํ˜ธ์ถœ๋ถ€์—์„œ getOrDefault(0))
*/
private Map<String, Integer> countStudiedChaptersByCourseIds(
private Map<String, Integer> countCompletedChaptersByCourseIds(
String userId, List<String> courseIds) {
if (courseIds.isEmpty()) {
return Map.of();
}

return lessonProgressRepository.countStudiedByUserIdAndCourseIdIn(userId, courseIds).stream()
return lessonProgressRepository.countCompletedByUserIdAndCourseIdIn(userId, courseIds).stream()
.collect(
Collectors.toMap(
row -> (String) row[0],
row -> Math.toIntExact((Long) row[1])));
}

/**
* ๋‹จ์ผ ๊ณผ์™ธ์˜ ์‚ฌ์šฉ์ž๋ณ„ ํ•™์Šต ๊ฐ•์˜(์ฑ•ํ„ฐ) ์ˆ˜๋ฅผ ์ง‘๊ณ„ํ•œ๋‹ค.
* ๋‹จ์ผ ๊ณผ์™ธ์˜ ์‚ฌ์šฉ์ž๋ณ„ ์™„๋ฃŒ(isCompleted=true) ๊ฐ•์˜(์ฑ•ํ„ฐ) ์ˆ˜๋ฅผ ์ง‘๊ณ„ํ•œ๋‹ค.
*
* @param userId ์‚ฌ์šฉ์ž ID
* @param courseId ๊ณผ์™ธ ID
* @return ํ•™์Šตํ•œ ์ฑ•ํ„ฐ ์ˆ˜
* @return ์™„๋ฃŒํ•œ ์ฑ•ํ„ฐ ์ˆ˜
*/
private int countStudiedChapters(String userId, String courseId) {
private int countCompletedChapters(String userId, String courseId) {
return Math.toIntExact(
lessonProgressRepository.countStudiedByCourseIdAndUserId(courseId, userId));
lessonProgressRepository.countCompletedByCourseIdAndUserId(courseId, userId));
}

/** ๊ณผ์™ธ๋ฅผ ์กฐํšŒํ•˜๊ณ  ์†Œ์œ ๊ถŒ์„ ๊ฒ€์ฆํ•œ๋‹ค. ์กด์žฌํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ์†Œ์œ ์ž๊ฐ€ ๋‹ค๋ฅผ ๊ฒฝ์šฐ CRS_001์„ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค (์กด์žฌ ๋…ธ์ถœ ๋ฐฉ์ง€). */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,23 @@ long countStudiedByCourseIdAndUserId(
+ "AND lp.isCompleted = true")
long countCompletedByCourseIdAndUserId(
@Param("courseId") String courseId, @Param("userId") String userId);

/**
* ์—ฌ๋Ÿฌ ๊ณผ์™ธ์˜ ์‚ฌ์šฉ์ž๋ณ„ ์™„๋ฃŒ(isCompleted=true) ์ฑ•ํ„ฐ ์ˆ˜๋ฅผ ํ•œ ๋ฒˆ์— ์ง‘๊ณ„ํ•œ๋‹ค (N+1 ๋ฐฉ์ง€).
*
* <p>completedLessons(์™„๋ฃŒ ๊ฐ•์˜ ์ˆ˜)๋Š” ์ง„ํ–‰๋ฅ (progress, ์™„๋ฃŒ ๊ธฐ์ค€)๊ณผ ์ผ๊ด€๋ผ์•ผ ํ•˜๋ฏ€๋กœ ์ง„ํ–‰์ค‘(currentSlideIdx>0)์€
* ์ œ์™ธํ•˜๊ณ  ์™„๋ฃŒ๋งŒ ์„ผ๋‹ค. ์ง„ํ–‰ ํฌํ•จ ์ง‘๊ณ„๋Š” countStudiedByUserIdAndCourseIdIn ์„ ๋ณ„๋„๋กœ ์“ด๋‹ค.
*
* @param userId ์‚ฌ์šฉ์ž ID
* @param courseIds ์ง‘๊ณ„ ๋Œ€์ƒ ๊ณผ์™ธ ID ๋ชฉ๋ก
* @return Object[] = {courseId, completedCount}
*/
@Query(
"SELECT lp.chapter.course.id, COUNT(lp) FROM LessonProgress lp "
+ "WHERE lp.user.id = :userId "
+ "AND lp.chapter.course.id IN :courseIds "
+ "AND lp.isCompleted = true "
+ "GROUP BY lp.chapter.course.id")
List<Object[]> countCompletedByUserIdAndCourseIdIn(
@Param("userId") String userId, @Param("courseIds") List<String> courseIds);
}
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,9 @@ void getMyCourses_returnsList() {
Course course = makeCourse(user);
given(courseRepository.findByUserId(eq(USER_ID), isNull(), isNull(), any(Pageable.class)))
.willReturn(new PageImpl<>(List.of(course)));
given(lessonProgressRepository.countStudiedByUserIdAndCourseIdIn(USER_ID, List.of(COURSE_ID)))
given(
lessonProgressRepository.countCompletedByUserIdAndCourseIdIn(
USER_ID, List.of(COURSE_ID)))
.willReturn(List.<Object[]>of(new Object[] {COURSE_ID, 2L}));

// when
Expand All @@ -320,6 +322,27 @@ void getMyCourses_returnsList() {
assertThat(result.getContent().get(0).id()).isEqualTo(COURSE_ID);
assertThat(result.getContent().get(0).completedLessons()).isEqualTo(2);
}

@Test
@DisplayName("์™„๋ฃŒํ•œ ๊ฐ•์˜๊ฐ€ ์—†์œผ๋ฉด(์ง„ํ–‰์ค‘๋งŒ ์กด์žฌ) completedLessons๋Š” 0์ด๋‹ค")
void getMyCourses_onlyInProgress_completedIsZero() {
// given โ€” ์™„๋ฃŒ ์ง‘๊ณ„ ์ฟผ๋ฆฌ๊ฐ€ ๋นˆ ๊ฒฐ๊ณผ(ํ‚ค ์—†์Œ) โ†’ ์ง„ํ–‰์ค‘๋งŒ ์žˆ๋Š” ์ฝ”์Šค (๋ฒ„๊ทธ ํšŒ๊ท€: 0%์ธ๋ฐ 1๊ฐ• ํ‘œ์‹œ ๋ฐฉ์ง€)
User user = makeUser(USER_ID);
Course course = makeCourse(user);
given(courseRepository.findByUserId(eq(USER_ID), isNull(), isNull(), any(Pageable.class)))
.willReturn(new PageImpl<>(List.of(course)));
given(
lessonProgressRepository.countCompletedByUserIdAndCourseIdIn(
USER_ID, List.of(COURSE_ID)))
.willReturn(List.<Object[]>of());

// when
OffsetPageResponse<CourseSummaryResponse> result =
courseService.getMyCourses(USER_ID, null, null, 0, 20);

// then
assertThat(result.getContent().get(0).completedLessons()).isZero();
}
}

@Nested
Expand All @@ -332,7 +355,7 @@ void getCourse_owner_returnsDetail() {
// given
User user = makeUser(USER_ID);
given(courseRepository.findById(COURSE_ID)).willReturn(Optional.of(makeCourse(user)));
given(lessonProgressRepository.countStudiedByCourseIdAndUserId(COURSE_ID, USER_ID))
given(lessonProgressRepository.countCompletedByCourseIdAndUserId(COURSE_ID, USER_ID))
.willReturn(2L);

// when
Expand Down
Loading