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 @@ -137,7 +137,7 @@ public EnrollmentResponse decideWaitingEnrollment(
@AuthenticationPrincipal AuthenticatedUser currentUser,
@Valid @RequestBody EnrollmentDecisionRequest request) {
AuthenticatedUser authenticatedUser = requireUser(currentUser);
return lectureService.decideWaitingEnrollment(lectureId, userId, authenticatedUser.userId(), authenticatedUser.role(), request);
return lectureService.decideWaitingEnrollment(lectureId, userId, authenticatedUser.role(), request);
}

@GetMapping("/enrollments/me")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public record LectureSummaryResponse(
String rejectionReason,
long enrolledCount,
long waitingCount,
String myEnrollmentStatus,
String lectureLocation,
LocalDate lectureDate,
LocalTime lectureTime,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Optional<LectureEnrollmentEntity> findFirstByLectureIdAndStatusOrderByRequestedA

List<LectureEnrollmentEntity> findAllByLectureId(Long lectureId);

List<LectureEnrollmentEntity> findAllByUserIdAndLectureIdIn(Long userId, Collection<Long> lectureIds);

@Modifying
@Transactional
void deleteByLectureId(Long lectureId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,28 +68,6 @@ public void promoteFirstWaitingUser(LectureEntity lecture, LocalDateTime now) {
}
}

public void promoteWaitingAfterDeadline(LectureEntity lecture, LocalDateTime now) {
if (lecture.getId() == null || lecture.getStatus() == LectureStatus.CLOSE) return;
if (!isAfterApplicationDeadline(lecture, now)) return;

int capacity = resolveTotalCapacity(lecture);
if (capacity <= 0) return;

List<LectureEnrollmentEntity> enrollments = lectureEnrollmentRepository.findAllByLectureId(lecture.getId());
long enrolledCount = enrollments.stream().filter(e -> e.getStatus() == EnrollmentStatus.ENROLLED).count();
if (enrolledCount >= capacity) return;

List<LectureEnrollmentEntity> waiting = sortByRequestedOrder(enrollments.stream()
.filter(e -> e.getStatus() == EnrollmentStatus.WAITING)
.toList());

for (LectureEnrollmentEntity enrollment : waiting) {
if (enrolledCount >= capacity) break;
enrollment.promoteToEnrolled();
enrolledCount++;
}
}

public void refreshLectureLifecycle(LectureEntity lecture, LocalDateTime now) {
if (lecture.getId() == null) {
LocalDateTime lectureEndDateTime = lecture.getLectureEndDateTime();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,9 @@ public Page<LectureSummaryResponse> getLectures(Pageable pageable, Long viewerId
? lectureRepository.findAllByApprovalStatusOrderByCreatedAtDesc(ApprovalStatus.APPROVED, pageable)
: lectureRepository.findVisibleToUser(ApprovalStatus.APPROVED, viewerId, pageable);
Map<Long, Map<EnrollmentStatus, Long>> enrollmentCountsByLectureId = getEnrollmentCountsByLectureIds(lectures.getContent());
Map<Long, String> myEnrollmentStatusByLectureId = getMyEnrollmentStatusByLectureIds(lectures.getContent(), viewerId);

return lectures.map(lecture -> toLectureSummary(lecture, enrollmentCountsByLectureId, viewerId));
return lectures.map(lecture -> toLectureSummary(lecture, enrollmentCountsByLectureId, myEnrollmentStatusByLectureId, viewerId));
}

@Transactional(readOnly = true)
Expand All @@ -103,7 +104,7 @@ public Page<LectureSummaryResponse> getPendingLectures(Role currentUserRole, Pag
Page<LectureEntity> lectures = lectureRepository.findAllByApprovalStatusOrderByCreatedAtDesc(ApprovalStatus.PENDING, pageable);
Map<Long, Map<EnrollmentStatus, Long>> enrollmentCountsByLectureId = getEnrollmentCountsByLectureIds(lectures.getContent());

return lectures.map(lecture -> toLectureSummary(lecture, enrollmentCountsByLectureId, null));
return lectures.map(lecture -> toLectureSummary(lecture, enrollmentCountsByLectureId, Map.of(), null));
}

@Transactional
Expand Down Expand Up @@ -171,7 +172,7 @@ public EnrollmentResponse enroll(Long lectureId, Long userId) {
// approvedAtยทcreatedAt์€ ์„œ๋ฒ„๊ฐ€ UTC๋กœ ์ฐ์€ ๊ฐ’์ด๋ผ ํ•œ๊ตญ ์‹œ๊ฐ„ ๋ฒฝ์‹œ๊ณ„๋กœ ์˜ฎ๊ฒจ์•ผ 16:20์ด ๋งž๋Š”๋‹ค.
LocalDateTime applicationOpenReference = toSchoolTime(
lecture.getApprovedAt() != null ? lecture.getApprovedAt() : lecture.getCreatedAt());
timeValidator.validateApplicationTime(applicationOpenReference, lecture.getApplicationDeadline(), now);
timeValidator.validateApplicationTime(applicationOpenReference, now);
boolean isAfterApplicationDeadline = isAfterApplicationDeadline(lecture, now);

lifecycleHandler.refreshLectureLifecycle(lecture, now);
Expand Down Expand Up @@ -259,6 +260,13 @@ public EnrollmentResponse cancelEnrollment(Long lectureId, Long userId) {
}

lectureEnrollmentRepository.delete(enrollment);
lectureEnrollmentRepository.flush();

// ๋งˆ๊ฐ ์ „์— ์‹ ์ฒญ์ž ์ž๋ฆฌ๊ฐ€ ๋น„๋ฉด ๋Œ€๊ธฐ 1๋ฒˆ์ด ํ•™๋…„ ๊ทœ์น™์— ๋งž๊ฒŒ ์˜ฌ๋ผ๊ฐ„๋‹ค.
// ๋งˆ๊ฐ ๋’ค์—๋Š” ์ž๋™ ์Šน๊ธ‰์ด ์—†๊ณ  ํ•™์ƒํšŒ๊ฐ€ ์ง์ ‘ ์ˆ˜๋ฝยท๊ฑฐ์ ˆํ•œ๋‹ค.
if (canceledStatus == EnrollmentStatus.ENROLLED && !isAfterApplicationDeadline(lecture, now)) {
lifecycleHandler.promoteFirstWaitingUser(lecture, now);
}

long enrolledCount = lectureEnrollmentRepository.countByLectureIdAndStatus(lectureId, EnrollmentStatus.ENROLLED);
long waitingCount = lectureEnrollmentRepository.countByLectureIdAndStatus(lectureId, EnrollmentStatus.WAITING);
Expand Down Expand Up @@ -314,6 +322,7 @@ private long countEnrolledInGrade(Long lectureId, Integer grade) {

private LectureSummaryResponse toLectureSummary(LectureEntity lecture,
Map<Long, Map<EnrollmentStatus, Long>> enrollmentCountsByLectureId,
Map<Long, String> myEnrollmentStatusByLectureId,
Long viewerId) {
if (lecture.getCreator() == null) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "๊ฐ•์˜ ์ƒ์„ฑ์ž ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.");
Expand All @@ -335,6 +344,7 @@ private LectureSummaryResponse toLectureSummary(LectureEntity lecture,
resolveRejectionReason(lecture, viewerId),
enrolledCount,
waitingCount,
myEnrollmentStatusByLectureId.get(lecture.getId()),
lecture.getLectureLocation(),
lecture.getLectureDate(),
lecture.getLectureTime(),
Expand All @@ -346,6 +356,21 @@ private LectureSummaryResponse toLectureSummary(LectureEntity lecture,
);
}

/**
* ๋ชฉ๋ก์— ์žˆ๋Š” ๊ฐ•์—ฐ๋“ค์— ๋Œ€ํ•ด ๋ณด๋Š” ์‚ฌ๋žŒ ๋ณธ์ธ์˜ ์‹ ์ฒญ ์ƒํƒœ๋ฅผ ํ•œ ๋ฒˆ์— ์ฝ๋Š”๋‹ค.
* ์ฒซ ํ™”๋ฉด์ด "์ด๋ฏธ ์‹ ์ฒญํ–ˆ๋Š”์ง€"๋ฅผ ์•Œ์•„์•ผ ์‹ ์ฒญ ๋ฒ„ํŠผ์„ ์ž˜๋ชป ์—ด์ง€ ์•Š๋Š”๋‹ค.
*/
private Map<Long, String> getMyEnrollmentStatusByLectureIds(List<LectureEntity> lectures, Long viewerId) {
if (viewerId == null || lectures.isEmpty()) return Map.of();

List<Long> lectureIds = lectures.stream().map(LectureEntity::getId).toList();

return lectureEnrollmentRepository.findAllByUserIdAndLectureIdIn(viewerId, lectureIds).stream()
.collect(Collectors.toMap(
enrollment -> enrollment.getLecture().getId(),
enrollment -> enrollment.getStatus().name()));
}

private Map<Long, Map<EnrollmentStatus, Long>> getEnrollmentCountsByLectureIds(List<LectureEntity> lectures) {
if (lectures.isEmpty()) return Map.of();

Expand Down Expand Up @@ -495,10 +520,11 @@ private List<EnrollmentUserResponse> filterEnrollmentsByStatus(List<LectureEnrol
}

@Transactional
public EnrollmentResponse decideWaitingEnrollment(Long lectureId, Long enrollmentUserId, Long currentUserId,
Role currentUserRole, EnrollmentDecisionRequest request) {
public EnrollmentResponse decideWaitingEnrollment(Long lectureId, Long enrollmentUserId, Role currentUserRole,
EnrollmentDecisionRequest request) {
LectureEntity lecture = requireLecture(lectureId);
validateCreatorOrAdmin(lecture, currentUserId, currentUserRole);
// ๋Œ€๊ธฐ์ž๋ฅผ ์‹ ์ฒญ์ž๋กœ ์˜ฌ๋ฆฌ๋Š” ํŒ๋‹จ์€ ํ•™์ƒํšŒ๋งŒ ํ•œ๋‹ค.
validateAdmin(currentUserRole);
LectureEnrollmentEntity enrollment = lectureEnrollmentRepository.findByLectureIdAndUserId(lectureId, enrollmentUserId)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "๋Œ€๊ธฐ ์‹ ์ฒญ ๋‚ด์—ญ์ด ์—†์Šต๋‹ˆ๋‹ค."));
if (enrollment.getStatus() != EnrollmentStatus.WAITING) {
Expand All @@ -509,6 +535,11 @@ public EnrollmentResponse decideWaitingEnrollment(Long lectureId, Long enrollmen
long waitingCount = lectureEnrollmentRepository.countByLectureIdAndStatus(lectureId, EnrollmentStatus.WAITING);
if (request.approved()) {
enrollment.promoteToEnrolled();
// ๋งˆ๊ฐ ๋’ค์—๋Š” ํ•™์ƒํšŒ๊ฐ€ ์˜ฌ๋ ค ์ฃผ๋Š” ๊ฒƒ์ด ์‹ ์ฒญ์ž๊ฐ€ ๋А๋Š” ์œ ์ผํ•œ ๊ธธ์ด๋ผ,
// ์—ฌ๊ธฐ์„œ๋„ ํ™•์ • ๊ธฐ์ค€(10๋ช…)์„ ๋‹ค์‹œ ๋ณธ๋‹ค. ๊ทธ๋Ÿฌ์ง€ ์•Š์œผ๋ฉด 10๋ช…์„ ๋„˜๊ฒจ๋„ '๊ฐœ์„ค ๋ถˆํ™•์ •'์œผ๋กœ ๋‚จ๋Š”๋‹ค.
if (lecture.getStatus() != LectureStatus.CLOSE && enrolledCount + 1 >= CONFIRM_THRESHOLD) {
lecture.confirm();
}
return new EnrollmentResponse(lectureId, EnrollmentStatus.ENROLLED.name(), enrolledCount + 1, waitingCount - 1, enrollment.getRequestedAt());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ public void validateApplicationDeadline(LocalDate lectureDate, LocalTime lecture
}
}

public void validateApplicationTime(LocalDateTime approvalTime, LocalDateTime deadline, LocalDateTime now) {
/**
* ์‹ ์ฒญ์ด ์—ด๋ฆฌ๋Š” ์‹œ๊ฐ๋งŒ ๋ณธ๋‹ค. ์Šน์ธํ•œ ๋‚  16:20๋ถ€ํ„ฐ์ด๊ณ , ์Šน์ธ์ด ์ด๋ฏธ 16:20์„ ๋„˜๊ฒผ์œผ๋ฉด ๋‹ค์Œ ๋‚  16:20๋ถ€ํ„ฐ๋‹ค.
*
* ๋งˆ๊ฐ ์‹œ๊ฐ์€ ์—ฌ๊ธฐ์„œ ๋ง‰์ง€ ์•Š๋Š”๋‹ค. ๋งˆ๊ฐ ๋’ค์—๋„ ๋Œ€๊ธฐ ์‹ ์ฒญ์€ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
* ๋งˆ๊ฐ ๋’ค์— ๋“ค์–ด์˜จ ์‹ ์ฒญ์„ ๋Œ€๊ธฐ๋กœ ๋Œ๋ฆฌ๋Š” ์ผ์€ LectureService.enroll์ด ๋งก๋Š”๋‹ค.
*/
public void validateApplicationTime(LocalDateTime approvalTime, LocalDateTime now) {
LocalDateTime openTime = approvalTime.toLocalDate().atTime(16, 20);
if (approvalTime.isAfter(openTime)) {
openTime = openTime.plusDays(1);
Expand All @@ -38,12 +44,5 @@ public void validateApplicationTime(LocalDateTime approvalTime, LocalDateTime de
"์ˆ˜๊ฐ• ์‹ ์ฒญ์€ " + openTime.toLocalDate() + " ์˜คํ›„ 4์‹œ 20๋ถ„๋ถ€ํ„ฐ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค."
);
}

if (deadline != null && now.isAfter(deadline)) {
throw new ResponseStatusException(
HttpStatus.FORBIDDEN,
"์ˆ˜๊ฐ• ์‹ ์ฒญ ๋งˆ๊ฐ ์‹œ๊ฐ„์ด ์ง€๋‚ฌ์Šต๋‹ˆ๋‹ค."
);
}
}
}
Loading
Loading