Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions src/main/java/com/daytodo/domain/course/entity/Course.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ public Course(
this.courseStatus = CourseStatus.PLANNING;
}

// 코스 시작 (계획 중 -> 진행 중). courseDate 가 도래해 투데이 조회 시점에 승격된다.
public void start() {
this.courseStatus = CourseStatus.IN_PROGRESS;
}

// 코스 종료
public void complete() {
this.courseStatus = CourseStatus.COMPLETED;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.springframework.data.repository.query.Param;

import java.time.LocalDate;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

Expand Down Expand Up @@ -38,21 +39,22 @@ List<Course> findAllByOwnerIdAndCourseDateBetweenOrderByCreatedAtDesc(

/*
* 투데이 코스 조회
* 사용자가 참여 중인 코스 중 해당 날짜의 진행 중 코스를 조회한다.
* 사용자가 참여 중인 코스 중 해당 날짜의 코스를 조회한다.
* PLANNING 은 조회 시점에 IN_PROGRESS 로 승격되므로 두 상태를 모두 대상으로 한다.
* 이론상 1건이지만 데이터 이상으로 여러 건일 수 있어 최근 생성 순으로 정렬해 첫 건을 사용한다.
*/
@Query("""
select cm.course from CourseMember cm
where cm.user.id = :userId
and cm.memberStatus = :memberStatus
and cm.course.courseDate = :courseDate
and cm.course.courseStatus = :courseStatus
and cm.course.courseStatus in :courseStatuses
order by cm.course.createdAt desc, cm.course.courseId desc
""")
List<Course> findMemberCoursesByDateAndStatus(
List<Course> findMemberCoursesByDateAndStatuses(
@Param("userId") Long userId,
@Param("courseDate") LocalDate courseDate,
@Param("courseStatus") CourseStatus courseStatus,
@Param("courseStatuses") Collection<CourseStatus> courseStatuses,
@Param("memberStatus") MemberStatus memberStatus
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,20 @@ public class CourseService {
private final RecommendationLikeRepository recommendationLikeRepository;
private final PlaceRepository placeRepository;
private final RecommendationCommentRepository recommendationCommentRepository;
private final TodayCoursePromoter todayCoursePromoter;

private final Clock clock;

// 진행중 목록/배너가 IN_PROGRESS 상태에 의존하므로, 조회 전에 오늘 날짜의
// PLANNING 코스를 IN_PROGRESS 로 승격시킨다(투데이 조회와 동일 규칙). 쓰기 발생.
@Transactional
public CourseResponse.Courses getCourses(Long userId, LocalDate startDate, LocalDate endDate) {
userService.getActiveUser(userId);
validatePeriod(startDate, endDate);
LocalDate today = LocalDate.now(clock);

todayCoursePromoter.promoteDueCourses(userId, today);

List<Course> inProgress = courseRepository.findMemberCoursesByStatus(
userId, MemberStatus.JOINED, CourseStatus.IN_PROGRESS
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.daytodo.domain.course.service;

import com.daytodo.domain.course.entity.Course;
import com.daytodo.domain.course.enums.CourseStatus;
import com.daytodo.domain.course.enums.MemberStatus;
import com.daytodo.domain.course.repository.CourseRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;

/**
* 코스 상태 지연 전환 헬퍼.
* 별도 스케줄러가 없으므로, courseDate 가 도래한 PLANNING 코스를
* 홈/투데이 조회 진입 시점에 IN_PROGRESS 로 승격시킨다.
* 두 조회 경로가 같은 규칙을 쓰도록 승격 로직을 한곳에 모은다.
*/
@Component
@RequiredArgsConstructor
public class TodayCoursePromoter {

private final CourseRepository courseRepository;

/**
* 사용자가 참여 중인 코스 중 courseDate 가 today 인 PLANNING 코스를 IN_PROGRESS 로 승격.
* 호출 측 트랜잭션에 합류하며(REQUIRED), 변경 감지로 커밋 시 반영된다.
*/
@Transactional
public void promoteDueCourses(Long userId, LocalDate today) {
courseRepository.findMemberCoursesByDateAndStatuses(
userId,
today,
List.of(CourseStatus.PLANNING),
MemberStatus.JOINED
).forEach(Course::start);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,23 @@ public class TodayCourseService {
private final CourseMemberRepository courseMemberRepository;
private final CoursePlaceRepository coursePlaceRepository;
private final MemoryPhotoRepository memoryPhotoRepository;
private final TodayCoursePromoter todayCoursePromoter;

/*
* 투데이 코스 조회
* 오늘 진행 중인 코스가 없으면 예외가 아니라 todayCourse: null 로 응답한다.
* 오늘 날짜의 코스가 없으면 예외가 아니라 todayCourse: null 로 응답한다.
* 별도의 상태 전이 스케줄러가 없으므로, 조회 진입 시점에 오늘 날짜의 PLANNING
* 코스를 IN_PROGRESS 로 승격시킨다(홈/투데이 공용 규칙).
*/
@Transactional(readOnly = true)
@Transactional
public TodayCourseResponse.GetTodayCourse getTodayCourse(Long userId) {
Optional<Course> todayCourse = courseRepository.findMemberCoursesByDateAndStatus(
LocalDate today = LocalDate.now();
todayCoursePromoter.promoteDueCourses(userId, today);

Optional<Course> todayCourse = courseRepository.findMemberCoursesByDateAndStatuses(
userId,
LocalDate.now(),
CourseStatus.IN_PROGRESS,
today,
List.of(CourseStatus.IN_PROGRESS),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
MemberStatus.JOINED
).stream().findFirst();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ class CourseQueryValidationTest {
@Test
@DisplayName("투데이 코스 관련 JPQL이 정상적으로 실행된다")
void queriesRun() {
assertThat(courseRepository.findMemberCoursesByDateAndStatus(
1L, LocalDate.now(), CourseStatus.IN_PROGRESS, MemberStatus.JOINED
assertThat(courseRepository.findMemberCoursesByDateAndStatuses(
1L, LocalDate.now(),
java.util.List.of(CourseStatus.PLANNING, CourseStatus.IN_PROGRESS),
MemberStatus.JOINED
)).isEmpty();

assertThat(courseMemberRepository.findMembersByCourseId(1L, MemberStatus.JOINED)).isEmpty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class CourseServiceTest {
@Mock RecommendationLikeRepository recommendationLikeRepository;
@Mock PlaceRepository placeRepository;
@Mock RecommendationCommentRepository recommendationCommentRepository;
@Mock TodayCoursePromoter todayCoursePromoter;

CourseService courseService;
User user;
Expand All @@ -88,6 +89,7 @@ void setUp() {
recommendationLikeRepository,
placeRepository,
recommendationCommentRepository,
todayCoursePromoter,
CLOCK
);
user = new User("user@example.com", "password", "user", null, LoginType.LOCAL);
Expand Down