-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat #38 매칭 필터링 및 알고리즘 구현 #38
base: dev
Are you sure you want to change the base?
The head ref may contain hidden characters: "feat/#30/\uB9E4\uCE6D-\uD544\uD130\uB9C1-\uBC0F-\uC54C\uACE0\uB9AC\uC998-\uAD6C\uD604"
Changes from all commits
eef7b34
d796daa
103f446
8edc548
43382ca
b305484
579a8dd
f29a3ba
2419893
109e7d2
18030a7
7f4004a
add4ca9
848e35d
d7fe04d
a290383
dd9a668
dee5512
53befbb
4ce39e3
f5d9b52
289fea5
68ef3a1
783fdd9
c7889f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,33 @@ | ||
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 { | ||
|
||
/** | ||
* 방을 찾는 메서드 | ||
* 이미 방에 들어가있는 멤버가 다시 요청했을 때 Optional.empty()를 반환하도록 로직을 구성해야함 | ||
* @param userId 방에 들어가려는 사용자 ID | ||
* @param startPoint 매칭 시작 지점 좌표 | ||
* @param destinationPoint 도착지 좌표 | ||
* @param startLongitude 시작 지점 경도 | ||
* @param startLatitude 시작 지점 위도 | ||
* @param destinationLongitude 도착 지점 경도 | ||
* @param destinationLatitude 도착 지점 위도 | ||
* @param criteria 방 검색에 필요한 기타 조건 (태그 등) | ||
* @return Optional<FindRoomResult> - 매칭 가능한 방 정보가 있으면 값이 있고, 없으면 empty | ||
*/ | ||
Optional<FindRoomResult> findRoom(Long userId, String startPoint, String destinationPoint, List<Tags> criteria); | ||
Optional<FindRoomResult> findRoom(Long userId, double startLongitude, double startLatitude, double destinationLongitude, double destinationLatitude, List<Tags> criteria); | ||
|
||
/** | ||
* 전체 매칭 방을 페이지 단위로 조회 | ||
* | ||
* @param pageNumber 페이지 번호 (0부터 시작) | ||
* @param pageSize 한 페이지에 포함될 매칭 방의 개수 | ||
* @return Page<MatchingRoom> - 페이지별 매칭 방 정보 | ||
*/ | ||
Page<MatchingRoom> findMatchingRooms(int pageNumber, int pageSize); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,29 +3,85 @@ | |
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 | ||
@RequiredArgsConstructor | ||
public class MockMatchingAlgorithmService implements MatchingAlgorithmService { | ||
|
||
private final MatchingRoomRepository matchingRoomRepository; | ||
private final MemberService memberService; | ||
|
||
private static final double SEARCH_RADIUS = 300.0; | ||
|
||
@Override | ||
public Optional<FindRoomResult> findRoom(Long userId, String startPoint, String destinationPoint, List<Tags> criteria) { | ||
List<MatchingRoom> 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<FindRoomResult> findRoom(Long userId, double startLongitude, double startLatitude, double destinationLongitude, double destinationLatitude, | ||
List<Tags> criteria) { | ||
/* | ||
사용자 ID로 사용자 정보 조회(이미 방에 참여하고 있는지 중복체크) | ||
*/ | ||
Members user = memberService.findById(userId); | ||
|
||
if (matchingRoomRepository.existsByMemberInMatchingRoom(user)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 예외를 던져주게 되면 매칭 이벤트가 종료 되나요?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 매칭 이벤트가 종료되거나 기존 매칭방이 삭제되는 것은 아니라고 생각해주시면 됩니다. 기존의 매칭방은 정상적으로 유지되고, 해당 부분에 대해서는 고민을 많이 했던 부분인데 400을 던져주는 방식으로 설계한 이유는, 그래서 동일한 조건으로 생성된 방이 이미 존재하는 상황에서는 중복된 방 생성을 방지하기 위해 |
||
throw new DuplicatedMatchingRoomException(); // * 추후 논의 후 리팩토링 필요 * 똑같은 조건으로 방 생성시 예외 던져주기 | ||
} | ||
/* | ||
위치 정보를 이용한 방 검색(300M 이내) | ||
*/ | ||
List<MatchingRoom> matchingRooms = matchingRoomRepository.findRoomsByStartAndDestination( | ||
startLongitude, | ||
startLatitude, | ||
destinationLongitude, | ||
destinationLatitude, | ||
SEARCH_RADIUS | ||
); | ||
/* | ||
ACTIVE 상태인 방만 필터링 | ||
*/ | ||
matchingRooms = matchingRooms.stream() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DB에서 가져올 때 Active인 방들만 가져오는 것은 어떨까요?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 원래는 Active 상태의 방과 태그 조건까지 모두 쿼리로 조회해 오도록 설계했었는데, 주영님의 의견을 반영하여 수정했습니다 해당 조건들을 모두 쿼리에서 처리하려면 쿼리가 너무 복잡하고 길어져서 서버단 성능에 부담이 갈 수도 있고, 가독성이 떨어질 수도 있다는 피드백이 있었어서, 해당 부분 고려한 뒤에 Active 상태와 태그 필터링은 서비스 단에서 처리하는 방식으로 이전하였습니다...! |
||
.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<MatchingRoom> 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); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 정적 경로 설정이 빠진 것 같아요! |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<MatchingRoom, Long> { | ||
|
||
@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<MatchingRoom> findRoomsByStartAndDestination( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 거리도 파라미터로 빼주고 호출하는 쪽에서 상수로 관리해주면 거리 변경이 필요할 때 유용할 것 같아욤 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 기존 쿼리의 <= 300 부분 :radius로 설정해줘서 아래 내용처럼 상수로 선언해서 사용하겠습니당
|
||
@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); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 객체 생성의 책임도 객체 내부로 이전하면 통일성 있고 좋을 것 같아요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 부분은 원래 구현되어 있던 내용을 그대로 사용했기 때문에 미처 확인하지 못했던 부분입니다 |
||
.startLongitude(startLongitude) | ||
.startLatitude(startLatitude) | ||
.startLocationName(matchRoomCreatedEvent.startName()) | ||
.endLongitude(endLongitude) | ||
.endLatitude(endLatitude) | ||
.endLocationName(matchRoomCreatedEvent.destinationName()) | ||
.build(); | ||
return this.routeRepository.save(route); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
현재는 서버에 저장된 Rount(가천대 - 정문) 노선만 제공을 하고 있으니, 서버에서 제공하는 정보를 토대로 클라이언트에서 위도 경도 값을 그대로 넣어줘야할 것 같아요!
그렇다면 입력받은 값에 대한 유효성 검증이 추가되면 좋을 것 같아요. 위도, 경도 값으로 Route가 존재하는지 조회를 해본다거나 하는