diff --git a/src/main/java/grep/neogulcoder/domain/attendance/controller/AttendanceController.java b/src/main/java/grep/neogulcoder/domain/attendance/controller/AttendanceController.java index ece8430f..4bb32737 100644 --- a/src/main/java/grep/neogulcoder/domain/attendance/controller/AttendanceController.java +++ b/src/main/java/grep/neogulcoder/domain/attendance/controller/AttendanceController.java @@ -5,6 +5,7 @@ import grep.neogulcoder.global.auth.Principal; import grep.neogulcoder.global.response.ApiResponse; import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; @@ -16,17 +17,17 @@ public class AttendanceController implements AttendanceSpecification { private final AttendanceService attendanceService; @GetMapping - public ApiResponse getAttendances(@PathVariable("studyId") Long studyId, - @AuthenticationPrincipal Principal userDetails) { + public ResponseEntity> getAttendances(@PathVariable("studyId") Long studyId, + @AuthenticationPrincipal Principal userDetails) { AttendanceInfoResponse attendances = attendanceService.getAttendances(studyId, userDetails.getUserId()); - return ApiResponse.success(attendances); + return ResponseEntity.ok(ApiResponse.success(attendances)); } @PostMapping - public ApiResponse createAttendance(@PathVariable("studyId") Long studyId, + public ResponseEntity> createAttendance(@PathVariable("studyId") Long studyId, @AuthenticationPrincipal Principal userDetails) { Long userId = userDetails.getUserId(); Long id = attendanceService.createAttendance(studyId, userId); - return ApiResponse.success(id); + return ResponseEntity.ok(ApiResponse.success(id)); } } diff --git a/src/main/java/grep/neogulcoder/domain/attendance/controller/AttendanceSpecification.java b/src/main/java/grep/neogulcoder/domain/attendance/controller/AttendanceSpecification.java index 89d24dd4..b43d3132 100644 --- a/src/main/java/grep/neogulcoder/domain/attendance/controller/AttendanceSpecification.java +++ b/src/main/java/grep/neogulcoder/domain/attendance/controller/AttendanceSpecification.java @@ -5,13 +5,14 @@ import grep.neogulcoder.global.response.ApiResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; @Tag(name = "Attendance", description = "출석 API") public interface AttendanceSpecification { @Operation(summary = "출석 조회", description = "일주일 단위로 출석을 조회합니다.") - ApiResponse getAttendances(Long studyId, Principal userDetails); + ResponseEntity> getAttendances(Long studyId, Principal userDetails); @Operation(summary = "출석 체크", description = "스터디에 출석을 합니다.") - ApiResponse createAttendance(Long studyId, Principal userDetails); + ResponseEntity> createAttendance(Long studyId, Principal userDetails); } diff --git a/src/main/java/grep/neogulcoder/domain/attendance/controller/dto/response/AttendanceInfoResponse.java b/src/main/java/grep/neogulcoder/domain/attendance/controller/dto/response/AttendanceInfoResponse.java index 96bddc85..12394ce3 100644 --- a/src/main/java/grep/neogulcoder/domain/attendance/controller/dto/response/AttendanceInfoResponse.java +++ b/src/main/java/grep/neogulcoder/domain/attendance/controller/dto/response/AttendanceInfoResponse.java @@ -10,10 +10,10 @@ public class AttendanceInfoResponse { @Schema(description = "출석일 목록") - private List attendances; + private final List attendances; @Schema(description = "출석률", example = "50") - private int attendanceRate; + private final int attendanceRate; @Builder private AttendanceInfoResponse(List attendances, int attendanceRate) { diff --git a/src/main/java/grep/neogulcoder/domain/attendance/controller/dto/response/AttendanceResponse.java b/src/main/java/grep/neogulcoder/domain/attendance/controller/dto/response/AttendanceResponse.java index aad78cfe..6208ffa6 100644 --- a/src/main/java/grep/neogulcoder/domain/attendance/controller/dto/response/AttendanceResponse.java +++ b/src/main/java/grep/neogulcoder/domain/attendance/controller/dto/response/AttendanceResponse.java @@ -11,13 +11,13 @@ public class AttendanceResponse { @Schema(description = "스터디 Id", example = "1") - private Long studyId; + private final Long studyId; @Schema(description = "유저 Id", example = "2") - private Long userId; + private final Long userId; @Schema(description = "출석일", example = "2025-07-10") - private LocalDate attendanceDate; + private final LocalDate attendanceDate; @Builder private AttendanceResponse(Long studyId, Long userId, LocalDate attendanceDate) { diff --git a/src/main/java/grep/neogulcoder/domain/attendance/service/AttendanceService.java b/src/main/java/grep/neogulcoder/domain/attendance/service/AttendanceService.java index cc393f8d..ee43a9ad 100644 --- a/src/main/java/grep/neogulcoder/domain/attendance/service/AttendanceService.java +++ b/src/main/java/grep/neogulcoder/domain/attendance/service/AttendanceService.java @@ -32,8 +32,7 @@ public class AttendanceService { private final StudyMemberRepository studyMemberRepository; public AttendanceInfoResponse getAttendances(Long studyId, Long userId) { - Study study = studyRepository.findByIdAndActivatedTrue(studyId) - .orElseThrow(() -> new NotFoundException(STUDY_NOT_FOUND)); + Study study = getStudyById(studyId); List attendances = attendanceRepository.findByStudyIdAndUserId(studyId, userId); List responses = attendances.stream() @@ -47,9 +46,7 @@ public AttendanceInfoResponse getAttendances(Long studyId, Long userId) { @Transactional public Long createAttendance(Long studyId, Long userId) { - Study study = studyRepository.findByIdAndActivatedTrue(studyId) - .orElseThrow(() -> new NotFoundException(STUDY_NOT_FOUND)); - + getStudyById(studyId); validateNotAlreadyChecked(studyId, userId); Attendance attendance = Attendance.create(studyId, userId); @@ -57,13 +54,18 @@ public Long createAttendance(Long studyId, Long userId) { return attendance.getId(); } + private Study getStudyById(Long studyId) { + return studyRepository.findById(studyId) + .orElseThrow(() -> new NotFoundException(STUDY_NOT_FOUND)); + } + private int getAttendanceRate(Long studyId, Long userId, Study study, List responses) { - LocalDate start = study.getStartDate().toLocalDate(); - LocalDate participated = studyMemberRepository.findCreatedDateByStudyIdAndUserId(studyId, userId).toLocalDate(); - LocalDate attendanceStart = start.isAfter(participated) ? start : participated; - LocalDate end = study.getEndDate().toLocalDate(); + LocalDate startDay = study.getStartDate().toLocalDate(); + LocalDate participatedDay = studyMemberRepository.findCreatedDateByStudyIdAndUserId(studyId, userId).toLocalDate(); + LocalDate attendanceStartDay = startDay.isAfter(participatedDay) ? startDay : participatedDay; + LocalDate endDay = study.getEndDate().toLocalDate(); - int totalDays = (int) ChronoUnit.DAYS.between(attendanceStart, end) + 1; + int totalDays = (int) ChronoUnit.DAYS.between(attendanceStartDay, endDay) + 1; int attendDays = responses.size(); int attendanceRate = totalDays == 0 ? 0 : Math.round(((float) attendDays / totalDays) * 100); diff --git a/src/main/java/grep/neogulcoder/domain/review/controller/dto/request/ReviewSaveRequest.java b/src/main/java/grep/neogulcoder/domain/review/controller/dto/request/ReviewSaveRequest.java index 143c5474..1592c175 100644 --- a/src/main/java/grep/neogulcoder/domain/review/controller/dto/request/ReviewSaveRequest.java +++ b/src/main/java/grep/neogulcoder/domain/review/controller/dto/request/ReviewSaveRequest.java @@ -3,6 +3,7 @@ import grep.neogulcoder.domain.review.ReviewType; import grep.neogulcoder.domain.review.service.request.ReviewSaveServiceRequest; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Builder; @@ -29,6 +30,7 @@ public class ReviewSaveRequest { private List reviewTag; @Schema(example = "너무 친절 하세요!", description = "주관 리뷰") + @NotBlank private String content; private ReviewSaveRequest() {} diff --git a/src/main/java/grep/neogulcoder/domain/study/Study.java b/src/main/java/grep/neogulcoder/domain/study/Study.java index 0036862c..d620915b 100644 --- a/src/main/java/grep/neogulcoder/domain/study/Study.java +++ b/src/main/java/grep/neogulcoder/domain/study/Study.java @@ -50,6 +50,8 @@ public class Study extends BaseEntity { @Version private Long version; + private static final int MAX_JOINED_STUDY_COUNT = 10; + protected Study() { } @@ -122,4 +124,8 @@ public boolean isReviewableAt(LocalDateTime currentDateTime) { return (currentDateTime.isEqual(this.endDate) || currentDateTime.isAfter(this.endDate)) && (currentDateTime.isEqual(reviewableDateTime) || currentDateTime.isBefore(reviewableDateTime)); } + + public static boolean isOverJoinLimit(int joinedStudyCount) { + return joinedStudyCount >= MAX_JOINED_STUDY_COUNT; + } } diff --git a/src/main/java/grep/neogulcoder/domain/study/service/StudyService.java b/src/main/java/grep/neogulcoder/domain/study/service/StudyService.java index 779283c8..b97b427c 100644 --- a/src/main/java/grep/neogulcoder/domain/study/service/StudyService.java +++ b/src/main/java/grep/neogulcoder/domain/study/service/StudyService.java @@ -40,9 +40,9 @@ import java.util.List; import java.util.Optional; -import static grep.neogulcoder.domain.study.enums.StudyMemberRole.LEADER; +import static grep.neogulcoder.domain.study.enums.StudyMemberRole.*; import static grep.neogulcoder.domain.study.exception.code.StudyErrorCode.*; -import static grep.neogulcoder.domain.users.exception.code.UserErrorCode.USER_NOT_FOUND; +import static grep.neogulcoder.domain.users.exception.code.UserErrorCode.*; @Transactional(readOnly = true) @RequiredArgsConstructor @@ -184,8 +184,8 @@ private StudyMember getStudyMemberById(Long studyId, Long userId) { } private void validateStudyCreateLimit(Long userId) { - int count = studyRepository.countByUserIdAndActivatedTrueAndFinishedFalse(userId); - if (count >= 10) { + int joinedStudyCount = studyRepository.countByUserIdAndActivatedTrueAndFinishedFalse(userId); + if (Study.isOverJoinLimit(joinedStudyCount)) { throw new BusinessException(STUDY_CREATE_LIMIT_EXCEEDED); } } diff --git a/src/main/java/grep/neogulcoder/domain/studyapplication/controller/ApplicationController.java b/src/main/java/grep/neogulcoder/domain/studyapplication/controller/ApplicationController.java index a0f30793..3e098977 100644 --- a/src/main/java/grep/neogulcoder/domain/studyapplication/controller/ApplicationController.java +++ b/src/main/java/grep/neogulcoder/domain/studyapplication/controller/ApplicationController.java @@ -11,6 +11,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; +import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; @@ -22,38 +23,38 @@ public class ApplicationController implements ApplicationSpecification { private final ApplicationService applicationService; @GetMapping("/{recruitment-post-id}/applications") - public ApiResponse getReceivedApplications(@PathVariable("recruitment-post-id") Long recruitmentPostId, - @PageableDefault(size = 5) Pageable pageable, - @AuthenticationPrincipal Principal userDetails) { - return ApiResponse.success(applicationService.getReceivedApplicationsPaging(recruitmentPostId, pageable, userDetails.getUserId())); + public ResponseEntity> getReceivedApplications(@PathVariable("recruitment-post-id") Long recruitmentPostId, + @PageableDefault(size = 5) Pageable pageable, + @AuthenticationPrincipal Principal userDetails) { + return ResponseEntity.ok(ApiResponse.success(applicationService.getReceivedApplicationsPaging(recruitmentPostId, pageable, userDetails.getUserId()))); } @GetMapping("/applications") - public ApiResponse getMyStudyApplications(@PageableDefault(size = 12) Pageable pageable, + public ResponseEntity> getMyStudyApplications(@PageableDefault(size = 12) Pageable pageable, @RequestParam(required = false) ApplicationStatus status, @AuthenticationPrincipal Principal userDetails) { - return ApiResponse.success(applicationService.getMyStudyApplicationsPaging(pageable, userDetails.getUserId(), status)); + return ResponseEntity.ok(ApiResponse.success(applicationService.getMyStudyApplicationsPaging(pageable, userDetails.getUserId(), status))); } @PostMapping("/{recruitment-post-id}/applications") - public ApiResponse createApplication(@PathVariable("recruitment-post-id") Long recruitmentPostId, + public ResponseEntity> createApplication(@PathVariable("recruitment-post-id") Long recruitmentPostId, @RequestBody @Valid ApplicationCreateRequest request, @AuthenticationPrincipal Principal userDetails) { Long id = applicationService.createApplication(recruitmentPostId, request, userDetails.getUserId()); - return ApiResponse.success(id); + return ResponseEntity.ok(ApiResponse.success(id)); } @PostMapping("/applications/{applicationId}/approve") - public ApiResponse approveApplication(@PathVariable("applicationId") Long applicationId, + public ResponseEntity> approveApplication(@PathVariable("applicationId") Long applicationId, @AuthenticationPrincipal Principal userDetails) { applicationService.approveApplication(applicationId, userDetails.getUserId()); - return ApiResponse.noContent(); + return ResponseEntity.ok(ApiResponse.noContent()); } @PostMapping("/applications/{applicationId}/reject") - public ApiResponse rejectApplication(@PathVariable("applicationId") Long applicationId, + public ResponseEntity> rejectApplication(@PathVariable("applicationId") Long applicationId, @AuthenticationPrincipal Principal userDetails) { applicationService.rejectApplication(applicationId, userDetails.getUserId()); - return ApiResponse.noContent(); + return ResponseEntity.ok(ApiResponse.noContent()); } } diff --git a/src/main/java/grep/neogulcoder/domain/studyapplication/controller/ApplicationSpecification.java b/src/main/java/grep/neogulcoder/domain/studyapplication/controller/ApplicationSpecification.java index 06b8d1f7..e22687b2 100644 --- a/src/main/java/grep/neogulcoder/domain/studyapplication/controller/ApplicationSpecification.java +++ b/src/main/java/grep/neogulcoder/domain/studyapplication/controller/ApplicationSpecification.java @@ -9,22 +9,23 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; @Tag(name = "StudyApplication", description = "스터디 신청 API") public interface ApplicationSpecification { @Operation(summary = "모집글 신청 목록 조회", description = "스터디장(모집글 작성자)이 신청 목록을 조회합니다.") - public ApiResponse getReceivedApplications(Long recruitmentPostId, Pageable pageable, Principal userDetails); + ResponseEntity> getReceivedApplications(Long recruitmentPostId, Pageable pageable, Principal userDetails); @Operation(summary = "내 스터디 신청 목록 조회", description = "내가 신청한 스터디의 목록을 조회합니다.") - ApiResponse getMyStudyApplications(Pageable pageable, ApplicationStatus status, Principal userDetails); + ResponseEntity> getMyStudyApplications(Pageable pageable, ApplicationStatus status, Principal userDetails); @Operation(summary = "스터디 신청 생성", description = "스터디를 신청합니다.") - ApiResponse createApplication(Long recruitmentPostId, ApplicationCreateRequest request, Principal userDetails); + ResponseEntity> createApplication(Long recruitmentPostId, ApplicationCreateRequest request, Principal userDetails); @Operation(summary = "스터디 신청 승인", description = "스터디장이 스터디 신청을 승인합니다.") - ApiResponse approveApplication(Long applicationId, Principal userDetails); + ResponseEntity> approveApplication(Long applicationId, Principal userDetails); @Operation(summary = "스터디 신청 거절", description = "스터디장이 스터디 신청을 거절합니다.") - ApiResponse rejectApplication(Long applicationId, Principal userDetails); + ResponseEntity> rejectApplication(Long applicationId, Principal userDetails); } diff --git a/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/MyApplicationPagingResponse.java b/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/MyApplicationPagingResponse.java index 9f652cfc..8fdc0774 100644 --- a/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/MyApplicationPagingResponse.java +++ b/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/MyApplicationPagingResponse.java @@ -27,16 +27,16 @@ public class MyApplicationPagingResponse { "\"status\": \"PENDING\"" + "}]" ) - private List applications; + private final List applications; @Schema(description = "총 페이지 수", example = "2") - private int totalPage; + private final int totalPage; @Schema(description = "총 요소 개수", example = "10") - private int totalElementCount; + private final int totalElementCount; @Schema(example = "false", description = "다음 페이지 여부") - private boolean hasNext; + private final boolean hasNext; @Builder private MyApplicationPagingResponse(Page page) { diff --git a/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/MyApplicationResponse.java b/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/MyApplicationResponse.java index eb380f83..9892c275 100644 --- a/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/MyApplicationResponse.java +++ b/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/MyApplicationResponse.java @@ -13,40 +13,40 @@ public class MyApplicationResponse { @Schema(description = "모집글 번호", example = "1") - private Long recruitmentPostId; + private final Long recruitmentPostId; @Schema(description = "스터디 이름", example = "자바 스터디") - private String name; + private final String name; @Schema(description = "스터디장 닉네임", example = "너굴") - private String leaderNickname; + private final String leaderNickname; @Schema(description = "정원", example = "4") - private int capacity; + private final int capacity; @Schema(description = "인원수", example = "3") - private int currentCount; + private final int currentCount; @Schema(description = "시작일", example = "2025-07-15") - private LocalDateTime startDate; + private final LocalDateTime startDate; @Schema(description = "대표 이미지", example = "http://localhost:8083/image.jpg") - private String imageUrl; + private final String imageUrl; @Schema(description = "스터디 소개", example = "자바 스터디입니다.") - private String introduction; + private final String introduction; @Schema(description = "카테고리", example = "IT") - private Category category; + private final Category category; @Schema(description = "타입", example = "ONLINE") - private StudyType studyType; + final StudyType studyType; @Schema(description = "열람 여부", example = "true") - private boolean isRead; + private final boolean isRead; @Schema(description = "신청 상태", example = "PENDING") - private ApplicationStatus status; + private final ApplicationStatus status; @QueryProjection public MyApplicationResponse(Long recruitmentPostId, String name, String leaderNickname, int capacity, int currentCount, LocalDateTime startDate, diff --git a/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/ReceivedApplicationPagingResponse.java b/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/ReceivedApplicationPagingResponse.java index 90cf3321..81288d45 100644 --- a/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/ReceivedApplicationPagingResponse.java +++ b/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/ReceivedApplicationPagingResponse.java @@ -14,6 +14,7 @@ public class ReceivedApplicationPagingResponse { description = "내 모집글에 지원한 신청 목록", example = "[{" + "\"applicationId\": 1," + + "\"userId\": 1," + "\"nickname\": \"너굴\"," + "\"profileImageUrl\": \"http://localhost:8083/image.jpg\"," + "\"buddyEnergy\": 30," + @@ -21,16 +22,16 @@ public class ReceivedApplicationPagingResponse { "\"applicationReason\": \"자바를 더 공부하고 싶어 지원합니다.\"" + "}]" ) - private List receivedApplications; + private final List receivedApplications; @Schema(description = "총 페이지 수", example = "2") - private int totalPage; + private final int totalPage; @Schema(description = "총 요소 개수", example = "10") - private int totalElementCount; + private final int totalElementCount; @Schema(description = "다음 페이지 여부", example = "false") - private boolean hasNext; + private final boolean hasNext; @Builder private ReceivedApplicationPagingResponse(Page page) { diff --git a/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/ReceivedApplicationResponse.java b/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/ReceivedApplicationResponse.java index 15a1d649..e45094b8 100644 --- a/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/ReceivedApplicationResponse.java +++ b/src/main/java/grep/neogulcoder/domain/studyapplication/controller/dto/response/ReceivedApplicationResponse.java @@ -10,26 +10,30 @@ public class ReceivedApplicationResponse { @Schema(description = "신청 번호", example = "1") - private Long applicationId; + private final Long applicationId; + + @Schema(description = "신청자 번호", example = "1") + private final Long userId; @Schema(description = "신청자 닉네임", example = "너굴") - private String nickname; + private final String nickname; @Schema(description = "신청자 프로필 이미지 URL", example = "http://localhost:8083/image.jpg") - private String profileImageUrl; + private final String profileImageUrl; @Schema(description = "신청자 버디에너지", example = "30") - private int buddyEnergy; + private final int buddyEnergy; @Schema(description = "신청 날짜", example = "2025-07-10T15:30:00") - private LocalDateTime createdDate; + private final LocalDateTime createdDate; @Schema(description = "스터디 신청 지원 동기", example = "자바를 더 공부하고 싶어 지원합니다.") - private String applicationReason; + private final String applicationReason; @QueryProjection - public ReceivedApplicationResponse(Long applicationId, String nickname, String profileImageUrl, int buddyEnergy, LocalDateTime createdDate, String applicationReason) { + public ReceivedApplicationResponse(Long applicationId, Long userId, String nickname, String profileImageUrl, int buddyEnergy, LocalDateTime createdDate, String applicationReason) { this.applicationId = applicationId; + this.userId = userId; this.nickname = nickname; this.profileImageUrl = profileImageUrl; this.buddyEnergy = buddyEnergy; diff --git a/src/main/java/grep/neogulcoder/domain/studyapplication/provider/ApplicationApprovedMessageProvider.java b/src/main/java/grep/neogulcoder/domain/studyapplication/provider/ApplicationApprovedMessageProvider.java index e3559192..a5d347b8 100644 --- a/src/main/java/grep/neogulcoder/domain/studyapplication/provider/ApplicationApprovedMessageProvider.java +++ b/src/main/java/grep/neogulcoder/domain/studyapplication/provider/ApplicationApprovedMessageProvider.java @@ -1,6 +1,7 @@ package grep.neogulcoder.domain.studyapplication.provider; -import grep.neogulcoder.domain.alram.type.*; +import grep.neogulcoder.domain.alram.type.AlarmType; +import grep.neogulcoder.domain.alram.type.DomainType; import grep.neogulcoder.domain.recruitment.post.RecruitmentPost; import grep.neogulcoder.domain.recruitment.post.repository.RecruitmentPostRepository; import grep.neogulcoder.domain.studyapplication.StudyApplication; @@ -10,8 +11,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import static grep.neogulcoder.domain.recruitment.RecruitmentErrorCode.*; -import static grep.neogulcoder.domain.studyapplication.exception.code.ApplicationErrorCode.*; +import static grep.neogulcoder.domain.recruitment.RecruitmentErrorCode.NOT_FOUND; +import static grep.neogulcoder.domain.studyapplication.exception.code.ApplicationErrorCode.APPLICATION_NOT_FOUND; @Component @RequiredArgsConstructor diff --git a/src/main/java/grep/neogulcoder/domain/studyapplication/provider/ApplicationMessageProvider.java b/src/main/java/grep/neogulcoder/domain/studyapplication/provider/ApplicationMessageProvider.java index 55e85b52..eb1de06b 100644 --- a/src/main/java/grep/neogulcoder/domain/studyapplication/provider/ApplicationMessageProvider.java +++ b/src/main/java/grep/neogulcoder/domain/studyapplication/provider/ApplicationMessageProvider.java @@ -9,7 +9,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import static grep.neogulcoder.domain.recruitment.RecruitmentErrorCode.*; +import static grep.neogulcoder.domain.recruitment.RecruitmentErrorCode.NOT_FOUND; @Component @RequiredArgsConstructor diff --git a/src/main/java/grep/neogulcoder/domain/studyapplication/provider/ApplicationRejectedMessageProvider.java b/src/main/java/grep/neogulcoder/domain/studyapplication/provider/ApplicationRejectedMessageProvider.java index f13f564d..9bf8a69f 100644 --- a/src/main/java/grep/neogulcoder/domain/studyapplication/provider/ApplicationRejectedMessageProvider.java +++ b/src/main/java/grep/neogulcoder/domain/studyapplication/provider/ApplicationRejectedMessageProvider.java @@ -11,8 +11,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import static grep.neogulcoder.domain.recruitment.RecruitmentErrorCode.*; -import static grep.neogulcoder.domain.studyapplication.exception.code.ApplicationErrorCode.*; +import static grep.neogulcoder.domain.recruitment.RecruitmentErrorCode.NOT_FOUND; +import static grep.neogulcoder.domain.studyapplication.exception.code.ApplicationErrorCode.APPLICATION_NOT_FOUND; @Component @RequiredArgsConstructor diff --git a/src/main/java/grep/neogulcoder/domain/studyapplication/repository/ApplicationQueryRepository.java b/src/main/java/grep/neogulcoder/domain/studyapplication/repository/ApplicationQueryRepository.java index eccb16ad..0e551c9f 100644 --- a/src/main/java/grep/neogulcoder/domain/studyapplication/repository/ApplicationQueryRepository.java +++ b/src/main/java/grep/neogulcoder/domain/studyapplication/repository/ApplicationQueryRepository.java @@ -17,13 +17,13 @@ import java.util.List; import java.util.function.Supplier; +import static grep.neogulcoder.domain.buddy.entity.QBuddyEnergy.buddyEnergy; import static grep.neogulcoder.domain.recruitment.post.QRecruitmentPost.recruitmentPost; import static grep.neogulcoder.domain.study.QStudy.study; import static grep.neogulcoder.domain.study.QStudyMember.studyMember; import static grep.neogulcoder.domain.study.enums.StudyMemberRole.LEADER; import static grep.neogulcoder.domain.studyapplication.QStudyApplication.studyApplication; import static grep.neogulcoder.domain.users.entity.QUser.user; -import static grep.neogulcoder.domain.buddy.entity.QBuddyEnergy.buddyEnergy; @Repository public class ApplicationQueryRepository { @@ -38,6 +38,7 @@ public Page findReceivedApplicationsPaging(Long rec List receivedApplications = queryFactory .select(new QReceivedApplicationResponse( studyApplication.id, + user.id, user.nickname, user.profileImageUrl, buddyEnergy.level, diff --git a/src/main/java/grep/neogulcoder/domain/studyapplication/service/ApplicationService.java b/src/main/java/grep/neogulcoder/domain/studyapplication/service/ApplicationService.java index 5327d5d0..6c0a3943 100644 --- a/src/main/java/grep/neogulcoder/domain/studyapplication/service/ApplicationService.java +++ b/src/main/java/grep/neogulcoder/domain/studyapplication/service/ApplicationService.java @@ -9,6 +9,7 @@ import grep.neogulcoder.domain.study.repository.StudyMemberQueryRepository; import grep.neogulcoder.domain.study.repository.StudyMemberRepository; import grep.neogulcoder.domain.study.repository.StudyRepository; +import grep.neogulcoder.domain.study.service.StudyManagementServiceFacade; import grep.neogulcoder.domain.studyapplication.ApplicationStatus; import grep.neogulcoder.domain.studyapplication.StudyApplication; import grep.neogulcoder.domain.studyapplication.controller.dto.request.ApplicationCreateRequest; @@ -29,8 +30,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static grep.neogulcoder.domain.recruitment.RecruitmentErrorCode.*; -import static grep.neogulcoder.domain.study.exception.code.StudyErrorCode.*; +import static grep.neogulcoder.domain.recruitment.RecruitmentErrorCode.NOT_FOUND; +import static grep.neogulcoder.domain.recruitment.RecruitmentErrorCode.NOT_OWNER; +import static grep.neogulcoder.domain.study.exception.code.StudyErrorCode.STUDY_NOT_FOUND; import static grep.neogulcoder.domain.studyapplication.exception.code.ApplicationErrorCode.*; @Transactional(readOnly = true) @@ -45,10 +47,11 @@ public class ApplicationService { private final StudyRepository studyRepository; private final StudyMemberQueryRepository studyMemberQueryRepository; private final ApplicationEventPublisher eventPublisher; + private final StudyManagementServiceFacade studyManagementServiceFacade; @Transactional public ReceivedApplicationPagingResponse getReceivedApplicationsPaging(Long recruitmentPostId, Pageable pageable, Long userId) { - RecruitmentPost recruitmentPost = findValidRecruitmentPost(recruitmentPostId); + RecruitmentPost recruitmentPost = getRecruitmentPostById(recruitmentPostId); validateOwner(userId, recruitmentPost); applicationRepository.markAllAsReadByRecruitmentPostId(recruitmentPostId); @@ -64,7 +67,7 @@ public MyApplicationPagingResponse getMyStudyApplicationsPaging(Pageable pageabl @Transactional public Long createApplication(Long recruitmentPostId, ApplicationCreateRequest request, Long userId) { - RecruitmentPost recruitmentPost = findValidRecruitmentPost(recruitmentPostId); + RecruitmentPost recruitmentPost = getRecruitmentPostById(recruitmentPostId); validateNotLeaderApply(recruitmentPost, userId); validateNotAlreadyApplied(recruitmentPostId, userId); @@ -80,9 +83,9 @@ public Long createApplication(Long recruitmentPostId, ApplicationCreateRequest r @Transactional public void approveApplication(Long applicationId, Long userId) { - StudyApplication application = findValidApplication(applicationId); - RecruitmentPost post = findValidRecruitmentPost(application.getRecruitmentPostId()); - Study study = findValidStudy(post); + StudyApplication application = getApplicationById(applicationId); + RecruitmentPost post = getRecruitmentPostById(application.getRecruitmentPostId()); + Study study = getStudyByRecruitmentPostId(post); validateOnlyLeaderCanApprove(study, userId); validateStatusIsApplying(application); @@ -92,16 +95,16 @@ public void approveApplication(Long applicationId, Long userId) { StudyMember studyMember = StudyMember.createMember(study, application.getUserId()); studyMemberRepository.save(studyMember); - study.increaseMemberCount(); + studyManagementServiceFacade.increaseMemberCount(study, userId); eventPublisher.publishEvent(new ApplicationStatusChangedEvent(applicationId, AlarmType.STUDY_APPLICATION_APPROVED)); } @Transactional public void rejectApplication(Long applicationId, Long userId) { - StudyApplication application = findValidApplication(applicationId); - RecruitmentPost post = findValidRecruitmentPost(application.getRecruitmentPostId()); - Study study = findValidStudy(post); + StudyApplication application = getApplicationById(applicationId); + RecruitmentPost post = getRecruitmentPostById(application.getRecruitmentPostId()); + Study study = getStudyByRecruitmentPostId(post); validateOnlyLeaderCanReject(study, userId); validateStatusIsApplying(application); @@ -111,22 +114,19 @@ public void rejectApplication(Long applicationId, Long userId) { eventPublisher.publishEvent(new ApplicationStatusChangedEvent(applicationId, AlarmType.STUDY_APPLICATION_REJECTED)); } - private Study findValidStudy(RecruitmentPost post) { - Study study = studyRepository.findByIdAndActivatedTrue(post.getStudyId()) + private Study getStudyByRecruitmentPostId(RecruitmentPost post) { + return studyRepository.findByIdAndActivatedTrue(post.getStudyId()) .orElseThrow(() -> new NotFoundException(STUDY_NOT_FOUND)); - return study; } - private StudyApplication findValidApplication(Long applicationId) { - StudyApplication application = applicationRepository.findById(applicationId) + private StudyApplication getApplicationById(Long applicationId) { + return applicationRepository.findById(applicationId) .orElseThrow(() -> new NotFoundException(APPLICATION_NOT_FOUND)); - return application; } - private RecruitmentPost findValidRecruitmentPost(Long recruitmentPostId) { - RecruitmentPost post = recruitmentPostRepository.findByIdAndActivatedTrue(recruitmentPostId) + private RecruitmentPost getRecruitmentPostById(Long recruitmentPostId) { + return recruitmentPostRepository.findByIdAndActivatedTrue(recruitmentPostId) .orElseThrow(() -> new NotFoundException(NOT_FOUND)); - return post; } private static void validateOwner(Long userId, RecruitmentPost recruitmentPost) { @@ -170,15 +170,15 @@ private void validateOnlyLeaderCanReject(Study study, Long userId) { } private void validateApplicantStudyLimit(Long userId) { - int count = studyMemberQueryRepository.countActiveUnfinishedStudies(userId); - if (count >= 10) { + int joinedStudyCount = studyMemberQueryRepository.countActiveUnfinishedStudies(userId); + if (Study.isOverJoinLimit(joinedStudyCount)) { throw new BusinessException(APPLICATION_PARTICIPATION_LIMIT_EXCEEDED); } } private void validateParticipantStudyLimit(Long userId) { - int count = studyMemberQueryRepository.countActiveUnfinishedStudies(userId); - if (count >= 10) { + int joinedStudyCount = studyMemberQueryRepository.countActiveUnfinishedStudies(userId); + if (Study.isOverJoinLimit(joinedStudyCount)) { throw new BusinessException(APPLICATION_PARTICIPANT_LIMIT_EXCEEDED); } } diff --git a/src/main/java/grep/neogulcoder/global/config/security/SecurityConfig.java b/src/main/java/grep/neogulcoder/global/config/security/SecurityConfig.java index c08ed123..808be0b2 100644 --- a/src/main/java/grep/neogulcoder/global/config/security/SecurityConfig.java +++ b/src/main/java/grep/neogulcoder/global/config/security/SecurityConfig.java @@ -78,7 +78,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, ClientRegistra (requests) -> requests .requestMatchers("/auth/**", "/", "/api/users/signup", - "/recruitment-posts", + "/recruitment-posts", "/recruitment-posts/*", "/oauth2/**", "/login/**", "/signup", diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 544555d7..b268d002 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -36,9 +36,9 @@ INSERT INTO study_member (study_id, user_id, role, participated, activated) VALU INSERT INTO study_member (study_id, user_id, role, participated, activated) VALUES (2, 5, 'LEADER', FALSE, TRUE); INSERT INTO study_member (study_id, user_id, role, participated, activated) VALUES (3, 2, 'LEADER', FALSE, TRUE); INSERT INTO study_member (study_id, user_id, role, participated, activated) VALUES (3, 1, 'MEMBER', FALSE, TRUE); -INSERT INTO study_member (study_id, user_id, role, participated, activated) VALUES (6, 7, 'LEADER', FALSE, TRUE); +INSERT INTO study_member (study_id, user_id, role, participated, activated) VALUES (6, 6, 'LEADER', FALSE, TRUE); +INSERT INTO study_member (study_id, user_id, role, participated, activated) VALUES (6, 7, 'MEMBER', FALSE, TRUE); INSERT INTO study_member (study_id, user_id, role, participated, activated) VALUES (6, 8, 'MEMBER', FALSE, TRUE); -INSERT INTO study_member (study_id, user_id, role, participated, activated) VALUES (6, 9, 'MEMBER', FALSE, TRUE); -- [ recruitment_post ] INSERT INTO recruitment_post (user_id, study_id, subject, content, recruitment_count, expired_date, status, activated, created_date) VALUES (3, 1, '자바 스터디 모집', '이펙티브 자바 공부하실분 구해요!!', 3, '2025-04-19', 'COMPLETE', true, '2025-08-02 12:00:00');