diff --git a/src/main/java/org/pingle/pingleserver/controller/MeetingController.java b/src/main/java/org/pingle/pingleserver/controller/MeetingController.java index f774d61..7602967 100644 --- a/src/main/java/org/pingle/pingleserver/controller/MeetingController.java +++ b/src/main/java/org/pingle/pingleserver/controller/MeetingController.java @@ -9,9 +9,12 @@ import org.pingle.pingleserver.controller.swagger.MeetingApi; import org.pingle.pingleserver.domain.Meeting; import org.pingle.pingleserver.domain.Pin; +import org.pingle.pingleserver.domain.enums.MCategory; +import org.pingle.pingleserver.domain.enums.SearchOrder; import org.pingle.pingleserver.dto.common.ApiResponse; import org.pingle.pingleserver.dto.request.MeetingRequest; import org.pingle.pingleserver.dto.response.ParticipantsResponse; +import org.pingle.pingleserver.dto.response.SearchResponse; import org.pingle.pingleserver.dto.type.SuccessMessage; import org.pingle.pingleserver.service.MeetingService; import org.pingle.pingleserver.service.PinService; @@ -60,4 +63,14 @@ public ApiResponse deleteMeeting(@UserId Long userId, @PathVariable("meetingI meetingService.deleteMeeting(userId, meetingId); return ApiResponse.success(SuccessMessage.OK); } + + @GetMapping("/search") + public ApiResponse getSearchResult(@UserId Long userId, + @RequestParam(required = false) String q, + @RequestParam(required = false) MCategory category, + @RequestParam Long teamId, + @RequestParam SearchOrder order) { + return ApiResponse.success(SuccessMessage.OK, meetingService.searchMeetings(userId , q, category, teamId, order)); + } } + diff --git a/src/main/java/org/pingle/pingleserver/domain/enums/SearchOrder.java b/src/main/java/org/pingle/pingleserver/domain/enums/SearchOrder.java new file mode 100644 index 0000000..afd9b1c --- /dev/null +++ b/src/main/java/org/pingle/pingleserver/domain/enums/SearchOrder.java @@ -0,0 +1,11 @@ +package org.pingle.pingleserver.domain.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum SearchOrder { + NEW("new"), UPCOMING("upcoming"); + private final String value; +} diff --git a/src/main/java/org/pingle/pingleserver/dto/response/SearchIndividualResponse.java b/src/main/java/org/pingle/pingleserver/dto/response/SearchIndividualResponse.java new file mode 100644 index 0000000..59e9884 --- /dev/null +++ b/src/main/java/org/pingle/pingleserver/dto/response/SearchIndividualResponse.java @@ -0,0 +1,52 @@ +package org.pingle.pingleserver.dto.response; + +import lombok.Builder; +import org.pingle.pingleserver.domain.Meeting; +import org.pingle.pingleserver.domain.UserMeeting; +import org.pingle.pingleserver.domain.enums.MCategory; +import org.pingle.pingleserver.domain.enums.MRole; +import org.pingle.pingleserver.dto.type.ErrorMessage; +import org.pingle.pingleserver.exception.CustomException; +import org.pingle.pingleserver.utils.TimeUtil; + +import java.util.List; + +@Builder +public record SearchIndividualResponse(Long id,MCategory category, String name, String ownerName, + String location, String date, String startAt, String endAt, int maxParticipants, + int curParticipants, boolean isParticipating, String chatLink, boolean isOwner) { + + public static SearchIndividualResponse of (Meeting meeting, Long userId) { + return new SearchIndividualResponse(meeting.getId(), meeting.getCategory(), meeting.getName(), getOwnerName(meeting), + meeting.getPin().getName(), TimeUtil.getDateFromDateTime(meeting.getStartAt()), + TimeUtil.getTimeFromDateTime(meeting.getStartAt()), TimeUtil.getTimeFromDateTime(meeting.getEndAt()), + meeting.getMaxParticipants(), meeting.getUserMeetingList().size(), + isParticipating(meeting, userId), meeting.getChatLink(), isOwner(meeting, userId)); + } + + private static String getOwnerName(Meeting meeting) { + List userMeetings = meeting.getUserMeetingList(); + if(userMeetings.isEmpty()) throw new CustomException(ErrorMessage.RESOURCE_NOT_FOUND); + for (UserMeeting userMeeting : userMeetings) { + if(userMeeting.getMeetingRole().equals(MRole.OWNER)) return userMeeting.getUser().getValidName(); + } + return null; + } + + private static boolean isParticipating(Meeting meeting, Long userId) { + List userMeetings = meeting.getUserMeetingList(); + for (UserMeeting userMeeting : userMeetings) { + if(userMeeting.getUser().getId().equals(userId)) return true; + } + return false; + } + private static boolean isOwner(Meeting meeting, Long userId) { + List userMeetings = meeting.getUserMeetingList(); + for (UserMeeting userMeeting : userMeetings) { + if (userMeeting.getMeetingRole().equals(MRole.OWNER) && userMeeting.getUser().getId().equals(userId)) return true; + } + return false; + } + +} + diff --git a/src/main/java/org/pingle/pingleserver/dto/response/SearchResponse.java b/src/main/java/org/pingle/pingleserver/dto/response/SearchResponse.java new file mode 100644 index 0000000..954511f --- /dev/null +++ b/src/main/java/org/pingle/pingleserver/dto/response/SearchResponse.java @@ -0,0 +1,9 @@ +package org.pingle.pingleserver.dto.response; + +import java.util.List; + +public record SearchResponse(int searchCount, List meetings) { + public static SearchResponse of(List meetings) { + return new SearchResponse(meetings.size(),meetings); + } +} diff --git a/src/main/java/org/pingle/pingleserver/repository/MeetingRepository.java b/src/main/java/org/pingle/pingleserver/repository/MeetingRepository.java index 1ee50fd..0cdc3c7 100644 --- a/src/main/java/org/pingle/pingleserver/repository/MeetingRepository.java +++ b/src/main/java/org/pingle/pingleserver/repository/MeetingRepository.java @@ -42,4 +42,21 @@ public interface MeetingRepository extends JpaRepository { List findByPinIdAndCategoryAndStartAtAfterOrderByStartAt(Long pinId, MCategory category, LocalDateTime currentTime); List findByPinIdAndStartAtAfterOrderByStartAt(Long pinId, LocalDateTime currentTime); + + @Query("SELECT m " + + "FROM Meeting m JOIN FETCH m.pin p join fetch m.userMeetingList um join FETCH um.user join fetch p.team t " + + "WHERE t.id = :teamId AND m.startAt > CURRENT_TIMESTAMP " + + "AND (m.category = :category OR :category IS NULL) " + //+ + "AND (m.name LIKE concat('%' ,COALESCE(:q, ''), '%') OR p.name LIKE concat('%' ,COALESCE(:q, ''), '%') OR p.address.address LIKE concat('%' ,COALESCE(:q, ''), '%')) " + + "ORDER BY m.createdAt DESC") + List findMeetingsByParameterOOrderByCreatedAt(String q, MCategory category, Long teamId); + + @Query("SELECT m " + + "FROM Meeting m JOIN FETCH m.pin p join fetch m.userMeetingList um join FETCH um.user join fetch p.team t " + + "WHERE t.id = :teamId AND m.startAt > CURRENT_TIMESTAMP " + + "AND (m.category = :category OR :category IS NULL) " + + "AND (m.name LIKE concat('%' ,COALESCE(:q, ''), '%') OR p.name LIKE concat('%' ,COALESCE(:q, ''), '%') OR p.address.address LIKE concat('%' ,COALESCE(:q, ''), '%')) " + + "ORDER BY m.startAt ASC") + List findMeetingsByParameterOOrderByStartAt(String q, MCategory category, Long teamId); + } diff --git a/src/main/java/org/pingle/pingleserver/service/MeetingService.java b/src/main/java/org/pingle/pingleserver/service/MeetingService.java index 8bb938a..9f8e9c9 100644 --- a/src/main/java/org/pingle/pingleserver/service/MeetingService.java +++ b/src/main/java/org/pingle/pingleserver/service/MeetingService.java @@ -4,13 +4,18 @@ import org.pingle.pingleserver.domain.Meeting; import org.pingle.pingleserver.domain.Pin; import org.pingle.pingleserver.domain.UserMeeting; +import org.pingle.pingleserver.domain.enums.MCategory; import org.pingle.pingleserver.domain.enums.MRole; +import org.pingle.pingleserver.domain.enums.SearchOrder; import org.pingle.pingleserver.dto.request.MeetingRequest; import org.pingle.pingleserver.dto.response.MyPingleResponse; import org.pingle.pingleserver.dto.response.ParticipantsResponse; +import org.pingle.pingleserver.dto.response.SearchIndividualResponse; +import org.pingle.pingleserver.dto.response.SearchResponse; import org.pingle.pingleserver.dto.type.ErrorMessage; import org.pingle.pingleserver.exception.CustomException; import org.pingle.pingleserver.repository.MeetingRepository; +import org.pingle.pingleserver.repository.TeamRepository; import org.pingle.pingleserver.repository.UserMeetingRepository; import org.pingle.pingleserver.utils.SlackUtil; import org.springframework.stereotype.Service; @@ -20,6 +25,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -29,6 +35,7 @@ public class MeetingService { private final SlackUtil slackUtil; private final MeetingRepository meetingRepository; private final UserMeetingRepository userMeetingRepository; + private final TeamRepository teamRepository; @Transactional public Meeting createMeeting(MeetingRequest request, Pin pin) { @@ -55,15 +62,15 @@ public ParticipantsResponse getParticipants(Long meetingId) { public List getMyPingles(Long userId, Long teamId, boolean participation) { List myMeetings = new ArrayList<>(); - if(participation) // 참여 완려 -> 이미 시작 endAt 이 현재보다 빠름 - myMeetings = meetingRepository.findParticipatedMeetingsForUsersInTeamOrderByTime(userId, teamId, LocalDateTime.now()); - if(!participation) // 참여하지 않은 것 == 나중에 일어날 것 -> endAt이 현재보다 늦음 + if (participation) // 참여 완려 -> 이미 시작 endAt 이 현재보다 빠름 + myMeetings = meetingRepository.findParticipatedMeetingsForUsersInTeamOrderByTime(userId, teamId, LocalDateTime.now()); + if (!participation) // 참여하지 않은 것 == 나중에 일어날 것 -> endAt이 현재보다 늦음 myMeetings = meetingRepository.findUnparticipatedMeetingsForUsersInTeamOrderByTime(userId, teamId, LocalDateTime.now()); return myMeetings.stream() .map(meeting -> MyPingleResponse.of(meeting, getOwnerName(meeting), isOwner(userId, meeting.getId()))).toList(); } - public String getOwnerName(Meeting meeting){ + public String getOwnerName(Meeting meeting) { Optional userMeeting = userMeetingRepository.findByMeetingAndMeetingRole(meeting, MRole.OWNER); if (userMeeting.isPresent()) { return userMeeting.get().getUser().getValidName(); @@ -71,18 +78,37 @@ public String getOwnerName(Meeting meeting){ return "(알 수 없음)"; } } - + private boolean isOwner(Long userId, Long meetingId) { return userMeetingRepository.existsByUserIdAndMeetingIdAndMeetingRole(userId, meetingId, MRole.OWNER); } - + @Transactional public void deleteMeeting(Long userId, Long meetingId) { Meeting meeting = meetingRepository.findById(meetingId).orElseThrow(() -> new CustomException(ErrorMessage.RESOURCE_NOT_FOUND)); UserMeeting userMeeting = userMeetingRepository.findByUserIdAndMeeting(userId, meeting).orElseThrow(() -> new CustomException(ErrorMessage.RESOURCE_NOT_FOUND)); - if(userMeeting.getMeetingRole().getValue().equals("participants")) + if (userMeeting.getMeetingRole().getValue().equals("participants")) throw new CustomException(ErrorMessage.PERMISSION_DENIED); meetingRepository.delete(meeting); } + + public SearchResponse searchMeetings(Long userId, String q, MCategory category, Long teamId, SearchOrder order) { + + if (q!=null && q.isBlank()) throw new CustomException(ErrorMessage.BAD_REQUEST); + if (!teamRepository.existsById(teamId)) throw new CustomException(ErrorMessage.RESOURCE_NOT_FOUND); + + if (order.getValue().equals(SearchOrder.NEW.getValue())) { + List responses = meetingRepository.findMeetingsByParameterOOrderByCreatedAt(q, category ,teamId) + .stream().map(meeting -> SearchIndividualResponse.of(meeting, userId)).collect(Collectors.toList()); + return SearchResponse.of(responses); + } + + if (order.getValue().equals(SearchOrder.UPCOMING.getValue())) { + List responses = meetingRepository.findMeetingsByParameterOOrderByStartAt(q, category, teamId) + .stream().map(meeting -> SearchIndividualResponse.of(meeting, userId)).collect(Collectors.toList()); + return SearchResponse.of(responses); + } + return null; + } }