Skip to content

Commit

Permalink
Feat/주차장 데이터 추가 (#82)
Browse files Browse the repository at this point in the history
* refactor: 부산, 서울 응답값에 JsonIgnoreProperties 추가

* test: 부산, 서울 변환 로직 테스트 추가

* refactor: 조회 로직 수정 및 EqualsAndHashCode 추가

* feat: 전국 주차장 데이터 추가

* refactor: 요청 url 및 URI 생성 방식 변경

* refactor: URI 생성 방식 변경

* refactor: 커낵션 타임아웃 설정

* fix: 좌표가 없는 데이터만 좌표를 가져오도록 수정

* fix: Location에서 제공되지 않는 값 public 상수로 변경

* refactor: SearchingCondition 도메인 서비스 패키지로 이동

* refactor: 어플리케이션단에서 Enum 컬랙션을 List 대신 Set 사용하도록 변경
  • Loading branch information
This2sho authored May 8, 2024
1 parent baed9ec commit d3027ed
Show file tree
Hide file tree
Showing 51 changed files with 3,743 additions and 133 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@
import com.parkingcomestrue.parking.support.exception.ClientExceptionInformation;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.stereotype.Component;

@Component
public class SearchConditionMapper {

public <E extends Enum<E> & SearchConditionAvailable> List<E> toEnums(Class<E> searchConditionAvailableClass,
List<String> descriptions) {
public <E extends Enum<E> & SearchConditionAvailable> Set<E> toEnums(Class<E> searchConditionAvailableClass,
List<String> descriptions) {
return descriptions.stream()
.map(description -> toEnum(searchConditionAvailableClass, description))
.toList();
.collect(Collectors.toSet());
}

public <E extends Enum<E> & SearchConditionAvailable> E toEnum(Class<E> searchConditionAvailableClass,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import com.parkingcomestrue.common.domain.parking.ParkingFeeCalculator;
import com.parkingcomestrue.common.domain.parking.ParkingType;
import com.parkingcomestrue.common.domain.parking.PayType;
import com.parkingcomestrue.common.domain.parking.SearchingCondition;
import com.parkingcomestrue.common.domain.parking.service.SearchingCondition;
import com.parkingcomestrue.common.domain.parking.repository.ParkingRepository;
import com.parkingcomestrue.common.domain.parking.service.ParkingFilteringService;
import com.parkingcomestrue.common.domain.searchcondition.FeeType;
Expand Down Expand Up @@ -89,10 +89,10 @@ private List<Parking> findParkingLotsByOrderCondition(String priority, ParkingQu
}

private SearchingCondition toSearchingCondition(ParkingSearchConditionRequest request) {
List<ParkingType> parkingTypes = searchConditionMapper.toEnums(ParkingType.class, request.getParkingTypes());
List<OperationType> operationTypes = searchConditionMapper.toEnums(OperationType.class,
Set<ParkingType> parkingTypes = searchConditionMapper.toEnums(ParkingType.class, request.getParkingTypes());
Set<OperationType> operationTypes = searchConditionMapper.toEnums(OperationType.class,
request.getOperationTypes());
List<PayType> payTypes = searchConditionMapper.toEnums(PayType.class, request.getPayTypes());
Set<PayType> payTypes = searchConditionMapper.toEnums(PayType.class, request.getPayTypes());
FeeType feeType = searchConditionMapper.toEnum(FeeType.class, request.getFeeType());

return new SearchingCondition(operationTypes, parkingTypes, payTypes, feeType, request.getHours());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import com.parkingcomestrue.common.domain.review.Content;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public record ReviewCreateRequest(List<String> contents) {

public List<Content> toContents() {
public Set<Content> toContents() {
return contents.stream()
.map(Content::find)
.toList();
.collect(Collectors.toSet());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.parkingcomestrue.common.domain.searchcondition.repository.SearchConditionRepository;
import com.parkingcomestrue.common.support.Association;
import java.util.List;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -41,9 +42,10 @@ private SearchConditionDto toSearchConditionDto(SearchCondition searchCondition)
);
}

private <E extends SearchConditionAvailable> List<String> toDescriptions(List<E> enums) {
private <E extends SearchConditionAvailable> List<String> toDescriptions(Set<E> enums) {
return enums.stream()
.map(SearchConditionAvailable::getDescription)
.sorted()
.toList();
}

Expand Down
2 changes: 1 addition & 1 deletion app-api/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ authcode:
cors:
allowedOrigins: ${ORIGIN:http://localhost:3000}

api-prefix: ${API_PREFIX:/api/}
api-prefix: ${API_PREFIX:/}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.parkingcomestrue.common.domain.searchcondition.SearchConditionAvailable;

import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
import org.junit.Test;
import org.junit.jupiter.params.ParameterizedTest;
Expand Down Expand Up @@ -63,7 +64,7 @@ static Stream<Arguments> parametersProvider1() {
List<String> descriptions,
List<E> expected) {
//given, when
List<E> actual = searchConditionMapper.toEnums(clazz, descriptions);
Set<E> actual = searchConditionMapper.toEnums(clazz, descriptions);

//then
assertThat(actual).containsExactlyInAnyOrderElementsOf(expected);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.parkingcomestrue.parking.application.review.dto.ReviewCreateRequest;
import java.time.LocalTime;
import java.util.List;
import java.util.Set;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -64,7 +65,7 @@ class ParkingServiceTest extends ContainerTest {
private Parking makeParking(String parkingName) {
return new Parking
(
new BaseInformation(parkingName, "010", "부산", List.of(PayType.NO_INFO), ParkingType.MECHANICAL,
new BaseInformation(parkingName, "010", "부산", Set.of(PayType.NO_INFO), ParkingType.MECHANICAL,
OperationType.PRIVATE),
Location.of(30d, 30d),
Space.of(100, 30),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.parkingcomestrue.common.support.Association;
import com.parkingcomestrue.common.support.exception.DomainException;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Test;
import repository.BasicMemberRepository;
import repository.BasicParkingRepository;
Expand Down Expand Up @@ -65,9 +66,9 @@ class ReviewServiceTest {
Association<Parking> parkingId = Association.from(parking.getId());
List<Member> reviewers = memberRepository.saveAndGet(3);

List<Content> contents1 = List.of(Content.LOW_PRICE);
List<Content> contents2 = List.of(Content.LOW_PRICE, Content.EASY_TO_PAY);
List<Content> contents3 = List.of(Content.LOW_PRICE, Content.EASY_TO_PAY, Content.GOOD_ACCESSIBILITY);
Set<Content> contents1 = Set.of(Content.LOW_PRICE);
Set<Content> contents2 = Set.of(Content.LOW_PRICE, Content.EASY_TO_PAY);
Set<Content> contents3 = Set.of(Content.LOW_PRICE, Content.EASY_TO_PAY, Content.GOOD_ACCESSIBILITY);
reviewRepository.save(
new Review(parkingId, Association.from(reviewers.get(0).getId()), contents1)
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.parkingcomestrue.external.config;

import com.parkingcomestrue.external.coordinate.CoordinateErrorHandler;
import java.time.Duration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
Expand Down Expand Up @@ -29,8 +30,9 @@ public RestTemplate coordinateRestTemplate(RestTemplateBuilder restTemplateBuild
@Qualifier("parkingApiRestTemplate")
public RestTemplate parkingApiRestTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder
.setConnectTimeout(Duration.ofSeconds(30))
.errorHandler(new ParkingApiErrorHandler())
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_UTF8_VALUE)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package com.parkingcomestrue.external.parkingapi.korea;

import com.parkingcomestrue.common.domain.parking.BaseInformation;
import com.parkingcomestrue.common.domain.parking.Fee;
import com.parkingcomestrue.common.domain.parking.FeePolicy;
import com.parkingcomestrue.common.domain.parking.FreeOperatingTime;
import com.parkingcomestrue.common.domain.parking.Location;
import com.parkingcomestrue.common.domain.parking.OperatingTime;
import com.parkingcomestrue.common.domain.parking.OperationType;
import com.parkingcomestrue.common.domain.parking.Parking;
import com.parkingcomestrue.common.domain.parking.ParkingType;
import com.parkingcomestrue.common.domain.parking.PayType;
import com.parkingcomestrue.common.domain.parking.Space;
import com.parkingcomestrue.common.domain.parking.TimeInfo;
import com.parkingcomestrue.common.domain.parking.TimeUnit;
import java.time.DateTimeException;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Component;

@Component
public class KoreaParkingAdapter {

private static final String NO_PROVIDE = "-1";
private static final String FREE = "무료";
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
private static final String HOURS_24 = "23:59";
private static final String HOURS_00 = "00:00";

public List<Parking> convert(KoreaParkingResponse response) {
return response.getResponse().getBody().getItems().stream()
.map(this::toParking)
.toList();
}

private Parking toParking(KoreaParkingResponse.Response.Body.Item item) {
return new Parking(
getBaseInformation(item),
getLocation(item),
getSpace(item),
getFreeOperatingTime(item),
getOperatingTime(item),
getFeePolicy(item)
);
}

private BaseInformation getBaseInformation(KoreaParkingResponse.Response.Body.Item item) {
return new BaseInformation(
item.getParkingName(),
item.getTel(),
filterAddress(item),
toPayTypes(item),
ParkingType.find(item.getParkingType()),
OperationType.find(item.getOperationType())
);
}

private String filterAddress(KoreaParkingResponse.Response.Body.Item item) {
if (Strings.isBlank(item.getOldAddress())) {
return item.getNewAddress();
}
return item.getOldAddress();
}

private Set<PayType> toPayTypes(KoreaParkingResponse.Response.Body.Item item) {
Set<PayType> payTypes = new HashSet<>();
for (PayType payType : PayType.values()) {
if (item.getPayType().contains(payType.getDescription())) {
payTypes.add(payType);
}
}
if (payTypes.isEmpty()) {
return Set.of(PayType.NO_INFO);
}
return payTypes;
}

private Location getLocation(KoreaParkingResponse.Response.Body.Item item) {
return Location.of(item.getLongitude(), item.getLatitude());
}

private Space getSpace(KoreaParkingResponse.Response.Body.Item item) {
return Space.of(item.getCapacity(), NO_PROVIDE);
}

private FreeOperatingTime getFreeOperatingTime(KoreaParkingResponse.Response.Body.Item item) {
if (item.getFeeInfo().equals(FREE)) {
return FreeOperatingTime.ALWAYS_FREE;
}
return FreeOperatingTime.ALWAYS_PAY;
}

private OperatingTime getOperatingTime(KoreaParkingResponse.Response.Body.Item item) {
return new OperatingTime(
toTimeInfo(item.getWeekDayBeginTime(), item.getWeekdayEndTime()),
toTimeInfo(item.getSaturdayBeginTime(), item.getSaturdayEndTime()),
toTimeInfo(item.getHolidayBeginTime(), item.getHolidayEndTime())
);
}

private TimeInfo toTimeInfo(String beginTime, String endTime) {
if (HOURS_00.equals(beginTime) && HOURS_24.equals(endTime)) {
return TimeInfo.ALL_DAY;
}
if (HOURS_00.equals(beginTime) && HOURS_00.equals(endTime)) {
return TimeInfo.CLOSED;
}
return new TimeInfo(parsingOperationTime(beginTime), parsingOperationTime(endTime));
}

private LocalTime parsingOperationTime(String time) {
if (time.equals(HOURS_24)) {
return LocalTime.MAX;
}
try {
return LocalTime.parse(time, TIME_FORMATTER);
} catch (DateTimeException e) {
return null;
}
}

private FeePolicy getFeePolicy(KoreaParkingResponse.Response.Body.Item item) {
return new FeePolicy(
Fee.from(item.getBaseFee()),
Fee.from(item.getExtraFee()),
TimeUnit.from(item.getBaseTimeUnit()),
TimeUnit.from(item.getExtraTimeUnit()),
Fee.from(item.getDayMaximumFee())
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.parkingcomestrue.external.parkingapi.korea;

import com.parkingcomestrue.common.domain.parking.Parking;
import com.parkingcomestrue.external.parkingapi.ParkingApiService;
import java.net.URI;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.DefaultUriBuilderFactory;

@Component
public class KoreaParkingApiService implements ParkingApiService {

private static final String URL = "http://api.data.go.kr/openapi/tn_pubr_prkplce_info_api";
private static final String RESULT_TYPE = "json";
private static final int SIZE = 250;
private static final String NORMAL_RESULT_CODE = "00";

@Value("${korea-parking-key}")
private String API_KEY;

private final KoreaParkingAdapter adapter;
private final RestTemplate restTemplate;

public KoreaParkingApiService(KoreaParkingAdapter adapter,
@Qualifier("parkingApiRestTemplate") RestTemplate restTemplate) {
this.adapter = adapter;
this.restTemplate = restTemplate;
}

@Override
public List<Parking> read() throws Exception {
Set<KoreaParkingResponse> result = new HashSet<>();
for (int pageNumber = 1; ; pageNumber++) {
KoreaParkingResponse response = call(pageNumber, SIZE);
String resultCode = response.getResponse().getHeader().getResultCode();
if (NORMAL_RESULT_CODE.equals(resultCode)) {
result.add(response);
continue;
}
break;
}
return result.stream()
.flatMap(response -> adapter.convert(response).stream())
.toList();
}

private KoreaParkingResponse call(int startIndex, int size) {
URI uri = makeUri(startIndex, size);
ResponseEntity<KoreaParkingResponse> response = restTemplate.getForEntity(uri, KoreaParkingResponse.class);
return response.getBody();
}

private URI makeUri(int startIndex, int size) {
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(URL);
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
return factory.builder()
.queryParam("serviceKey", API_KEY)
.queryParam("pageNo", startIndex)
.queryParam("numOfRows", size)
.queryParam("type", RESULT_TYPE)
.build();
}
}
Loading

0 comments on commit d3027ed

Please sign in to comment.