diff --git a/src/main/java/com/gachtaxi/domain/matching/algorithm/service/MatchingAlgorithmService.java b/src/main/java/com/gachtaxi/domain/matching/algorithm/service/MatchingAlgorithmService.java index 778256e6..449548f5 100644 --- a/src/main/java/com/gachtaxi/domain/matching/algorithm/service/MatchingAlgorithmService.java +++ b/src/main/java/com/gachtaxi/domain/matching/algorithm/service/MatchingAlgorithmService.java @@ -1,9 +1,11 @@ package com.gachtaxi.domain.matching.algorithm.service; import com.gachtaxi.domain.matching.algorithm.dto.FindRoomResult; +import com.gachtaxi.domain.matching.common.entity.MatchingRoom; import com.gachtaxi.domain.matching.common.entity.enums.Tags; import java.util.List; import java.util.Optional; +import org.springframework.data.domain.Page; public interface MatchingAlgorithmService { @@ -11,10 +13,21 @@ public interface MatchingAlgorithmService { * 방을 찾는 메서드 * 이미 방에 들어가있는 멤버가 다시 요청했을 때 Optional.empty()를 반환하도록 로직을 구성해야함 * @param userId 방에 들어가려는 사용자 ID - * @param startPoint 매칭 시작 지점 좌표 - * @param destinationPoint 도착지 좌표 + * @param startLongitude 시작 지점 경도 + * @param startLatitude 시작 지점 위도 + * @param destinationLongitude 도착 지점 경도 + * @param destinationLatitude 도착 지점 위도 * @param criteria 방 검색에 필요한 기타 조건 (태그 등) * @return Optional - 매칭 가능한 방 정보가 있으면 값이 있고, 없으면 empty */ - Optional findRoom(Long userId, String startPoint, String destinationPoint, List criteria); + Optional findRoom(Long userId, double startLongitude, double startLatitude, double destinationLongitude, double destinationLatitude, List criteria); + + /** + * 전체 매칭 방을 페이지 단위로 조회 + * + * @param pageNumber 페이지 번호 (0부터 시작) + * @param pageSize 한 페이지에 포함될 매칭 방의 개수 + * @return Page - 페이지별 매칭 방 정보 + */ + Page findMatchingRooms(int pageNumber, int pageSize); } diff --git a/src/main/java/com/gachtaxi/domain/matching/algorithm/service/MockMatchingAlgorithmService.java b/src/main/java/com/gachtaxi/domain/matching/algorithm/service/MockMatchingAlgorithmService.java index 655d629d..31ea0762 100644 --- a/src/main/java/com/gachtaxi/domain/matching/algorithm/service/MockMatchingAlgorithmService.java +++ b/src/main/java/com/gachtaxi/domain/matching/algorithm/service/MockMatchingAlgorithmService.java @@ -3,10 +3,17 @@ import com.gachtaxi.domain.matching.algorithm.dto.FindRoomResult; import com.gachtaxi.domain.matching.common.entity.MatchingRoom; import com.gachtaxi.domain.matching.common.entity.enums.Tags; +import com.gachtaxi.domain.matching.common.exception.DuplicatedMatchingRoomException; +import com.gachtaxi.domain.matching.common.exception.PageNotFoundException; import com.gachtaxi.domain.matching.common.repository.MatchingRoomRepository; +import com.gachtaxi.domain.members.entity.Members; +import com.gachtaxi.domain.members.service.MemberService; import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; @Service @@ -14,18 +21,67 @@ public class MockMatchingAlgorithmService implements MatchingAlgorithmService { private final MatchingRoomRepository matchingRoomRepository; + private final MemberService memberService; + + private static final double SEARCH_RADIUS = 300.0; @Override - public Optional findRoom(Long userId, String startPoint, String destinationPoint, List criteria) { - List matchingRoomList = this.matchingRoomRepository.findAll(); - if (!matchingRoomList.isEmpty()) { - MatchingRoom matchingRoom = matchingRoomList.get(0); - return Optional.of( - FindRoomResult.builder() - .roomId(matchingRoom.getId()) - .maxCapacity(matchingRoom.getCapacity()) - .build()); + public Optional findRoom(Long userId, double startLongitude, double startLatitude, double destinationLongitude, double destinationLatitude, + List criteria) { + /* + 사용자 ID로 사용자 정보 조회(이미 방에 참여하고 있는지 중복체크) + */ + Members user = memberService.findById(userId); + + if (matchingRoomRepository.existsByMemberInMatchingRoom(user)) { + throw new DuplicatedMatchingRoomException(); // * 추후 논의 후 리팩토링 필요 * 똑같은 조건으로 방 생성시 예외 던져주기 + } + /* + 위치 정보를 이용한 방 검색(300M 이내) + */ + List matchingRooms = matchingRoomRepository.findRoomsByStartAndDestination( + startLongitude, + startLatitude, + destinationLongitude, + destinationLatitude, + SEARCH_RADIUS + ); + /* + ACTIVE 상태인 방만 필터링 + */ + matchingRooms = matchingRooms.stream() + .filter(MatchingRoom::isActiveMatchingRoom) + .toList(); + /* + 태그 조건이 있는 경우에 태그정보까지 필터링 + */ + if (criteria != null && !criteria.isEmpty()) { + matchingRooms = matchingRooms.stream() + .filter(room -> criteria.stream().anyMatch(room::containsTag)) + .toList(); } + /* + 조건에 맞는 방이 있으면 첫 번째 방의 상세 정보 반환 + */ + if (!matchingRooms.isEmpty()) { + MatchingRoom room = matchingRooms.get(0); + return Optional.of(room.toFindRoomResult()); + } + /* + 조건에 맞는 방이 없으면 empty 반환 + */ return Optional.empty(); } + @Override + public Page findMatchingRooms(int pageNumber, int pageSize) { + + if (pageNumber < 0) { + throw new PageNotFoundException(); + } + + PageRequest pageable = PageRequest.of(pageNumber, pageSize, Sort.by(Sort.Direction.DESC, "id")); + + return matchingRoomRepository.findAll(pageable); + } + } diff --git a/src/main/java/com/gachtaxi/domain/matching/common/entity/MatchingRoom.java b/src/main/java/com/gachtaxi/domain/matching/common/entity/MatchingRoom.java index 2f51ceb6..e625fd1e 100644 --- a/src/main/java/com/gachtaxi/domain/matching/common/entity/MatchingRoom.java +++ b/src/main/java/com/gachtaxi/domain/matching/common/entity/MatchingRoom.java @@ -1,6 +1,8 @@ package com.gachtaxi.domain.matching.common.entity; +import com.gachtaxi.domain.matching.algorithm.dto.FindRoomResult; import com.gachtaxi.domain.matching.common.entity.enums.MatchingRoomStatus; +import com.gachtaxi.domain.matching.common.entity.enums.Tags; import com.gachtaxi.domain.members.entity.Members; import com.gachtaxi.global.common.entity.BaseEntity; import jakarta.persistence.CascadeType; @@ -59,4 +61,14 @@ public class MatchingRoom extends BaseEntity { public boolean isActiveMatchingRoom() { return this.matchingRoomStatus == MatchingRoomStatus.ACTIVE; } + public boolean containsTag(Tags tag) { + return this.matchingRoomTagInfo.stream() + .anyMatch(tagInfo -> tagInfo.matchesTag(tag)); + } + public FindRoomResult toFindRoomResult() { + return FindRoomResult.builder() + .roomId(this.getId()) + .maxCapacity(this.getCapacity()) + .build(); + } } diff --git a/src/main/java/com/gachtaxi/domain/matching/common/entity/MatchingRoomTagInfo.java b/src/main/java/com/gachtaxi/domain/matching/common/entity/MatchingRoomTagInfo.java index bd21d37e..8bbad8a9 100644 --- a/src/main/java/com/gachtaxi/domain/matching/common/entity/MatchingRoomTagInfo.java +++ b/src/main/java/com/gachtaxi/domain/matching/common/entity/MatchingRoomTagInfo.java @@ -24,4 +24,8 @@ public class MatchingRoomTagInfo extends BaseEntity { @Enumerated(EnumType.STRING) private Tags tags; + + public boolean matchesTag(Tags tag) { + return this.tags == tag; + } } diff --git a/src/main/java/com/gachtaxi/domain/matching/common/entity/Route.java b/src/main/java/com/gachtaxi/domain/matching/common/entity/Route.java index bb148948..1a942627 100644 --- a/src/main/java/com/gachtaxi/domain/matching/common/entity/Route.java +++ b/src/main/java/com/gachtaxi/domain/matching/common/entity/Route.java @@ -15,9 +15,11 @@ @AllArgsConstructor(access = AccessLevel.PRIVATE) public class Route extends BaseEntity { - private String startLocationCoordinate; + private double startLongitude; + private double startLatitude; private String startLocationName; - private String endLocationCoordinate; + private double endLongitude; + private double endLatitude; private String endLocationName; } diff --git a/src/main/java/com/gachtaxi/domain/matching/common/exception/DuplicatedMatchingRoomException.java b/src/main/java/com/gachtaxi/domain/matching/common/exception/DuplicatedMatchingRoomException.java new file mode 100644 index 00000000..3d13f73f --- /dev/null +++ b/src/main/java/com/gachtaxi/domain/matching/common/exception/DuplicatedMatchingRoomException.java @@ -0,0 +1,12 @@ +package com.gachtaxi.domain.matching.common.exception; + +import static com.gachtaxi.domain.matching.common.exception.ErrorMessage.DUPLICATED_MATCHING_ROOM; +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +import com.gachtaxi.global.common.exception.BaseException; + +public class DuplicatedMatchingRoomException extends BaseException { + public DuplicatedMatchingRoomException() { + super(BAD_REQUEST, DUPLICATED_MATCHING_ROOM.getMessage()); + } +} diff --git a/src/main/java/com/gachtaxi/domain/matching/common/exception/ErrorMessage.java b/src/main/java/com/gachtaxi/domain/matching/common/exception/ErrorMessage.java index f7569666..b51b80b8 100644 --- a/src/main/java/com/gachtaxi/domain/matching/common/exception/ErrorMessage.java +++ b/src/main/java/com/gachtaxi/domain/matching/common/exception/ErrorMessage.java @@ -8,7 +8,9 @@ public enum ErrorMessage { NO_SUCH_MATCHING_ROOM("해당 매칭 방이 존재하지 않습니다."), - NOT_ACTIVE_MATCHING_ROOM("열린 매칭 방이 아닙니다."); + NOT_ACTIVE_MATCHING_ROOM("열린 매칭 방이 아닙니다."), + DUPLICATED_MATCHING_ROOM("이미 존재하는 매칭 방입니다."), + NOT_FOUND_PAGE("페이지 번호는 0 이상이어야 합니다."); private final String message; } diff --git a/src/main/java/com/gachtaxi/domain/matching/common/exception/PageNotFoundException.java b/src/main/java/com/gachtaxi/domain/matching/common/exception/PageNotFoundException.java new file mode 100644 index 00000000..96e01c83 --- /dev/null +++ b/src/main/java/com/gachtaxi/domain/matching/common/exception/PageNotFoundException.java @@ -0,0 +1,10 @@ +package com.gachtaxi.domain.matching.common.exception; + +import com.gachtaxi.global.common.exception.BaseException; +import org.springframework.http.HttpStatus; + +public class PageNotFoundException extends BaseException { + public PageNotFoundException() { + super(HttpStatus.NOT_FOUND, ErrorMessage.NOT_FOUND_PAGE.getMessage()); + } +} diff --git a/src/main/java/com/gachtaxi/domain/matching/common/repository/MatchingRoomRepository.java b/src/main/java/com/gachtaxi/domain/matching/common/repository/MatchingRoomRepository.java index a95e57f1..264dc0bc 100644 --- a/src/main/java/com/gachtaxi/domain/matching/common/repository/MatchingRoomRepository.java +++ b/src/main/java/com/gachtaxi/domain/matching/common/repository/MatchingRoomRepository.java @@ -1,10 +1,28 @@ package com.gachtaxi.domain.matching.common.repository; import com.gachtaxi.domain.matching.common.entity.MatchingRoom; +import com.gachtaxi.domain.members.entity.Members; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository public interface MatchingRoomRepository extends JpaRepository { - + @Query("SELECT r FROM MatchingRoom r " + + "WHERE " + + "FUNCTION('ST_Distance_Sphere', FUNCTION('POINT', :startLongitude, :startLatitude), FUNCTION('POINT', r.route.startLongitude, r.route.startLatitude)) <= :radius " + + "AND FUNCTION('ST_Distance_Sphere', FUNCTION('POINT', :destinationLongitude, :destinationLatitude), FUNCTION('POINT', r.route.endLongitude, r.route.endLatitude)) <= :radius ") + List findRoomsByStartAndDestination( + @Param("startLongitude") double startLongitude, + @Param("startLatitude") double startLatitude, + @Param("destinationLongitude") double destinationLongitude, + @Param("destinationLatitude") double destinationLatitude, + @Param("radius") double radius + ); + @Query("SELECT CASE WHEN COUNT(m) > 0 THEN true ELSE false END " + + "FROM MatchingRoom r JOIN r.memberMatchingRoomChargingInfo m " + + "WHERE m.members = :user") + boolean existsByMemberInMatchingRoom(@Param("user") Members user); } diff --git a/src/main/java/com/gachtaxi/domain/matching/common/service/AutoMatchingService.java b/src/main/java/com/gachtaxi/domain/matching/common/service/AutoMatchingService.java index 2428f12f..56b960b3 100644 --- a/src/main/java/com/gachtaxi/domain/matching/common/service/AutoMatchingService.java +++ b/src/main/java/com/gachtaxi/domain/matching/common/service/AutoMatchingService.java @@ -42,9 +42,17 @@ public AutoMatchingPostResponse handlerAutoRequestMatching( AutoMatchingPostRequest autoMatchingPostRequest ) { List criteria = autoMatchingPostRequest.getCriteria(); + + String[] startCoordinates = autoMatchingPostRequest.startPoint().split(","); + double startLongitude = Double.parseDouble(startCoordinates[0]); + double startLatitude = Double.parseDouble(startCoordinates[1]); + + String[] destinationCoordinates = autoMatchingPostRequest.destinationPoint().split(","); + double destinationLongitude = Double.parseDouble(destinationCoordinates[0]); + double destinationLatitude = Double.parseDouble(destinationCoordinates[1]); + Optional optionalRoom = - this.matchingAlgorithmService.findRoom(memberId, autoMatchingPostRequest.startPoint(), - autoMatchingPostRequest.destinationPoint(), criteria); + this.matchingAlgorithmService.findRoom(memberId, startLongitude, startLatitude, destinationLongitude, destinationLatitude, criteria); optionalRoom .ifPresentOrElse( diff --git a/src/main/java/com/gachtaxi/domain/matching/common/service/MatchingRoomService.java b/src/main/java/com/gachtaxi/domain/matching/common/service/MatchingRoomService.java index 1eab6c64..703bc0ad 100644 --- a/src/main/java/com/gachtaxi/domain/matching/common/service/MatchingRoomService.java +++ b/src/main/java/com/gachtaxi/domain/matching/common/service/MatchingRoomService.java @@ -57,13 +57,22 @@ public MatchingRoom save(MatchRoomCreatedEvent matchRoomCreatedEvent) { } private Route saveRoute(MatchRoomCreatedEvent matchRoomCreatedEvent) { - Route route = Route.builder() - .startLocationCoordinate(matchRoomCreatedEvent.startPoint()) - .startLocationName(matchRoomCreatedEvent.startName()) - .endLocationCoordinate(matchRoomCreatedEvent.destinationPoint()) - .endLocationName(matchRoomCreatedEvent.destinationName()) - .build(); + String[] startCoordinates = matchRoomCreatedEvent.startPoint().split(","); + double startLongitude = Double.parseDouble(startCoordinates[0]); + double startLatitude = Double.parseDouble(startCoordinates[1]); + String[] endCoordinates = matchRoomCreatedEvent.destinationPoint().split(","); + double endLongitude = Double.parseDouble(endCoordinates[0]); + double endLatitude = Double.parseDouble(endCoordinates[1]); + + Route route = Route.builder() + .startLongitude(startLongitude) + .startLatitude(startLatitude) + .startLocationName(matchRoomCreatedEvent.startName()) + .endLongitude(endLongitude) + .endLatitude(endLatitude) + .endLocationName(matchRoomCreatedEvent.destinationName()) + .build(); return this.routeRepository.save(route); }