Skip to content

Commit

Permalink
#18 [Refactor] locationService 인터페이스로 변경
Browse files Browse the repository at this point in the history
  • Loading branch information
JSoi committed Jul 22, 2022
1 parent 89112c2 commit 7458af9
Showing 1 changed file with 6 additions and 307 deletions.
313 changes: 6 additions & 307 deletions src/main/java/com/mpnp/baechelin/api/service/LocationService.java
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);
}

0 comments on commit 7458af9

Please sign in to comment.