diff --git a/backend/src/main/java/middle_point_search/backend/common/properties/GoogleProperties.java b/backend/src/main/java/middle_point_search/backend/common/properties/GoogleProperties.java new file mode 100644 index 00000000..8fe00e51 --- /dev/null +++ b/backend/src/main/java/middle_point_search/backend/common/properties/GoogleProperties.java @@ -0,0 +1,27 @@ +package middle_point_search.backend.common.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@ConfigurationProperties(prefix = "google") +public class GoogleProperties { + + private String key; + private String keyName; + private String baseUrl; + private Map map; + + @Getter + @Setter + public static class Map { + + private String distanceMatrixUrl; + private String origin; + private String destination; + private String placeIdUrl; + } +} diff --git a/backend/src/main/java/middle_point_search/backend/common/properties/conf/PropertyConfig.java b/backend/src/main/java/middle_point_search/backend/common/properties/conf/PropertyConfig.java index a2d6ecf6..16719b00 100644 --- a/backend/src/main/java/middle_point_search/backend/common/properties/conf/PropertyConfig.java +++ b/backend/src/main/java/middle_point_search/backend/common/properties/conf/PropertyConfig.java @@ -3,6 +3,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; +import middle_point_search.backend.common.properties.GoogleProperties; import middle_point_search.backend.common.properties.JwtProperties; import middle_point_search.backend.common.properties.KakaoProperties; import middle_point_search.backend.common.properties.MarketProperties; @@ -18,7 +19,8 @@ SecurityProperties.class, MarketProperties.class, KakaoProperties.class, - RedisProperties.class + RedisProperties.class, + GoogleProperties.class }) public class PropertyConfig { } diff --git a/backend/src/main/java/middle_point_search/backend/common/webClient/conf/WebClientConf.java b/backend/src/main/java/middle_point_search/backend/common/webClient/conf/WebClientConf.java index 12351dfb..a6bf23e6 100644 --- a/backend/src/main/java/middle_point_search/backend/common/webClient/conf/WebClientConf.java +++ b/backend/src/main/java/middle_point_search/backend/common/webClient/conf/WebClientConf.java @@ -2,6 +2,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.util.DefaultUriBuilderFactory; @@ -9,6 +11,7 @@ import io.netty.channel.ChannelOption; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import middle_point_search.backend.common.properties.GoogleProperties; import middle_point_search.backend.common.properties.KakaoProperties; import middle_point_search.backend.common.properties.MarketProperties; import reactor.netty.http.client.HttpClient; @@ -20,6 +23,7 @@ public class WebClientConf { private final MarketProperties marketProperties; private final KakaoProperties kakaoProperties; + private final GoogleProperties googleProperties; private HttpClient httpClient = HttpClient.create() @@ -48,4 +52,15 @@ public WebClient webClientForKakao() { .clientConnector(new ReactorClientHttpConnector(httpClient)) .build(); } + + @Bean + public WebClient webClientForGoogle() { + return WebClient.builder() + .baseUrl(googleProperties.getBaseUrl()) + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) + .codecs(configurer -> configurer.defaultCodecs() + .maxInMemorySize(2 * 1024 * 1024)) // 응답 payload가 클 경우 나는 에러 방지, 최대 2MB + .clientConnector(new ReactorClientHttpConnector(httpClient)) + .build(); + } } diff --git a/backend/src/main/java/middle_point_search/backend/common/webClient/util/WebClientUtil.java b/backend/src/main/java/middle_point_search/backend/common/webClient/util/WebClientUtil.java index 0371c71c..fb2208cb 100644 --- a/backend/src/main/java/middle_point_search/backend/common/webClient/util/WebClientUtil.java +++ b/backend/src/main/java/middle_point_search/backend/common/webClient/util/WebClientUtil.java @@ -13,6 +13,7 @@ import lombok.RequiredArgsConstructor; import middle_point_search.backend.common.exception.CustomException; +import middle_point_search.backend.common.properties.GoogleProperties; import middle_point_search.backend.common.properties.KakaoProperties; import middle_point_search.backend.common.properties.MarketProperties; import reactor.core.publisher.Mono; @@ -23,6 +24,7 @@ public class WebClientUtil { private final KakaoProperties kakaoProperties; private final MarketProperties marketProperties; + private final GoogleProperties googleProperties; @Qualifier("webClientForMarket") private final WebClient webClientForMarket; @@ -30,6 +32,9 @@ public class WebClientUtil { @Qualifier("webClientForKakao") private final WebClient webClientForKakao; + @Qualifier("webClientForGoogle") + private final WebClient webClientForGoogle; + // WebClient Conf 세팅을 이용하며, url과 응답 클래스 및 파라미터를 제공하여 요청을 해 Mono로 응답받는 메서드 public T getMarket(String url, MultiValueMap params, Class response) { return webClientForMarket @@ -83,12 +88,15 @@ public T getKakao(String url, MultiValueMap params, Class .block(); } - // WebClient Conf 세팅을 이용하며, url과 응답 클래스를 제공하여 요청을 해 Mono로 응답받는 메서드 - public T getKakao(String url, Class response) { - return webClientForKakao + // WebClient Conf 세팅을 이용하며, googleMap에 대해 url과 응답 클래스를 제공하여 요청을 해 Mono로 응답받는 메서드 + public T getGoogle(String url, MultiValueMap params, Class response) { + return webClientForGoogle .method(HttpMethod.GET) - .uri(url) - .header(HttpHeaders.AUTHORIZATION, kakaoProperties.getKey()) + .uri(uriBuilder -> uriBuilder + .path(url) + .queryParams(params) + .queryParam(googleProperties.getKeyName(), googleProperties.getKey()) + .build()) .retrieve() .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.error(CustomException.from(BAD_REQUEST))) diff --git a/backend/src/main/java/middle_point_search/backend/domains/google/dto/DistanceMatrixResponse.java b/backend/src/main/java/middle_point_search/backend/domains/google/dto/DistanceMatrixResponse.java new file mode 100644 index 00000000..1364a764 --- /dev/null +++ b/backend/src/main/java/middle_point_search/backend/domains/google/dto/DistanceMatrixResponse.java @@ -0,0 +1,43 @@ +package middle_point_search.backend.domains.google.dto; + +import java.util.List; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class DistanceMatrixResponse extends GoogleApiResponse { + + private List destination_addresses; + private List origin_addresses; + private List rows; + + @Getter + @NoArgsConstructor + public static class Row { + private List elements; + } + + @Getter + @NoArgsConstructor + public static class Element { + private Distance distance; + private Duration duration; + private String status; + } + + @Getter + @NoArgsConstructor + public static class Distance { + private String text; + private int value; + } + + @Getter + @NoArgsConstructor + public static class Duration { + private String text; + private int value; + } +} \ No newline at end of file diff --git a/backend/src/main/java/middle_point_search/backend/domains/google/dto/GoogleApiResponse.java b/backend/src/main/java/middle_point_search/backend/domains/google/dto/GoogleApiResponse.java new file mode 100644 index 00000000..ab733609 --- /dev/null +++ b/backend/src/main/java/middle_point_search/backend/domains/google/dto/GoogleApiResponse.java @@ -0,0 +1,8 @@ +package middle_point_search.backend.domains.google.dto; + +import lombok.Getter; + +@Getter +public abstract class GoogleApiResponse { + private String status; +} diff --git a/backend/src/main/java/middle_point_search/backend/domains/google/dto/ReverseGeocodeResponse.java b/backend/src/main/java/middle_point_search/backend/domains/google/dto/ReverseGeocodeResponse.java new file mode 100644 index 00000000..4ee11c4e --- /dev/null +++ b/backend/src/main/java/middle_point_search/backend/domains/google/dto/ReverseGeocodeResponse.java @@ -0,0 +1,61 @@ +package middle_point_search.backend.domains.google.dto; + +import java.util.List; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ReverseGeocodeResponse extends GoogleApiResponse { + private PlusCode plus_code; + private List results; + + @Getter + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class PlusCode { + private String compound_code; + private String global_code; + } + + @Getter + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class Result { + private List address_components; + private String formatted_address; + private Geometry geometry; + private String place_id; + private List types; + + @Getter + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class AddressComponent { + private String long_name; + private String short_name; + private List types; + } + + @Getter + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class Geometry { + private Location location; + private String location_type; + private Viewport viewport; + + @Getter + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class Location { + private Double lat; + private Double lng; + } + + @Getter + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class Viewport { + private Location northeast; + private Location southwest; + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/middle_point_search/backend/domains/google/service/GoogleService.java b/backend/src/main/java/middle_point_search/backend/domains/google/service/GoogleService.java new file mode 100644 index 00000000..552b07bf --- /dev/null +++ b/backend/src/main/java/middle_point_search/backend/domains/google/service/GoogleService.java @@ -0,0 +1,88 @@ +package middle_point_search.backend.domains.google.service; + +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import lombok.RequiredArgsConstructor; +import middle_point_search.backend.common.exception.CustomException; +import middle_point_search.backend.common.exception.errorCode.UserErrorCode; +import middle_point_search.backend.common.properties.GoogleProperties; +import middle_point_search.backend.common.webClient.util.WebClientUtil; +import middle_point_search.backend.domains.google.dto.DistanceMatrixResponse; +import middle_point_search.backend.domains.google.dto.GoogleApiResponse; +import middle_point_search.backend.domains.google.dto.ReverseGeocodeResponse; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class GoogleService { + + private final WebClientUtil webClientUtil; + private final GoogleProperties googleProperties; + + // 이동 시간 조회 + public DistanceMatrixResponse findTravelTimes(String destPlaceId, List originPlaceIds) { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add(googleProperties.getMap().getOrigin(), makePlaceIdsQuery(originPlaceIds)); + params.add(googleProperties.getMap().getDestination(), makePlaceIdQuery(destPlaceId)); + params.add("language", "ko"); + params.add("mode", "transit"); + params.add("region", "KR"); + + DistanceMatrixResponse response = webClientUtil.getGoogle(googleProperties.getMap().getDistanceMatrixUrl(), + params, DistanceMatrixResponse.class); + + // 상태코드 체크 + checkGoogleApiResponseStatus(response); + + return response; + } + + // id들을 |로 구분하여 query문을 만들어줌 + private String makePlaceIdsQuery(List placeIds) { + StringBuilder query = new StringBuilder(); + // coordinate 사이에 |를 넣어줌, 마지막에 | 없음 + for (int i = 0; i < placeIds.size(); i++) { + query.append("place_id:").append(placeIds.get(i)); + if (i != placeIds.size() - 1) { + query.append("|"); + } + } + return query.toString(); + } + + // 장소 ID 쿼리 파라미터 생성 + private String makePlaceIdQuery(String placeId) { + return "place_id:" + placeId; + } + + // 구글 placeId 찾기 + public String findGooglePlaceId(Double latitude, Double longitude) { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("latlng", latitude + "," + longitude); + params.add("language", "ko"); + + ReverseGeocodeResponse response = webClientUtil.getGoogle( + googleProperties.getMap().getPlaceIdUrl(), + params, + ReverseGeocodeResponse.class + ); + + // 상태코드 체크 + checkGoogleApiResponseStatus(response); + + return response.getResults().get(0).getPlace_id(); + } + + // 상태코드 확인 + public void checkGoogleApiResponseStatus(Object response) { + GoogleApiResponse googleApiResponse = (GoogleApiResponse) response; + if (!googleApiResponse.getStatus().equals("OK")) { + throw CustomException.from(UserErrorCode.API_INTERNAL_SERVER_ERROR); + } + } +} diff --git a/backend/src/main/java/middle_point_search/backend/domains/market/controller/MarketController.java b/backend/src/main/java/middle_point_search/backend/domains/market/controller/MarketController.java index 9912bdc7..d371afd7 100644 --- a/backend/src/main/java/middle_point_search/backend/domains/market/controller/MarketController.java +++ b/backend/src/main/java/middle_point_search/backend/domains/market/controller/MarketController.java @@ -7,8 +7,6 @@ import org.springframework.web.bind.annotation.RestController; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -28,10 +26,6 @@ public class MarketController { @PostMapping("/market") @Operation( summary = "중간 장소 리스트 업데이트", - parameters = { - @Parameter(name = "RoomId", description = "roomId 필요", in = ParameterIn.HEADER), - @Parameter(name = "RoomType", description = "roomType 필요. [TOGETHER, SELF] 중 하나", in = ParameterIn.HEADER) - }, description = """ 중간 지점으로 선정될 장소를 업데이트 한다. diff --git a/backend/src/main/java/middle_point_search/backend/domains/midPoint/controller/MidPointController.java b/backend/src/main/java/middle_point_search/backend/domains/midPoint/controller/MidPointController.java index 7c1873ca..e63b3435 100644 --- a/backend/src/main/java/middle_point_search/backend/domains/midPoint/controller/MidPointController.java +++ b/backend/src/main/java/middle_point_search/backend/domains/midPoint/controller/MidPointController.java @@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import io.swagger.v3.oas.annotations.Operation; @@ -18,6 +19,7 @@ import middle_point_search.backend.common.dto.ErrorResponse; import middle_point_search.backend.common.util.MemberLoader; import middle_point_search.backend.domains.midPoint.dto.MidPointDTO.MidPointsFindResponse; +import middle_point_search.backend.domains.midPoint.dto.MidPointDTO.TravelTimesFindResponse; import middle_point_search.backend.domains.midPoint.service.MidPointService; @Tag(name = "MID POINT API", description = "중간지점에 대한 API입니다.") @@ -77,4 +79,62 @@ public ResponseEntity>> MidPointsFind( return ResponseEntity.ok(DataResponse.from(midPoints)); } + + @GetMapping("/rooms/{roomId}/travel-time") + @Operation( + summary = "중간 지점까지의 이동 시간 조회", + description = """ + 중간 지점까지의 이동 시간 조회하기. + + AccessToken 필요.""", + responses = { + @ApiResponse( + responseCode = "200", + description = "성공" + ), + @ApiResponse( + responseCode = "400", + description = "잘못된 요청입니다.(외부 API 실패) [C-201]", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + @ApiResponse( + responseCode = "401", + description = "인증에 실패하였습니다.[C-101]", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + @ApiResponse( + responseCode = "402", + description = "Access Token을 재발급해야합니다.[A-004]", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + @ApiResponse( + responseCode = "403", + description = "해당 방의 회원이 아닙니다.[MR-003]", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + @ApiResponse( + responseCode = "404", + description = "방에 입력된 장소가 없습니다.[P-201]", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + @ApiResponse( + responseCode = "500", + description = "API 서버에 문제가 발생하였습니다.[S-001]", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ) + } + ) + public ResponseEntity> findPath( + @PathVariable Long roomId, + @RequestParam Double destLatitude, + @RequestParam Double destLongitude + ) { + final Long memberId = memberLoader.getMemberId(); + + return ResponseEntity.ok(DataResponse.from(midPointService.findTravelTimes( + roomId, + memberId, + destLatitude, + destLongitude))); + } } diff --git a/backend/src/main/java/middle_point_search/backend/domains/midPoint/dto/MidPointDTO.java b/backend/src/main/java/middle_point_search/backend/domains/midPoint/dto/MidPointDTO.java index 0371bd5f..d0382fde 100644 --- a/backend/src/main/java/middle_point_search/backend/domains/midPoint/dto/MidPointDTO.java +++ b/backend/src/main/java/middle_point_search/backend/domains/midPoint/dto/MidPointDTO.java @@ -69,4 +69,59 @@ public static MidPointsFindResponse from(Market market) { ); } } + + @Getter + @AllArgsConstructor + public static class TravelTimesFindResponse { + private List elements; + + @Getter + @AllArgsConstructor + public static class Element { + private String status; + private Long placeId; + private Duration duration; + private Distance distance; + + @Getter + @AllArgsConstructor(access = AccessLevel.PRIVATE) + public static class Duration { + private String text; + private int value; + } + + @Getter + @AllArgsConstructor(access = AccessLevel.PRIVATE) + public static class Distance { + private String text; + private int value; + } + + public static Element from( + Long placeId, + String durationText, + int durationValue, + String distanceText, + int distanceValue + ) { + return new Element( + "OK", + placeId, + new Element.Duration(durationText, durationValue), + new Element.Distance(distanceText, distanceValue)); + } + + public static Element noContent(Long placeId) { + return new Element( + "ZERO_RESULTS", + placeId, + null, + null); + } + } + + public static TravelTimesFindResponse from(List elements) { + return new TravelTimesFindResponse(elements); + } + } } diff --git a/backend/src/main/java/middle_point_search/backend/domains/midPoint/service/MidPointService.java b/backend/src/main/java/middle_point_search/backend/domains/midPoint/service/MidPointService.java index 5cec579a..1c32b57d 100644 --- a/backend/src/main/java/middle_point_search/backend/domains/midPoint/service/MidPointService.java +++ b/backend/src/main/java/middle_point_search/backend/domains/midPoint/service/MidPointService.java @@ -1,14 +1,21 @@ package middle_point_search.backend.domains.midPoint.service; +import static middle_point_search.backend.common.exception.errorCode.UserErrorCode.*; + import java.util.List; +import java.util.stream.IntStream; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import lombok.RequiredArgsConstructor; +import middle_point_search.backend.common.exception.CustomException; +import middle_point_search.backend.domains.google.dto.DistanceMatrixResponse; +import middle_point_search.backend.domains.google.service.GoogleService; import middle_point_search.backend.domains.memberRoom.MemberRoomValidateService; import middle_point_search.backend.domains.midPoint.dto.MidPointDTO.AddressDTO; import middle_point_search.backend.domains.midPoint.dto.MidPointDTO.MidPointsFindResponse; +import middle_point_search.backend.domains.midPoint.dto.MidPointDTO.TravelTimesFindResponse; import middle_point_search.backend.domains.midPoint.util.MidPointUtil; import middle_point_search.backend.domains.place.domain.Place; import middle_point_search.backend.domains.place.repository.PlaceRepository; @@ -21,6 +28,7 @@ public class MidPointService { private final MidPointUtil midPointUtil; private final PlaceRepository placeRepository; private final MemberRoomValidateService memberRoomValidateService; + private final GoogleService googleService; // 주어진 주소들로 중간 장소 리스트를 조회하는 메서드 public List findMidPoints(List addressDTOs) { @@ -39,4 +47,59 @@ public List findMidPointsByRoomId(Long memberId, Long roo return findMidPoints(addressDTOs); } + + // 방 장소들의 중간지점까지의 이동시간을 조회하는 메서드 + public TravelTimesFindResponse findTravelTimes(Long roomId, Long memberId, Double latitude, Double longitude) { + // 회원이 방에 속해있는지 확인 + memberRoomValidateService.validateAuthorizedMember(memberId, roomId); + + // 장소 조회 및 검증 + List places = placeRepository.findAllByRoom_Id(roomId); + validateOriginPlacesExist(places); + + // 방에 속한 장소들의 좌표를 가져옴 + List originPlaceIds = places.stream() + .map(Place::getGooglePlaceId) + .toList(); + + // 목적지 placeId 조회 + String destinationPlaceId = googleService.findGooglePlaceId(latitude, longitude); + + // Google API 호출 및 결과 처리 + DistanceMatrixResponse distanceMatrixResponse = googleService.findTravelTimes(destinationPlaceId, originPlaceIds); + + // 응답 생성 + List elements = createTravelTimeElements(places, distanceMatrixResponse); + + return TravelTimesFindResponse.from(elements); + } + + // 방에 속한 장소가 없을 때 예외처리 + private void validateOriginPlacesExist(List places) { + if (places.isEmpty()) { + throw CustomException.from(PLACE_NOT_FOUND); + } + } + + // 이동 시간 요소 생성 로직 + private List createTravelTimeElements(List places, DistanceMatrixResponse response) { + return IntStream.range(0, places.size()) + .mapToObj(i -> { + Place place = places.get(i); + DistanceMatrixResponse.Element responseElement = response.getRows().get(i).getElements().get(0); + + if ("ZERO_RESULTS".equals(responseElement.getStatus())) { + return TravelTimesFindResponse.Element.noContent(place.getId()); + } + + return TravelTimesFindResponse.Element.from( + place.getId(), + responseElement.getDuration().getText(), + responseElement.getDuration().getValue(), + responseElement.getDistance().getText(), + responseElement.getDistance().getValue() + ); + }) + .toList(); // 불변 리스트 생성 + } } diff --git a/backend/src/main/java/middle_point_search/backend/domains/place/controller/PlaceController.java b/backend/src/main/java/middle_point_search/backend/domains/place/controller/PlaceController.java index 7d758a61..6e44822a 100644 --- a/backend/src/main/java/middle_point_search/backend/domains/place/controller/PlaceController.java +++ b/backend/src/main/java/middle_point_search/backend/domains/place/controller/PlaceController.java @@ -27,7 +27,7 @@ @Tag(name = "PLACE API", description = "회원 장소에 대한 API입니다.") @RestController @RequiredArgsConstructor -@RequestMapping("/api/place-rooms") +@RequestMapping("/api/places") public class PlaceController { private final PlaceService placeService; @@ -122,7 +122,7 @@ public ResponseEntity> placesFind( return ResponseEntity.ok(DataResponse.from(response)); } - @DeleteMapping("/rooms/{roomId}") + @DeleteMapping("/{placeId}") @Operation( summary = "장소 삭제하기", description = """ @@ -157,11 +157,11 @@ public ResponseEntity> placesFind( } ) public ResponseEntity> placeDelete( - @PathVariable("roomId") Long roomId + @PathVariable("placeId") Long placeId ) { Long memberId = memberLoader.getMemberId(); - placeService.deletePlace(memberId, roomId); + placeService.deletePlace(memberId, placeId); return ResponseEntity.ok(DataResponse.ok()); } diff --git a/backend/src/main/java/middle_point_search/backend/domains/place/domain/Place.java b/backend/src/main/java/middle_point_search/backend/domains/place/domain/Place.java index e616553c..f35cfe7b 100644 --- a/backend/src/main/java/middle_point_search/backend/domains/place/domain/Place.java +++ b/backend/src/main/java/middle_point_search/backend/domains/place/domain/Place.java @@ -49,18 +49,11 @@ public class Place { @JoinColumn(name = "member_id") private Member member; - private Place(String siDo, String siGunGu, String roadNameAddress, Double addressLatitude, - Double addressLongitude, Room room) { - this.siDo = siDo; - this.siGunGu = siGunGu; - this.roadNameAddress = roadNameAddress; - this.addressLatitude = addressLatitude; - this.addressLongitude = addressLongitude; - addRoom(room); - } + @Column(nullable = false) + private String googlePlaceId; private Place(String siDo, String siGunGu, String roadNameAddress, Double addressLatitude, - Double addressLongitude, Room room, Member member) { + Double addressLongitude, Room room, Member member, String googlePlaceId) { this.siDo = siDo; this.siGunGu = siGunGu; this.roadNameAddress = roadNameAddress; @@ -68,26 +61,19 @@ private Place(String siDo, String siGunGu, String roadNameAddress, Double addres this.addressLongitude = addressLongitude; addRoom(room); this.member = member; + this.googlePlaceId = googlePlaceId; } - public static Place from(PlaceSaveOrUpdateRequest placeSaveOrUpdateRequest, Room room, Member member) { - String siDo = placeSaveOrUpdateRequest.getSiDo(); - String siGunGu = placeSaveOrUpdateRequest.getSiGunGu(); - String roadNameAddress = placeSaveOrUpdateRequest.getRoadNameAddress(); - Double addressLatitude = placeSaveOrUpdateRequest.getAddressLat(); - Double addressLongitude = placeSaveOrUpdateRequest.getAddressLong(); - - return new Place(siDo, siGunGu, roadNameAddress, addressLatitude, addressLongitude, room, member); - } - - public static Place from(PlaceSaveOrUpdateRequest placeSaveOrUpdateRequest, Room room) { + public static Place from(PlaceSaveOrUpdateRequest placeSaveOrUpdateRequest, Room room, Member member, + String googlePlaceId) { String siDo = placeSaveOrUpdateRequest.getSiDo(); String siGunGu = placeSaveOrUpdateRequest.getSiGunGu(); String roadNameAddress = placeSaveOrUpdateRequest.getRoadNameAddress(); Double addressLatitude = placeSaveOrUpdateRequest.getAddressLat(); Double addressLongitude = placeSaveOrUpdateRequest.getAddressLong(); - return new Place(siDo, siGunGu, roadNameAddress, addressLatitude, addressLongitude, room); + return new Place(siDo, siGunGu, roadNameAddress, addressLatitude, addressLongitude, room, member, + googlePlaceId); } public PlaceVO toVO() { diff --git a/backend/src/main/java/middle_point_search/backend/domains/place/repository/PlaceRepository.java b/backend/src/main/java/middle_point_search/backend/domains/place/repository/PlaceRepository.java index 4c9cf589..966c613c 100644 --- a/backend/src/main/java/middle_point_search/backend/domains/place/repository/PlaceRepository.java +++ b/backend/src/main/java/middle_point_search/backend/domains/place/repository/PlaceRepository.java @@ -9,4 +9,6 @@ public interface PlaceRepository extends JpaRepository { List findAllByRoom_Id(Long roomId); + + void deleteByIdAndRoom_Id(Long placeId, Long roomId); } diff --git a/backend/src/main/java/middle_point_search/backend/domains/place/service/PlaceService.java b/backend/src/main/java/middle_point_search/backend/domains/place/service/PlaceService.java index 8b74069c..8311e48b 100644 --- a/backend/src/main/java/middle_point_search/backend/domains/place/service/PlaceService.java +++ b/backend/src/main/java/middle_point_search/backend/domains/place/service/PlaceService.java @@ -11,6 +11,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import middle_point_search.backend.common.exception.CustomException; +import middle_point_search.backend.domains.google.service.GoogleService; import middle_point_search.backend.domains.member.domain.Member; import middle_point_search.backend.domains.memberRoom.MemberRoomValidateService; import middle_point_search.backend.domains.place.domain.Place; @@ -30,6 +31,7 @@ public class PlaceService { private final PlaceRepository placeRepository; private final RoomService roomService; private final MemberRoomValidateService memberRoomValidateService; + private final GoogleService googleService; //장소 저장 @Transactional(rollbackFor = {CustomException.class}) @@ -40,7 +42,10 @@ public void savePlace(Long roomId, Member member, PlaceSaveOrUpdateRequest reque Room room = roomService.findRoom(roomId) .orElseThrow(() -> CustomException.from(ROOM_NOT_FOUND)); - placeRepository.save(Place.from(request, room, member)); + // 구글 placeId 조회 + String googlePlaceId = googleService.findGooglePlaceId(request.getAddressLat(), request.getAddressLong()); + + placeRepository.save(Place.from(request, room, member, googlePlaceId)); } // 장소 조회 @@ -61,9 +66,12 @@ public PlacesFindResponse findPlaces(Long memberId, Long roomId) { // 장소 삭제 @Transactional public void deletePlace(Long memberId, Long placeId) { + Place place = placeRepository.findById(placeId) + .orElseThrow(() -> CustomException.from(PLACE_NOT_FOUND)); + // 회원이 방에 속해있는지 확인 - memberRoomValidateService.validateAuthorizedMember(memberId, placeId); + memberRoomValidateService.validateAuthorizedMember(memberId, place.getRoom().getId()); - placeRepository.deleteById(placeId); + placeRepository.deleteByIdAndRoom_Id(placeId, place.getRoom().getId()); } }