-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#18 [Refactor] locationService 인터페이스로 변경
- Loading branch information
Showing
1 changed file
with
6 additions
and
307 deletions.
There are no files selected for viewing
313 changes: 6 additions & 307 deletions
313
src/main/java/com/mpnp/baechelin/api/service/LocationService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,318 +1,17 @@ | ||
package com.mpnp.baechelin.api.service; | ||
|
||
import com.mpnp.baechelin.api.model.LocationAddressSearchForm; | ||
import com.mpnp.baechelin.common.httpclient.HttpConfig; | ||
import com.mpnp.baechelin.api.model.LocationKeywordSearchForm; | ||
import com.mpnp.baechelin.store.domain.Category; | ||
import com.mpnp.baechelin.api.dto.LocationInfoDto; | ||
import com.mpnp.baechelin.api.dto.LocationPartDto; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.http.*; | ||
import org.springframework.http.client.reactive.ReactorClientHttpConnector; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.web.client.RestTemplate; | ||
import org.springframework.web.reactive.function.client.WebClient; | ||
import org.springframework.web.util.UriComponentsBuilder; | ||
|
||
import javax.transaction.Transactional; | ||
import java.net.URI; | ||
import java.util.*; | ||
import java.util.List; | ||
|
||
@Service | ||
@Slf4j | ||
@RequiredArgsConstructor | ||
@Transactional | ||
public class LocationService { | ||
private final HttpConfig httpConfig; | ||
public interface LocationService { | ||
|
||
@Value("${kakao.api.key}") | ||
private String kakaoApiKey; | ||
LocationPartDto.LatLong convertAddressToGeo(String address); | ||
|
||
/** | ||
* @param address 주소 | ||
* @return LocationKeywordSearchForm의 규격에 맞는 결과 하나를 가져옴 | ||
*/ | ||
public LocationKeywordSearchForm convertAddressToGeoWC(String address) { | ||
WebClient client = WebClient.builder() | ||
.baseUrl("https://dapi.kakao.com/v2/local/search/keyword.json") | ||
.defaultUriVariables(Collections.singletonMap("url", "https://dapi.kakao.com/v2/local/search/keyword.json")) | ||
.clientConnector(new ReactorClientHttpConnector(httpConfig.httpClient())) // 위의 타임아웃 적용 | ||
.build(); | ||
return client.get().uri(uriBuilder | ||
-> uriBuilder.queryParam("query", address) | ||
// .queryParam("category_group_code", "FD6") // 음식점으로 특정 - FD6 | ||
.queryParam("page", 1) | ||
.queryParam("size", 1) | ||
.build()) | ||
.accept(MediaType.APPLICATION_JSON) | ||
.header("Authorization", "") | ||
.retrieve().bodyToMono(LocationKeywordSearchForm.class).flux() | ||
.toStream() | ||
.findFirst() | ||
.orElse(null); | ||
} | ||
LocationInfoDto.LocationResponse convertGeoAndStoreNameToKeyword(String lat, String lng, String storeName); | ||
|
||
/** | ||
* @param lat 위도 | ||
* @param lng 경도 | ||
* @param storeName 업장명 | ||
* @return 위도, 경도, 업장명을 만족하는 장소 찾기 | ||
*/ | ||
public LocationKeywordSearchForm getCategoryByLatLngKeywordWC(String lat, String lng, String storeName) { | ||
WebClient client = WebClient.builder() | ||
.baseUrl("https://dapi.kakao.com/v2/local/search/keyword.json") | ||
.defaultUriVariables(Collections.singletonMap("url", "https://dapi.kakao.com/v2/local/search/keyword.json")) | ||
.clientConnector(new ReactorClientHttpConnector(httpConfig.httpClient())) // 위의타임아웃 적용 | ||
.build(); | ||
return client.get().uri(uriBuilder | ||
-> uriBuilder.queryParam("query", storeName) | ||
// .queryParam("category_group_code", "FD6") // 음식점으로 특정 - FD6 | ||
.queryParam("x", lng)//위도, 경도 지정 | ||
.queryParam("y", lat) | ||
.queryParam("radius", 200) | ||
.queryParam("page", 1) | ||
.queryParam("size", 1) | ||
.build()) | ||
.accept(MediaType.APPLICATION_JSON) | ||
.header("Authorization", kakaoApiKey) | ||
.retrieve().bodyToMono(LocationKeywordSearchForm.class) | ||
.flux() | ||
.toStream().findFirst() | ||
.orElse(null); | ||
} | ||
List<LocationInfoDto.LocationResponse> convertGeoAndAddressToKeyword(String lat, String lng, String address); | ||
|
||
/** | ||
* @param address 주소 | ||
* @return 위도, 경도, Status를 가지는 Map 반환 | ||
* 주소 -> 위도/경도 이므로 <<1개 반환>> | ||
* Controller, Service에서 같은 Map 형태로 사용 | ||
*/ | ||
public LocationPartDto.LatLong convertAddressToGeo(String address) { | ||
// status, latitude, longitude 를 키로 가지는 HashMap 생성 | ||
LocationPartDto.LatLong locLl = LocationPartDto.LatLong.builder().build(); | ||
LocationKeywordSearchForm locationKeywordSearchForm = getLatLngByAddressRT(address); | ||
if (locationKeywordSearchForm == null) { // 비어 있을 때 status-false 저장 | ||
return locLl; | ||
} | ||
LocationKeywordSearchForm.Documents latLngDoc | ||
= Arrays.stream(locationKeywordSearchForm.getDocuments()).findAny().orElse(null); | ||
if (latLngDoc != null) { | ||
locLl = LocationPartDto.LatLong.builder() | ||
.latitude(latLngDoc.getY()) | ||
.longitude(latLngDoc.getX()) | ||
.status(true) | ||
.build(); | ||
} | ||
return locLl; | ||
} | ||
|
||
/** | ||
* @param address 변환할 주소 | ||
* @return RestTemplate를 이용해 변환한 위도, 경도 | ||
* 위도/경도 -> 주소 이므로 <<1개 반환>> | ||
*/ | ||
public LocationKeywordSearchForm getLatLngByAddressRT(String address) { | ||
HttpHeaders headers = new HttpHeaders(); | ||
headers.setAccept(List.of(MediaType.APPLICATION_JSON)); | ||
headers.setContentType(MediaType.APPLICATION_JSON); | ||
headers.set("Authorization", kakaoApiKey); | ||
URI uri = UriComponentsBuilder | ||
.fromUriString("https://dapi.kakao.com/v2/local/search/keyword.json") | ||
.queryParam("query", address) | ||
.queryParam("page", 1) | ||
.queryParam("size", 1) | ||
.encode() | ||
.build() | ||
.toUri(); | ||
|
||
RestTemplate restTemplate = new RestTemplate(); | ||
ResponseEntity<LocationKeywordSearchForm> resultRe = restTemplate.exchange( | ||
uri, HttpMethod.GET, new HttpEntity<>(headers), LocationKeywordSearchForm.class | ||
); | ||
return resultRe.getBody(); | ||
} | ||
// 주소 넣는 것으로 바꾸기 | ||
// 주소 넣는 것으로 바꾸기 | ||
|
||
/** | ||
* @param lat 위도 | ||
* @param lng 경도 | ||
* @param keyword 검색 키워드 | ||
* @return 음식점, 카페 검색을 통해 얻은 결과 - V1, 1개의 결과만을 반환 | ||
*/ | ||
public LocationKeywordSearchForm getCategoryByLatLngKeywordRT(String lat, String lng, String keyword) { | ||
LocationKeywordSearchForm searchFormResult = getCategoryByCodeRT(lat, lng, keyword, "FD6", 1); | ||
if (searchFormResult == null) { | ||
searchFormResult = getCategoryByCodeRT(lat, lng, keyword, "CE7", 1); | ||
} | ||
return searchFormResult; | ||
} | ||
|
||
/** | ||
* @param lat 위도 | ||
* @param lng 경도 | ||
* @param storeName 업장명 | ||
* @param cateCode 카테고리 코드 | ||
* @return 위도, 경도, 업장명, 카테고리 코드 조건에 맞는 정보를 리턴 | ||
*/ | ||
public LocationKeywordSearchForm getCategoryByCodeRT(String lat, String lng, String storeName, String cateCode, int page) { | ||
HttpHeaders headers = new HttpHeaders(); | ||
headers.setAccept(List.of(MediaType.APPLICATION_JSON)); | ||
headers.setContentType(MediaType.APPLICATION_JSON); | ||
headers.set("Authorization", kakaoApiKey); | ||
URI uri = UriComponentsBuilder | ||
.fromUriString("https://dapi.kakao.com/v2/local/search/keyword.json") | ||
.queryParam("query", storeName) | ||
.queryParam("x", lng)//위도, 경도 지정 | ||
.queryParam("y", lat) | ||
.queryParam("category_group_code", cateCode) // 카테고리 그룹을 설정 | ||
.queryParam("radius", 20) | ||
.queryParam("page", page) | ||
.queryParam("size", 15) | ||
.encode() | ||
.build() | ||
.toUri(); | ||
|
||
RestTemplate restTemplate = new RestTemplate(); | ||
ResponseEntity<LocationKeywordSearchForm> resultRe = restTemplate.exchange( | ||
uri, HttpMethod.GET, new HttpEntity<>(headers), LocationKeywordSearchForm.class | ||
); | ||
return resultRe.getBody(); | ||
} | ||
|
||
/** | ||
* @param lat 검색할 위도 | ||
* @param lng 검색할 경도 | ||
* @param storeName 검색할 업장명 | ||
* @return 위도, 경도, 업장명을 통해 업장의 정보 반환 | ||
*/ | ||
public LocationInfoDto.LocationResponse convertGeoAndStoreNameToKeyword(String lat, String lng, String storeName) { | ||
LocationKeywordSearchForm locationKeywordSearchForm = getCategoryByLatLngKeywordRT(lat, lng, storeName); | ||
// 위도, 경도, 업장명을 가지고 업장 정보를 찾는다 | ||
if (locationKeywordSearchForm == null) { | ||
return null; | ||
} | ||
LocationKeywordSearchForm.Documents latLngDoc | ||
= Arrays.stream(locationKeywordSearchForm.getDocuments()).findFirst().orElse(null); | ||
if (latLngDoc == null) { | ||
return null; | ||
} | ||
return LocationInfoDto.LocationResponse.builder() | ||
.storeId(Integer.parseInt(latLngDoc.getId())) | ||
.latitude(latLngDoc.getY()) | ||
.longitude(latLngDoc.getX()) | ||
.category(categoryFilter(latLngDoc.getCategory_name())) | ||
.storeId(Integer.parseInt(latLngDoc.getId())) | ||
.storeName(latLngDoc.getPlace_name()) | ||
.phoneNumber(latLngDoc.getPhone()).build(); | ||
} | ||
|
||
/** | ||
* @param lat 위도 | ||
* @param lng 경도 | ||
* @param address 주소 | ||
* @return 업장명 대신에 주소를 입력해 해당 건물에 있는 업장을 배리어 프리 시설로 등록한다 | ||
*/ | ||
|
||
// TODO 페이징 까지 완료하기 - 여러 건이 필요 | ||
public List<LocationInfoDto.LocationResponse> convertGeoAndAddressToKeyword(String lat, String lng, String address) { | ||
List<LocationInfoDto.LocationResponse> resultList = new ArrayList<>(); | ||
getStoreResults(lat, lng, address, "FD6", resultList); | ||
getStoreResults(lat, lng, address, "CE7", resultList); | ||
return resultList; | ||
} | ||
|
||
/** | ||
* @param lat 위도 | ||
* @param lng 경도 | ||
* @param address 주소 | ||
* @param type 검색 타입 | ||
* @param resultList 검색 결과 | ||
*/ | ||
private void getStoreResults(String lat, String lng, String address, String type, List<LocationInfoDto.LocationResponse> resultList) { | ||
LocationKeywordSearchForm locationKeywordSearchForm; | ||
int page = 1; | ||
do { | ||
locationKeywordSearchForm = getCategoryByCodeRT(lat, lng, address, type, page++); | ||
// 위도, 경도, 업장명을 가지고 업장 정보를 찾는다 | ||
if (locationKeywordSearchForm == null) { | ||
return; | ||
} | ||
LocationKeywordSearchForm.Documents[] latLngDocArr = locationKeywordSearchForm.getDocuments(); | ||
// 다음 페이지가 있는지 조사가 필요 - SearchForm에서 확인한다 | ||
for (LocationKeywordSearchForm.Documents latLngDoc : latLngDocArr) { | ||
if (latLngDoc != null) { | ||
LocationInfoDto.LocationResponse newResult = LocationInfoDto.LocationResponse.builder() | ||
.latitude(latLngDoc.getY()) | ||
.longitude(latLngDoc.getX()) | ||
.category(categoryFilter(latLngDoc.getCategory_name())) | ||
.storeName(latLngDoc.getPlace_name()) | ||
.storeId(Integer.parseInt(latLngDoc.getId())) | ||
.phoneNumber(latLngDoc.getPhone()) | ||
.build(); | ||
if (newResult.validate()) { | ||
resultList.add(newResult); | ||
} | ||
} | ||
} | ||
} while (locationKeywordSearchForm.getMeta().is_end()); // 마지막 페이지까지 검사 | ||
} | ||
|
||
|
||
/** | ||
* @param category 변환할 카테고리 | ||
* @return 카테고리의 중분류를 추출해 반환 | ||
*/ | ||
private String categoryFilter(String category) { | ||
if (category == null) { | ||
return Category.ETC.getDesc(); | ||
} else if (category.contains(">")) { | ||
return Category.giveCategory(category.split(" > ")[1]).getDesc(); | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
/** | ||
* @param lat 위도 | ||
* @param lng 경도 | ||
* @return 위도, 경도를 카카오맵 API(RestTemplate)를 통해 주소로 변환 후 Map에 넣어 반환 | ||
*/ | ||
public LocationPartDto.Address convertGeoToAddressRT(String lat, String lng) { | ||
HttpHeaders headers = new HttpHeaders(); | ||
headers.setAccept(List.of(MediaType.APPLICATION_JSON)); | ||
headers.setContentType(MediaType.APPLICATION_JSON); | ||
headers.set("Authorization", kakaoApiKey); | ||
URI uri = UriComponentsBuilder | ||
.fromUriString("https://dapi.kakao.com/v2/local/geo/coord2address.json") | ||
.queryParam("x", lng)//위도, 경도 지정 | ||
.queryParam("y", lat) | ||
.encode() | ||
.build() | ||
.toUri(); | ||
|
||
RestTemplate restTemplate = new RestTemplate(); | ||
ResponseEntity<LocationAddressSearchForm> resultRe = restTemplate.exchange( | ||
uri, HttpMethod.GET, new HttpEntity<>(headers), LocationAddressSearchForm.class | ||
); | ||
LocationAddressSearchForm locationKeywordSearchForm = resultRe.getBody(); | ||
return formToDto(locationKeywordSearchForm); | ||
} | ||
|
||
private LocationPartDto.Address formToDto(LocationAddressSearchForm resultRe) { | ||
LocationPartDto.Address addressInfoDto = LocationPartDto.Address.builder().build(); | ||
if (resultRe == null) { | ||
return addressInfoDto; | ||
} | ||
LocationAddressSearchForm.TotalAddress address = Arrays.stream(resultRe.getDocuments()).findFirst().orElse(null); | ||
if (address != null) { | ||
addressInfoDto = LocationPartDto.Address.builder() | ||
.address(address.getAddress().getAddress_name()) | ||
.status(true) | ||
.build(); | ||
} | ||
return addressInfoDto; | ||
} | ||
LocationPartDto.Address convertGeoToAddress(String lat, String lng); | ||
} |