Skip to content

Commit

Permalink
feat: 추천 모임 목록 조회 구현 (#523)
Browse files Browse the repository at this point in the history
* feat(MeetingSearchRepository): 추천 모임 조회 메서드 구현

- 부연 설명 : 파라미터(meetingIds) 가 비어있을 경우, '지금 모집중인 모임' 반환

* docs(MeetingV2Api): 추천 모임 조회 API 스웨거 문서 작성

* feat(MeetingV2ServiceImpl): 추천 모임 조회 비즈니스 코드 구현

* feat(MeetingV2Controller): 추천 모임 조회 API 컨트롤러 및 Dto 구현

* fix(MeetingSearchRepository): NPE 문제 해결

* chore(MeetingSearchRepository): 삼항연산자 -> if 문으로 수정

* chore(MeetingV2GetRecommendDto): of -> from 변경
  • Loading branch information
mikekks authored Jan 7, 2025
1 parent 98d7657 commit 53adbbe
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package org.sopt.makers.crew.main.entity.meeting;

import java.util.List;

import org.sopt.makers.crew.main.global.util.Time;
import org.sopt.makers.crew.main.meeting.v2.dto.query.MeetingV2GetAllMeetingQueryDto;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface MeetingSearchRepository {
Page<Meeting> findAllByQuery(MeetingV2GetAllMeetingQueryDto queryCommand, Pageable pageable, Time time);

List<Meeting> findRecommendMeetings(List<Integer> meetingIds, Time time);
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
@RequiredArgsConstructor
public class MeetingSearchRepositoryImpl implements MeetingSearchRepository {
private final JPAQueryFactory queryFactory;
//private final Time time;

/**
* @note: canJoinOnlyActiveGeneration 처리 유의
Expand All @@ -48,6 +47,26 @@ public Page<Meeting> findAllByQuery(MeetingV2GetAllMeetingQueryDto queryCommand,
PageRequest.of(pageable.getPageNumber(), pageable.getPageSize()), countQuery::fetchFirst);
}

/**
* @param meetingIds : 조회하려는 모임 id 리스트
* @implSpec : meetingIds 가 null 인 경우, '지금 모집중인 모임' 반환
* */
@Override
public List<Meeting> findRecommendMeetings(List<Integer> meetingIds, Time time) {

JPAQuery<Meeting> query = queryFactory.selectFrom(meeting)
.innerJoin(meeting.user, user)
.fetchJoin();

if (meetingIds == null) {
query.where(eqStatus(List.of(String.valueOf(EnMeetingStatus.APPLY_ABLE.getValue())), time));
return query.fetch();
}

query.where(meeting.id.in(meetingIds));
return query.fetch();
}

private List<Meeting> getMeetings(MeetingV2GetAllMeetingQueryDto queryCommand, Pageable pageable, Time time) {
return queryFactory
.selectFrom(meeting)
Expand Down Expand Up @@ -153,7 +172,7 @@ private BooleanExpression eqJoinableParts(MeetingJoinablePart[] joinableParts) {

// SQL 템플릿을 사용하여 BooleanExpression 생성
return Expressions.booleanTemplate(
"arraycontains({0}, "+ joinablePartsToString + ") || '' = 'true'",
"arraycontains({0}, " + joinablePartsToString + ") || '' = 'true'",
meeting.joinableParts,
joinablePartsToString
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetAllMeetingDto;
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetMeetingBannerResponseDto;
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetMeetingByIdResponseDto;
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetRecommendDto;
import org.sopt.makers.crew.main.meeting.v2.dto.response.PreSignedUrlResponseDto;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand Down Expand Up @@ -134,4 +136,12 @@ ResponseEntity<AppliesCsvFileUrlResponseDto> getAppliesCsvFileUrl(
@ApiResponse(responseCode = "400", description = "모임이 없습니다.", content = @Content),})
ResponseEntity<MeetingV2GetMeetingByIdResponseDto> getMeetingById(@PathVariable Integer meetingId,
Principal principal);

@Operation(summary = "추천 모임 목록 조회", description = "추천 모임 목록 조회, 쿼리파라미터가 없는 경우 '지금 모집중인 모임' 반환")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "추천 모임 목록 조회 성공"),
@ApiResponse(responseCode = "400", description = "모임이 없습니다.", content = @Content)
})
ResponseEntity<MeetingV2GetRecommendDto> getRecommendMeetingsByIds(
@RequestParam(name = "meetingIds", required = false) @Parameter(description = "추천할 모임들의 ID 리스트", example = "[101, 102, 103]") List<Integer> meetingIds,
Principal principal);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetAllMeetingDto;
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetMeetingBannerResponseDto;
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetMeetingByIdResponseDto;
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetRecommendDto;
import org.sopt.makers.crew.main.meeting.v2.dto.response.PreSignedUrlResponseDto;
import org.sopt.makers.crew.main.meeting.v2.service.MeetingV2Service;
import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -196,4 +197,14 @@ public ResponseEntity<MeetingV2GetMeetingByIdResponseDto> getMeetingById(@PathVa

return ResponseEntity.ok(meetingV2Service.getMeetingById(meetingId, userId));
}

@Override
@GetMapping("/recommend")
public ResponseEntity<MeetingV2GetRecommendDto> getRecommendMeetingsByIds(
@RequestParam(name = "meetingIds", required = false) List<Integer> meetingIds,
Principal principal) {
Integer userId = UserUtil.getUserId(principal);

return ResponseEntity.ok().body(meetingV2Service.getRecommendMeetingsByIds(meetingIds, userId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.sopt.makers.crew.main.meeting.v2.dto.response;

import java.util.List;

import org.sopt.makers.crew.main.global.dto.MeetingResponseDto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

@Schema(name = "MeetingV2GetRecommendDto", description = "추천 모임 목록 조회 응답 Dto")
public record MeetingV2GetRecommendDto(
@Schema(description = "모임 객체 목록", example = "")
@NotNull
List<MeetingResponseDto> meetings
) {
public static MeetingV2GetRecommendDto from(List<MeetingResponseDto> meetings) {
return new MeetingV2GetRecommendDto(meetings);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetAllMeetingDto;
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetMeetingBannerResponseDto;
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetMeetingByIdResponseDto;
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetRecommendDto;

public interface MeetingV2Service {

Expand Down Expand Up @@ -47,4 +48,6 @@ AppliesCsvFileUrlResponseDto getAppliesCsvFileUrl(Integer meetingId, List<Intege
Integer userId);

MeetingV2GetMeetingByIdResponseDto getMeetingById(Integer meetingId, Integer userId);

MeetingV2GetRecommendDto getRecommendMeetingsByIds(List<Integer> meetingIds, Integer userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetMeetingBannerResponseDto;
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetMeetingBannerResponseUserDto;
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetMeetingByIdResponseDto;
import org.sopt.makers.crew.main.meeting.v2.dto.response.MeetingV2GetRecommendDto;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.domain.Page;
Expand Down Expand Up @@ -448,6 +449,22 @@ public MeetingV2GetMeetingByIdResponseDto getMeetingById(Integer meetingId, Inte
meetingLeader, applyWholeInfoDtos, time.now());
}

@Override
public MeetingV2GetRecommendDto getRecommendMeetingsByIds(List<Integer> meetingIds, Integer userId) {

List<Meeting> meetings = meetingRepository.findRecommendMeetings(meetingIds, time);
List<Integer> foundMeetingIds = meetings.stream().map(Meeting::getId).toList();

Applies allApplies = new Applies(applyRepository.findAllByMeetingIdIn(foundMeetingIds));

List<MeetingResponseDto> meetingResponseDtos = meetings.stream()
.map(meeting -> MeetingResponseDto.of(meeting, meeting.getUser(),
allApplies.getApprovedCount(meeting.getId()), time.now()))
.toList();

return MeetingV2GetRecommendDto.from(meetingResponseDtos);
}

private void deleteCsvFile(String filePath) {
try {
Files.deleteIfExists(Paths.get(filePath));
Expand Down

0 comments on commit 53adbbe

Please sign in to comment.