Skip to content

Commit 285b198

Browse files
authored
Merge pull request #173 from prgrms-web-devcourse-final-project/feature/EA3-167-study-detail
[EA3-167] feature: 스터디 종료 기능 추가
2 parents 493fe8d + 87078b9 commit 285b198

File tree

9 files changed

+92
-19
lines changed

9 files changed

+92
-19
lines changed

src/main/java/grep/neogul_coder/domain/admin/controller/dto/response/AdminStudyResponse.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class AdminStudyResponse {
2222
private Category category;
2323

2424
@Schema(description = "스터디 종료 여부", example = "false")
25-
private boolean isFinished;
25+
private boolean finished;
2626

2727
@Schema(description = "활성화 여부", example = "true")
2828
private boolean activated;
@@ -33,7 +33,7 @@ private AdminStudyResponse(Long id, String name, Category category, boolean isFi
3333
this.id = id;
3434
this.name = name;
3535
this.category = category;
36-
this.isFinished = isFinished;
36+
this.finished = isFinished;
3737
this.activated = activated;
3838
}
3939

@@ -42,7 +42,7 @@ public static AdminStudyResponse from(Study study) {
4242
.id(study.getId())
4343
.name(study.getName())
4444
.category(study.getCategory())
45-
.isFinished(study.getEndDate().toLocalDate().isBefore(LocalDate.now()))
45+
.isFinished(study.isFinished())
4646
.activated(study.getActivated())
4747
.build();
4848
}

src/main/java/grep/neogul_coder/domain/study/Study.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public class Study extends BaseEntity {
4343

4444
private boolean extended;
4545

46+
private boolean finished;
47+
4648
protected Study() {}
4749

4850
@Builder
@@ -60,6 +62,7 @@ private Study(Long originStudyId, String name, Category category, int capacity,
6062
this.introduction = introduction;
6163
this.imageUrl = imageUrl;
6264
this.extended = false;
65+
this.finished = false;
6366
}
6467

6568
public void update(String name, Category category, int capacity, StudyType studyType,
@@ -101,4 +104,8 @@ public boolean alreadyExtended() {
101104
public void extend() {
102105
this.extended = true;
103106
}
107+
108+
public void finish() {
109+
this.finished = true;
110+
}
104111
}

src/main/java/grep/neogul_coder/domain/study/controller/dto/response/StudyItemResponse.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
package grep.neogul_coder.domain.study.controller.dto.response;
22

33
import com.querydsl.core.annotations.QueryProjection;
4-
import grep.neogul_coder.domain.study.Study;
54
import grep.neogul_coder.domain.study.enums.Category;
65
import grep.neogul_coder.domain.study.enums.StudyType;
76
import io.swagger.v3.oas.annotations.media.Schema;
8-
import lombok.Builder;
97
import lombok.Getter;
108

11-
import java.time.LocalDate;
129
import java.time.LocalDateTime;
1310

1411
@Getter
@@ -48,13 +45,11 @@ public class StudyItemResponse {
4845
private StudyType studyType;
4946

5047
@Schema(description = "종료 여부", example = "false")
51-
public boolean isFinished() {
52-
return endDate.toLocalDate().isBefore(LocalDate.now());
53-
}
48+
private boolean finished;
5449

5550
@QueryProjection
5651
public StudyItemResponse(Long studyId, String name, String leaderNickname, int capacity, int currentCount, LocalDateTime startDate,
57-
LocalDateTime endDate, String imageUrl, String introduction, Category category, StudyType studyType) {
52+
LocalDateTime endDate, String imageUrl, String introduction, Category category, StudyType studyType, boolean finished) {
5853
this.studyId = studyId;
5954
this.name = name;
6055
this.leaderNickname = leaderNickname;
@@ -66,5 +61,6 @@ public StudyItemResponse(Long studyId, String name, String leaderNickname, int c
6661
this.introduction = introduction;
6762
this.category = category;
6863
this.studyType = studyType;
64+
this.finished = finished;
6965
}
7066
}

src/main/java/grep/neogul_coder/domain/study/repository/StudyQueryRepository.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ public Page<StudyItemResponse> findMyStudiesPaging(Pageable pageable, Long userI
4242
study.imageUrl,
4343
study.introduction,
4444
study.category,
45-
study.studyType
45+
study.studyType,
46+
study.finished
4647
))
4748
.from(studyMember)
4849
.join(user).on(user.id.eq(studyMember.userId))
@@ -78,7 +79,8 @@ public List<StudyItemResponse> findMyStudies(Long userId) {
7879
study.imageUrl,
7980
study.introduction,
8081
study.category,
81-
study.studyType
82+
study.studyType,
83+
study.finished
8284
))
8385
.from(studyMember)
8486
.join(user).on(user.id.eq(studyMember.userId))

src/main/java/grep/neogul_coder/domain/study/repository/StudyRepository.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
import org.springframework.data.jpa.repository.Query;
77
import org.springframework.data.repository.query.Param;
88

9-
import java.util.Optional;
9+
import java.time.LocalDateTime;
1010
import java.util.List;
11+
import java.util.Optional;
1112

1213
public interface StudyRepository extends JpaRepository<Study, Long> {
1314
List<Study> findByIdIn(List<Long> studyIds);
@@ -19,4 +20,10 @@ public interface StudyRepository extends JpaRepository<Study, Long> {
1920
@Modifying(clearAutomatically = true)
2021
@Query("update Study s set s.activated = false where s.id = :studyId")
2122
void deactivateByStudyId(@Param("studyId") Long studyId);
23+
24+
@Query("select s from Study s where s.endDate >= :endDateStart and s.endDate < :endDateEnd and s.finished = false and s.activated = true")
25+
List<Study> findStudiesEndingIn7Days(@Param("endDateStart") LocalDateTime endDateStart, @Param("endDateEnd") LocalDateTime endDateEnd);
26+
27+
@Query("select s from Study s where s.endDate < :now and s.finished = false and s.activated = true")
28+
List<Study> findStudiesToBeFinished(@Param("now") LocalDateTime now);
2229
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package grep.neogul_coder.domain.study.scheduler;
2+
3+
import grep.neogul_coder.domain.study.Study;
4+
import grep.neogul_coder.domain.study.service.StudySchedulerService;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.scheduling.annotation.Scheduled;
7+
import org.springframework.stereotype.Component;
8+
9+
import java.util.List;
10+
11+
@Component
12+
@RequiredArgsConstructor
13+
public class StudyScheduler {
14+
15+
private final StudySchedulerService studySchedulerService;
16+
17+
@Scheduled(cron = "0 0 0 * * *")
18+
public void processEndingStudies() {
19+
List<Study> studiesEndingIn7Days = studySchedulerService.findStudiesEndingIn7Days();
20+
21+
studySchedulerService.finalizeStudies();
22+
}
23+
}

src/main/java/grep/neogul_coder/domain/study/service/StudyManagementService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
import java.util.List;
2020
import java.util.Random;
2121

22-
import static grep.neogul_coder.domain.study.enums.StudyMemberRole.*;
22+
import static grep.neogul_coder.domain.study.enums.StudyMemberRole.LEADER;
23+
import static grep.neogul_coder.domain.study.enums.StudyMemberRole.MEMBER;
2324
import static grep.neogul_coder.domain.study.exception.code.StudyErrorCode.*;
2425

2526
@Transactional(readOnly = true)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package grep.neogul_coder.domain.study.service;
2+
3+
import grep.neogul_coder.domain.study.Study;
4+
import grep.neogul_coder.domain.study.repository.StudyRepository;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.stereotype.Service;
7+
import org.springframework.transaction.annotation.Transactional;
8+
9+
import java.time.LocalDate;
10+
import java.time.LocalDateTime;
11+
import java.util.List;
12+
13+
@Transactional(readOnly = true)
14+
@RequiredArgsConstructor
15+
@Service
16+
public class StudySchedulerService {
17+
18+
private final StudyRepository studyRepository;
19+
20+
public List<Study> findStudiesEndingIn7Days() {
21+
LocalDate targetEndDate = LocalDate.now().plusDays(7);
22+
LocalDateTime endDateStart = targetEndDate.atStartOfDay();
23+
LocalDateTime endDateEnd = targetEndDate.plusDays(1).atStartOfDay();
24+
25+
return studyRepository.findStudiesEndingIn7Days(endDateStart, endDateEnd);
26+
}
27+
28+
@Transactional
29+
public void finalizeStudies() {
30+
LocalDateTime now = LocalDateTime.now();
31+
List<Study> studiesToBeFinished = studyRepository.findStudiesToBeFinished(now);
32+
33+
for (Study study : studiesToBeFinished) {
34+
study.finish();
35+
}
36+
}
37+
}

src/main/resources/data.sql

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ INSERT INTO member (email, password, nickname, profile_image_url, role, oauth_id
44
INSERT INTO member (email, password, nickname, profile_image_url, role, oauth_id, oauth_provider) VALUES ('[email protected]', '$2a$10$WZzvlwlN6FVtQvGUXw2CIeNQvT5fPfA4qN99NisD2GOyCeuC4W0t2','NPy2EpXd$6', 'https://placekitten.com/842/456', 'ROLE_USER', NULL, NULL);
55
INSERT INTO member (email, password, nickname, profile_image_url, role, oauth_id, oauth_provider) VALUES ('[email protected]', '$2a$10$WZzvlwlN6FVtQvGUXw2CIeNQvT5fPfA4qN99NisD2GOyCeuC4W0t2', '$c3VDMSkF)','https://placekitten.com/812/623', 'ROLE_ADMIN', NULL, NULL);
66

7-
INSERT INTO study (origin_study_id, name, category, capacity, current_count, study_type, location, start_date, end_date, introduction, image_url, extended, activated) VALUES (NULL, '자바 스터디', 'IT', 10, 1, 'ONLINE', NULL, '2025-08-01', '2025-12-31', '자바 스터디에 오신 것을 환영합니다.', 'https://example.com/image.jpg', FALSE, TRUE);
8-
INSERT INTO study (origin_study_id, name, category, capacity, current_count, study_type, location, start_date, end_date, introduction, image_url, extended, activated) VALUES (NULL, '파이썬 스터디', 'IT', 8, 1, 'OFFLINE', '대구', '2025-09-01', '2026-01-31', '파이썬 기초부터 심화까지 학습합니다.', 'https://example.com/python.jpg', FALSE, TRUE);
9-
INSERT INTO study (origin_study_id, name, category, capacity, current_count, study_type, location, start_date, end_date, introduction, image_url, extended, activated) VALUES (NULL, '디자인 스터디', 'DESIGN', 6, 1, 'HYBRID', '서울', '2025-07-15', '2025-10-15', 'UI/UX 디자인 실습 중심 스터디입니다.', 'https://example.com/design.jpg', FALSE, TRUE);
10-
INSERT INTO study (origin_study_id, name, category, capacity, current_count, study_type, location, start_date, end_date, introduction, image_url, extended, activated) VALUES (NULL, '7급 공무원 스터디', 'EXAM', 12, 1, 'ONLINE', NULL, '2025-08-10', '2025-12-20', '7급 공무원 대비 스터디입니다.', 'https://example.com/exam.jpg', FALSE, TRUE);
11-
INSERT INTO study (origin_study_id, name, category, capacity, current_count, study_type, location, start_date, end_date, introduction, image_url, extended, activated) VALUES (NULL, '토익 스터디', 'LANGUAGE', 9, 1, 'OFFLINE', '광주', '2025-09-05', '2026-02-28', '토익 스터디입니다.', 'https://example.com/datascience.jpg', FALSE, TRUE);
7+
INSERT INTO study (origin_study_id, name, category, capacity, current_count, study_type, location, start_date, end_date, introduction, image_url, extended, activated, finished) VALUES (NULL, '자바 스터디', 'IT', 10, 1, 'ONLINE', NULL, '2025-08-01', '2025-12-31', '자바 스터디에 오신 것을 환영합니다.', 'https://example.com/image.jpg', FALSE, TRUE, FALSE);
8+
INSERT INTO study (origin_study_id, name, category, capacity, current_count, study_type, location, start_date, end_date, introduction, image_url, extended, activated, finished) VALUES (NULL, '파이썬 스터디', 'IT', 8, 1, 'OFFLINE', '대구', '2025-09-01', '2026-01-31', '파이썬 기초부터 심화까지 학습합니다.', 'https://example.com/python.jpg', FALSE, TRUE, FALSE);
9+
INSERT INTO study (origin_study_id, name, category, capacity, current_count, study_type, location, start_date, end_date, introduction, image_url, extended, activated, finished) VALUES (NULL, '디자인 스터디', 'DESIGN', 6, 1, 'HYBRID', '서울', '2025-07-15', '2025-10-15', 'UI/UX 디자인 실습 중심 스터디입니다.', 'https://example.com/design.jpg', FALSE, TRUE, FALSE);
10+
INSERT INTO study (origin_study_id, name, category, capacity, current_count, study_type, location, start_date, end_date, introduction, image_url, extended, activated, finished) VALUES (NULL, '7급 공무원 스터디', 'EXAM', 12, 1, 'ONLINE', NULL, '2025-08-10', '2025-12-20', '7급 공무원 대비 스터디입니다.', 'https://example.com/exam.jpg', FALSE, TRUE, FALSE);
11+
INSERT INTO study (origin_study_id, name, category, capacity, current_count, study_type, location, start_date, end_date, introduction, image_url, extended, activated, finished) VALUES (NULL, '토익 스터디', 'LANGUAGE', 9, 1, 'OFFLINE', '광주', '2025-09-05', '2026-02-28', '토익 스터디입니다.', 'https://example.com/datascience.jpg', FALSE, TRUE, FALSE);
1212

1313
INSERT INTO study_post (study_id, user_id, title, category, content) VALUES (5, 3, '자바 스터디 1주차 공지.', 'NOTICE', '1주차 스터디 내용은 가위바위보 게임 만들기 입니다. 모두 각자 만드시고 설명 하는 시간을 가지겠습니다.');
1414
INSERT INTO study_post (study_id, user_id, title, category, content) VALUES (4, 4, '익명 클래스 자료 공유', 'FREE', '동물 이라는 인터페이스가 있을때 구현체는 강아지, 고양이 등이 있습니다. 구현을 하면 여러 구현 클래스가 필요합니다 이를 줄이기 위해 익명클래스를 사용할 수 있습니다.');

0 commit comments

Comments
 (0)