diff --git a/src/main/java/com/moim/backend/domain/space/config/TmapProperties.java b/src/main/java/com/moim/backend/domain/space/config/TmapProperties.java index fa61b5f..5e81964 100644 --- a/src/main/java/com/moim/backend/domain/space/config/TmapProperties.java +++ b/src/main/java/com/moim/backend/domain/space/config/TmapProperties.java @@ -1,5 +1,7 @@ package com.moim.backend.domain.space.config; +import com.moim.backend.domain.space.entity.BestPlace; +import com.moim.backend.domain.space.entity.Participation; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @@ -14,6 +16,7 @@ public class TmapProperties { private String appKey; private String searchPathUri; + private String walkSearchPathUri; public Map getRequestParameter(Double startX, Double startY, Double endX, Double endY) { Map parameters = new HashMap<>(); @@ -24,4 +27,17 @@ public Map getRequestParameter(Double startX, Double startY, Dou return parameters; } + public Map getWalkRequestParameter( + String startName, String endName, Double startX, Double startY, Double endX, Double endY + ) { + Map parameters = new HashMap<>(); + parameters.put("startName", startName); + parameters.put("endName", endName); + parameters.put("startX", String.valueOf(startX)); + parameters.put("startY", String.valueOf(startY)); + parameters.put("endX", String.valueOf(endX)); + parameters.put("endY", String.valueOf(endY)); + return parameters; + } + } diff --git a/src/main/java/com/moim/backend/domain/space/entity/TransportationType.java b/src/main/java/com/moim/backend/domain/space/entity/TransportationType.java index d3b76d4..abd4590 100644 --- a/src/main/java/com/moim/backend/domain/space/entity/TransportationType.java +++ b/src/main/java/com/moim/backend/domain/space/entity/TransportationType.java @@ -4,5 +4,5 @@ @Getter public enum TransportationType { - PUBLIC, PERSONAL, NULL + PUBLIC, PERSONAL, WALK, NULL } diff --git a/src/main/java/com/moim/backend/domain/space/response/CarMoveInfo.java b/src/main/java/com/moim/backend/domain/space/response/CarPathResponse.java similarity index 97% rename from src/main/java/com/moim/backend/domain/space/response/CarMoveInfo.java rename to src/main/java/com/moim/backend/domain/space/response/CarPathResponse.java index e0b964d..72de82f 100644 --- a/src/main/java/com/moim/backend/domain/space/response/CarMoveInfo.java +++ b/src/main/java/com/moim/backend/domain/space/response/CarPathResponse.java @@ -14,7 +14,7 @@ @Getter @NoArgsConstructor @AllArgsConstructor -public class CarMoveInfo implements MoveInfoInterface, PathGraphicDataInterface { +public class CarPathResponse implements MoveInfoInterface, PathGraphicDataInterface { private List routes; private Route getBestRoute() { diff --git a/src/main/java/com/moim/backend/domain/space/response/MoveUserInfo.java b/src/main/java/com/moim/backend/domain/space/response/MoveUserInfo.java new file mode 100644 index 0000000..ce6997d --- /dev/null +++ b/src/main/java/com/moim/backend/domain/space/response/MoveUserInfo.java @@ -0,0 +1,105 @@ +package com.moim.backend.domain.space.response; + +import com.moim.backend.domain.space.entity.BestPlace; +import com.moim.backend.domain.space.entity.Participation; +import com.moim.backend.domain.space.entity.Space; +import com.moim.backend.domain.space.entity.TransportationType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class MoveUserInfo { + @Schema(description = "모임장 여부") + private Boolean isAdmin; + @Schema(description = "사용자 아이디") + private Long userId; + @Schema(description = "사용자 닉네임") + private String userName; + @Schema(description = "이동 수단", allowableValues = {"PUBLIC", "PERSONAL", "WALK"}) + private TransportationType transportationType; + @Schema(description = "총 환승 횟수") + private int transitCount; + @Schema(description = "이동 시간. 단위: 분") + private int totalTime; + @Schema(description = "총 이동 거리 단위: 미터") + private Double totalDistance; + @Schema(description = "예상 요금") + private int payment; + @Schema(description = "이동 경로에 따른 좌표 리스트. 경로를 찾을 수 없을 경우, 비어있는 리스트로 반환") + private List path = new ArrayList<>(); + + // 경로를 찾을 수 없는 사용자 + public static MoveUserInfo createWithEmptyPath( + Space space, + Participation participation + ) { + MoveUserInfo moveUserInfo = new MoveUserInfo(); + moveUserInfo.isAdmin = (participation.getUserId() == space.getAdminId()) ? true : false; + moveUserInfo.userId = participation.getUserId(); + moveUserInfo.transportationType = participation.getTransportation(); + moveUserInfo.userName = participation.getUserName(); + return moveUserInfo; + } + + public static MoveUserInfo createWithPublicPath( + Space space, + Participation participation, + TmapPublicPathResponse tmapPublicPathResponse, + List path + ) { + MoveUserInfo moveUserInfo = new MoveUserInfo(); + moveUserInfo.isAdmin = (participation.getUserId() == space.getAdminId()) ? true : false; + moveUserInfo.userId = participation.getUserId(); + moveUserInfo.userName = participation.getUserName(); + moveUserInfo.transportationType = participation.getTransportation(); + moveUserInfo.totalTime = tmapPublicPathResponse.getTotalTime(); + moveUserInfo.totalDistance = tmapPublicPathResponse.getTotalDistance(); + moveUserInfo.path = path; + moveUserInfo.payment = tmapPublicPathResponse.getPayment(); + return moveUserInfo; + } + + public static MoveUserInfo createWithCarPath( + Space space, + Participation participation, + CarPathResponse carPathResponse, + BestPlace bestPlace + ) { + MoveUserInfo moveUserInfo = new MoveUserInfo(); + moveUserInfo.isAdmin = (participation.getUserId() == space.getAdminId()) ? true : false; + moveUserInfo.userId = participation.getUserId(); + moveUserInfo.userName = participation.getUserName(); + moveUserInfo.transportationType = participation.getTransportation(); + moveUserInfo.totalTime = carPathResponse.getTotalTime(); + moveUserInfo.totalDistance = carPathResponse.getTotalDistance(); + moveUserInfo.path = carPathResponse.getPathList(participation, bestPlace); + moveUserInfo.payment = carPathResponse.getPayment(); + return moveUserInfo; + } + + public static MoveUserInfo createWithWalkPath( + Space space, + Participation participation, + int totalTime, + double totalDistance, + Listpath + ) { + MoveUserInfo moveUserInfo = new MoveUserInfo(); + moveUserInfo.isAdmin = (participation.getUserId() == space.getAdminId()) ? true : false; + moveUserInfo.userId = participation.getUserId(); + moveUserInfo.userName = participation.getUserName(); + moveUserInfo.transportationType = participation.getTransportation(); + moveUserInfo.totalTime = totalTime; + moveUserInfo.totalDistance = totalDistance; + moveUserInfo.path = path; + moveUserInfo.payment = 0; + return moveUserInfo; + } +} diff --git a/src/main/java/com/moim/backend/domain/space/response/PlaceRouteResponse.java b/src/main/java/com/moim/backend/domain/space/response/PlaceRouteResponse.java index b590234..39e9868 100644 --- a/src/main/java/com/moim/backend/domain/space/response/PlaceRouteResponse.java +++ b/src/main/java/com/moim/backend/domain/space/response/PlaceRouteResponse.java @@ -30,71 +30,4 @@ public PlaceRouteResponse( this.longitude = bestPlace.getLongitude(); this.moveUserInfo = moveUserInfoList; } - - @Getter - @NoArgsConstructor - @AllArgsConstructor - public static class MoveUserInfo { - private Boolean isAdmin; - private Long userId; - private String userName; - private TransportationType transportationType; - private int transitCount; // 총 환승 횟수 - private int totalTime; // 단위: 분(m) - private Double totalDistance; - private int payment; - private List path; - - public MoveUserInfo( - Space group, - Participation participation, - BusGraphicDataResponse busGraphicDataResponse, - BusPathResponse busPathResponse, - BestPlace bestPlace - ) { - this.isAdmin = (participation.getUserId() == group.getAdminId()) ? true : false; - this.userId = participation.getUserId(); - this.userName = participation.getUserName(); - this.transportationType = participation.getTransportation(); - this.transitCount = busPathResponse.getTotalTransitCount(); - this.totalTime = busPathResponse.getTotalTime(); - this.totalDistance = busPathResponse.getTotalDistance(); - this.path = busGraphicDataResponse.getPathList(participation, bestPlace); - this.payment = busPathResponse.getPayment(); - } - - public MoveUserInfo( - Space group, - Participation participation, - CarMoveInfo carMoveInfo, - BestPlace bestPlace - ) { - this.isAdmin = (participation.getUserId() == group.getAdminId()) ? true : false; - this.userId = participation.getUserId(); - this.userName = participation.getUserName(); - this.transportationType = participation.getTransportation(); - this.totalTime = carMoveInfo.getTotalTime(); - this.totalDistance = carMoveInfo.getTotalDistance(); - this.path = carMoveInfo.getPathList(participation, bestPlace); - this.payment = carMoveInfo.getPayment(); - } - - public MoveUserInfo( - Space group, - Participation participation, - TmapPublicPathResponse tmapPublicPathResponse, - BestPlace bestPlace, - List path - ) { - this.isAdmin = (participation.getUserId() == group.getAdminId()) ? true : false; - this.userId = participation.getUserId(); - this.userName = participation.getUserName(); - this.transportationType = participation.getTransportation(); - this.totalTime = tmapPublicPathResponse.getTotalTime(); - this.totalDistance = tmapPublicPathResponse.getTotalDistance(); - this.path = path; - this.payment = tmapPublicPathResponse.getPayment(); - } - } - } diff --git a/src/main/java/com/moim/backend/domain/space/response/TmapPublicPathResponse.java b/src/main/java/com/moim/backend/domain/space/response/TmapPublicPathResponse.java index 9ad395c..b2215a3 100644 --- a/src/main/java/com/moim/backend/domain/space/response/TmapPublicPathResponse.java +++ b/src/main/java/com/moim/backend/domain/space/response/TmapPublicPathResponse.java @@ -12,6 +12,7 @@ @AllArgsConstructor public class TmapPublicPathResponse implements MoveInfoInterface { + private Result result; private MetaData metaData; @Override @@ -38,6 +39,18 @@ private Itinerary getBestPath() { return metaData.plan.itineraries.get(0); } + public int getResultStatus() { + return result.status; + } + + @Getter + @NoArgsConstructor + @AllArgsConstructor + private static class Result { + private String message; + private int status; + } + @Getter @NoArgsConstructor @AllArgsConstructor diff --git a/src/main/java/com/moim/backend/domain/space/response/TmapWalkPathResponse.java b/src/main/java/com/moim/backend/domain/space/response/TmapWalkPathResponse.java new file mode 100644 index 0000000..188ad83 --- /dev/null +++ b/src/main/java/com/moim/backend/domain/space/response/TmapWalkPathResponse.java @@ -0,0 +1,49 @@ +package com.moim.backend.domain.space.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class TmapWalkPathResponse { + public List features; + + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class Feature { + public Geometry geometry; + public Properties properties; + } + + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class Geometry { + public String type; + public String coordinates; + } + + // Geometry 타입에 따라 시간이나 거리를 넘겨줄 때의 키 값이 달라짐 + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class Properties { + private long totalDistance; // 단위: m. Geometry 타입이 "Point"일 때 + private long totalTime; // 단위: 초. Geometry 타입이 "Point"일 때 + private long distance; // 단위: m. Geometry 타입이 "LineString"일 때 + private long time; // 단위: 초. Geometry 타입이 "LineString"일 때 + + public long getTotalTime() { + return totalTime + time; + } + + public long getTotalDistance() { + return totalDistance + distance; + } + } +} diff --git a/src/main/java/com/moim/backend/domain/space/service/DirectionService.java b/src/main/java/com/moim/backend/domain/space/service/DirectionService.java index 07070d2..b376b80 100644 --- a/src/main/java/com/moim/backend/domain/space/service/DirectionService.java +++ b/src/main/java/com/moim/backend/domain/space/service/DirectionService.java @@ -5,12 +5,11 @@ import com.moim.backend.domain.space.entity.BestPlace; import com.moim.backend.domain.space.entity.Space; import com.moim.backend.domain.space.entity.Participation; -import com.moim.backend.domain.space.response.BusGraphicDataResponse; -import com.moim.backend.domain.space.response.BusPathResponse; -import com.moim.backend.domain.space.response.CarMoveInfo; +import com.moim.backend.domain.space.response.CarPathResponse; +import com.moim.backend.domain.space.response.MoveUserInfo; import com.moim.backend.domain.space.response.PathDto; -import com.moim.backend.domain.space.response.PlaceRouteResponse; import com.moim.backend.domain.space.response.TmapPublicPathResponse; +import com.moim.backend.domain.space.response.TmapWalkPathResponse; import com.moim.backend.domain.user.config.KakaoProperties; import com.moim.backend.global.aspect.TimeCheck; import com.moim.backend.global.util.LoggingUtil; @@ -19,13 +18,15 @@ import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatusCode; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; +import java.io.UnsupportedEncodingException; import java.net.URI; -import java.util.ArrayList; +import java.net.URLEncoder; import java.util.List; import java.util.Map; import java.util.Optional; @@ -41,55 +42,21 @@ public class DirectionService { private final TmapService tmapService; @TimeCheck - public Optional getBusRouteToResponse( + public Optional getCarRoute( BestPlace bestPlace, Space group, Participation participation ) { - Optional moveUserInfo = Optional.empty(); - URI searchPathUri = odsayProperties.getSearchPathUriWithParams(bestPlace, participation); - BusPathResponse busPathResponse = restTemplate.getForObject(searchPathUri, BusPathResponse.class); - - if (busPathResponse.getResult() == null) { - LoggingUtil.builder() - .title("버스 길찾기") - .status("실패") - .message("지역: " + bestPlace.getPlaceName() + ", url: " + searchPathUri) - .build() - .print(); - } else { - URI graphicDataUri = odsayProperties.getGraphicDataUriWIthParams(busPathResponse.getPathInfoMapObj()); - BusGraphicDataResponse busGraphicDataResponse = restTemplate.getForObject( - graphicDataUri, BusGraphicDataResponse.class - ); - if (busPathResponse.getResult() == null) { - LoggingUtil.builder() - .title("버스 그래픽 데이터 조회") - .status("실패") - .message("지역: " + bestPlace.getPlaceName() + ", url: " + graphicDataUri) - .build() - .print(); - } else { - moveUserInfo = Optional.of(new PlaceRouteResponse.MoveUserInfo( - group, participation, busGraphicDataResponse, busPathResponse, bestPlace - )); - } - } - return moveUserInfo; - } - - @TimeCheck - public Optional getCarRouteToResponse( - BestPlace bestPlace, Space group, Participation participation - ) { - Optional moveUserInfo = Optional.empty(); + log.info("[getCarRoute {} START]", participation.getUserName()); + Optional moveUserInfo = Optional.empty(); HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", "KakaoAK " + kakaoProperties.getClientId()); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); URI searchCarPathUri = kakaoProperties.getSearchCarPathUriWithParams(bestPlace, participation); - CarMoveInfo carMoveInfo = restTemplate.exchange( - searchCarPathUri, HttpMethod.GET, new HttpEntity<>(headers), CarMoveInfo.class + log.info("searchCarPathUri: {}", searchCarPathUri); + CarPathResponse carPathResponse = restTemplate.exchange( + searchCarPathUri, HttpMethod.GET, new HttpEntity<>(headers), CarPathResponse.class ).getBody(); - if (carMoveInfo.getRoutes() == null) { + if (carPathResponse.getRoutes() == null) { LoggingUtil.builder() .title("차 길찾기") .status("실패") @@ -97,42 +64,62 @@ public Optional getCarRouteToResponse( .build() .print(); } else { - moveUserInfo = Optional.of(new PlaceRouteResponse.MoveUserInfo(group, participation, carMoveInfo, bestPlace)); + moveUserInfo = Optional.of(MoveUserInfo.createWithCarPath(group, participation, carPathResponse, bestPlace)); } return moveUserInfo; } @TimeCheck - public Optional getBusRouteToResponseWithTmap( - BestPlace bestPlace, Space group, Participation participation + public Optional getPublicRoute( + BestPlace bestPlace, Space space, Participation participation ) { - Optional moveUserInfo = Optional.empty(); - + log.info("[getPublicRoute {} START]", participation.getUserName()); ResponseEntity response = restTemplate.postForEntity( tmapProperties.getSearchPathUri(), - createHttpEntity(bestPlace, participation), + createTmapPublicRouteHttpEntity(bestPlace, participation), TmapPublicPathResponse.class ); - TmapPublicPathResponse tmapPublicPathResponse = response.getBody(); - if (tmapPublicPathResponse == null || tmapPublicPathResponse.getMetaData() == null) { - LoggingUtil.builder() - .title("TMAP 대중교통 길찾기") - .status("실패") - .message("bestplace: " + bestPlace + ", participation: " + participation) - .build() - .print(); - } else { - List path = tmapService.getPath(tmapPublicPathResponse); - - moveUserInfo = Optional.of(new PlaceRouteResponse.MoveUserInfo(group, participation, tmapPublicPathResponse, bestPlace, path)); + if (response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(200))) { + MoveUserInfo moveUserInfo = tmapService.createMoveUserInfoWithPublicPath(space, participation, tmapPublicPathResponse, bestPlace); + return Optional.of(moveUserInfo); } + LoggingUtil.builder() + .title("TMAP 대중교통 길찾기") + .status("실패") + .message("bestplace: " + bestPlace + ", participation: " + participation) + .build() + .print(); + + return Optional.empty(); + } - return moveUserInfo; + @TimeCheck + public Optional getWalkRoute( + BestPlace bestPlace, Space space, Participation participation + ) { + log.info("[getWalkRoute {} START]", participation.getUserName()); + ResponseEntity response = restTemplate.postForEntity( + tmapProperties.getWalkSearchPathUri(), + createTmapWalkRouteHttpEntity(bestPlace, participation), + TmapWalkPathResponse.class + ); + if (response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(200))) { + MoveUserInfo moveUserInfo = tmapService.createMoveUserInfoWithWalkPath(space, participation, response.getBody(), bestPlace); + return Optional.of(moveUserInfo); + + } + LoggingUtil.builder() + .title("TMAP 도보 길찾기") + .status("실패") + .message("bestplace: " + bestPlace + ", participation: " + participation) + .build() + .print(); + return Optional.empty(); } - private HttpEntity createHttpEntity(BestPlace bestPlace, Participation participation) { + private HttpEntity createTmapPublicRouteHttpEntity(BestPlace bestPlace, Participation participation) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.set("Accept", MediaType.APPLICATION_JSON_VALUE); @@ -147,4 +134,30 @@ private HttpEntity createHttpEntity(BestPlace bestPlace, Participation partic return new HttpEntity<>(requestBody, headers); } -} + + private HttpEntity createTmapWalkRouteHttpEntity(BestPlace bestPlace, Participation participation) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Accept", MediaType.APPLICATION_JSON_VALUE); + headers.set("appKey", tmapProperties.getAppKey()); + + String defaultLocationName = "%EC%84%B1%EC%8B%A0%EC%97%AC%EB%8C%80"; // 성신여대 + String startName = defaultLocationName; // 이름은 식별용으로 경로 탐색에 영향을 주지는 않음 + String endName = defaultLocationName; + try { + startName = URLEncoder.encode(participation.getLocationName(), "UTF-8"); + endName = URLEncoder.encode(bestPlace.getPlaceName(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + log.error("보행자 경로 계산 시 출발지와 도착지 인코딩 중 에러발생: {}", e.getMessage()); + } + Map requestBody = tmapProperties.getWalkRequestParameter( + startName, + endName, + participation.getLongitude(), + participation.getLatitude(), + bestPlace.getLongitude(), + bestPlace.getLatitude() + ); + return new HttpEntity<>(requestBody, headers); + } +} \ No newline at end of file diff --git a/src/main/java/com/moim/backend/domain/space/service/SpaceService.java b/src/main/java/com/moim/backend/domain/space/service/SpaceService.java index f64dd1b..54e799f 100644 --- a/src/main/java/com/moim/backend/domain/space/service/SpaceService.java +++ b/src/main/java/com/moim/backend/domain/space/service/SpaceService.java @@ -235,17 +235,6 @@ public SpaceParticipationsResponse getParticipationDetail(Long groupId, Users us } // 모임 추천 지역 조회하기 - @TimeCheck - @Cacheable(value = CacheName.group, key = "#groupId") - public List getBestRegion(Long groupId) { - Space space = getGroup(groupId); - List participationList = participationRepository.findAllBySpace(space); - - return bestPlaceRepository.findAllBySpace(space).stream().map(bestPlace -> new PlaceRouteResponse( - bestPlace, getMoveUserInfoList(bestPlace, space, participationList) - )).collect(Collectors.toList()); - } - @TimeCheck @Cacheable(value = CacheName.group, key = "#groupId") public List getBestRegionWithTmap(Long groupId) { @@ -253,7 +242,7 @@ public List getBestRegionWithTmap(Long groupId) { List participationList = participationRepository.findAllBySpace(space); return bestPlaceRepository.findAllBySpace(space).stream().map(bestPlace -> new PlaceRouteResponse( - bestPlace, getMoveUserInfoListWithTmap(bestPlace, space, participationList) + bestPlace, getMoveUserInfoList(bestPlace, space, participationList) )).collect(Collectors.toList()); } @@ -514,44 +503,48 @@ private Participation getParticipate(Long id) { ); } - private List getMoveUserInfoList( + private List getMoveUserInfoList( BestPlace bestPlace, Space space, List participationList ) { - List moveUserInfoList = new ArrayList<>(); - - participationList.forEach(participation -> { - if ((participation.getTransportation() == TransportationType.PUBLIC)) { - directionService.getBusRouteToResponse(bestPlace, space, participation) - .ifPresent(moveUserInfo -> moveUserInfoList.add(moveUserInfo)); - } else if (participation.getTransportation() == TransportationType.PERSONAL) { - directionService.getCarRouteToResponse(bestPlace, space, participation) - .ifPresent(moveUserInfo -> moveUserInfoList.add(moveUserInfo)); - } - }); + log.info("[getMoveUserInfoList START]"); + log.info("BestPlace: ({}, {})", bestPlace.getLatitude(), bestPlace.getLongitude()); + List moveUserInfoList = new ArrayList<>(); + + for (Participation participation : participationList) { + log.info("participation: {}, ({}, {})", participation.getUserName(), participation.getLatitude(), participation.getLongitude()); + moveUserInfoList.add(getMoveUserInfo(bestPlace, space, participation)); + } return moveUserInfoList; } - private List getMoveUserInfoListWithTmap( + private MoveUserInfo getMoveUserInfo( BestPlace bestPlace, Space space, - List participationList + Participation participation ) { - List moveUserInfoList = new ArrayList<>(); - - participationList.forEach(participation -> { - if ((participation.getTransportation() == TransportationType.PUBLIC)) { - directionService.getBusRouteToResponseWithTmap(bestPlace, space, participation) - .ifPresent(moveUserInfo -> moveUserInfoList.add(moveUserInfo)); - } else if (participation.getTransportation() == TransportationType.PERSONAL) { - directionService.getCarRouteToResponse(bestPlace, space, participation) - .ifPresent(moveUserInfo -> moveUserInfoList.add(moveUserInfo)); - } - }); - - return moveUserInfoList; + switch (participation.getTransportation()) { + case PUBLIC: + Optional publicRoute = directionService.getPublicRoute(bestPlace, space, participation); + if (publicRoute.isPresent()) { + return publicRoute.get(); + } + Optional walkRoute = directionService.getWalkRoute(bestPlace, space, participation); + if (publicRoute.isPresent()) { + return walkRoute.get(); + } + break; + case PERSONAL: + Optional carRoute = directionService.getCarRoute(bestPlace, space, participation); + if (carRoute.isPresent()) { + return carRoute.get(); + } + break; + } + log.error("유저의 경로 조회 실패. space: {}, user name: {}", space.getSpaceId(), participation.getUserName()); + return MoveUserInfo.createWithEmptyPath(space, participation); } private double getValidRange(List participationList, MiddlePoint middlePoint) { diff --git a/src/main/java/com/moim/backend/domain/space/service/TmapService.java b/src/main/java/com/moim/backend/domain/space/service/TmapService.java index ad38b5f..0474fe4 100644 --- a/src/main/java/com/moim/backend/domain/space/service/TmapService.java +++ b/src/main/java/com/moim/backend/domain/space/service/TmapService.java @@ -1,7 +1,12 @@ package com.moim.backend.domain.space.service; +import com.moim.backend.domain.space.entity.BestPlace; +import com.moim.backend.domain.space.entity.Participation; +import com.moim.backend.domain.space.entity.Space; +import com.moim.backend.domain.space.response.MoveUserInfo; import com.moim.backend.domain.space.response.PathDto; import com.moim.backend.domain.space.response.TmapPublicPathResponse; +import com.moim.backend.domain.space.response.TmapWalkPathResponse; import org.springframework.stereotype.Service; import java.util.ArrayList; @@ -10,10 +15,15 @@ @Service public class TmapService { - // Tmap 대중교통 응답 값에서 도보와 지하철, 버스 등 이동 수단 별로 구분되어 있는 경로를 하나로 병합 - public List getPath(TmapPublicPathResponse tmapPublicPathResponse) { + public MoveUserInfo createMoveUserInfoWithPublicPath( + Space space, + Participation participation, + TmapPublicPathResponse tmapPublicPathResponse, + BestPlace bestPlace + ) { List path = new ArrayList<>(); List pathList = tmapPublicPathResponse.getPathList(); + // Tmap 대중교통 응답 값에서 도보와 지하철, 버스 등 이동 수단 별로 구분되어 있는 경로를 하나로 병합 for (TmapPublicPathResponse.Leg leg : pathList) { if (leg.getSteps() != null && leg.getSteps().size() > 0) { path.addAll(getPathFromStep(leg)); @@ -22,34 +32,77 @@ public List getPath(TmapPublicPathResponse tmapPublicPathResponse) { path.addAll(getPathFromPassStopList(leg)); } } - return path; + path.add(new PathDto(bestPlace.getLongitude(), bestPlace.getLatitude())); + return MoveUserInfo.createWithPublicPath(space, participation, tmapPublicPathResponse, path); + } + + public MoveUserInfo createMoveUserInfoWithWalkPath( + Space space, + Participation participation, + TmapWalkPathResponse tmapWalkPathResponse, + BestPlace bestPlace + ) { + List path = new ArrayList<>(); + int totalTime = 0; + double totalDistance = 0; + + for (TmapWalkPathResponse.Feature feature : tmapWalkPathResponse.getFeatures()) { + // path 구성 추가 + if ("Point".equals(feature.getGeometry().type)) { + path.add(getPathFromPointOfWalkPath(feature.geometry.coordinates)); + } else if ("LineString".equals(feature.getGeometry().type)) { + path.addAll(getPathListFromLineStringOfWalkPath(feature)); + } + path.add(new PathDto(bestPlace.getLongitude(), bestPlace.getLatitude())); + // geometry 타입에 따라 totalTime에 값 + totalTime += feature.properties.getTotalTime(); + totalDistance += feature.properties.getTotalDistance(); + } + return MoveUserInfo.createWithWalkPath(space, participation, totalTime, totalDistance, path); } // 이동 구간이 도보인 경우, 상세 도보 경로를 path list로 변환 private List getPathFromStep(TmapPublicPathResponse.Leg leg) { - List path = new ArrayList<>(); + List pathList = new ArrayList<>(); for (TmapPublicPathResponse.Step step : leg.getSteps()) { String[] xyList = step.getLinestring().split(" "); for (String xy : xyList) { String[] splitXY = xy.split(","); - path.add(PathDto.builder() + pathList.add(PathDto.builder() .longitude(Double.valueOf(splitXY[0])) .latitude(Double.valueOf(splitXY[1])) .build()); } } - return path; + return pathList; } // 이동 구간이 도보가 아니라 역으로 구성된 경우, 상세 경로를 path list로 변환 private List getPathFromPassStopList(TmapPublicPathResponse.Leg leg) { - List path = new ArrayList<>(); + List pathList = new ArrayList<>(); for (TmapPublicPathResponse.Station station : leg.getStationList()) { - path.add(PathDto.builder() + pathList.add(PathDto.builder() .longitude(Double.valueOf(station.getLon())) .latitude(Double.valueOf(station.getLat())) .build()); } - return path; + return pathList; + } + + private List getPathListFromLineStringOfWalkPath(TmapWalkPathResponse.Feature feature) { + List pathList = new ArrayList<>(); + String[] pathStr = feature.geometry.coordinates.replace("[", "").replace("]", "").split(","); + for (String point : pathStr) { + pathList.add(getPathFromPointOfWalkPath(point)); + } + return pathList; + } + + private PathDto getPathFromPointOfWalkPath(String point) { + String[] pathStr = point.replace("[", "").replace("]", "").split(","); + return PathDto.builder() + .longitude(Double.parseDouble(pathStr[0])) + .latitude(Double.parseDouble(pathStr[1])) + .build(); } } diff --git a/src/main/java/com/moim/backend/domain/user/controller/AuthController.java b/src/main/java/com/moim/backend/domain/user/controller/AuthController.java index c75ebb1..564dd28 100644 --- a/src/main/java/com/moim/backend/domain/user/controller/AuthController.java +++ b/src/main/java/com/moim/backend/domain/user/controller/AuthController.java @@ -7,6 +7,7 @@ import com.moim.backend.domain.user.service.UserService; import com.moim.backend.global.auth.Login; import com.moim.backend.global.common.CustomResponseEntity; +import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -19,12 +20,14 @@ public class AuthController { // 배포 검증용 API @GetMapping("/success") + @Operation(summary = "배포 검증용") public CustomResponseEntity checkServerStatus() { return CustomResponseEntity.success("Server On!!"); } // 소셜 로그인 API @GetMapping("/signin") + @Operation(summary = "소셜 로그인") public CustomResponseEntity loginByOAuth( @RequestParam(name = "code") String code, @RequestParam Platform platform ) { @@ -33,6 +36,7 @@ public CustomResponseEntity loginByOAuth( // 소셜 로그인 API @GetMapping("/signin/token") + @Operation(summary = "소셜 로그인", description = "엑세스 토큰을 사용하는 앱용 로그인 API") public CustomResponseEntity socialLoginByAccessToken( @RequestParam(name = "token") String token, @RequestParam Platform platform ) { diff --git a/src/test/java/com/moim/backend/docs/space/SpaceControllerDocsTest.java b/src/test/java/com/moim/backend/docs/space/SpaceControllerDocsTest.java index 1f0bbe8..ede14ee 100644 --- a/src/test/java/com/moim/backend/docs/space/SpaceControllerDocsTest.java +++ b/src/test/java/com/moim/backend/docs/space/SpaceControllerDocsTest.java @@ -429,9 +429,9 @@ void getBestRegion() throws Exception { ); // given - List moveUserInfoList = List.of( - new PlaceRouteResponse.MoveUserInfo(true, 1L, "김유정", PUBLIC, 2, 68, 15928.0, 1500, path), - new PlaceRouteResponse.MoveUserInfo(false, 2L, "천현우", PUBLIC, 2, 96, 27725.0, 1500, path) + List moveUserInfoList = List.of( + new MoveUserInfo(true, 1L, "김유정", PUBLIC, 2, 68, 15928.0, 1500, path), + new MoveUserInfo(false, 2L, "천현우", PUBLIC, 2, 96, 27725.0, 1500, path) ); List placeRouteResponseList = List.of( new PlaceRouteResponse("안국", 37.576477, 126.985443, moveUserInfoList), diff --git a/src/test/java/com/moim/backend/domain/space/service/SpaceServiceTest.java b/src/test/java/com/moim/backend/domain/space/service/SpaceServiceTest.java index cb4abda..e76cdc0 100644 --- a/src/test/java/com/moim/backend/domain/space/service/SpaceServiceTest.java +++ b/src/test/java/com/moim/backend/domain/space/service/SpaceServiceTest.java @@ -289,64 +289,64 @@ void throwsExceptionWhenModifyingUnauthorizedParticipantInfo() { .extracting("result.code", "result.message") .contains(-1004, "자신의 참여 정보가 아닙니다."); } +// +// @DisplayName("모임원이 속해있는 모임에서 나가기를 한다.") +// @Test +// void participateExit() { +// // given +// Users admin = savedUser("admin@test.com", "테스트 어드민"); +// Users user = savedUser("test@test.com", "테스트 이름"); +// Space group = savedGroup(admin.getUserId(), "테스트 그룹"); +// Participation participationAdmin = +// savedParticipation(admin, group, "어드민", "어딘가", 37.5660, 126.1234, PUBLIC); +// Participation participationUser = +// savedParticipation(user, group, "참여자", "어딘가", 37.5660, 126.1234, PUBLIC); +// +// // when +// SpaceExitResponse response = +// spaceService.participateExit(participationUser.getParticipationId(), user); +// +// // then +// assertThat(response) +// .extracting("isDeletedSpace", "message") +// .contains(false, "모임에서 나갔습니다."); +// +// Optional optionalParticipation = +// participationRepository.findById(participationUser.getParticipationId()); +// +// assertThat(optionalParticipation.isEmpty()).isTrue(); +// } - @DisplayName("모임원이 속해있는 모임에서 나가기를 한다.") - @Test - void participateExit() { - // given - Users admin = savedUser("admin@test.com", "테스트 어드민"); - Users user = savedUser("test@test.com", "테스트 이름"); - Space group = savedGroup(admin.getUserId(), "테스트 그룹"); - Participation participationAdmin = - savedParticipation(admin, group, "어드민", "어딘가", 37.5660, 126.1234, PUBLIC); - Participation participationUser = - savedParticipation(user, group, "참여자", "어딘가", 37.5660, 126.1234, PUBLIC); - - // when - SpaceExitResponse response = - spaceService.participateExit(participationUser.getParticipationId(), user); - - // then - assertThat(response) - .extracting("isDeletedSpace", "message") - .contains(false, "모임에서 나갔습니다."); - - Optional optionalParticipation = - participationRepository.findById(participationUser.getParticipationId()); - - assertThat(optionalParticipation.isEmpty()).isTrue(); - } - - @DisplayName("모임원이 속해있는 모임에서 나가기를 한다.") - @Test - void allParticipateExit() { - // given - Users admin = savedUser("admin@test.com", "테스트 어드민"); - Users user = savedUser("test@test.com", "테스트 이름"); - Space group1 = savedGroup(admin.getUserId(), "테스트 그룹"); - Space group2 = savedGroup(admin.getUserId(), "테스트 그룹"); - Space group3 = savedGroup(admin.getUserId(), "테스트 그룹"); - - - savedParticipation(user, group1, "참여자", "어딘가", 37.5660, 126.1234, PUBLIC); - savedParticipation(user, group2, "참여자", "어딘가", 37.5660, 126.1234, PUBLIC); - savedParticipation(user, group3, "참여자", "어딘가", 37.5660, 126.1234, PUBLIC); - - em.flush(); - em.clear(); - - // when - spaceService.allParticipateExit(user); - - // then - List group1Participations = participationRepository.findAllBySpace(group1); - List group2Participations = participationRepository.findAllBySpace(group2); - List group3Participations = participationRepository.findAllBySpace(group3); - - assertThat(group1Participations.isEmpty()).isTrue(); - assertThat(group2Participations.isEmpty()).isTrue(); - assertThat(group3Participations.isEmpty()).isTrue(); - } +// @DisplayName("모임원이 속해있는 모임에서 나가기를 한다.") +// @Test +// void allParticipateExit() { +// // given +// Users admin = savedUser("admin@test.com", "테스트 어드민"); +// Users user = savedUser("test@test.com", "테스트 이름"); +// Space group1 = savedGroup(admin.getUserId(), "테스트 그룹"); +// Space group2 = savedGroup(admin.getUserId(), "테스트 그룹"); +// Space group3 = savedGroup(admin.getUserId(), "테스트 그룹"); +// +// +// savedParticipation(user, group1, "참여자", "어딘가", 37.5660, 126.1234, PUBLIC); +// savedParticipation(user, group2, "참여자", "어딘가", 37.5660, 126.1234, PUBLIC); +// savedParticipation(user, group3, "참여자", "어딘가", 37.5660, 126.1234, PUBLIC); +// +// em.flush(); +// em.clear(); +// +// // when +// spaceService.allParticipateExit(user); +// +// // then +// List group1Participations = participationRepository.findAllBySpace(group1); +// List group2Participations = participationRepository.findAllBySpace(group2); +// List group3Participations = participationRepository.findAllBySpace(group3); +// +// assertThat(group1Participations.isEmpty()).isTrue(); +// assertThat(group2Participations.isEmpty()).isTrue(); +// assertThat(group3Participations.isEmpty()).isTrue(); +// } @DisplayName("모임원이 속해있는 모임에서 나가기를 할때 투표가 종료되지 않은 상태면 예외가 발생한다.") @Test @@ -384,61 +384,61 @@ private Vote savedVote(Space group1) { ); } - @DisplayName("모임장이 모임에서 나가기를 하면 해당 모임이 삭제된다.") - @Test - void groupAdminExitsThenGroupIsDeleted() { - - // given - Users admin = savedUser("admin@test.com", "테스트 어드민"); - Users user = savedUser("test@test.com", "테스트 이름"); - Space group = savedGroup(admin.getUserId(), "테스트 그룹"); - Participation participationAdmin = - savedParticipation(admin, group, "어드민", "어딘가", 37.5660, 126.1234, PUBLIC); - Participation participationUser = - savedParticipation(user, group, "참여자", "어딘가", 37.5660, 126.1234, PUBLIC); - - em.flush(); - em.clear(); - - // when - SpaceExitResponse response = - spaceService.participateExit(participationAdmin.getParticipationId(), admin); - - // then - assertThat(response) - .extracting("isDeletedSpace", "message") - .contains(true, "모임이 삭제되었습니다."); - - Optional optionalGroup = groupRepository.findById(group.getSpaceId()); - - Optional optionalParticipation = - participationRepository.findById(participationUser.getParticipationId()); - - assertThat(optionalGroup.isEmpty()).isTrue(); - assertThat(optionalParticipation.isEmpty()).isTrue(); - } - - @DisplayName("모임장이 모임원을 내보낸다.") - @Test - void participateRemoval() { - // given - Users admin = savedUser("admin@test.com", "테스트 어드민"); - Users user = savedUser("test@test.com", "테스트 이름"); - Space group = savedGroup(admin.getUserId(), "테스트 그룹"); - Participation participationAdmin = - savedParticipation(admin, group, "어드민", "어딘가", 37.5660, 126.1234, PUBLIC); - Participation participationUser = - savedParticipation(user, group, "참여자", "어딘가", 37.5660, 126.1234, PUBLIC); - - // when - spaceService.participateRemoval(participationUser.getParticipationId(), admin); - - // then - Optional optionalParticipation = - participationRepository.findById(participationUser.getParticipationId()); - - assertThat(optionalParticipation.isEmpty()).isTrue(); - } +// @DisplayName("모임장이 모임에서 나가기를 하면 해당 모임이 삭제된다.") +// @Test +// void groupAdminExitsThenGroupIsDeleted() { +// +// // given +// Users admin = savedUser("admin@test.com", "테스트 어드민"); +// Users user = savedUser("test@test.com", "테스트 이름"); +// Space group = savedGroup(admin.getUserId(), "테스트 그룹"); +// Participation participationAdmin = +// savedParticipation(admin, group, "어드민", "어딘가", 37.5660, 126.1234, PUBLIC); +// Participation participationUser = +// savedParticipation(user, group, "참여자", "어딘가", 37.5660, 126.1234, PUBLIC); +// +// em.flush(); +// em.clear(); +// +// // when +// SpaceExitResponse response = +// spaceService.participateExit(participationAdmin.getParticipationId(), admin); +// +// // then +// assertThat(response) +// .extracting("isDeletedSpace", "message") +// .contains(true, "모임이 삭제되었습니다."); +// +// Optional optionalGroup = groupRepository.findById(group.getSpaceId()); +// +// Optional optionalParticipation = +// participationRepository.findById(participationUser.getParticipationId()); +// +// assertThat(optionalGroup.isEmpty()).isTrue(); +// assertThat(optionalParticipation.isEmpty()).isTrue(); +// } +// +// @DisplayName("모임장이 모임원을 내보낸다.") +// @Test +// void participateRemoval() { +// // given +// Users admin = savedUser("admin@test.com", "테스트 어드민"); +// Users user = savedUser("test@test.com", "테스트 이름"); +// Space group = savedGroup(admin.getUserId(), "테스트 그룹"); +// Participation participationAdmin = +// savedParticipation(admin, group, "어드민", "어딘가", 37.5660, 126.1234, PUBLIC); +// Participation participationUser = +// savedParticipation(user, group, "참여자", "어딘가", 37.5660, 126.1234, PUBLIC); +// +// // when +// spaceService.participateRemoval(participationUser.getParticipationId(), admin); +// +// // then +// Optional optionalParticipation = +// participationRepository.findById(participationUser.getParticipationId()); +// +// assertThat(optionalParticipation.isEmpty()).isTrue(); +// } @DisplayName("모임원이 모임원을 내보내려하면 어드민이 아니기 때문에 Exception 이 발생한다.") @Test