Skip to content

Commit c7a4953

Browse files
Merge pull request #201 from prgrms-web-devcourse-final-project/develop
chore: develop → main 브랜치 머지
2 parents 32dcd63 + eef7014 commit c7a4953

31 files changed

+676
-209
lines changed

src/main/java/grep/neogulcoder/domain/recruitment/post/controller/RecruitmentPostController.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import grep.neogulcoder.domain.recruitment.post.controller.dto.request.RecruitmentPostStatusUpdateRequest;
44
import grep.neogulcoder.domain.recruitment.post.controller.dto.request.RecruitmentPostUpdateRequest;
5-
import grep.neogulcoder.domain.recruitment.post.controller.dto.response.RecruitmentApplicationPagingInfo;
65
import grep.neogulcoder.domain.recruitment.post.controller.dto.response.RecruitmentPostInfo;
76
import grep.neogulcoder.domain.recruitment.post.controller.dto.response.RecruitmentPostPagingInfo;
87
import grep.neogulcoder.domain.recruitment.post.service.RecruitmentPostService;
@@ -76,11 +75,4 @@ public ApiResponse<Long> changeStatus(@PathVariable("recruitment-post-id") long
7675
long postId = recruitmentPostService.updateStatus(request.toServiceRequest(), recruitmentPostId, userDetails.getUserId());
7776
return ApiResponse.success(postId);
7877
}
79-
80-
@GetMapping("{recruitment-post-id}/applications")
81-
public ApiResponse<RecruitmentApplicationPagingInfo> getApplications(@PageableDefault(size = 5) Pageable pageable,
82-
@PathVariable("recruitment-post-id") long recruitmentPostId) {
83-
return ApiResponse.success(new RecruitmentApplicationPagingInfo());
84-
}
85-
8678
}

src/main/java/grep/neogulcoder/domain/recruitment/post/controller/RecruitmentPostSpecification.java

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import grep.neogulcoder.domain.recruitment.post.controller.dto.request.RecruitmentPostStatusUpdateRequest;
44
import grep.neogulcoder.domain.recruitment.post.controller.dto.request.RecruitmentPostUpdateRequest;
5-
import grep.neogulcoder.domain.recruitment.post.controller.dto.response.RecruitmentApplicationPagingInfo;
65
import grep.neogulcoder.domain.recruitment.post.controller.dto.response.RecruitmentPostInfo;
76
import grep.neogulcoder.domain.recruitment.post.controller.dto.response.RecruitmentPostPagingInfo;
87
import grep.neogulcoder.domain.study.enums.Category;
@@ -144,32 +143,4 @@ ApiResponse<RecruitmentPostPagingInfo> getMyPostPagingInfo(Pageable pageable, Ca
144143
"""
145144
)
146145
ApiResponse<RecruitmentPostPagingInfo> getPagingInfo(Pageable pageable, Category category, StudyType studyType, String keyword);
147-
148-
@Operation(
149-
summary = "스터디 신청한 회원 목록 페이징 조회",
150-
description = """
151-
특정 모집글에 신청한 회원 목록을 페이징하여 조회합니다.
152-
153-
✅ 요청 형식:
154-
`GET /recruitment-posts/{id}/applications?page=0&size=5`
155-
156-
✅ 응답 예시:
157-
```json
158-
{
159-
"applicationInfos": [
160-
{
161-
"nickname": "테스터",
162-
"buddyEnergy": 30,
163-
"createdDate": "2025-07-13T15:30:00",
164-
"applicationReason": "자바를 더 공부 하고싶어요!"
165-
}
166-
],
167-
"totalPage": 3,
168-
"totalElementCount": 20
169-
}
170-
```
171-
"""
172-
)
173-
ApiResponse<RecruitmentApplicationPagingInfo> getApplications(Pageable pageable, long recruitmentPostId);
174-
175146
}

src/main/java/grep/neogulcoder/domain/recruitment/post/controller/dto/response/RecruitmentApplicationPagingInfo.java

Lines changed: 0 additions & 36 deletions
This file was deleted.

src/main/java/grep/neogulcoder/domain/study/controller/StudyController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public ApiResponse<StudyHeaderResponse> getStudyHeader(@PathVariable("studyId")
4646

4747
@GetMapping("/{studyId}")
4848
public ApiResponse<StudyResponse> getStudy(@PathVariable("studyId") Long studyId) {
49-
return ApiResponse.success(new StudyResponse());
49+
return ApiResponse.success(studyService.getStudy(studyId));
5050
}
5151

5252
@GetMapping("/me/images")
@@ -77,7 +77,7 @@ public ApiResponse<Long> createStudy(@RequestPart("request") @Valid StudyCreateR
7777
return ApiResponse.success(id);
7878
}
7979

80-
@PutMapping("/{studyId}")
80+
@PutMapping(value = "/{studyId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
8181
public ApiResponse<Void> updateStudy(@PathVariable("studyId") Long studyId,
8282
@RequestPart @Valid StudyUpdateRequest request,
8383
@RequestPart(value = "image", required = false) MultipartFile image,

src/main/java/grep/neogulcoder/domain/study/controller/dto/request/StudyCreateRequest.java

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class StudyCreateRequest {
3535
private String location;
3636

3737
@NotNull
38-
@Schema(description = "시작일", example = "2025-07-15")
38+
@Schema(description = "시작일", example = "2025-07-25T10:00:00")
3939
private LocalDateTime startDate;
4040

4141
@NotNull
@@ -45,14 +45,11 @@ public class StudyCreateRequest {
4545
@Schema(description = "스터디 소개", example = "자바 스터디입니다.")
4646
private String introduction;
4747

48-
@Schema(description = "대표 이미지", example = "http://localhost:8083/image.jpg")
49-
private String imageUrl;
50-
5148
private StudyCreateRequest() {}
5249

5350
@Builder
5451
private StudyCreateRequest(String name, Category category, int capacity, StudyType studyType, String location,
55-
LocalDateTime startDate, LocalDateTime endDate, String introduction, String imageUrl) {
52+
LocalDateTime startDate, LocalDateTime endDate, String introduction) {
5653
this.name = name;
5754
this.category = category;
5855
this.capacity = capacity;
@@ -61,20 +58,19 @@ private StudyCreateRequest(String name, Category category, int capacity, StudyTy
6158
this.startDate = startDate;
6259
this.endDate = endDate;
6360
this.introduction = introduction;
64-
this.imageUrl = imageUrl;
6561
}
6662

6763
public Study toEntity(String imageUrl) {
6864
return Study.builder()
69-
.name(this.name)
70-
.category(this.category)
71-
.capacity(this.capacity)
72-
.studyType(this.studyType)
73-
.location(this.location)
74-
.startDate(this.startDate)
75-
.endDate(this.endDate)
76-
.introduction(this.introduction)
77-
.imageUrl(imageUrl)
78-
.build();
65+
.name(this.name)
66+
.category(this.category)
67+
.capacity(this.capacity)
68+
.studyType(this.studyType)
69+
.location(this.location)
70+
.startDate(this.startDate)
71+
.endDate(this.endDate)
72+
.introduction(this.introduction)
73+
.imageUrl(imageUrl)
74+
.build();
7975
}
8076
}

src/main/java/grep/neogulcoder/domain/study/controller/dto/request/StudyUpdateRequest.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,18 @@ public class StudyUpdateRequest {
3838
@Schema(description = "스터디 소개", example = "자바 스터디입니다.")
3939
private String introduction;
4040

41-
@Schema(description = "대표 이미지", example = "http://localhost:8083/image.jpg")
42-
private String imageUrl;
43-
4441
private StudyUpdateRequest() {}
4542

4643
@Builder
47-
private StudyUpdateRequest(String name, Category category, int capacity, StudyType studyType, String location,
48-
LocalDateTime startDate, String introduction, String imageUrl) {
44+
private StudyUpdateRequest(String name, Category category, int capacity, StudyType studyType,
45+
String location, LocalDateTime startDate, String introduction) {
4946
this.name = name;
5047
this.category = category;
5148
this.capacity = capacity;
5249
this.studyType = studyType;
5350
this.location = location;
5451
this.startDate = startDate;
5552
this.introduction = introduction;
56-
this.imageUrl = imageUrl;
5753
}
5854

5955
public Study toEntity(String imageUrl) {

src/main/java/grep/neogulcoder/domain/study/controller/dto/response/StudyResponse.java

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package grep.neogulcoder.domain.study.controller.dto.response;
22

3-
import grep.neogulcoder.domain.attendance.controller.dto.response.AttendanceResponse;
43
import grep.neogulcoder.domain.calender.controller.dto.response.TeamCalendarResponse;
5-
import grep.neogulcoder.domain.studypost.controller.dto.StudyPostListResponse;
4+
import grep.neogulcoder.domain.study.Study;
5+
import grep.neogulcoder.domain.studypost.controller.dto.response.FreePostInfo;
6+
import grep.neogulcoder.domain.studypost.controller.dto.response.NoticePostInfo;
67
import io.swagger.v3.oas.annotations.media.Schema;
8+
import lombok.Builder;
79
import lombok.Getter;
810

911
import java.util.List;
@@ -26,15 +28,39 @@ public class StudyResponse {
2628
@Schema(description = "스터디 총 게시물 수", example = "10")
2729
private int totalPostCount;
2830

29-
@Schema(description = "출석 정보")
30-
private List<AttendanceResponse> attendances;
31+
@Schema(description = "팀 달력")
32+
private List<TeamCalendarResponse> teamCalendars;
3133

32-
@Schema(description = "출석률", example = "60")
33-
private int attendanceRate;
34+
@Schema(description = "스터디 공지글 2개")
35+
private List<NoticePostInfo> noticePosts;
3436

35-
@Schema(description = "팀 달력")
36-
private List<TeamCalendarResponse> teamCalenders;
37+
@Schema(description = "스터디 자유글 3개")
38+
private List<FreePostInfo> freePosts;
39+
40+
@Builder
41+
private StudyResponse(int progressDays, int totalDays, int capacity, int currentCount, int totalPostCount,
42+
List<TeamCalendarResponse> teamCalendars, List<NoticePostInfo> noticePosts, List<FreePostInfo> freePosts) {
43+
this.progressDays = progressDays;
44+
this.totalDays = totalDays;
45+
this.capacity = capacity;
46+
this.currentCount = currentCount;
47+
this.totalPostCount = totalPostCount;
48+
this.teamCalendars = teamCalendars;
49+
this.noticePosts = noticePosts;
50+
this.freePosts = freePosts;
51+
}
3752

38-
@Schema(description = "게시글 리스트")
39-
private List<StudyPostListResponse> studyPosts;
53+
public static StudyResponse from(Study study, int progressDays, int totalDays, int totalPostCount,
54+
List<TeamCalendarResponse> teamCalendars, List<NoticePostInfo> noticePosts, List<FreePostInfo> freePosts) {
55+
return StudyResponse.builder()
56+
.progressDays(progressDays)
57+
.totalDays(totalDays)
58+
.capacity(study.getCapacity())
59+
.currentCount(study.getCurrentCount())
60+
.totalPostCount(totalPostCount)
61+
.teamCalendars(teamCalendars)
62+
.noticePosts(noticePosts)
63+
.freePosts(freePosts)
64+
.build();
65+
}
4066
}

src/main/java/grep/neogulcoder/domain/study/service/StudyService.java

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package grep.neogulcoder.domain.study.service;
22

3+
import grep.neogulcoder.domain.calender.controller.dto.response.TeamCalendarResponse;
4+
import grep.neogulcoder.domain.calender.service.TeamCalendarService;
35
import grep.neogulcoder.domain.recruitment.post.repository.RecruitmentPostRepository;
46
import grep.neogulcoder.domain.study.Study;
57
import grep.neogulcoder.domain.study.StudyMember;
@@ -12,6 +14,10 @@
1214
import grep.neogulcoder.domain.study.repository.StudyMemberRepository;
1315
import grep.neogulcoder.domain.study.repository.StudyQueryRepository;
1416
import grep.neogulcoder.domain.study.repository.StudyRepository;
17+
import grep.neogulcoder.domain.studypost.controller.dto.response.FreePostInfo;
18+
import grep.neogulcoder.domain.studypost.controller.dto.response.NoticePostInfo;
19+
import grep.neogulcoder.domain.studypost.repository.StudyPostQueryRepository;
20+
import grep.neogulcoder.domain.studypost.repository.StudyPostRepository;
1521
import grep.neogulcoder.domain.users.entity.User;
1622
import grep.neogulcoder.domain.users.repository.UserRepository;
1723
import grep.neogulcoder.global.exception.business.BusinessException;
@@ -27,6 +33,8 @@
2733
import org.springframework.web.multipart.MultipartFile;
2834

2935
import java.io.IOException;
36+
import java.time.LocalDate;
37+
import java.time.temporal.ChronoUnit;
3038
import java.util.List;
3139
import java.util.Optional;
3240

@@ -46,6 +54,9 @@ public class StudyService {
4654
private final RecruitmentPostRepository recruitmentPostRepository;
4755
private final StudyMemberQueryRepository studyMemberQueryRepository;
4856
private final UserRepository userRepository;
57+
private final StudyPostRepository studyPostRepository;
58+
private final StudyPostQueryRepository studyPostQueryRepository;
59+
private final TeamCalendarService teamCalendarService;
4960

5061
public StudyItemPagingResponse getMyStudiesPaging(Pageable pageable, Long userId, Boolean finished) {
5162
Page<StudyItemResponse> page = studyQueryRepository.findMyStudiesPaging(pageable, userId, finished);
@@ -62,12 +73,30 @@ public List<StudyItemResponse> getMyStudies(Long userId) {
6273
}
6374

6475
public StudyHeaderResponse getStudyHeader(Long studyId) {
65-
Study study = studyRepository.findByIdAndActivatedTrue(studyId)
66-
.orElseThrow(() -> new NotFoundException(STUDY_NOT_FOUND));
76+
Study study = findValidStudy(studyId);
6777

6878
return StudyHeaderResponse.from(study);
6979
}
7080

81+
public StudyResponse getStudy(Long studyId) {
82+
Study study = findValidStudy(studyId);
83+
84+
int progressDays = (int) ChronoUnit.DAYS.between(study.getStartDate().toLocalDate(), LocalDate.now()) + 1;
85+
int totalDays = (int) ChronoUnit.DAYS.between(study.getStartDate().toLocalDate(), study.getEndDate().toLocalDate()) + 1;
86+
progressDays = Math.max(0, Math.min(progressDays, totalDays));
87+
int totalPostCount = studyPostRepository.countByStudyIdAndActivatedTrue(studyId);
88+
89+
LocalDate now = LocalDate.now();
90+
int currentYear = now.getYear();
91+
int currentMonth = now.getMonthValue();
92+
List<TeamCalendarResponse> teamCalendars = teamCalendarService.findByMonth(studyId, currentYear, currentMonth);
93+
94+
List<NoticePostInfo> noticePosts = studyPostQueryRepository.findLatestNoticeInfoBy(studyId);
95+
List<FreePostInfo> freePosts = studyPostQueryRepository.findLatestFreeInfoBy(studyId);
96+
97+
return StudyResponse.from(study, progressDays, totalDays, totalPostCount, teamCalendars, noticePosts, freePosts);
98+
}
99+
71100
public List<StudyImageResponse> getStudyImages(Long userId) {
72101
List<Study> myStudiesImage = studyMemberRepository.findStudiesByUserId(userId);
73102

@@ -77,8 +106,7 @@ public List<StudyImageResponse> getStudyImages(Long userId) {
77106
}
78107

79108
public StudyInfoResponse getMyStudyContent(Long studyId, Long userId) {
80-
Study study = studyRepository.findByIdAndActivatedTrue(studyId)
81-
.orElseThrow(() -> new NotFoundException(STUDY_NOT_FOUND));
109+
Study study = findValidStudy(studyId);
82110

83111
validateStudyMember(studyId, userId);
84112
validateStudyLeader(studyId, userId);
@@ -118,8 +146,7 @@ public Long createStudy(StudyCreateRequest request, Long userId, MultipartFile i
118146

119147
@Transactional
120148
public void updateStudy(Long studyId, StudyUpdateRequest request, Long userId, MultipartFile image) throws IOException {
121-
Study study = studyRepository.findById(studyId)
122-
.orElseThrow(() -> new NotFoundException(STUDY_NOT_FOUND));
149+
Study study = findValidStudy(studyId);
123150

124151
validateLocation(request.getStudyType(), request.getLocation());
125152
validateStudyMember(studyId, userId);
@@ -142,8 +169,7 @@ public void updateStudy(Long studyId, StudyUpdateRequest request, Long userId, M
142169

143170
@Transactional
144171
public void deleteStudy(Long studyId, Long userId) {
145-
Study study = studyRepository.findById(studyId)
146-
.orElseThrow(() -> new NotFoundException(STUDY_NOT_FOUND));
172+
Study study = findValidStudy(studyId);
147173

148174
validateStudyMember(studyId, userId);
149175
validateStudyLeader(studyId, userId);
@@ -156,12 +182,16 @@ public void deleteStudy(Long studyId, Long userId) {
156182

157183
@Transactional
158184
public void deleteStudyByAdmin(Long studyId) {
159-
Study study = studyRepository.findById(studyId)
160-
.orElseThrow(() -> new NotFoundException(STUDY_NOT_FOUND));
185+
Study study = findValidStudy(studyId);
161186

162187
study.delete();
163188
}
164189

190+
private Study findValidStudy(Long studyId) {
191+
return studyRepository.findById(studyId)
192+
.orElseThrow(() -> new NotFoundException(STUDY_NOT_FOUND));
193+
}
194+
165195
private static void validateLocation(StudyType studyType, String location) {
166196
if ((studyType == StudyType.OFFLINE || studyType == StudyType.HYBRID) && (location == null || location.isBlank())) {
167197
throw new BusinessException(STUDY_LOCATION_REQUIRED);

0 commit comments

Comments
 (0)