From 06f795f4b527db874761dac12b69bfbe777aa4c8 Mon Sep 17 00:00:00 2001 From: This2sho Date: Fri, 17 May 2024 17:52:32 +0900 Subject: [PATCH 01/18] =?UTF-8?q?fix:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EB=B0=8F=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=EC=8B=9C=EC=97=90=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=ED=95=84=EC=9A=94=20=EC=97=86=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=EC=BD=94=EB=93=9C=EB=8F=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/parkingcomestrue/parking/config/WebMvcConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app-api/src/main/java/com/parkingcomestrue/parking/config/WebMvcConfig.java b/app-api/src/main/java/com/parkingcomestrue/parking/config/WebMvcConfig.java index b71f727..2fc3412 100644 --- a/app-api/src/main/java/com/parkingcomestrue/parking/config/WebMvcConfig.java +++ b/app-api/src/main/java/com/parkingcomestrue/parking/config/WebMvcConfig.java @@ -37,7 +37,8 @@ public void addInterceptors(InterceptorRegistry registry) { "/signup", "/signin", "/parkings/**", - "/actuator/**" + "/actuator/**", + "/authcode/**" )); } From e906096d119e923df0bdd9e278d36249f1c77c03 Mon Sep 17 00:00:00 2001 From: This2sho Date: Thu, 30 May 2024 17:52:11 +0900 Subject: [PATCH 02/18] =?UTF-8?q?feat:=20=EC=A3=BC=EC=B0=A8=EC=9E=A5=20bat?= =?UTF-8?q?ch=EB=A1=9C=20insert=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app-scheduler/build.gradle | 2 + .../respository/ParkingBatchRepository.java | 10 +++ .../ParkingBatchRepositoryImpl.java | 54 +++++++++++++ .../respository/ParkingBulkRepository.java | 80 +++++++++++++++++++ .../domain/parking/FreeOperatingTime.java | 24 ++++++ .../common/domain/parking/Location.java | 2 +- 6 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBatchRepository.java create mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBatchRepositoryImpl.java create mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBulkRepository.java diff --git a/app-scheduler/build.gradle b/app-scheduler/build.gradle index ef72720..31bc8ce 100644 --- a/app-scheduler/build.gradle +++ b/app-scheduler/build.gradle @@ -10,4 +10,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-actuator' // 프로메테우스 추가 implementation 'io.micrometer:micrometer-registry-prometheus' + + implementation group: 'org.hibernate.orm', name: 'hibernate-spatial', version: '6.3.1.Final' } diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBatchRepository.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBatchRepository.java new file mode 100644 index 0000000..6bccfbc --- /dev/null +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBatchRepository.java @@ -0,0 +1,10 @@ +package com.parkingcomestrue.external.respository; + +import com.parkingcomestrue.common.domain.parking.Parking; +import com.parkingcomestrue.common.domain.parking.repository.ParkingRepository; +import java.util.List; + +public interface ParkingBatchRepository extends ParkingRepository { + + void saveWithBatch(List parkingLots); +} diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBatchRepositoryImpl.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBatchRepositoryImpl.java new file mode 100644 index 0000000..a2546cd --- /dev/null +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBatchRepositoryImpl.java @@ -0,0 +1,54 @@ +package com.parkingcomestrue.external.respository; + +import com.parkingcomestrue.common.domain.parking.Parking; +import com.parkingcomestrue.common.domain.parking.repository.ParkingRepository; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import org.locationtech.jts.geom.Point; +import org.springframework.stereotype.Repository; + +@RequiredArgsConstructor +@Repository +public class ParkingBatchRepositoryImpl implements ParkingBatchRepository { + + private final int BATCH_SIZE = 2000; + + private final ParkingBulkRepository parkingBulkRepository; + private final ParkingRepository parkingRepository; + + @Override + public void saveWithBatch(List parkingLots) { + for (int i = 0; i < parkingLots.size(); i += BATCH_SIZE) { + int end = Math.min(i + BATCH_SIZE, parkingLots.size()); + List subParkingLots = parkingLots.subList(i, end); + parkingBulkRepository.saveAllWithBulk(subParkingLots); + } + } + + @Override + public Optional findById(Long id) { + return parkingRepository.findById(id); + } + + @Override + public Set findAllByBaseInformationNameIn(Set parkingNames) { + return parkingRepository.findAllByBaseInformationNameIn(parkingNames); + } + + @Override + public void saveAll(Iterable parkingLots) { + parkingRepository.saveAll(parkingLots); + } + + @Override + public List findAroundParkingLotsOrderByDistance(Point point, int radius) { + return parkingRepository.findAroundParkingLotsOrderByDistance(point, radius); + } + + @Override + public List findAroundParkingLots(Point point, int radius) { + return parkingRepository.findAroundParkingLots(point, radius); + } +} diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBulkRepository.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBulkRepository.java new file mode 100644 index 0000000..d109efc --- /dev/null +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBulkRepository.java @@ -0,0 +1,80 @@ +package com.parkingcomestrue.external.respository; + +import com.parkingcomestrue.common.domain.parking.Location; +import com.parkingcomestrue.common.domain.parking.Parking; +import java.sql.PreparedStatement; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.ParameterizedPreparedStatementSetter; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Repository +public class ParkingBulkRepository { + + private final ParameterizedPreparedStatementSetter PARKING_PARAMETERIZED_PREPARED_STATEMENT_SETTER = (PreparedStatement ps, Parking parking) -> { + ps.setInt(1, parking.getFeePolicy().getBaseFee().getFee()); + ps.setInt(2, parking.getFeePolicy().getBaseTimeUnit().getTimeUnit()); + ps.setInt(3, parking.getFeePolicy().getExtraFee().getFee()); + ps.setInt(4, parking.getFeePolicy().getExtraTimeUnit().getTimeUnit()); + ps.setInt(5, parking.getFeePolicy().getDayMaximumFee().getFee()); + + ps.setInt(6, parking.getSpace().getCapacity()); + ps.setInt(7, parking.getSpace().getCurrentParking()); + + ps.setObject(8, parking.getOperatingTime().getHolidayBeginTime()); + ps.setObject(9, parking.getOperatingTime().getHolidayEndTime()); + ps.setObject(10, parking.getFreeOperatingTime().getHolidayBeginTime()); + ps.setObject(11, parking.getFreeOperatingTime().getHolidayEndTime()); + + ps.setObject(12, parking.getOperatingTime().getSaturdayBeginTime()); + ps.setObject(13, parking.getOperatingTime().getSaturdayEndTime()); + ps.setObject(14, parking.getFreeOperatingTime().getSaturdayBeginTime()); + ps.setObject(15, parking.getFreeOperatingTime().getSaturdayEndTime()); + + ps.setObject(16, parking.getOperatingTime().getWeekdayBeginTime()); + ps.setObject(17, parking.getOperatingTime().getWeekdayEndTime()); + ps.setObject(18, parking.getFreeOperatingTime().getWeekdayBeginTime()); + ps.setObject(19, parking.getFreeOperatingTime().getWeekdayEndTime()); + + ps.setObject(20, parking.getCreatedAt()); + ps.setObject(21, parking.getUpdatedAt()); + + ps.setString(22, parking.getBaseInformation().getAddress()); + ps.setString(23, parking.getBaseInformation().getName()); + ps.setString(24, parking.getBaseInformation().getTel()); + ps.setString(25, parking.getBaseInformation().getOperationType().name()); + ps.setString(26, parking.getBaseInformation().getParkingType().name()); + ps.setString(27, parking.getBaseInformation().getPayTypesName()); + ps.setString(28, toWKT(parking.getLocation())); + }; + + private String toWKT(Location location) { + return "POINT(" + location.getLatitude() + " " + location.getLongitude() + ")"; + } + + private final JdbcTemplate jdbcTemplate; + + @Transactional + public void saveAllWithBulk(List parkingLots) { + String sql = "INSERT INTO parking " + + "(base_fee, base_time_unit, extra_fee, extra_time_unit, day_maximum_fee, " + + "capacity, current_parking, " + + "holiday_begin_time, holiday_end_time, holiday_free_begin_time, holiday_free_end_time, " + + "saturday_begin_time, saturday_end_time, saturday_free_begin_time, saturday_free_end_time, " + + "weekday_begin_time, weekday_end_time, weekday_free_begin_time, weekday_free_end_time, " + + "created_at, updated_at, " + + "address, name, tel, operation_type, parking_type, pay_types, location) " + + "VALUES " + + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ST_GeomFromText(?))"; + + jdbcTemplate.batchUpdate( + sql, + parkingLots, + parkingLots.size(), + PARKING_PARAMETERIZED_PREPARED_STATEMENT_SETTER + ); + } +} diff --git a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/FreeOperatingTime.java b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/FreeOperatingTime.java index 46d742d..44dbd4f 100644 --- a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/FreeOperatingTime.java +++ b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/FreeOperatingTime.java @@ -70,4 +70,28 @@ private int calculateMinutes(LocalTime localTime) { } return localTime.getHour() * 60 + localTime.getMinute(); } + + public LocalTime getWeekdayBeginTime() { + return weekday.getBeginTime(); + } + + public LocalTime getWeekdayEndTime() { + return weekday.getEndTime(); + } + + public LocalTime getSaturdayBeginTime() { + return saturday.getBeginTime(); + } + + public LocalTime getSaturdayEndTime() { + return saturday.getEndTime(); + } + + public LocalTime getHolidayBeginTime() { + return holiday.getBeginTime(); + } + + public LocalTime getHolidayEndTime() { + return holiday.getEndTime(); + } } diff --git a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/Location.java b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/Location.java index a1b8672..daa7e2e 100644 --- a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/Location.java +++ b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/Location.java @@ -39,7 +39,7 @@ public static Location of(Double longitude, Double latitude) { try { verifyLocation(longitude, latitude); return new Location(longitude, latitude); - } catch (NullPointerException e) { + } catch (NullPointerException | DomainException e) { return NO_PROVIDE; } } From fde58fab00ab18e55b2f30975878d7bba99de2ec Mon Sep 17 00:00:00 2001 From: This2sho Date: Thu, 30 May 2024 19:10:55 +0900 Subject: [PATCH 03/18] =?UTF-8?q?feat:=20=EC=A3=BC=EC=B0=A8=EC=9E=A5=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B9=84=EB=8F=99=EA=B8=B0?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=BD=EC=96=B4=EC=98=A4=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../external/api/AsyncApiExecutor.java | 20 ++++++++ .../parkingapi/korea/KoreaParkingAdapter.java | 2 +- .../korea/KoreaParkingApiService.java | 43 ++++++++++------ .../korea/KoreaParkingResponse.java | 3 +- .../scheduler/ParkingUpdateScheduler.java | 14 +++--- .../external/api/AsyncApiExecutorTest.java | 50 +++++++++++++++++++ .../common/domain/parking/Parking.java | 4 ++ 7 files changed, 111 insertions(+), 25 deletions(-) create mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/api/AsyncApiExecutor.java rename app-scheduler/src/main/java/com/parkingcomestrue/external/{ => api}/parkingapi/korea/KoreaParkingAdapter.java (98%) rename app-scheduler/src/main/java/com/parkingcomestrue/external/{ => api}/parkingapi/korea/KoreaParkingApiService.java (58%) rename app-scheduler/src/main/java/com/parkingcomestrue/external/{ => api}/parkingapi/korea/KoreaParkingResponse.java (96%) create mode 100644 app-scheduler/src/test/java/com/parkingcomestrue/external/api/AsyncApiExecutorTest.java diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/AsyncApiExecutor.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/AsyncApiExecutor.java new file mode 100644 index 0000000..52354c6 --- /dev/null +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/AsyncApiExecutor.java @@ -0,0 +1,20 @@ +package com.parkingcomestrue.external.api; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Supplier; + +public class AsyncApiExecutor { + + private static final ExecutorService executorService = Executors.newFixedThreadPool(100, (Runnable r) -> { + Thread thread = new Thread(r); + thread.setDaemon(true); + return thread; + } + ); + + public static CompletableFuture executeAsync(Supplier supplier) { + return CompletableFuture.supplyAsync(supplier::get, executorService); + } +} diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/korea/KoreaParkingAdapter.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingAdapter.java similarity index 98% rename from app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/korea/KoreaParkingAdapter.java rename to app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingAdapter.java index ea0f373..07db341 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/korea/KoreaParkingAdapter.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingAdapter.java @@ -1,4 +1,4 @@ -package com.parkingcomestrue.external.parkingapi.korea; +package com.parkingcomestrue.external.api.parkingapi.korea; import com.parkingcomestrue.common.domain.parking.BaseInformation; import com.parkingcomestrue.common.domain.parking.Fee; diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/korea/KoreaParkingApiService.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingApiService.java similarity index 58% rename from app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/korea/KoreaParkingApiService.java rename to app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingApiService.java index 4ff9b3d..00efd10 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/korea/KoreaParkingApiService.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingApiService.java @@ -1,11 +1,15 @@ -package com.parkingcomestrue.external.parkingapi.korea; +package com.parkingcomestrue.external.api.parkingapi.korea; import com.parkingcomestrue.common.domain.parking.Parking; -import com.parkingcomestrue.external.parkingapi.ParkingApiService; +import com.parkingcomestrue.external.api.AsyncApiExecutor; +import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; import java.net.URI; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; @@ -35,25 +39,32 @@ public KoreaParkingApiService(KoreaParkingAdapter adapter, @Override public List read() throws Exception { - Set 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; - } + int pageNumber = 1; + KoreaParkingResponse response = call(pageNumber++, SIZE).getBody(); + int lastPageNumber = response.getResponse().getBody().getTotalCount() / SIZE + 1; + + Set result = new HashSet<>(lastPageNumber); + result.add(response); + + List> apis = Stream.iterate(pageNumber, i -> i <= lastPageNumber, + i -> i + 1) + .map(i -> AsyncApiExecutor.executeAsync(() -> call(i, SIZE).getBody())) + .toList(); + + Set responses = apis.stream() + .map(CompletableFuture::join) + .collect(Collectors.toSet()); + + result.addAll(responses); + return result.stream() - .flatMap(response -> adapter.convert(response).stream()) + .flatMap(koreaParkingResponse -> adapter.convert(koreaParkingResponse).stream()) .toList(); } - private KoreaParkingResponse call(int startIndex, int size) { + private ResponseEntity call(int startIndex, int size) { URI uri = makeUri(startIndex, size); - ResponseEntity response = restTemplate.getForEntity(uri, KoreaParkingResponse.class); - return response.getBody(); + return restTemplate.getForEntity(uri, KoreaParkingResponse.class); } private URI makeUri(int startIndex, int size) { diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/korea/KoreaParkingResponse.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingResponse.java similarity index 96% rename from app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/korea/KoreaParkingResponse.java rename to app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingResponse.java index 5b926a4..e063204 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/korea/KoreaParkingResponse.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingResponse.java @@ -1,4 +1,4 @@ -package com.parkingcomestrue.external.parkingapi.korea; +package com.parkingcomestrue.external.api.parkingapi.korea; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @@ -29,6 +29,7 @@ public static class Header { public static class Body { private List items; + private int totalCount; @Getter @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java index 4d603f8..4b33386 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java @@ -1,10 +1,10 @@ package com.parkingcomestrue.external.scheduler; -import com.parkingcomestrue.external.coordinate.CoordinateApiService; -import com.parkingcomestrue.external.parkingapi.ParkingApiService; +import com.parkingcomestrue.external.respository.ParkingBatchRepository; import com.parkingcomestrue.common.domain.parking.Location; import com.parkingcomestrue.common.domain.parking.Parking; -import com.parkingcomestrue.common.domain.parking.repository.ParkingRepository; +import com.parkingcomestrue.external.api.coordinate.CoordinateApiService; +import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -27,7 +27,7 @@ public class ParkingUpdateScheduler { private final List parkingApiServices; private final CoordinateApiService coordinateApiService; - private final ParkingRepository parkingRepository; + private final ParkingBatchRepository parkingBatchRepository; @Scheduled(cron = "0 */30 * * * *") public void autoUpdateOfferCurrentParking() { @@ -63,7 +63,7 @@ private List read(ParkingApiService parkingApiService) { } private Map findAllByName(Set names) { - return parkingRepository.findAllByBaseInformationNameIn(names) + return parkingBatchRepository.findAllByBaseInformationNameIn(names) .stream() .collect(toParkingMap()); } @@ -83,12 +83,12 @@ private void saveNewParkingLots(Map parkingLots, Map newParkingLots) { for (Parking parking : newParkingLots) { - if (!parking.getLocation().equals(Location.NO_PROVIDE)) { + if (parking.isLocationAvailable()) { continue; } Location locationByAddress = coordinateApiService.extractLocationByAddress( diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/external/api/AsyncApiExecutorTest.java b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/AsyncApiExecutorTest.java new file mode 100644 index 0000000..ad80da2 --- /dev/null +++ b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/AsyncApiExecutorTest.java @@ -0,0 +1,50 @@ +package com.parkingcomestrue.external.api; + +import com.parkingcomestrue.external.api.AsyncApiExecutor; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; + +class AsyncApiExecutorTest { + + private static final int MINUTE = 1000; + + @Test + void executeAsync_메서드를_사용하면_100개의_스레드로_비동기_동작한다() { + //given + int pageNumber = 1; + int lastPageNumber = 100; + + //when + long start = System.currentTimeMillis(); + List> testCalls = Stream.iterate(pageNumber, i -> i <= lastPageNumber, i -> i + 1) + .map(i -> AsyncApiExecutor.executeAsync(() -> testCall(i))) + .toList(); + long end = System.currentTimeMillis(); + + Integer sum = CompletableFuture.allOf(testCalls.toArray(new CompletableFuture[0])) + .thenApply(Void -> testCalls.stream() + .mapToInt(CompletableFuture::join) + .sum()) + .join(); + + Integer expected = lastPageNumber * (lastPageNumber + 1) / 2; // 가우스 합 + + //then + SoftAssertions.assertSoftly(soft -> { + soft.assertThat((end - start)).isLessThanOrEqualTo(100 * MINUTE); + soft.assertThat(sum).isEqualTo(expected); + }); + } + + private Integer testCall(Integer pageNumber) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return pageNumber; + } +} diff --git a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/Parking.java b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/Parking.java index e50e74b..1a29773 100644 --- a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/Parking.java +++ b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/Parking.java @@ -138,6 +138,10 @@ public int calculateUpdatedDiff(LocalDateTime now) { return diffMinute.intValue(); } + public boolean isLocationAvailable() { + return !location.equals(Location.NO_PROVIDE); + } + @Override public boolean equals(Object o) { if (this == o) { From 2c41564bfd09da023479b03acd35f5efa3b117e4 Mon Sep 17 00:00:00 2001 From: This2sho Date: Sun, 9 Jun 2024 19:23:51 +0900 Subject: [PATCH 04/18] =?UTF-8?q?feat:=20=EC=BB=A4=EB=82=B5=EC=85=98=20?= =?UTF-8?q?=ED=83=80=EC=9E=84=20=EC=95=84=EC=9B=83,=20=EB=A6=AC=EB=93=9C?= =?UTF-8?q?=20=ED=83=80=EC=9E=84=20=EC=95=84=EC=9B=83=20=EB=B0=8F=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=EC=8B=9C=20=EC=9E=AC=EC=8B=9C=EB=8F=84=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app-scheduler/build.gradle | 4 ++++ .../external/config/RestTemplateConfig.java | 24 +++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/app-scheduler/build.gradle b/app-scheduler/build.gradle index 31bc8ce..2dff959 100644 --- a/app-scheduler/build.gradle +++ b/app-scheduler/build.gradle @@ -12,4 +12,8 @@ dependencies { implementation 'io.micrometer:micrometer-registry-prometheus' implementation group: 'org.hibernate.orm', name: 'hibernate-spatial', version: '6.3.1.Final' + + implementation 'org.springframework.boot:spring-boot-starter-aop' + + implementation 'org.springframework.retry:spring-retry:2.0.6' } diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/config/RestTemplateConfig.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/config/RestTemplateConfig.java index e9d5f30..7ffd199 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/config/RestTemplateConfig.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/config/RestTemplateConfig.java @@ -1,6 +1,6 @@ package com.parkingcomestrue.external.config; -import com.parkingcomestrue.external.coordinate.CoordinateErrorHandler; +import com.parkingcomestrue.external.api.coordinate.CoordinateErrorHandler; import java.time.Duration; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; @@ -9,6 +9,9 @@ import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.retry.policy.SimpleRetryPolicy; +import org.springframework.retry.support.RetryTemplate; import org.springframework.web.client.RestTemplate; @Configuration @@ -21,18 +24,35 @@ public class RestTemplateConfig { public RestTemplate coordinateRestTemplate(RestTemplateBuilder restTemplateBuilder, @Value("${kakao.key}") String kakaoUrl) { return restTemplateBuilder + .setConnectTimeout(Duration.ofSeconds(5)) + .setReadTimeout(Duration.ofSeconds(5)) .errorHandler(new CoordinateErrorHandler()) .defaultHeader(AUTH_HEADER, kakaoUrl) + .additionalInterceptors(clientHttpRequestInterceptor()) .build(); } + private ClientHttpRequestInterceptor clientHttpRequestInterceptor() { + return (request, body, execution) -> { + RetryTemplate retryTemplate = new RetryTemplate(); + retryTemplate.setRetryPolicy(new SimpleRetryPolicy(3)); + try { + return retryTemplate.execute(context -> execution.execute(request, body)); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + }; + } + @Bean @Qualifier("parkingApiRestTemplate") public RestTemplate parkingApiRestTemplate(RestTemplateBuilder restTemplateBuilder) { return restTemplateBuilder - .setConnectTimeout(Duration.ofSeconds(30)) + .setConnectTimeout(Duration.ofSeconds(5)) + .setReadTimeout(Duration.ofSeconds(60)) .errorHandler(new ParkingApiErrorHandler()) .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_UTF8_VALUE) + .additionalInterceptors(clientHttpRequestInterceptor()) .build(); } } From 9d06db0e3dc0084757205674d8a44484b756df48 Mon Sep 17 00:00:00 2001 From: This2sho Date: Sun, 9 Jun 2024 19:24:39 +0900 Subject: [PATCH 05/18] =?UTF-8?q?feat:=20=ED=97=AC=EC=8A=A4=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../external/api/HealthChecker.java | 8 ++++++++ .../api/parkingapi/HealthCheckResponse.java | 15 +++++++++++++++ .../fake/ExceptionParkingApiService.java | 15 +++++++++++++-- .../fake/NotOfferCurrentParkingApiService.java | 17 ++++++++++++++--- .../fake/OfferCurrentParkingApiService.java | 17 ++++++++++++++--- 5 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/api/HealthChecker.java create mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/HealthCheckResponse.java diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/HealthChecker.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/HealthChecker.java new file mode 100644 index 0000000..fbacefd --- /dev/null +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/HealthChecker.java @@ -0,0 +1,8 @@ +package com.parkingcomestrue.external.api; + +import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; + +public interface HealthChecker { + + HealthCheckResponse check(); +} diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/HealthCheckResponse.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/HealthCheckResponse.java new file mode 100644 index 0000000..e892af6 --- /dev/null +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/HealthCheckResponse.java @@ -0,0 +1,15 @@ +package com.parkingcomestrue.external.api.parkingapi; + +import lombok.Getter; + +@Getter +public class HealthCheckResponse { + + boolean isHealthy; + int totalSize; + + public HealthCheckResponse(boolean isHealthy, int totalSize) { + this.isHealthy = isHealthy; + this.totalSize = totalSize; + } +} diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/fake/ExceptionParkingApiService.java b/app-scheduler/src/test/java/com/parkingcomestrue/fake/ExceptionParkingApiService.java index 0a80190..a821dc7 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/fake/ExceptionParkingApiService.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/fake/ExceptionParkingApiService.java @@ -1,8 +1,9 @@ package com.parkingcomestrue.fake; +import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; import com.parkingcomestrue.external.support.exception.SchedulerException; import com.parkingcomestrue.external.support.exception.SchedulerExceptionInformation; -import com.parkingcomestrue.external.parkingapi.ParkingApiService; +import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; import com.parkingcomestrue.common.domain.parking.Parking; import java.util.List; @@ -14,7 +15,17 @@ public boolean offerCurrentParking() { } @Override - public List read() { + public List read(int pageNumber, int size) { throw new SchedulerException(SchedulerExceptionInformation.INVALID_CONNECT); } + + @Override + public int getReadSize() { + return 0; + } + + @Override + public HealthCheckResponse check() { + return new HealthCheckResponse(false, 0); + } } diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/fake/NotOfferCurrentParkingApiService.java b/app-scheduler/src/test/java/com/parkingcomestrue/fake/NotOfferCurrentParkingApiService.java index 20cbbf4..b8af544 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/fake/NotOfferCurrentParkingApiService.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/fake/NotOfferCurrentParkingApiService.java @@ -12,7 +12,8 @@ import com.parkingcomestrue.common.domain.parking.PayType; import com.parkingcomestrue.common.domain.parking.Space; import com.parkingcomestrue.common.domain.parking.TimeUnit; -import com.parkingcomestrue.external.parkingapi.ParkingApiService; +import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; +import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -31,9 +32,9 @@ public boolean offerCurrentParking() { } @Override - public List read() { + public List read(int pageNumber, int size) { LinkedList result = new LinkedList<>(); - for (int i = 0; i < readSize; i++) { + for (int i = 0; i < size; i++) { Parking parking = new Parking( new BaseInformation("not offer parking" + i, "051-000" + i, "부산시 어딘가 " + i, Set.of(PayType.NO_INFO), @@ -51,7 +52,17 @@ public List read() { return result; } + @Override + public int getReadSize() { + return readSize; + } + public void setReadSize(int readSize) { this.readSize = readSize; } + + @Override + public HealthCheckResponse check() { + return new HealthCheckResponse(true, readSize); + } } diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/fake/OfferCurrentParkingApiService.java b/app-scheduler/src/test/java/com/parkingcomestrue/fake/OfferCurrentParkingApiService.java index 364d55a..1fa93c9 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/fake/OfferCurrentParkingApiService.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/fake/OfferCurrentParkingApiService.java @@ -12,7 +12,8 @@ import com.parkingcomestrue.common.domain.parking.PayType; import com.parkingcomestrue.common.domain.parking.Space; import com.parkingcomestrue.common.domain.parking.TimeUnit; -import com.parkingcomestrue.external.parkingapi.ParkingApiService; +import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; +import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -31,9 +32,9 @@ public boolean offerCurrentParking() { } @Override - public List read() { + public List read(int pageNumber, int size) { LinkedList result = new LinkedList<>(); - for (int i = 0; i < readSize; i++) { + for (int i = 0; i < size; i++) { Parking parking = new Parking( new BaseInformation("offer parking" + i, "02-000" + i, "서울시 어딘가 " + i, Set.of(PayType.NO_INFO), ParkingType.NO_INFO, @@ -50,7 +51,17 @@ public List read() { return result; } + @Override + public int getReadSize() { + return readSize; + } + public void setReadSize(int readSize) { this.readSize = readSize; } + + @Override + public HealthCheckResponse check() { + return new HealthCheckResponse(true, readSize); + } } From 472db6937c797f9182f28a2cb98d44489167b011 Mon Sep 17 00:00:00 2001 From: This2sho Date: Sun, 9 Jun 2024 19:28:40 +0900 Subject: [PATCH 06/18] =?UTF-8?q?feat:=20=EC=84=9C=ED=82=B7=20=EB=B8=8C?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=BB=A4=20=ED=8C=A8=ED=84=B4=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../external/api/ApiCounter.java | 59 ++++++++++++ .../external/api/CircuitBreaker.java | 17 ++++ .../external/api/CircuitBreakerAspect.java | 53 +++++++++++ .../external/api/ApiCounterTest.java | 91 +++++++++++++++++++ .../api/CircuitBreakerAspectTest.java | 56 ++++++++++++ .../fake/CircuitBreakerTestService.java | 14 +++ 6 files changed, 290 insertions(+) create mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/api/ApiCounter.java create mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/api/CircuitBreaker.java create mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/api/CircuitBreakerAspect.java create mode 100644 app-scheduler/src/test/java/com/parkingcomestrue/external/api/ApiCounterTest.java create mode 100644 app-scheduler/src/test/java/com/parkingcomestrue/external/api/CircuitBreakerAspectTest.java create mode 100644 app-scheduler/src/test/java/com/parkingcomestrue/fake/CircuitBreakerTestService.java diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/ApiCounter.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/ApiCounter.java new file mode 100644 index 0000000..7959963 --- /dev/null +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/ApiCounter.java @@ -0,0 +1,59 @@ +package com.parkingcomestrue.external.api; + +public class ApiCounter { + + private final double MIN_TOTAL_COUNT; + + private double totalCount; + private double errorCount; + private boolean isClosed; + + public ApiCounter() { + this.MIN_TOTAL_COUNT = 10; + this.totalCount = 0; + this.errorCount = 0; + this.isClosed = false; + } + + public ApiCounter(double minTotalCount) { + this.MIN_TOTAL_COUNT = minTotalCount; + this.totalCount = 0; + this.errorCount = 0; + this.isClosed = false; + } + + public synchronized void countUp() { + totalCount++; + } + + public synchronized void errorCountUp() { + totalCount++; + errorCount++; + } + + public void reset() { + totalCount = 0; + errorCount = 0; + isClosed = false; + } + + public boolean isClosed() { + return isClosed; + } + + public void close() { + isClosed = true; + } + + public boolean isErrorRateOverThan(double errorRate) { + if (totalCount < MIN_TOTAL_COUNT) { + return false; + } + double currentErrorRate = errorCount / totalCount; + return currentErrorRate >= errorRate; + } + + public double getTotalCount() { + return totalCount; + } +} diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/CircuitBreaker.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/CircuitBreaker.java new file mode 100644 index 0000000..60f4534 --- /dev/null +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/CircuitBreaker.java @@ -0,0 +1,17 @@ +package com.parkingcomestrue.external.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface CircuitBreaker { + + int minTotalCount() default 10; + double errorRate() default 0.2; + long resetTime() default 30; + TimeUnit timeUnit() default TimeUnit.MINUTES; +} diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/CircuitBreakerAspect.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/CircuitBreakerAspect.java new file mode 100644 index 0000000..17e4185 --- /dev/null +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/CircuitBreakerAspect.java @@ -0,0 +1,53 @@ +package com.parkingcomestrue.external.api; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +@Slf4j +@Aspect +@Component +public class CircuitBreakerAspect { + + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10); + private final Map map = new ConcurrentHashMap<>(); + + @Around("@annotation(annotation)") + public Object around(ProceedingJoinPoint proceedingJoinPoint, CircuitBreaker annotation) { + ApiCounter apiCounter = getApiCounter(proceedingJoinPoint, annotation.minTotalCount()); + if (apiCounter.isClosed()) { + log.warn("현재 해당 {} API는 오류로 인해 중지되었습니다.", proceedingJoinPoint.getTarget()); + return null; + } + try { + Object result = proceedingJoinPoint.proceed(); + apiCounter.countUp(); + return result; + } catch (Throwable e) { + handleError(annotation, apiCounter); + return null; + } + } + + private ApiCounter getApiCounter(ProceedingJoinPoint proceedingJoinPoint, int minTotalCount) { + Object target = proceedingJoinPoint.getTarget(); + if (!map.containsKey(target)) { + map.put(target, new ApiCounter(minTotalCount)); + } + return map.get(target); + } + + private void handleError(CircuitBreaker annotation, ApiCounter apiCounter) { + apiCounter.errorCountUp(); + if (apiCounter.isErrorRateOverThan(annotation.errorRate())) { + apiCounter.close(); + scheduler.schedule(apiCounter::reset, annotation.resetTime(), annotation.timeUnit()); + } + } +} diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/external/api/ApiCounterTest.java b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/ApiCounterTest.java new file mode 100644 index 0000000..b26bd30 --- /dev/null +++ b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/ApiCounterTest.java @@ -0,0 +1,91 @@ +package com.parkingcomestrue.external.api; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.junit.jupiter.api.Test; + +class ApiCounterTest { + + @Test + void 전체_요청이_최소_횟수를_넘고_예외가_특정_지수를_넘어가면_true를_반환한다() { + //given + ApiCounter apiCounter = new ApiCounter(); + + for (int i = 0; i < 8; i++) { + apiCounter.countUp(); + } + for (int i = 0; i < 2; i++) { + apiCounter.errorCountUp(); + } + + //when + boolean actual = apiCounter.isErrorRateOverThan(0.2); + + //then + assertThat(actual).isTrue(); + } + + @Test + void 여러_스레드에서도_전체_요청이_최소_횟수를_넘고_예외가_특정_지수를_넘어가면_true를_반환한다() throws InterruptedException { + //given + ExecutorService executorService = Executors.newFixedThreadPool(30); + ApiCounter apiCounter = new ApiCounter(); + int threadCount = 1000; + CountDownLatch latch = new CountDownLatch(threadCount); + + //when + for (int i = 0; i < threadCount; i++) { + if (i % 10 == 0 || i % 10 == 1) { + executorService.submit(() -> { + try { + apiCounter.errorCountUp(); + } finally { + latch.countDown(); + } + }); + continue; + } + executorService.submit(() -> { + try { + apiCounter.countUp(); + } finally { + latch.countDown(); + } + }); + } + + latch.await(); + boolean actual = apiCounter.isErrorRateOverThan(0.2); + + //then + assertThat(actual).isTrue(); + } + + @Test + void 여러_스레드에서_카운트를_증가시킬수있다() throws InterruptedException { + //given + ExecutorService executorService = Executors.newFixedThreadPool(30); + ApiCounter apiCounter = new ApiCounter(); + int threadCount = 1000; + CountDownLatch latch = new CountDownLatch(threadCount); + + //when + for (int i = 0; i < threadCount; i++) { + executorService.submit(() -> { + try { + apiCounter.countUp(); + } finally { + latch.countDown(); + } + }); + } + + latch.await(); + + //then + assertThat(apiCounter.getTotalCount()).isEqualTo(threadCount); + } +} diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/external/api/CircuitBreakerAspectTest.java b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/CircuitBreakerAspectTest.java new file mode 100644 index 0000000..1704c8e --- /dev/null +++ b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/CircuitBreakerAspectTest.java @@ -0,0 +1,56 @@ +package com.parkingcomestrue.external.api; + +import com.parkingcomestrue.fake.CircuitBreakerTestService; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class CircuitBreakerAspectTest { + + /** + * 요청 중 20%의 예외가 발생하면 api 요청 잠김 + * 잠긴 후, 2초 후에 다시 요청보내지도록 reset + */ + @Autowired + private CircuitBreakerTestService service; + + private boolean[] isExecuted = {false, false}; + private final long SECOND = 1000; + + @Test + void 서비스에_에러가_특정_지수를_넘으면_요청이_잠긴다() { + //given + for (int i = 0; i < 8; i++) { + service.call(() -> {}); + } + for (int i = 0; i < 2; i++) { + service.call(() -> {throw new RuntimeException();}); + } + + //when + service.call(() -> {isExecuted[0] = true;}); + + //then + Assertions.assertThat(isExecuted[0]).isFalse(); + } + + @Test + void 서비스가_잠긴후_특정시간이_지나면_다시_요청을_보낼수있다() throws InterruptedException { + //given + for (int i = 0; i < 8; i++) { + service.call(() -> {}); + } + for (int i = 0; i < 2; i++) { + service.call(() -> {throw new RuntimeException();}); + } + Thread.sleep(2 * SECOND); + + //when + service.call(() -> {isExecuted[1] = true;}); + + //then + Assertions.assertThat(isExecuted[1]).isTrue(); + } +} diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/fake/CircuitBreakerTestService.java b/app-scheduler/src/test/java/com/parkingcomestrue/fake/CircuitBreakerTestService.java new file mode 100644 index 0000000..becd42c --- /dev/null +++ b/app-scheduler/src/test/java/com/parkingcomestrue/fake/CircuitBreakerTestService.java @@ -0,0 +1,14 @@ +package com.parkingcomestrue.fake; + +import com.parkingcomestrue.external.api.CircuitBreaker; +import java.util.concurrent.TimeUnit; +import org.springframework.stereotype.Service; + +@Service +public class CircuitBreakerTestService { + + @CircuitBreaker(resetTime = 2, timeUnit = TimeUnit.SECONDS) + public void call(Runnable runnable) { + runnable.run(); + } +} From d3391e639f6fc8213a0d766ed7536efec0d36544 Mon Sep 17 00:00:00 2001 From: This2sho Date: Sun, 9 Jun 2024 19:31:34 +0900 Subject: [PATCH 07/18] =?UTF-8?q?feat:=20=ED=97=AC=EC=8A=A4=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20api=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coordinate/CoordinateApiService.java | 26 ++++++++++++++----- .../coordinate/CoordinateErrorHandler.java | 2 +- .../coordinate/dto/CoordinateResponse.java | 2 +- .../pusan/PusanPublicParkingAdapter.java | 2 +- .../pusan/PusanPublicParkingResponse.java | 11 +++++++- .../seoul/SeoulPublicParkingAdapter.java | 2 +- .../seoul/SeoulPublicParkingResponse.java | 13 +++++++++- .../coordinate/CoordinateApiServiceTest.java | 4 +-- .../korea/KoreaParkingAdapterTest.java | 4 ++- .../pusan/PusanPublicParkingAdapterTest.java | 4 ++- .../seoul/SeoulPublicParkingAdapterTest.java | 4 ++- 11 files changed, 56 insertions(+), 18 deletions(-) rename app-scheduler/src/main/java/com/parkingcomestrue/external/{ => api}/coordinate/CoordinateApiService.java (64%) rename app-scheduler/src/main/java/com/parkingcomestrue/external/{ => api}/coordinate/CoordinateErrorHandler.java (94%) rename app-scheduler/src/main/java/com/parkingcomestrue/external/{ => api}/coordinate/dto/CoordinateResponse.java (94%) rename app-scheduler/src/main/java/com/parkingcomestrue/external/{ => api}/parkingapi/pusan/PusanPublicParkingAdapter.java (98%) rename app-scheduler/src/main/java/com/parkingcomestrue/external/{ => api}/parkingapi/pusan/PusanPublicParkingResponse.java (89%) rename app-scheduler/src/main/java/com/parkingcomestrue/external/{ => api}/parkingapi/seoul/SeoulPublicParkingAdapter.java (99%) rename app-scheduler/src/main/java/com/parkingcomestrue/external/{ => api}/parkingapi/seoul/SeoulPublicParkingResponse.java (84%) rename app-scheduler/src/test/java/com/parkingcomestrue/external/{ => api}/coordinate/CoordinateApiServiceTest.java (92%) rename app-scheduler/src/test/java/com/parkingcomestrue/external/{ => api}/parkingapi/korea/KoreaParkingAdapterTest.java (95%) rename app-scheduler/src/test/java/com/parkingcomestrue/external/{ => api}/parkingapi/pusan/PusanPublicParkingAdapterTest.java (94%) rename app-scheduler/src/test/java/com/parkingcomestrue/external/{ => api}/parkingapi/seoul/SeoulPublicParkingAdapterTest.java (94%) diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/coordinate/CoordinateApiService.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/coordinate/CoordinateApiService.java similarity index 64% rename from app-scheduler/src/main/java/com/parkingcomestrue/external/coordinate/CoordinateApiService.java rename to app-scheduler/src/main/java/com/parkingcomestrue/external/api/coordinate/CoordinateApiService.java index d1d8d3b..4d9041c 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/coordinate/CoordinateApiService.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/coordinate/CoordinateApiService.java @@ -1,8 +1,9 @@ -package com.parkingcomestrue.external.coordinate; +package com.parkingcomestrue.external.api.coordinate; import com.parkingcomestrue.common.domain.parking.Location; -import com.parkingcomestrue.external.coordinate.dto.CoordinateResponse; -import com.parkingcomestrue.external.coordinate.dto.CoordinateResponse.ExactLocation; +import com.parkingcomestrue.external.api.HealthChecker; +import com.parkingcomestrue.external.api.coordinate.dto.CoordinateResponse; +import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -13,7 +14,7 @@ import org.springframework.web.util.UriComponentsBuilder; @Component -public class CoordinateApiService { +public class CoordinateApiService implements HealthChecker { private static final String KAKAO_URL = "https://dapi.kakao.com/v2/local/search/address.json"; @@ -32,12 +33,12 @@ public Location extractLocationByAddress(String address, Location location) { return location; } - ExactLocation exactLocation = getExactLocation(result); + CoordinateResponse.ExactLocation exactLocation = getExactLocation(result); return Location.of(exactLocation.getLongitude(), exactLocation.getLatitude()); } - private ExactLocation getExactLocation(ResponseEntity result) { - List exactLocations = result.getBody().getExactLocations(); + private CoordinateResponse.ExactLocation getExactLocation(ResponseEntity result) { + List exactLocations = result.getBody().getExactLocations(); return exactLocations.get(0); } @@ -59,4 +60,15 @@ private boolean isEmptyResultData(ResponseEntity result) { Integer matchingDataCount = result.getBody().getMeta().getTotalCount(); return matchingDataCount == 0; } + + @Override + public HealthCheckResponse check() { + UriComponents uriComponents = makeCompleteUri("health check"); + ResponseEntity response = connect(uriComponents); + return new HealthCheckResponse(isHealthy(response), 1); + } + + private boolean isHealthy(ResponseEntity response) { + return response.getStatusCode().is2xxSuccessful(); + } } diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/coordinate/CoordinateErrorHandler.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/coordinate/CoordinateErrorHandler.java similarity index 94% rename from app-scheduler/src/main/java/com/parkingcomestrue/external/coordinate/CoordinateErrorHandler.java rename to app-scheduler/src/main/java/com/parkingcomestrue/external/api/coordinate/CoordinateErrorHandler.java index 83114fa..d94e2bb 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/coordinate/CoordinateErrorHandler.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/coordinate/CoordinateErrorHandler.java @@ -1,4 +1,4 @@ -package com.parkingcomestrue.external.coordinate; +package com.parkingcomestrue.external.api.coordinate; import com.parkingcomestrue.external.support.exception.SchedulerException; import com.parkingcomestrue.external.support.exception.SchedulerExceptionInformation; diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/coordinate/dto/CoordinateResponse.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/coordinate/dto/CoordinateResponse.java similarity index 94% rename from app-scheduler/src/main/java/com/parkingcomestrue/external/coordinate/dto/CoordinateResponse.java rename to app-scheduler/src/main/java/com/parkingcomestrue/external/api/coordinate/dto/CoordinateResponse.java index b91f283..96555d8 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/coordinate/dto/CoordinateResponse.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/coordinate/dto/CoordinateResponse.java @@ -1,4 +1,4 @@ -package com.parkingcomestrue.external.coordinate.dto; +package com.parkingcomestrue.external.api.coordinate.dto; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/pusan/PusanPublicParkingAdapter.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingAdapter.java similarity index 98% rename from app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/pusan/PusanPublicParkingAdapter.java rename to app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingAdapter.java index 70aa27c..9881d1f 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/pusan/PusanPublicParkingAdapter.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingAdapter.java @@ -1,4 +1,4 @@ -package com.parkingcomestrue.external.parkingapi.pusan; +package com.parkingcomestrue.external.api.parkingapi.pusan; import com.parkingcomestrue.common.domain.parking.BaseInformation; import com.parkingcomestrue.common.domain.parking.Fee; diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/pusan/PusanPublicParkingResponse.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingResponse.java similarity index 89% rename from app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/pusan/PusanPublicParkingResponse.java rename to app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingResponse.java index 48d3142..6c74024 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/pusan/PusanPublicParkingResponse.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingResponse.java @@ -1,4 +1,4 @@ -package com.parkingcomestrue.external.parkingapi.pusan; +package com.parkingcomestrue.external.api.parkingapi.pusan; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @@ -14,12 +14,21 @@ public class PusanPublicParkingResponse { @Getter @JsonIgnoreProperties(ignoreUnknown = true) public static class ParkingInfo { + + private Header header; private Body body; + @Getter + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Header { + private String resultCode; + } + @Getter @JsonIgnoreProperties(ignoreUnknown = true) public static class Body { private Items items; + private int totalCount; } @Getter diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/seoul/SeoulPublicParkingAdapter.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingAdapter.java similarity index 99% rename from app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/seoul/SeoulPublicParkingAdapter.java rename to app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingAdapter.java index 878760c..127711c 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/seoul/SeoulPublicParkingAdapter.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingAdapter.java @@ -1,4 +1,4 @@ -package com.parkingcomestrue.external.parkingapi.seoul; +package com.parkingcomestrue.external.api.parkingapi.seoul; import com.parkingcomestrue.common.domain.parking.BaseInformation; import com.parkingcomestrue.common.domain.parking.Fee; diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/seoul/SeoulPublicParkingResponse.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingResponse.java similarity index 84% rename from app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/seoul/SeoulPublicParkingResponse.java rename to app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingResponse.java index 9bf9de0..425b715 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/seoul/SeoulPublicParkingResponse.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingResponse.java @@ -1,4 +1,4 @@ -package com.parkingcomestrue.external.parkingapi.seoul; +package com.parkingcomestrue.external.api.parkingapi.seoul; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @@ -17,9 +17,20 @@ public class SeoulPublicParkingResponse { @JsonIgnoreProperties(ignoreUnknown = true) public static class ParkingInfo { + @JsonProperty("RESULT") + private Result result; + @JsonProperty("row") private List rows; + @Getter + @JsonNaming(value = PropertyNamingStrategies.UpperSnakeCaseStrategy.class) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Result { + + private String code; + } + @Getter @JsonNaming(value = PropertyNamingStrategies.UpperSnakeCaseStrategy.class) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/external/coordinate/CoordinateApiServiceTest.java b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/coordinate/CoordinateApiServiceTest.java similarity index 92% rename from app-scheduler/src/test/java/com/parkingcomestrue/external/coordinate/CoordinateApiServiceTest.java rename to app-scheduler/src/test/java/com/parkingcomestrue/external/api/coordinate/CoordinateApiServiceTest.java index e1cb42a..52528d7 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/external/coordinate/CoordinateApiServiceTest.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/coordinate/CoordinateApiServiceTest.java @@ -1,4 +1,4 @@ -package com.parkingcomestrue.external.coordinate; +package com.parkingcomestrue.external.api.coordinate; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; @@ -6,7 +6,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; -import com.parkingcomestrue.external.coordinate.dto.CoordinateResponse; +import com.parkingcomestrue.external.api.coordinate.dto.CoordinateResponse; import com.parkingcomestrue.common.domain.parking.Location; import java.util.Collections; import org.junit.jupiter.api.Test; diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/external/parkingapi/korea/KoreaParkingAdapterTest.java b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingAdapterTest.java similarity index 95% rename from app-scheduler/src/test/java/com/parkingcomestrue/external/parkingapi/korea/KoreaParkingAdapterTest.java rename to app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingAdapterTest.java index 4cdd1d8..1bf7116 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/external/parkingapi/korea/KoreaParkingAdapterTest.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingAdapterTest.java @@ -1,4 +1,4 @@ -package com.parkingcomestrue.external.parkingapi.korea; +package com.parkingcomestrue.external.api.parkingapi.korea; import com.fasterxml.jackson.databind.ObjectMapper; import com.parkingcomestrue.common.domain.parking.Fee; @@ -8,6 +8,8 @@ import com.parkingcomestrue.common.domain.parking.PayType; import com.parkingcomestrue.common.domain.parking.TimeInfo; import com.parkingcomestrue.common.domain.parking.TimeUnit; +import com.parkingcomestrue.external.api.parkingapi.korea.KoreaParkingAdapter; +import com.parkingcomestrue.external.api.parkingapi.korea.KoreaParkingResponse; import java.io.File; import java.io.IOException; import org.assertj.core.api.SoftAssertions; diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/external/parkingapi/pusan/PusanPublicParkingAdapterTest.java b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingAdapterTest.java similarity index 94% rename from app-scheduler/src/test/java/com/parkingcomestrue/external/parkingapi/pusan/PusanPublicParkingAdapterTest.java rename to app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingAdapterTest.java index e312053..4479a43 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/external/parkingapi/pusan/PusanPublicParkingAdapterTest.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingAdapterTest.java @@ -1,4 +1,4 @@ -package com.parkingcomestrue.external.parkingapi.pusan; +package com.parkingcomestrue.external.api.parkingapi.pusan; import com.fasterxml.jackson.databind.ObjectMapper; import com.parkingcomestrue.common.domain.parking.Fee; @@ -8,6 +8,8 @@ import com.parkingcomestrue.common.domain.parking.PayType; import com.parkingcomestrue.common.domain.parking.TimeInfo; import com.parkingcomestrue.common.domain.parking.TimeUnit; +import com.parkingcomestrue.external.api.parkingapi.pusan.PusanPublicParkingAdapter; +import com.parkingcomestrue.external.api.parkingapi.pusan.PusanPublicParkingResponse; import java.io.File; import java.io.IOException; import org.assertj.core.api.SoftAssertions; diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/external/parkingapi/seoul/SeoulPublicParkingAdapterTest.java b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingAdapterTest.java similarity index 94% rename from app-scheduler/src/test/java/com/parkingcomestrue/external/parkingapi/seoul/SeoulPublicParkingAdapterTest.java rename to app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingAdapterTest.java index 075fa10..7845979 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/external/parkingapi/seoul/SeoulPublicParkingAdapterTest.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingAdapterTest.java @@ -1,4 +1,4 @@ -package com.parkingcomestrue.external.parkingapi.seoul; +package com.parkingcomestrue.external.api.parkingapi.seoul; import com.fasterxml.jackson.databind.ObjectMapper; import com.parkingcomestrue.common.domain.parking.Fee; @@ -8,6 +8,8 @@ import com.parkingcomestrue.common.domain.parking.PayType; import com.parkingcomestrue.common.domain.parking.TimeInfo; import com.parkingcomestrue.common.domain.parking.TimeUnit; +import com.parkingcomestrue.external.api.parkingapi.seoul.SeoulPublicParkingAdapter; +import com.parkingcomestrue.external.api.parkingapi.seoul.SeoulPublicParkingResponse; import java.io.File; import java.io.IOException; import java.time.LocalTime; From 7e783ef86d63114da3d272eeeef864edce1e36f5 Mon Sep 17 00:00:00 2001 From: This2sho Date: Sun, 9 Jun 2024 19:32:42 +0900 Subject: [PATCH 08/18] =?UTF-8?q?feat:=20=EC=84=9C=ED=82=B7=20=EB=B8=8C?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=BB=A4=20=EC=96=B4=EB=85=B8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/parkingapi/ParkingApiService.java | 16 ++++ .../korea/KoreaParkingApiService.java | 52 ++++++------ .../pusan/PusanPublicParkingApiService.java | 37 ++++++--- .../seoul/SeoulPublicParkingApiService.java | 80 +++++++++++++++++++ .../parkingapi/ParkingApiService.java | 13 --- .../seoul/SeoulPublicParkingApiService.java | 72 ----------------- .../scheduler/ParkingUpdateScheduler.java | 64 ++++++++++++--- .../scheduler/ParkingUpdateSchedulerTest.java | 12 +-- .../fake/FakeCoordinateApiService.java | 2 +- .../fake/FakeParkingBatchRepository.java | 48 +++++++++++ 10 files changed, 255 insertions(+), 141 deletions(-) create mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/ParkingApiService.java rename app-scheduler/src/main/java/com/parkingcomestrue/external/{ => api}/parkingapi/pusan/PusanPublicParkingApiService.java (58%) create mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingApiService.java delete mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/ParkingApiService.java delete mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/seoul/SeoulPublicParkingApiService.java create mode 100644 app-scheduler/src/test/java/com/parkingcomestrue/fake/FakeParkingBatchRepository.java diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/ParkingApiService.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/ParkingApiService.java new file mode 100644 index 0000000..63229c5 --- /dev/null +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/ParkingApiService.java @@ -0,0 +1,16 @@ +package com.parkingcomestrue.external.api.parkingapi; + +import com.parkingcomestrue.common.domain.parking.Parking; +import com.parkingcomestrue.external.api.HealthChecker; +import java.util.List; + +public interface ParkingApiService extends HealthChecker { + + default boolean offerCurrentParking() { + return false; + } + + List read(int pageNumber, int size); + + int getReadSize(); +} diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingApiService.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingApiService.java index 00efd10..ee5c12c 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingApiService.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingApiService.java @@ -1,15 +1,11 @@ package com.parkingcomestrue.external.api.parkingapi.korea; import com.parkingcomestrue.common.domain.parking.Parking; -import com.parkingcomestrue.external.api.AsyncApiExecutor; +import com.parkingcomestrue.external.api.CircuitBreaker; +import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; import java.net.URI; -import java.util.HashSet; import java.util.List; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; @@ -38,32 +34,19 @@ public KoreaParkingApiService(KoreaParkingAdapter adapter, } @Override - public List read() throws Exception { - int pageNumber = 1; - KoreaParkingResponse response = call(pageNumber++, SIZE).getBody(); - int lastPageNumber = response.getResponse().getBody().getTotalCount() / SIZE + 1; - - Set result = new HashSet<>(lastPageNumber); - result.add(response); - - List> apis = Stream.iterate(pageNumber, i -> i <= lastPageNumber, - i -> i + 1) - .map(i -> AsyncApiExecutor.executeAsync(() -> call(i, SIZE).getBody())) - .toList(); - - Set responses = apis.stream() - .map(CompletableFuture::join) - .collect(Collectors.toSet()); - - result.addAll(responses); + @CircuitBreaker + public List read(int pageNumber, int size) { + ResponseEntity response = call(pageNumber, size); + return adapter.convert(response.getBody()); + } - return result.stream() - .flatMap(koreaParkingResponse -> adapter.convert(koreaParkingResponse).stream()) - .toList(); + @Override + public int getReadSize() { + return SIZE; } - private ResponseEntity call(int startIndex, int size) { - URI uri = makeUri(startIndex, size); + private ResponseEntity call(int pageNumber, int size) { + URI uri = makeUri(pageNumber, size); return restTemplate.getForEntity(uri, KoreaParkingResponse.class); } @@ -77,4 +60,15 @@ private URI makeUri(int startIndex, int size) { .queryParam("type", RESULT_TYPE) .build(); } + + @Override + public HealthCheckResponse check() { + ResponseEntity response = call(1, 1); + return new HealthCheckResponse(isHealthy(response), response.getBody().getResponse().getBody().getTotalCount()); + } + + private boolean isHealthy(ResponseEntity response) { + return response.getStatusCode().is2xxSuccessful() && response.getBody().getResponse().getHeader() + .getResultCode().equals(NORMAL_RESULT_CODE); + } } diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/pusan/PusanPublicParkingApiService.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingApiService.java similarity index 58% rename from app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/pusan/PusanPublicParkingApiService.java rename to app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingApiService.java index 32162e0..9dbb75a 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/pusan/PusanPublicParkingApiService.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingApiService.java @@ -1,7 +1,9 @@ -package com.parkingcomestrue.external.parkingapi.pusan; +package com.parkingcomestrue.external.api.parkingapi.pusan; import com.parkingcomestrue.common.domain.parking.Parking; -import com.parkingcomestrue.external.parkingapi.ParkingApiService; +import com.parkingcomestrue.external.api.CircuitBreaker; +import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; +import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; import java.net.URI; import java.util.List; import org.springframework.beans.factory.annotation.Qualifier; @@ -17,6 +19,7 @@ public class PusanPublicParkingApiService implements ParkingApiService { private static final String URL = "http://apis.data.go.kr/6260000/BusanPblcPrkngInfoService/getPblcPrkngInfo"; private static final String RESULT_TYPE = "json"; private static final int SIZE = 1000; + private static String NORMAL_CODE = "00"; @Value("${pusan-public-parking-key}") private String API_KEY; @@ -31,16 +34,15 @@ public PusanPublicParkingApiService(PusanPublicParkingAdapter adapter, } @Override - public List read() throws Exception { - PusanPublicParkingResponse response = call(1, SIZE); + @CircuitBreaker + public List read(int pageNumber, int size) { + PusanPublicParkingResponse response = call(pageNumber, size).getBody(); return adapter.convert(response); } - private PusanPublicParkingResponse call(int startIndex, int size) { - URI uri = makeUri(startIndex, size); - ResponseEntity response = restTemplate.getForEntity(uri, - PusanPublicParkingResponse.class); - return response.getBody(); + private ResponseEntity call(int pageNumber, int size) { + URI uri = makeUri(pageNumber, size); + return restTemplate.getForEntity(uri, PusanPublicParkingResponse.class); } private URI makeUri(int startIndex, int size) { @@ -58,4 +60,21 @@ private URI makeUri(int startIndex, int size) { public boolean offerCurrentParking() { return true; } + + @Override + public HealthCheckResponse check() { + ResponseEntity response = call(1, 1); + return new HealthCheckResponse(isHealthy(response), + response.getBody().getGetParkingInfoDetails().getBody().getTotalCount()); + } + + private boolean isHealthy(ResponseEntity response) { + return response.getStatusCode().is2xxSuccessful() && response.getBody().getGetParkingInfoDetails().getHeader() + .getResultCode().equals(NORMAL_CODE); + } + + @Override + public int getReadSize() { + return SIZE; + } } diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingApiService.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingApiService.java new file mode 100644 index 0000000..1a970d0 --- /dev/null +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingApiService.java @@ -0,0 +1,80 @@ +package com.parkingcomestrue.external.api.parkingapi.seoul; + +import com.parkingcomestrue.external.api.CircuitBreaker; +import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; +import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; +import com.parkingcomestrue.common.domain.parking.Parking; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.List; +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.UriComponentsBuilder; + +@Component +public class SeoulPublicParkingApiService implements ParkingApiService { + + private static final String URL = "http://openapi.seoul.go.kr:8088"; + private static final String API_NAME = "GetParkingInfo"; + private static final String RESULT_TYPE = "json"; + private static final int SIZE = 1000; + private static final String NORMAL_CODE = "INFO-000"; + + @Value("${seoul-public-parking-key}") + private String API_KEY; + + private final SeoulPublicParkingAdapter adapter; + private final RestTemplate restTemplate; + + public SeoulPublicParkingApiService(SeoulPublicParkingAdapter adapter, + @Qualifier("parkingApiRestTemplate") RestTemplate restTemplate) { + this.adapter = adapter; + this.restTemplate = restTemplate; + } + + @Override + @CircuitBreaker + public List read(int pageNumber, int size) { + int startIndex = (pageNumber - 1) * size + 1; + ResponseEntity response = call(startIndex, startIndex + size - 1); + return adapter.convert(response.getBody()); + } + + private ResponseEntity call(int startIndex, int lastIndex) { + URI uri = makeUri(startIndex, lastIndex); + return restTemplate.getForEntity(uri, SeoulPublicParkingResponse.class); + } + + private URI makeUri(int startIndex, int endIndex) { + return UriComponentsBuilder + .fromHttpUrl(URL) + .pathSegment(API_KEY, RESULT_TYPE, API_NAME, String.valueOf(startIndex), String.valueOf(endIndex)) + .encode(StandardCharsets.UTF_8) + .build() + .toUri(); + } + + @Override + public boolean offerCurrentParking() { + return true; + } + + @Override + public HealthCheckResponse check() { + ResponseEntity response = call(1, 1); + return new HealthCheckResponse(isHealthy(response), SIZE * 2); + } + + private boolean isHealthy(ResponseEntity response) { + return response.getStatusCode() + .is2xxSuccessful() && response.getBody().getParkingInfo().getResult().getCode().equals(NORMAL_CODE); + } + + @Override + public int getReadSize() { + return SIZE; + } +} diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/ParkingApiService.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/ParkingApiService.java deleted file mode 100644 index 877d6a6..0000000 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/ParkingApiService.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.parkingcomestrue.external.parkingapi; - -import com.parkingcomestrue.common.domain.parking.Parking; -import java.util.List; - -public interface ParkingApiService { - - default boolean offerCurrentParking() { - return false; - } - - List read() throws Exception; -} diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/seoul/SeoulPublicParkingApiService.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/seoul/SeoulPublicParkingApiService.java deleted file mode 100644 index c009bc9..0000000 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/parkingapi/seoul/SeoulPublicParkingApiService.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.parkingcomestrue.external.parkingapi.seoul; - -import com.parkingcomestrue.external.parkingapi.ParkingApiService; -import com.parkingcomestrue.common.domain.parking.Parking; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -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.UriComponentsBuilder; - -@Component -public class SeoulPublicParkingApiService implements ParkingApiService { - - private static final String URL = "http://openapi.seoul.go.kr:8088"; - private static final String API_NAME = "GetParkingInfo"; - private static final String RESULT_TYPE = "json"; - private static final int SIZE = 1000; - - @Value("${seoul-public-parking-key}") - private String API_KEY; - - private final SeoulPublicParkingAdapter adapter; - private final RestTemplate restTemplate; - - public SeoulPublicParkingApiService(SeoulPublicParkingAdapter adapter, - @Qualifier("parkingApiRestTemplate") RestTemplate restTemplate) { - this.adapter = adapter; - this.restTemplate = restTemplate; - } - - @Override - public List read() throws Exception { - List response = call(); - return response.stream() - .map(adapter::convert) - .flatMap(Collection::stream) - .toList(); - } - - private List call() { - List result = new LinkedList<>(); - for (int i = 0; i < 2 * SIZE; i += SIZE) { - URI uri = makeUri(String.valueOf(i), String.valueOf(i + SIZE - 1)); - - ResponseEntity response = restTemplate.getForEntity(uri, - SeoulPublicParkingResponse.class); - result.add(response.getBody()); - } - return Collections.unmodifiableList(result); - } - - private URI makeUri(String startIndex, String endIndex) { - return UriComponentsBuilder - .fromHttpUrl(URL) - .pathSegment(API_KEY, RESULT_TYPE, API_NAME, startIndex, endIndex) - .encode(StandardCharsets.UTF_8) - .build() - .toUri(); - } - - @Override - public boolean offerCurrentParking() { - return true; - } -} diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java index 4b33386..bc939bf 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java @@ -1,20 +1,24 @@ package com.parkingcomestrue.external.scheduler; -import com.parkingcomestrue.external.respository.ParkingBatchRepository; import com.parkingcomestrue.common.domain.parking.Location; import com.parkingcomestrue.common.domain.parking.Parking; +import com.parkingcomestrue.external.api.AsyncApiExecutor; import com.parkingcomestrue.external.api.coordinate.CoordinateApiService; +import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; +import com.parkingcomestrue.external.respository.ParkingBatchRepository; import java.util.Collection; -import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collector; import java.util.stream.Collectors; +import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; @@ -38,20 +42,58 @@ public void autoUpdateOfferCurrentParking() { } private Map readBy(Predicate currentParkingAvailable) { + List parkingApis = filterBy(currentParkingAvailable); + Map result = new HashMap<>(); + for (ParkingApiService parkingApi : parkingApis) { + HealthCheckResponse healthCheckResponse = parkingApi.check(); + + log.info("api = {}", parkingApi); + long start = System.currentTimeMillis(); + + if (healthCheckResponse.isHealthy()) { + List>> responses = fetchParkingDataAsync( + parkingApi, healthCheckResponse.getTotalSize()); + Map response = collectParkingData(responses); + result.putAll(response); + } + + long end = System.currentTimeMillis(); + log.info("read Time = {}", end - start); + } + return result; + } + + private List filterBy(Predicate currentParkingAvailable) { return parkingApiServices.stream() .filter(currentParkingAvailable) - .map(this::read) - .flatMap(Collection::stream) - .collect(toParkingMap()); + .toList(); + } + + private List>> fetchParkingDataAsync(ParkingApiService parkingApi, int totalSize) { + int readSize = parkingApi.getReadSize(); + int lastPageNumber = calculateLastPageNumber(totalSize, readSize); + + return Stream.iterate(1, i -> i <= lastPageNumber, i -> i + 1) + .map(i -> AsyncApiExecutor.executeAsync(() -> parkingApi.read(i, readSize))) + .toList(); } - private List read(ParkingApiService parkingApiService) { - try { - return parkingApiService.read(); - } catch (Exception e) { - log.warn("Error while converting {} to Parking {}", parkingApiService.getClass(), e.getMessage()); - return Collections.emptyList(); + private int calculateLastPageNumber(int totalSize, int readSize) { + int lastPageNumber = totalSize / readSize; + if (totalSize % readSize == 0) { + return lastPageNumber; } + return lastPageNumber + 1; + } + + private Map collectParkingData(List>> responses) { + List> parkingLots = responses.stream() + .map(CompletableFuture::join) + .toList(); + + return parkingLots.stream() + .flatMap(Collection::stream) + .collect(toParkingMap()); } private Collector> toParkingMap() { diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/external/scheduler/ParkingUpdateSchedulerTest.java b/app-scheduler/src/test/java/com/parkingcomestrue/external/scheduler/ParkingUpdateSchedulerTest.java index ba9a9ff..14fe90f 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/external/scheduler/ParkingUpdateSchedulerTest.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/external/scheduler/ParkingUpdateSchedulerTest.java @@ -1,20 +1,20 @@ package com.parkingcomestrue.external.scheduler; -import com.parkingcomestrue.external.coordinate.CoordinateApiService; +import com.parkingcomestrue.external.api.coordinate.CoordinateApiService; import com.parkingcomestrue.fake.ExceptionParkingApiService; import com.parkingcomestrue.fake.FakeCoordinateApiService; +import com.parkingcomestrue.fake.FakeParkingBatchRepository; import com.parkingcomestrue.fake.NotOfferCurrentParkingApiService; import com.parkingcomestrue.fake.OfferCurrentParkingApiService; import java.util.List; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import repository.BasicParkingRepository; class ParkingUpdateSchedulerTest { - private final BasicParkingRepository parkingRepository = new BasicParkingRepository(); + private final FakeParkingBatchRepository parkingRepository = new FakeParkingBatchRepository(); private final CoordinateApiService coordinateService = new FakeCoordinateApiService(); @DisplayName("실시간 주차 대수를 제공하는 API에서 주차장이 0~4까지 저장되어 있는 상태에서 0~9까지 주차장을 읽어와 업데이트한다.") @@ -22,7 +22,7 @@ class ParkingUpdateSchedulerTest { void autoUpdateOfferCurrentParking() { //given OfferCurrentParkingApiService offerCurrentParkingApiService = new OfferCurrentParkingApiService(5); - parkingRepository.saveAll(offerCurrentParkingApiService.read()); + parkingRepository.saveAll(offerCurrentParkingApiService.read(0, offerCurrentParkingApiService.getReadSize())); int readSize = 10; offerCurrentParkingApiService.setReadSize(readSize); @@ -45,7 +45,7 @@ void autoUpdateNotOfferCurrentParking() { //given NotOfferCurrentParkingApiService notOfferCurrentParkingApiService = new NotOfferCurrentParkingApiService( 5); - parkingRepository.saveAll(notOfferCurrentParkingApiService.read()); + parkingRepository.saveAll(notOfferCurrentParkingApiService.read(0, notOfferCurrentParkingApiService.getReadSize())); int readSize = 10; notOfferCurrentParkingApiService.setReadSize(readSize); @@ -69,7 +69,7 @@ void notAffectBetweenOfferAndNotOfferCurrentParking() { OfferCurrentParkingApiService offerCurrentParkingApiService = new OfferCurrentParkingApiService(5); NotOfferCurrentParkingApiService notOfferCurrentParkingApiService = new NotOfferCurrentParkingApiService( 5); - parkingRepository.saveAll(offerCurrentParkingApiService.read()); + parkingRepository.saveAll(offerCurrentParkingApiService.read(0, offerCurrentParkingApiService.getReadSize())); int readSize = 10; notOfferCurrentParkingApiService.setReadSize(readSize); diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/fake/FakeCoordinateApiService.java b/app-scheduler/src/test/java/com/parkingcomestrue/fake/FakeCoordinateApiService.java index 567ecf5..4744073 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/fake/FakeCoordinateApiService.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/fake/FakeCoordinateApiService.java @@ -1,6 +1,6 @@ package com.parkingcomestrue.fake; -import com.parkingcomestrue.external.coordinate.CoordinateApiService; +import com.parkingcomestrue.external.api.coordinate.CoordinateApiService; import com.parkingcomestrue.common.domain.parking.Location; public class FakeCoordinateApiService extends CoordinateApiService { diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/fake/FakeParkingBatchRepository.java b/app-scheduler/src/test/java/com/parkingcomestrue/fake/FakeParkingBatchRepository.java new file mode 100644 index 0000000..35bce49 --- /dev/null +++ b/app-scheduler/src/test/java/com/parkingcomestrue/fake/FakeParkingBatchRepository.java @@ -0,0 +1,48 @@ +package com.parkingcomestrue.fake; + +import com.parkingcomestrue.external.respository.ParkingBatchRepository; +import com.parkingcomestrue.common.domain.parking.Parking; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.locationtech.jts.geom.Point; +import repository.BasicParkingRepository; + +public class FakeParkingBatchRepository implements ParkingBatchRepository { + + BasicParkingRepository parkingRepository = new BasicParkingRepository(); + + @Override + public void saveWithBatch(List parkingLots) { + parkingRepository.saveAll(parkingLots); + } + + @Override + public Optional findById(Long id) { + return parkingRepository.findById(id); + } + + @Override + public List findAroundParkingLots(Point point, int radius) { + return parkingRepository.findAroundParkingLots(point, radius); + } + + @Override + public List findAroundParkingLotsOrderByDistance(Point point, int radius) { + return parkingRepository.findAroundParkingLotsOrderByDistance(point, radius); + } + + @Override + public Set findAllByBaseInformationNameIn(Set parkingNames) { + return parkingRepository.findAllByBaseInformationNameIn(parkingNames); + } + + @Override + public void saveAll(Iterable parkingLots) { + parkingRepository.saveAll(parkingLots); + } + + public int count() { + return parkingRepository.count(); + } +} From 59b7bfd875fbb463cd9fcbd9ee472e9960147186 Mon Sep 17 00:00:00 2001 From: This2sho Date: Sun, 9 Jun 2024 19:39:53 +0900 Subject: [PATCH 09/18] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=9A=A9=20log=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../external/scheduler/ParkingUpdateScheduler.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java index bc939bf..21ed157 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java @@ -46,19 +46,12 @@ private Map readBy(Predicate currentParkingA Map result = new HashMap<>(); for (ParkingApiService parkingApi : parkingApis) { HealthCheckResponse healthCheckResponse = parkingApi.check(); - - log.info("api = {}", parkingApi); - long start = System.currentTimeMillis(); - if (healthCheckResponse.isHealthy()) { List>> responses = fetchParkingDataAsync( parkingApi, healthCheckResponse.getTotalSize()); Map response = collectParkingData(responses); result.putAll(response); } - - long end = System.currentTimeMillis(); - log.info("read Time = {}", end - start); } return result; } From 90d5d671feb229b2adb9a34f8ad61f58d55e85f5 Mon Sep 17 00:00:00 2001 From: This2sho Date: Sun, 9 Jun 2024 20:15:44 +0900 Subject: [PATCH 10/18] =?UTF-8?q?test:=20thread=20sleep=20=EB=8C=80?= =?UTF-8?q?=EC=8B=A0=20future=20get=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../external/api/CircuitBreakerAspectTest.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/external/api/CircuitBreakerAspectTest.java b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/CircuitBreakerAspectTest.java index 1704c8e..a82fc94 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/external/api/CircuitBreakerAspectTest.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/CircuitBreakerAspectTest.java @@ -1,6 +1,11 @@ package com.parkingcomestrue.external.api; import com.parkingcomestrue.fake.CircuitBreakerTestService; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -17,7 +22,6 @@ class CircuitBreakerAspectTest { private CircuitBreakerTestService service; private boolean[] isExecuted = {false, false}; - private final long SECOND = 1000; @Test void 서비스에_에러가_특정_지수를_넘으면_요청이_잠긴다() { @@ -30,14 +34,14 @@ class CircuitBreakerAspectTest { } //when - service.call(() -> {isExecuted[0] = true;}); + service.call(() -> isExecuted[0] = true); //then Assertions.assertThat(isExecuted[0]).isFalse(); } @Test - void 서비스가_잠긴후_특정시간이_지나면_다시_요청을_보낼수있다() throws InterruptedException { + void 서비스가_잠긴후_특정시간이_지나면_다시_요청을_보낼수있다() throws ExecutionException, InterruptedException { //given for (int i = 0; i < 8; i++) { service.call(() -> {}); @@ -45,10 +49,12 @@ class CircuitBreakerAspectTest { for (int i = 0; i < 2; i++) { service.call(() -> {throw new RuntimeException();}); } - Thread.sleep(2 * SECOND); + ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); //when - service.call(() -> {isExecuted[1] = true;}); + ScheduledFuture future = scheduler.schedule(() -> service.call(() -> isExecuted[1] = true), 2, + TimeUnit.SECONDS); + future.get(); //then Assertions.assertThat(isExecuted[1]).isTrue(); From 78ababc2b2939b1d0e8eb198e0728f3ddd0d6e27 Mon Sep 17 00:00:00 2001 From: This2sho Date: Sun, 9 Jun 2024 20:30:59 +0900 Subject: [PATCH 11/18] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=8B=9C=20flyway=20=EC=95=88=EB=8F=8C=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fake/CircuitBreakerTestService.java | 3 ++- app-scheduler/src/test/resources/application.yml | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 app-scheduler/src/test/resources/application.yml diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/fake/CircuitBreakerTestService.java b/app-scheduler/src/test/java/com/parkingcomestrue/fake/CircuitBreakerTestService.java index becd42c..7137e30 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/fake/CircuitBreakerTestService.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/fake/CircuitBreakerTestService.java @@ -2,9 +2,10 @@ import com.parkingcomestrue.external.api.CircuitBreaker; import java.util.concurrent.TimeUnit; +import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; -@Service +@Component public class CircuitBreakerTestService { @CircuitBreaker(resetTime = 2, timeUnit = TimeUnit.SECONDS) diff --git a/app-scheduler/src/test/resources/application.yml b/app-scheduler/src/test/resources/application.yml new file mode 100644 index 0000000..1e8049a --- /dev/null +++ b/app-scheduler/src/test/resources/application.yml @@ -0,0 +1,13 @@ +spring: + profiles: + active: ${PROFILE:dev} + + flyway: + enabled: false + +# API KEY +kakao: + key: ${KAKAO_API_KEY:kakao} +seoul-public-parking-key: ${SEOUL_API_KEY:seoul} +pusan-public-parking-key: ${PUSAN_API_KEY:pusan} +korea-parking-key: ${KOREA_API_KEY:korea} From 81e989ebfc85fb29a3acb132b64d221d8f82ea98 Mon Sep 17 00:00:00 2001 From: This2sho Date: Tue, 11 Jun 2024 04:46:35 +0900 Subject: [PATCH 12/18] =?UTF-8?q?refactor:=20ExecutorService=20Bean?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../external/api/AsyncApiExecutor.java | 20 ------------------- .../external/api/AsyncApiExecutorConfig.java | 19 ++++++++++++++++++ .../scheduler/ParkingUpdateScheduler.java | 5 +++-- ...t.java => AsyncApiExecutorConfigTest.java} | 13 +++++++++--- .../scheduler/ParkingUpdateSchedulerTest.java | 19 ++++++++++++++---- 5 files changed, 47 insertions(+), 29 deletions(-) delete mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/api/AsyncApiExecutor.java create mode 100644 app-scheduler/src/main/java/com/parkingcomestrue/external/api/AsyncApiExecutorConfig.java rename app-scheduler/src/test/java/com/parkingcomestrue/external/api/{AsyncApiExecutorTest.java => AsyncApiExecutorConfigTest.java} (78%) diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/AsyncApiExecutor.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/AsyncApiExecutor.java deleted file mode 100644 index 52354c6..0000000 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/AsyncApiExecutor.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.parkingcomestrue.external.api; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.function.Supplier; - -public class AsyncApiExecutor { - - private static final ExecutorService executorService = Executors.newFixedThreadPool(100, (Runnable r) -> { - Thread thread = new Thread(r); - thread.setDaemon(true); - return thread; - } - ); - - public static CompletableFuture executeAsync(Supplier supplier) { - return CompletableFuture.supplyAsync(supplier::get, executorService); - } -} diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/AsyncApiExecutorConfig.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/AsyncApiExecutorConfig.java new file mode 100644 index 0000000..8b09875 --- /dev/null +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/AsyncApiExecutorConfig.java @@ -0,0 +1,19 @@ +package com.parkingcomestrue.external.api; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AsyncApiExecutorConfig { + + @Bean + public ExecutorService executorService() { + return Executors.newFixedThreadPool(100, (Runnable r) -> { + Thread thread = new Thread(r); + thread.setDaemon(true); + return thread; + }); + } +} diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java index 21ed157..6b473e8 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java @@ -2,7 +2,6 @@ import com.parkingcomestrue.common.domain.parking.Location; import com.parkingcomestrue.common.domain.parking.Parking; -import com.parkingcomestrue.external.api.AsyncApiExecutor; import com.parkingcomestrue.external.api.coordinate.CoordinateApiService; import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; @@ -13,6 +12,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.function.Predicate; @@ -32,6 +32,7 @@ public class ParkingUpdateScheduler { private final List parkingApiServices; private final CoordinateApiService coordinateApiService; private final ParkingBatchRepository parkingBatchRepository; + private final ExecutorService executorService; @Scheduled(cron = "0 */30 * * * *") public void autoUpdateOfferCurrentParking() { @@ -67,7 +68,7 @@ private List>> fetchParkingDataAsync(ParkingApiS int lastPageNumber = calculateLastPageNumber(totalSize, readSize); return Stream.iterate(1, i -> i <= lastPageNumber, i -> i + 1) - .map(i -> AsyncApiExecutor.executeAsync(() -> parkingApi.read(i, readSize))) + .map(i -> CompletableFuture.supplyAsync(() -> parkingApi.read(i, readSize), executorService)) .toList(); } diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/external/api/AsyncApiExecutorTest.java b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/AsyncApiExecutorConfigTest.java similarity index 78% rename from app-scheduler/src/test/java/com/parkingcomestrue/external/api/AsyncApiExecutorTest.java rename to app-scheduler/src/test/java/com/parkingcomestrue/external/api/AsyncApiExecutorConfigTest.java index ad80da2..5547394 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/external/api/AsyncApiExecutorTest.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/AsyncApiExecutorConfigTest.java @@ -1,16 +1,23 @@ package com.parkingcomestrue.external.api; -import com.parkingcomestrue.external.api.AsyncApiExecutor; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.stream.Stream; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; -class AsyncApiExecutorTest { +class AsyncApiExecutorConfigTest { private static final int MINUTE = 1000; + private final ExecutorService executorService = Executors.newFixedThreadPool(100, (Runnable r) -> { + Thread thread = new Thread(r); + thread.setDaemon(true); + return thread; + }); + @Test void executeAsync_메서드를_사용하면_100개의_스레드로_비동기_동작한다() { //given @@ -20,7 +27,7 @@ class AsyncApiExecutorTest { //when long start = System.currentTimeMillis(); List> testCalls = Stream.iterate(pageNumber, i -> i <= lastPageNumber, i -> i + 1) - .map(i -> AsyncApiExecutor.executeAsync(() -> testCall(i))) + .map(i -> CompletableFuture.supplyAsync(() -> testCall(i), executorService)) .toList(); long end = System.currentTimeMillis(); diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/external/scheduler/ParkingUpdateSchedulerTest.java b/app-scheduler/src/test/java/com/parkingcomestrue/external/scheduler/ParkingUpdateSchedulerTest.java index 14fe90f..1082c54 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/external/scheduler/ParkingUpdateSchedulerTest.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/external/scheduler/ParkingUpdateSchedulerTest.java @@ -8,6 +8,8 @@ import com.parkingcomestrue.fake.NotOfferCurrentParkingApiService; import com.parkingcomestrue.fake.OfferCurrentParkingApiService; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -16,6 +18,11 @@ class ParkingUpdateSchedulerTest { private final FakeParkingBatchRepository parkingRepository = new FakeParkingBatchRepository(); private final CoordinateApiService coordinateService = new FakeCoordinateApiService(); + private final ExecutorService executorService = Executors.newFixedThreadPool(100, (Runnable r) -> { + Thread thread = new Thread(r); + thread.setDaemon(true); + return thread; + }); @DisplayName("실시간 주차 대수를 제공하는 API에서 주차장이 0~4까지 저장되어 있는 상태에서 0~9까지 주차장을 읽어와 업데이트한다.") @Test @@ -29,7 +36,8 @@ void autoUpdateOfferCurrentParking() { ParkingUpdateScheduler scheduler = new ParkingUpdateScheduler( List.of(offerCurrentParkingApiService), coordinateService, - parkingRepository + parkingRepository, + executorService ); //when @@ -52,7 +60,8 @@ void autoUpdateNotOfferCurrentParking() { ParkingUpdateScheduler scheduler = new ParkingUpdateScheduler( List.of(notOfferCurrentParkingApiService), coordinateService, - parkingRepository + parkingRepository, + executorService ); //when @@ -76,7 +85,8 @@ void notAffectBetweenOfferAndNotOfferCurrentParking() { ParkingUpdateScheduler scheduler = new ParkingUpdateScheduler( List.of(offerCurrentParkingApiService, notOfferCurrentParkingApiService), coordinateService, - parkingRepository + parkingRepository, + executorService ); //when @@ -93,7 +103,8 @@ void autoUpdateWithExceptionApi() { ParkingUpdateScheduler scheduler = new ParkingUpdateScheduler( List.of(new OfferCurrentParkingApiService(5), new ExceptionParkingApiService()), coordinateService, - parkingRepository + parkingRepository, + executorService ); //when From dc4ae8e732d9d38d3369b5db656c8adb7330ec3e Mon Sep 17 00:00:00 2001 From: This2sho Date: Tue, 11 Jun 2024 04:47:31 +0900 Subject: [PATCH 13/18] =?UTF-8?q?refactor:=20synchronized=20=ED=82=A4?= =?UTF-8?q?=EC=9B=8C=EB=93=9C=20=EB=8C=80=EC=8B=A0=20AtomicInteger=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - close 대신 open 이라는 네이밍으로 변경 --- .../external/api/ApiCounter.java | 71 ++++++++++++------- .../external/api/CircuitBreakerAspect.java | 4 +- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/ApiCounter.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/ApiCounter.java index 7959963..1a7b250 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/ApiCounter.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/ApiCounter.java @@ -1,59 +1,76 @@ package com.parkingcomestrue.external.api; +import java.util.concurrent.atomic.AtomicInteger; + public class ApiCounter { - private final double MIN_TOTAL_COUNT; + private final int MIN_TOTAL_COUNT; - private double totalCount; - private double errorCount; - private boolean isClosed; + private AtomicInteger totalCount; + private AtomicInteger errorCount; + private boolean isOpened; public ApiCounter() { this.MIN_TOTAL_COUNT = 10; - this.totalCount = 0; - this.errorCount = 0; - this.isClosed = false; + this.totalCount = new AtomicInteger(0); + this.errorCount = new AtomicInteger(0); + this.isOpened = false; } - public ApiCounter(double minTotalCount) { + public ApiCounter(int minTotalCount) { this.MIN_TOTAL_COUNT = minTotalCount; - this.totalCount = 0; - this.errorCount = 0; - this.isClosed = false; + this.totalCount = new AtomicInteger(0); + this.errorCount = new AtomicInteger(0); + this.isOpened = false; } - public synchronized void countUp() { - totalCount++; + public void countUp() { + while (true) { + int expected = getTotalCount(); + int newValue = expected + 1; + if (totalCount.compareAndSet(expected, newValue)) { + return; + } + } } - public synchronized void errorCountUp() { - totalCount++; - errorCount++; + public void errorCountUp() { + countUp(); + while (true) { + int expected = getErrorCount(); + int newValue = expected + 1; + if (errorCount.compareAndSet(expected, newValue)) { + return; + } + } } public void reset() { - totalCount = 0; - errorCount = 0; - isClosed = false; + totalCount = new AtomicInteger(0); + errorCount = new AtomicInteger(0); + isOpened = false; } - public boolean isClosed() { - return isClosed; + public boolean isOpened() { + return isOpened; } - public void close() { - isClosed = true; + public void open() { + isOpened = true; } public boolean isErrorRateOverThan(double errorRate) { - if (totalCount < MIN_TOTAL_COUNT) { + int currentTotalCount = getTotalCount(); + int currentErrorCount = getErrorCount(); + if (currentTotalCount < MIN_TOTAL_COUNT) { return false; } - double currentErrorRate = errorCount / totalCount; + double currentErrorRate = (double) currentErrorCount / currentTotalCount; return currentErrorRate >= errorRate; } - public double getTotalCount() { - return totalCount; + public int getTotalCount() { + return totalCount.get(); } + public int getErrorCount() { return errorCount.get(); } } diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/CircuitBreakerAspect.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/CircuitBreakerAspect.java index 17e4185..b22f600 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/CircuitBreakerAspect.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/CircuitBreakerAspect.java @@ -21,7 +21,7 @@ public class CircuitBreakerAspect { @Around("@annotation(annotation)") public Object around(ProceedingJoinPoint proceedingJoinPoint, CircuitBreaker annotation) { ApiCounter apiCounter = getApiCounter(proceedingJoinPoint, annotation.minTotalCount()); - if (apiCounter.isClosed()) { + if (apiCounter.isOpened()) { log.warn("현재 해당 {} API는 오류로 인해 중지되었습니다.", proceedingJoinPoint.getTarget()); return null; } @@ -46,7 +46,7 @@ private ApiCounter getApiCounter(ProceedingJoinPoint proceedingJoinPoint, int mi private void handleError(CircuitBreaker annotation, ApiCounter apiCounter) { apiCounter.errorCountUp(); if (apiCounter.isErrorRateOverThan(annotation.errorRate())) { - apiCounter.close(); + apiCounter.open(); scheduler.schedule(apiCounter::reset, annotation.resetTime(), annotation.timeUnit()); } } From 017a847bae2f90868a080d0c4205c14e9a5907f1 Mon Sep 17 00:00:00 2001 From: This2sho Date: Tue, 11 Jun 2024 05:00:40 +0900 Subject: [PATCH 14/18] =?UTF-8?q?refactor:=20HealthCheckResponse=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../external/api/{parkingapi => }/HealthCheckResponse.java | 2 +- .../java/com/parkingcomestrue/external/api/HealthChecker.java | 2 -- .../external/api/coordinate/CoordinateApiService.java | 2 +- .../external/api/parkingapi/korea/KoreaParkingApiService.java | 2 +- .../api/parkingapi/pusan/PusanPublicParkingApiService.java | 2 +- .../api/parkingapi/seoul/SeoulPublicParkingApiService.java | 2 +- .../external/scheduler/ParkingUpdateScheduler.java | 2 +- 7 files changed, 6 insertions(+), 8 deletions(-) rename app-scheduler/src/main/java/com/parkingcomestrue/external/api/{parkingapi => }/HealthCheckResponse.java (82%) diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/HealthCheckResponse.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/HealthCheckResponse.java similarity index 82% rename from app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/HealthCheckResponse.java rename to app-scheduler/src/main/java/com/parkingcomestrue/external/api/HealthCheckResponse.java index e892af6..85da6b3 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/HealthCheckResponse.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/HealthCheckResponse.java @@ -1,4 +1,4 @@ -package com.parkingcomestrue.external.api.parkingapi; +package com.parkingcomestrue.external.api; import lombok.Getter; diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/HealthChecker.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/HealthChecker.java index fbacefd..f1da52e 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/HealthChecker.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/HealthChecker.java @@ -1,7 +1,5 @@ package com.parkingcomestrue.external.api; -import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; - public interface HealthChecker { HealthCheckResponse check(); diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/coordinate/CoordinateApiService.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/coordinate/CoordinateApiService.java index 4d9041c..3bc7ce1 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/coordinate/CoordinateApiService.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/coordinate/CoordinateApiService.java @@ -3,7 +3,7 @@ import com.parkingcomestrue.common.domain.parking.Location; import com.parkingcomestrue.external.api.HealthChecker; import com.parkingcomestrue.external.api.coordinate.dto.CoordinateResponse; -import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; +import com.parkingcomestrue.external.api.HealthCheckResponse; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingApiService.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingApiService.java index ee5c12c..876c6a0 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingApiService.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingApiService.java @@ -2,7 +2,7 @@ import com.parkingcomestrue.common.domain.parking.Parking; import com.parkingcomestrue.external.api.CircuitBreaker; -import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; +import com.parkingcomestrue.external.api.HealthCheckResponse; import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; import java.net.URI; import java.util.List; diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingApiService.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingApiService.java index 9dbb75a..9a1c26b 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingApiService.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingApiService.java @@ -2,7 +2,7 @@ import com.parkingcomestrue.common.domain.parking.Parking; import com.parkingcomestrue.external.api.CircuitBreaker; -import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; +import com.parkingcomestrue.external.api.HealthCheckResponse; import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; import java.net.URI; import java.util.List; diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingApiService.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingApiService.java index 1a970d0..1a0c0dd 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingApiService.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingApiService.java @@ -1,7 +1,7 @@ package com.parkingcomestrue.external.api.parkingapi.seoul; import com.parkingcomestrue.external.api.CircuitBreaker; -import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; +import com.parkingcomestrue.external.api.HealthCheckResponse; import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; import com.parkingcomestrue.common.domain.parking.Parking; import java.net.URI; diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java index 6b473e8..633ff68 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/scheduler/ParkingUpdateScheduler.java @@ -3,7 +3,7 @@ import com.parkingcomestrue.common.domain.parking.Location; import com.parkingcomestrue.common.domain.parking.Parking; import com.parkingcomestrue.external.api.coordinate.CoordinateApiService; -import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; +import com.parkingcomestrue.external.api.HealthCheckResponse; import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; import com.parkingcomestrue.external.respository.ParkingBatchRepository; import java.util.Collection; From 7ba4298caa3e01f7d2c10bc24843ba9c3c7cb8b1 Mon Sep 17 00:00:00 2001 From: This2sho Date: Tue, 11 Jun 2024 05:24:31 +0900 Subject: [PATCH 15/18] =?UTF-8?q?refactor:=20HealthCheckResponse=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/parkingcomestrue/fake/ExceptionParkingApiService.java | 2 +- .../parkingcomestrue/fake/NotOfferCurrentParkingApiService.java | 2 +- .../parkingcomestrue/fake/OfferCurrentParkingApiService.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/fake/ExceptionParkingApiService.java b/app-scheduler/src/test/java/com/parkingcomestrue/fake/ExceptionParkingApiService.java index a821dc7..97da6ef 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/fake/ExceptionParkingApiService.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/fake/ExceptionParkingApiService.java @@ -1,6 +1,6 @@ package com.parkingcomestrue.fake; -import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; +import com.parkingcomestrue.external.api.HealthCheckResponse; import com.parkingcomestrue.external.support.exception.SchedulerException; import com.parkingcomestrue.external.support.exception.SchedulerExceptionInformation; import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/fake/NotOfferCurrentParkingApiService.java b/app-scheduler/src/test/java/com/parkingcomestrue/fake/NotOfferCurrentParkingApiService.java index b8af544..d290703 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/fake/NotOfferCurrentParkingApiService.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/fake/NotOfferCurrentParkingApiService.java @@ -12,7 +12,7 @@ import com.parkingcomestrue.common.domain.parking.PayType; import com.parkingcomestrue.common.domain.parking.Space; import com.parkingcomestrue.common.domain.parking.TimeUnit; -import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; +import com.parkingcomestrue.external.api.HealthCheckResponse; import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; import java.util.LinkedList; import java.util.List; diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/fake/OfferCurrentParkingApiService.java b/app-scheduler/src/test/java/com/parkingcomestrue/fake/OfferCurrentParkingApiService.java index 1fa93c9..045c679 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/fake/OfferCurrentParkingApiService.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/fake/OfferCurrentParkingApiService.java @@ -12,7 +12,7 @@ import com.parkingcomestrue.common.domain.parking.PayType; import com.parkingcomestrue.common.domain.parking.Space; import com.parkingcomestrue.common.domain.parking.TimeUnit; -import com.parkingcomestrue.external.api.parkingapi.HealthCheckResponse; +import com.parkingcomestrue.external.api.HealthCheckResponse; import com.parkingcomestrue.external.api.parkingapi.ParkingApiService; import java.util.LinkedList; import java.util.List; From 270e6a27c0d8a2b6caad472a31a23b52017e8a06 Mon Sep 17 00:00:00 2001 From: This2sho Date: Thu, 13 Jun 2024 23:24:38 +0900 Subject: [PATCH 16/18] =?UTF-8?q?fix:=20=EC=A2=8C=ED=91=9C=EA=B3=84=20?= =?UTF-8?q?=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../external/respository/ParkingBulkRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBulkRepository.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBulkRepository.java index d109efc..de9e13b 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBulkRepository.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBulkRepository.java @@ -68,7 +68,7 @@ public void saveAllWithBulk(List parkingLots) { + "created_at, updated_at, " + "address, name, tel, operation_type, parking_type, pay_types, location) " + "VALUES " - + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ST_GeomFromText(?))"; + + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ST_GeomFromText(?, 4326))"; jdbcTemplate.batchUpdate( sql, From 4b85021c54686993399081b68f59f5ab9c95f848 Mon Sep 17 00:00:00 2001 From: This2sho Date: Fri, 14 Jun 2024 00:24:38 +0900 Subject: [PATCH 17/18] =?UTF-8?q?refactor:=20=EC=83=9D=EC=84=B1,=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=9D=BC=20=EC=A7=81=EC=A0=91=20=EB=84=A3?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/parkingcomestrue/common/domain/AuditingEntity.java | 6 ++++-- .../com/parkingcomestrue/common/domain/parking/Parking.java | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/domain/src/main/java/com/parkingcomestrue/common/domain/AuditingEntity.java b/domain/src/main/java/com/parkingcomestrue/common/domain/AuditingEntity.java index fb3139f..7495057 100644 --- a/domain/src/main/java/com/parkingcomestrue/common/domain/AuditingEntity.java +++ b/domain/src/main/java/com/parkingcomestrue/common/domain/AuditingEntity.java @@ -1,5 +1,6 @@ package com.parkingcomestrue.common.domain; +import jakarta.persistence.Column; import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; import java.time.LocalDateTime; @@ -14,8 +15,9 @@ public abstract class AuditingEntity { @CreatedDate - private LocalDateTime createdAt; + @Column(updatable = false) + protected LocalDateTime createdAt; @LastModifiedDate - private LocalDateTime updatedAt; + protected LocalDateTime updatedAt; } diff --git a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/Parking.java b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/Parking.java index 5935b3f..dbd29af 100644 --- a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/Parking.java +++ b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/Parking.java @@ -58,6 +58,7 @@ private Parking(Long id, BaseInformation baseInformation, Location location, Spa this.freeOperatingTime = freeOperatingTime; this.operatingTime = operatingTime; this.feePolicy = feePolicy; + this.createdAt = LocalDateTime.now(); } public Parking(BaseInformation baseInformation, Location location, Space space, @@ -68,6 +69,8 @@ public Parking(BaseInformation baseInformation, Location location, Space space, this.freeOperatingTime = freeOperatingTime; this.operatingTime = operatingTime; this.feePolicy = feePolicy; + this.createdAt = LocalDateTime.now(); + this.updatedAt = LocalDateTime.now(); } public int calculatePayOfChargeMinutes(DayParking dayParking) { @@ -87,10 +90,12 @@ public void update(Parking updated) { this.freeOperatingTime = updated.freeOperatingTime; this.operatingTime = updated.operatingTime; this.feePolicy = updated.feePolicy; + this.updatedAt = LocalDateTime.now(); } public void update(Location location) { this.location = location; + this.updatedAt = LocalDateTime.now(); } public boolean containsOperationType(Set operationTypes) { From 879a941bd0870ab4205c5f243f0a2ef8e0d689b4 Mon Sep 17 00:00:00 2001 From: This2sho Date: Fri, 14 Jun 2024 03:11:22 +0900 Subject: [PATCH 18/18] =?UTF-8?q?refactor:=20=EC=9A=B4=EC=98=81=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=ED=91=9C=ED=98=84=20=EB=B0=A9=EC=8B=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parkingapi/korea/KoreaParkingAdapter.java | 4 +- .../pusan/PusanPublicParkingAdapter.java | 4 +- .../seoul/SeoulPublicParkingAdapter.java | 4 +- .../respository/ParkingBulkRepository.java | 44 ++++++------ .../korea/KoreaParkingAdapterTest.java | 8 +-- .../pusan/PusanPublicParkingAdapterTest.java | 8 +-- .../seoul/SeoulPublicParkingAdapterTest.java | 8 +-- .../domain/parking/FreeOperatingTime.java | 67 ++++++------------- .../common/domain/parking/OperatingTime.java | 47 ++++++------- .../domain/parking/ParkingFeeCalculator.java | 6 +- .../common/domain/parking/TimeInfo.java | 17 ++++- .../infra/converter/TimeInfoConverter.java | 25 +++++++ .../mysql/V5.0.0__change_column_name.sql | 24 +++++++ .../domain/parking/FreeOperatingTimeTest.java | 6 +- .../parking/ParkingFeeCalculatorTest.java | 8 ++- .../common/domain/parking/TimeInfoTest.java | 11 +-- .../converter/TimeInfoConverterTest.java | 36 ++++++++++ 17 files changed, 195 insertions(+), 132 deletions(-) create mode 100644 domain/src/main/java/com/parkingcomestrue/common/infra/converter/TimeInfoConverter.java create mode 100644 domain/src/main/resources/db/migration/mysql/V5.0.0__change_column_name.sql create mode 100644 domain/src/test/java/com/parkingcomestrue/common/infra/converter/TimeInfoConverterTest.java diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingAdapter.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingAdapter.java index 07db341..0be879b 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingAdapter.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingAdapter.java @@ -1,5 +1,7 @@ package com.parkingcomestrue.external.api.parkingapi.korea; +import static com.parkingcomestrue.common.domain.parking.TimeInfo.MAX_END_TIME; + import com.parkingcomestrue.common.domain.parking.BaseInformation; import com.parkingcomestrue.common.domain.parking.Fee; import com.parkingcomestrue.common.domain.parking.FeePolicy; @@ -114,7 +116,7 @@ private TimeInfo toTimeInfo(String beginTime, String endTime) { private LocalTime parsingOperationTime(String time) { if (time.equals(HOURS_24)) { - return LocalTime.MAX; + return MAX_END_TIME; } try { return LocalTime.parse(time, TIME_FORMATTER); diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingAdapter.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingAdapter.java index 9881d1f..f3488f2 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingAdapter.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingAdapter.java @@ -1,5 +1,7 @@ package com.parkingcomestrue.external.api.parkingapi.pusan; +import static com.parkingcomestrue.common.domain.parking.TimeInfo.MAX_END_TIME; + import com.parkingcomestrue.common.domain.parking.BaseInformation; import com.parkingcomestrue.common.domain.parking.Fee; import com.parkingcomestrue.common.domain.parking.FeePolicy; @@ -105,7 +107,7 @@ private TimeInfo toTimeInfo(String beginTime, String endTime) { private LocalTime parsingOperationTime(String time) { if (time.equals(HOURS_24)) { - return LocalTime.MAX; + return MAX_END_TIME; } try { return LocalTime.parse(time, TIME_FORMATTER); diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingAdapter.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingAdapter.java index 127711c..cb93ed1 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingAdapter.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingAdapter.java @@ -1,5 +1,7 @@ package com.parkingcomestrue.external.api.parkingapi.seoul; +import static com.parkingcomestrue.common.domain.parking.TimeInfo.MAX_END_TIME; + import com.parkingcomestrue.common.domain.parking.BaseInformation; import com.parkingcomestrue.common.domain.parking.Fee; import com.parkingcomestrue.common.domain.parking.FeePolicy; @@ -126,7 +128,7 @@ private OperatingTime getOperatingTime(final SeoulPublicParkingResponse.ParkingI private LocalTime parsingOperationTime(String time) { if (time.equals(HOURS_24)) { - return LocalTime.MAX; + return MAX_END_TIME; } try { return LocalTime.parse(time, TIME_FORMATTER); diff --git a/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBulkRepository.java b/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBulkRepository.java index de9e13b..bdd9779 100644 --- a/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBulkRepository.java +++ b/app-scheduler/src/main/java/com/parkingcomestrue/external/respository/ParkingBulkRepository.java @@ -24,31 +24,25 @@ public class ParkingBulkRepository { ps.setInt(6, parking.getSpace().getCapacity()); ps.setInt(7, parking.getSpace().getCurrentParking()); - ps.setObject(8, parking.getOperatingTime().getHolidayBeginTime()); - ps.setObject(9, parking.getOperatingTime().getHolidayEndTime()); - ps.setObject(10, parking.getFreeOperatingTime().getHolidayBeginTime()); - ps.setObject(11, parking.getFreeOperatingTime().getHolidayEndTime()); + ps.setString(8, parking.getOperatingTime().getHolidayOperatingTime().toString()); + ps.setString(9, parking.getFreeOperatingTime().getHolidayFreeOperatingTime().toString()); - ps.setObject(12, parking.getOperatingTime().getSaturdayBeginTime()); - ps.setObject(13, parking.getOperatingTime().getSaturdayEndTime()); - ps.setObject(14, parking.getFreeOperatingTime().getSaturdayBeginTime()); - ps.setObject(15, parking.getFreeOperatingTime().getSaturdayEndTime()); + ps.setString(10, parking.getOperatingTime().getSaturdayOperatingTime().toString()); + ps.setString(11, parking.getFreeOperatingTime().getSaturdayFreeOperatingTime().toString()); - ps.setObject(16, parking.getOperatingTime().getWeekdayBeginTime()); - ps.setObject(17, parking.getOperatingTime().getWeekdayEndTime()); - ps.setObject(18, parking.getFreeOperatingTime().getWeekdayBeginTime()); - ps.setObject(19, parking.getFreeOperatingTime().getWeekdayEndTime()); + ps.setString(12, parking.getOperatingTime().getWeekdayOperatingTime().toString()); + ps.setString(13, parking.getFreeOperatingTime().getWeekdayFreeOperatingTime().toString()); - ps.setObject(20, parking.getCreatedAt()); - ps.setObject(21, parking.getUpdatedAt()); + ps.setObject(14, parking.getCreatedAt()); + ps.setObject(15, parking.getUpdatedAt()); - ps.setString(22, parking.getBaseInformation().getAddress()); - ps.setString(23, parking.getBaseInformation().getName()); - ps.setString(24, parking.getBaseInformation().getTel()); - ps.setString(25, parking.getBaseInformation().getOperationType().name()); - ps.setString(26, parking.getBaseInformation().getParkingType().name()); - ps.setString(27, parking.getBaseInformation().getPayTypesName()); - ps.setString(28, toWKT(parking.getLocation())); + ps.setString(16, parking.getBaseInformation().getAddress()); + ps.setString(17, parking.getBaseInformation().getName()); + ps.setString(18, parking.getBaseInformation().getTel()); + ps.setString(19, parking.getBaseInformation().getOperationType().name()); + ps.setString(20, parking.getBaseInformation().getParkingType().name()); + ps.setString(21, parking.getBaseInformation().getPayTypesName()); + ps.setString(22, toWKT(parking.getLocation())); }; private String toWKT(Location location) { @@ -62,13 +56,13 @@ public void saveAllWithBulk(List parkingLots) { String sql = "INSERT INTO parking " + "(base_fee, base_time_unit, extra_fee, extra_time_unit, day_maximum_fee, " + "capacity, current_parking, " - + "holiday_begin_time, holiday_end_time, holiday_free_begin_time, holiday_free_end_time, " - + "saturday_begin_time, saturday_end_time, saturday_free_begin_time, saturday_free_end_time, " - + "weekday_begin_time, weekday_end_time, weekday_free_begin_time, weekday_free_end_time, " + + "holiday_operating_time, holiday_free_operating_time, " + + "saturday_operating_time, saturday_free_operating_time, " + + "weekday_operating_time, weekday_free_operating_time, " + "created_at, updated_at, " + "address, name, tel, operation_type, parking_type, pay_types, location) " + "VALUES " - + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ST_GeomFromText(?, 4326))"; + + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ST_GeomFromText(?, 4326))"; jdbcTemplate.batchUpdate( sql, diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingAdapterTest.java b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingAdapterTest.java index 1bf7116..ca5325d 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingAdapterTest.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/korea/KoreaParkingAdapterTest.java @@ -8,8 +8,6 @@ import com.parkingcomestrue.common.domain.parking.PayType; import com.parkingcomestrue.common.domain.parking.TimeInfo; import com.parkingcomestrue.common.domain.parking.TimeUnit; -import com.parkingcomestrue.external.api.parkingapi.korea.KoreaParkingAdapter; -import com.parkingcomestrue.external.api.parkingapi.korea.KoreaParkingResponse; import java.io.File; import java.io.IOException; import org.assertj.core.api.SoftAssertions; @@ -77,9 +75,9 @@ class KoreaParkingAdapterTest { soft.assertThat(parking.getFeePolicy().getExtraTimeUnit()).isEqualTo(TimeUnit.NO_INFO); soft.assertThat(parking.getFeePolicy().getDayMaximumFee()).isEqualTo(Fee.NO_INFO); - soft.assertThat(parking.getOperatingTime().getWeekday()).isEqualTo(TimeInfo.ALL_DAY); - soft.assertThat(parking.getOperatingTime().getSaturday()).isEqualTo(TimeInfo.ALL_DAY); - soft.assertThat(parking.getOperatingTime().getHoliday()).isEqualTo(TimeInfo.ALL_DAY); + soft.assertThat(parking.getOperatingTime().getWeekdayOperatingTime()).isEqualTo(TimeInfo.ALL_DAY); + soft.assertThat(parking.getOperatingTime().getSaturdayOperatingTime()).isEqualTo(TimeInfo.ALL_DAY); + soft.assertThat(parking.getOperatingTime().getHolidayOperatingTime()).isEqualTo(TimeInfo.ALL_DAY); soft.assertThat(parking.getBaseInformation().getPayTypesDescription()).isEqualTo(PayType.CARD.getDescription()); } diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingAdapterTest.java b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingAdapterTest.java index 4479a43..317cbae 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingAdapterTest.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/pusan/PusanPublicParkingAdapterTest.java @@ -8,8 +8,6 @@ import com.parkingcomestrue.common.domain.parking.PayType; import com.parkingcomestrue.common.domain.parking.TimeInfo; import com.parkingcomestrue.common.domain.parking.TimeUnit; -import com.parkingcomestrue.external.api.parkingapi.pusan.PusanPublicParkingAdapter; -import com.parkingcomestrue.external.api.parkingapi.pusan.PusanPublicParkingResponse; import java.io.File; import java.io.IOException; import org.assertj.core.api.SoftAssertions; @@ -78,9 +76,9 @@ class PusanPublicParkingAdapterTest { soft.assertThat(parking.getFeePolicy().getExtraTimeUnit()).isEqualTo(TimeUnit.from(10)); soft.assertThat(parking.getFeePolicy().getDayMaximumFee()).isEqualTo(Fee.ZERO); - soft.assertThat(parking.getOperatingTime().getWeekday()).isEqualTo(TimeInfo.ALL_DAY); - soft.assertThat(parking.getOperatingTime().getSaturday()).isEqualTo(TimeInfo.ALL_DAY); - soft.assertThat(parking.getOperatingTime().getHoliday()).isEqualTo(TimeInfo.ALL_DAY); + soft.assertThat(parking.getOperatingTime().getWeekdayOperatingTime()).isEqualTo(TimeInfo.ALL_DAY); + soft.assertThat(parking.getOperatingTime().getSaturdayOperatingTime()).isEqualTo(TimeInfo.ALL_DAY); + soft.assertThat(parking.getOperatingTime().getHolidayOperatingTime()).isEqualTo(TimeInfo.ALL_DAY); soft.assertThat(parking.getBaseInformation().getPayTypesDescription()).isEqualTo(PayType.NO_INFO.getDescription()); } diff --git a/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingAdapterTest.java b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingAdapterTest.java index 7845979..fa49f36 100644 --- a/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingAdapterTest.java +++ b/app-scheduler/src/test/java/com/parkingcomestrue/external/api/parkingapi/seoul/SeoulPublicParkingAdapterTest.java @@ -8,8 +8,6 @@ import com.parkingcomestrue.common.domain.parking.PayType; import com.parkingcomestrue.common.domain.parking.TimeInfo; import com.parkingcomestrue.common.domain.parking.TimeUnit; -import com.parkingcomestrue.external.api.parkingapi.seoul.SeoulPublicParkingAdapter; -import com.parkingcomestrue.external.api.parkingapi.seoul.SeoulPublicParkingResponse; import java.io.File; import java.io.IOException; import java.time.LocalTime; @@ -78,9 +76,9 @@ class SeoulPublicParkingAdapterTest { soft.assertThat(parking.getFeePolicy().getExtraTimeUnit()).isEqualTo(TimeUnit.from(5)); soft.assertThat(parking.getFeePolicy().getDayMaximumFee()).isEqualTo(Fee.ZERO); - soft.assertThat(parking.getOperatingTime().getWeekday()).isEqualTo(new TimeInfo(LocalTime.of(9, 0), LocalTime.of(19, 0))); - soft.assertThat(parking.getOperatingTime().getSaturday()).isEqualTo(new TimeInfo(LocalTime.of(9, 0), LocalTime.of(15, 0))); - soft.assertThat(parking.getOperatingTime().getHoliday()).isEqualTo(new TimeInfo(LocalTime.of(0, 0), LocalTime.of(0, 0))); + soft.assertThat(parking.getOperatingTime().getWeekdayOperatingTime()).isEqualTo(new TimeInfo(LocalTime.of(9, 0), LocalTime.of(19, 0))); + soft.assertThat(parking.getOperatingTime().getSaturdayOperatingTime()).isEqualTo(new TimeInfo(LocalTime.of(9, 0), LocalTime.of(15, 0))); + soft.assertThat(parking.getOperatingTime().getHolidayOperatingTime()).isEqualTo(new TimeInfo(LocalTime.of(0, 0), LocalTime.of(0, 0))); soft.assertThat(parking.getBaseInformation().getPayTypesDescription()).isEqualTo(PayType.NO_INFO.getDescription()); } diff --git a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/FreeOperatingTime.java b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/FreeOperatingTime.java index 44dbd4f..9b2447a 100644 --- a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/FreeOperatingTime.java +++ b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/FreeOperatingTime.java @@ -1,15 +1,18 @@ package com.parkingcomestrue.common.domain.parking; -import jakarta.persistence.AttributeOverride; -import jakarta.persistence.Column; +import static com.parkingcomestrue.common.domain.parking.TimeInfo.MAX_END_TIME; + +import com.parkingcomestrue.common.infra.converter.TimeInfoConverter; +import jakarta.persistence.Convert; import jakarta.persistence.Embeddable; -import jakarta.persistence.Embedded; import java.time.LocalTime; import lombok.AccessLevel; +import lombok.Getter; import lombok.NoArgsConstructor; @NoArgsConstructor(access = AccessLevel.PROTECTED) @Embeddable +@Getter public class FreeOperatingTime { public static final FreeOperatingTime ALWAYS_PAY = new FreeOperatingTime(TimeInfo.CLOSED, TimeInfo.CLOSED, @@ -17,25 +20,19 @@ public class FreeOperatingTime { public static final FreeOperatingTime ALWAYS_FREE = new FreeOperatingTime(TimeInfo.ALL_DAY, TimeInfo.ALL_DAY, TimeInfo.ALL_DAY); - @AttributeOverride(name = "beginTime", column = @Column(name = "weekday_free_begin_time")) - @AttributeOverride(name = "endTime", column = @Column(name = "weekday_free_end_time")) - @Embedded - private TimeInfo weekday; + @Convert(converter = TimeInfoConverter.class) + private TimeInfo weekdayFreeOperatingTime; - @AttributeOverride(name = "beginTime", column = @Column(name = "saturday_free_begin_time")) - @AttributeOverride(name = "endTime", column = @Column(name = "saturday_free_end_time")) - @Embedded - private TimeInfo saturday; + @Convert(converter = TimeInfoConverter.class) + private TimeInfo saturdayFreeOperatingTime; - @AttributeOverride(name = "beginTime", column = @Column(name = "holiday_free_begin_time")) - @AttributeOverride(name = "endTime", column = @Column(name = "holiday_free_end_time")) - @Embedded - private TimeInfo holiday; + @Convert(converter = TimeInfoConverter.class) + private TimeInfo holidayFreeOperatingTime; - public FreeOperatingTime(TimeInfo weekday, TimeInfo saturday, TimeInfo holiday) { - this.weekday = weekday; - this.saturday = saturday; - this.holiday = holiday; + public FreeOperatingTime(TimeInfo weekdayFreeOperatingTime, TimeInfo saturdayFreeOperatingTime, TimeInfo holidayFreeOperatingTime) { + this.weekdayFreeOperatingTime = weekdayFreeOperatingTime; + this.saturdayFreeOperatingTime = saturdayFreeOperatingTime; + this.holidayFreeOperatingTime = holidayFreeOperatingTime; } public int calculateNonFreeUsageMinutes(DayParking dayParking) { @@ -52,12 +49,12 @@ public int calculateNonFreeUsageMinutes(DayParking dayParking) { private TimeInfo getTodayTimeInfo(DayParking dayParking) { if (dayParking.isWeekDay()) { - return weekday; + return weekdayFreeOperatingTime; } if (dayParking.isSaturday()) { - return saturday; + return saturdayFreeOperatingTime; } - return holiday; + return holidayFreeOperatingTime; } private boolean isFreeDay(TimeInfo today) { @@ -65,33 +62,9 @@ private boolean isFreeDay(TimeInfo today) { } private int calculateMinutes(LocalTime localTime) { - if (localTime.equals(LocalTime.MAX)) { + if (localTime.equals(MAX_END_TIME)) { return localTime.getHour() * 60 + localTime.getMinute() + 1; } return localTime.getHour() * 60 + localTime.getMinute(); } - - public LocalTime getWeekdayBeginTime() { - return weekday.getBeginTime(); - } - - public LocalTime getWeekdayEndTime() { - return weekday.getEndTime(); - } - - public LocalTime getSaturdayBeginTime() { - return saturday.getBeginTime(); - } - - public LocalTime getSaturdayEndTime() { - return saturday.getEndTime(); - } - - public LocalTime getHolidayBeginTime() { - return holiday.getBeginTime(); - } - - public LocalTime getHolidayEndTime() { - return holiday.getEndTime(); - } } diff --git a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/OperatingTime.java b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/OperatingTime.java index 65cdde8..5d06f9d 100644 --- a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/OperatingTime.java +++ b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/OperatingTime.java @@ -1,9 +1,8 @@ package com.parkingcomestrue.common.domain.parking; -import jakarta.persistence.AttributeOverride; -import jakarta.persistence.Column; +import com.parkingcomestrue.common.infra.converter.TimeInfoConverter; +import jakarta.persistence.Convert; import jakarta.persistence.Embeddable; -import jakarta.persistence.Embedded; import java.time.LocalTime; import lombok.AccessLevel; import lombok.Getter; @@ -17,50 +16,44 @@ public class OperatingTime { public static final OperatingTime ALWAYS_OPEN = new OperatingTime(TimeInfo.ALL_DAY, TimeInfo.ALL_DAY, TimeInfo.ALL_DAY); - @AttributeOverride(name = "beginTime", column = @Column(name = "weekday_begin_time")) - @AttributeOverride(name = "endTime", column = @Column(name = "weekday_end_time")) - @Embedded - private TimeInfo weekday; + @Convert(converter = TimeInfoConverter.class) + private TimeInfo weekdayOperatingTime; - @AttributeOverride(name = "beginTime", column = @Column(name = "saturday_begin_time")) - @AttributeOverride(name = "endTime", column = @Column(name = "saturday_end_time")) - @Embedded - private TimeInfo saturday; + @Convert(converter = TimeInfoConverter.class) + private TimeInfo saturdayOperatingTime; - @AttributeOverride(name = "beginTime", column = @Column(name = "holiday_begin_time")) - @AttributeOverride(name = "endTime", column = @Column(name = "holiday_end_time")) - @Embedded - private TimeInfo holiday; + @Convert(converter = TimeInfoConverter.class) + private TimeInfo holidayOperatingTime; - public OperatingTime(TimeInfo weekday, - TimeInfo saturday, - TimeInfo holiday) { - this.weekday = weekday; - this.saturday = saturday; - this.holiday = holiday; + public OperatingTime(TimeInfo weekdayOperatingTime, + TimeInfo saturdayOperatingTime, + TimeInfo holidayOperatingTime) { + this.weekdayOperatingTime = weekdayOperatingTime; + this.saturdayOperatingTime = saturdayOperatingTime; + this.holidayOperatingTime = holidayOperatingTime; } public LocalTime getWeekdayBeginTime() { - return weekday.getBeginTime(); + return weekdayOperatingTime.getBeginTime(); } public LocalTime getWeekdayEndTime() { - return weekday.getEndTime(); + return weekdayOperatingTime.getEndTime(); } public LocalTime getSaturdayBeginTime() { - return saturday.getBeginTime(); + return saturdayOperatingTime.getBeginTime(); } public LocalTime getSaturdayEndTime() { - return saturday.getEndTime(); + return saturdayOperatingTime.getEndTime(); } public LocalTime getHolidayBeginTime() { - return holiday.getBeginTime(); + return holidayOperatingTime.getBeginTime(); } public LocalTime getHolidayEndTime() { - return holiday.getEndTime(); + return holidayOperatingTime.getEndTime(); } } diff --git a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/ParkingFeeCalculator.java b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/ParkingFeeCalculator.java index fbf8c8d..f61f424 100644 --- a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/ParkingFeeCalculator.java +++ b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/ParkingFeeCalculator.java @@ -1,5 +1,7 @@ package com.parkingcomestrue.common.domain.parking; +import static com.parkingcomestrue.common.domain.parking.TimeInfo.MAX_END_TIME; + import java.time.LocalDateTime; import java.time.LocalTime; import java.util.ArrayList; @@ -36,7 +38,7 @@ private List separateDate(LocalDateTime beginTime, LocalDateTime end dayParkingDates.add(makeFirstDayParking(beginTime)); beginTime = beginTime.plusDays(1); while (!isSameDate(beginTime, endTime)) { - dayParkingDates.add(new DayParking(Day.from(beginTime.getDayOfWeek()), LocalTime.MIN, LocalTime.MAX)); + dayParkingDates.add(new DayParking(Day.from(beginTime.getDayOfWeek()), LocalTime.MIN, MAX_END_TIME)); beginTime = beginTime.plusDays(1); } dayParkingDates.add(makeLastDayParking(endTime)); @@ -62,7 +64,7 @@ private boolean isSameDayOfMonth(LocalDateTime beginTime, LocalDateTime endTime) } private DayParking makeFirstDayParking(LocalDateTime beginTime) { - return new DayParking(Day.from(beginTime.getDayOfWeek()), beginTime.toLocalTime(), LocalTime.MAX); + return new DayParking(Day.from(beginTime.getDayOfWeek()), beginTime.toLocalTime(), MAX_END_TIME); } private DayParking makeLastDayParking(LocalDateTime endTime) { diff --git a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/TimeInfo.java b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/TimeInfo.java index 5d209a8..be58f63 100644 --- a/domain/src/main/java/com/parkingcomestrue/common/domain/parking/TimeInfo.java +++ b/domain/src/main/java/com/parkingcomestrue/common/domain/parking/TimeInfo.java @@ -2,6 +2,7 @@ import jakarta.persistence.Embeddable; import java.time.LocalTime; +import java.time.format.DateTimeFormatter; import lombok.AccessLevel; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -14,7 +15,10 @@ public class TimeInfo { public static final TimeInfo CLOSED = new TimeInfo(LocalTime.MIN, LocalTime.MIN); - public static final TimeInfo ALL_DAY = new TimeInfo(LocalTime.MIN, LocalTime.MAX); + public static final LocalTime MAX_END_TIME = LocalTime.of(23, 59); + public static final TimeInfo ALL_DAY = new TimeInfo(LocalTime.MIN, MAX_END_TIME); + private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); + private static final String DELIMITER = "~"; private LocalTime beginTime; private LocalTime endTime; @@ -26,7 +30,7 @@ public TimeInfo(LocalTime beginTime, LocalTime endTime) { public int calculateOverlapMinutes(LocalTime beginTime, LocalTime endTime) { if (this.endTime.isBefore(this.beginTime)) { - TimeInfo today = new TimeInfo(this.beginTime, LocalTime.MAX); + TimeInfo today = new TimeInfo(this.beginTime, MAX_END_TIME); TimeInfo tomorrow = new TimeInfo(LocalTime.MIN, this.endTime); return today.calculateOverlapMinutes(beginTime, endTime) + tomorrow.calculateOverlapMinutes(beginTime, endTime); @@ -56,9 +60,16 @@ private int calculateBetweenMinutes(LocalTime beginTime, LocalTime endTime) { } private int calculateMinutes(LocalTime localTime) { - if (localTime.equals(LocalTime.MAX)) { + if (localTime.equals(MAX_END_TIME)) { return localTime.getHour() * 60 + localTime.getMinute() + 1; } return localTime.getHour() * 60 + localTime.getMinute(); } + + @Override + public String toString() { + String beginTime = this.beginTime.format(TIME_FORMATTER); + String endTime = this.endTime.format(TIME_FORMATTER); + return beginTime + DELIMITER + endTime; + } } diff --git a/domain/src/main/java/com/parkingcomestrue/common/infra/converter/TimeInfoConverter.java b/domain/src/main/java/com/parkingcomestrue/common/infra/converter/TimeInfoConverter.java new file mode 100644 index 0000000..4a99608 --- /dev/null +++ b/domain/src/main/java/com/parkingcomestrue/common/infra/converter/TimeInfoConverter.java @@ -0,0 +1,25 @@ +package com.parkingcomestrue.common.infra.converter; + +import com.parkingcomestrue.common.domain.parking.TimeInfo; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +@Converter +public class TimeInfoConverter implements AttributeConverter { + + private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); + private static final String DELIMITER = "~"; + + @Override + public String convertToDatabaseColumn(TimeInfo attribute) { + return attribute.toString(); + } + + @Override + public TimeInfo convertToEntityAttribute(String dbData) { + String[] times = dbData.split(DELIMITER); + return new TimeInfo(LocalTime.parse(times[0], TIME_FORMATTER), LocalTime.parse(times[1], TIME_FORMATTER)); + } +} diff --git a/domain/src/main/resources/db/migration/mysql/V5.0.0__change_column_name.sql b/domain/src/main/resources/db/migration/mysql/V5.0.0__change_column_name.sql new file mode 100644 index 0000000..464912c --- /dev/null +++ b/domain/src/main/resources/db/migration/mysql/V5.0.0__change_column_name.sql @@ -0,0 +1,24 @@ +ALTER TABLE parking + DROP COLUMN holiday_begin_time, + DROP COLUMN holiday_end_time, + DROP COLUMN holiday_free_begin_time, + DROP COLUMN holiday_free_end_time, + DROP COLUMN saturday_begin_time, + DROP COLUMN saturday_end_time, + DROP COLUMN saturday_free_begin_time, + DROP COLUMN saturday_free_end_time, + DROP COLUMN weekday_begin_time, + DROP COLUMN weekday_end_time, + DROP COLUMN weekday_free_begin_time, + DROP COLUMN weekday_free_end_time; + +ALTER TABLE parking + ADD COLUMN holiday_operating_time VARCHAR(20), + ADD COLUMN holiday_free_operating_time VARCHAR(20), + ADD COLUMN saturday_operating_time VARCHAR(20), + ADD COLUMN saturday_free_operating_time VARCHAR(20), + ADD COLUMN weekday_operating_time VARCHAR(20), + ADD COLUMN weekday_free_operating_time VARCHAR(20); + + + diff --git a/domain/src/test/java/com/parkingcomestrue/common/domain/parking/FreeOperatingTimeTest.java b/domain/src/test/java/com/parkingcomestrue/common/domain/parking/FreeOperatingTimeTest.java index 6d34ed6..5c5e40e 100644 --- a/domain/src/test/java/com/parkingcomestrue/common/domain/parking/FreeOperatingTimeTest.java +++ b/domain/src/test/java/com/parkingcomestrue/common/domain/parking/FreeOperatingTimeTest.java @@ -1,5 +1,7 @@ package com.parkingcomestrue.common.domain.parking; +import static com.parkingcomestrue.common.domain.parking.TimeInfo.MAX_END_TIME; + import com.parkingcomestrue.common.domain.parking.Day; import com.parkingcomestrue.common.domain.parking.DayParking; import com.parkingcomestrue.common.domain.parking.FreeOperatingTime; @@ -36,7 +38,7 @@ static Stream getFreeOperatingTime() { */ Arguments.of( FreeOperatingTime.ALWAYS_FREE, - new DayParking(Day.WEEKDAY, LocalTime.MIN, LocalTime.MAX), + new DayParking(Day.WEEKDAY, LocalTime.MIN, MAX_END_TIME), 0 ), /* @@ -58,7 +60,7 @@ static Stream getFreeOperatingTime() { TimeInfo.ALL_DAY, TimeInfo.ALL_DAY ), - new DayParking(Day.WEEKDAY, LocalTime.MIN, LocalTime.MAX), + new DayParking(Day.WEEKDAY, LocalTime.MIN, MAX_END_TIME), 1200 ), /* diff --git a/domain/src/test/java/com/parkingcomestrue/common/domain/parking/ParkingFeeCalculatorTest.java b/domain/src/test/java/com/parkingcomestrue/common/domain/parking/ParkingFeeCalculatorTest.java index d0cd954..79e96b3 100644 --- a/domain/src/test/java/com/parkingcomestrue/common/domain/parking/ParkingFeeCalculatorTest.java +++ b/domain/src/test/java/com/parkingcomestrue/common/domain/parking/ParkingFeeCalculatorTest.java @@ -1,5 +1,7 @@ package com.parkingcomestrue.common.domain.parking; +import static com.parkingcomestrue.common.domain.parking.TimeInfo.MAX_END_TIME; + import com.parkingcomestrue.common.domain.parking.BaseInformation; import com.parkingcomestrue.common.domain.parking.Fee; import com.parkingcomestrue.common.domain.parking.FeePolicy; @@ -267,7 +269,7 @@ static Stream getParkingFeeCalculator() { ), new TimeInfo( LocalTime.of(6, 0), - LocalTime.MAX + MAX_END_TIME ), TimeInfo.ALL_DAY ), @@ -325,7 +327,7 @@ static Stream getParkingFeeCalculator() { TimeInfo.ALL_DAY, new TimeInfo( LocalTime.of(4, 0), - LocalTime.MAX + MAX_END_TIME ) ), new OperatingTime(), @@ -384,7 +386,7 @@ static Stream getParkingFeeCalculator() { TimeInfo.ALL_DAY, new TimeInfo( LocalTime.of(4, 0), - LocalTime.MAX + MAX_END_TIME ) ), new OperatingTime(), diff --git a/domain/src/test/java/com/parkingcomestrue/common/domain/parking/TimeInfoTest.java b/domain/src/test/java/com/parkingcomestrue/common/domain/parking/TimeInfoTest.java index ffaefbe..2814a63 100644 --- a/domain/src/test/java/com/parkingcomestrue/common/domain/parking/TimeInfoTest.java +++ b/domain/src/test/java/com/parkingcomestrue/common/domain/parking/TimeInfoTest.java @@ -1,6 +1,7 @@ package com.parkingcomestrue.common.domain.parking; -import com.parkingcomestrue.common.domain.parking.TimeInfo; +import static com.parkingcomestrue.common.domain.parking.TimeInfo.MAX_END_TIME; + import java.time.LocalTime; import java.util.stream.Stream; import org.assertj.core.api.Assertions; @@ -26,9 +27,9 @@ static Stream getTimeInfos() { 겹침 시간 : 00:00 ~ 24:00 -> 1440분 */ Arguments.of( - new TimeInfo(LocalTime.MIN, LocalTime.MAX), + new TimeInfo(LocalTime.MIN, MAX_END_TIME), LocalTime.MIN, - LocalTime.MAX, + MAX_END_TIME, 1440), /* 무료 운영 시간 : 00:00 ~ 24:00 @@ -36,7 +37,7 @@ static Stream getTimeInfos() { 겹침 시간 : 01:30 ~ 22:11 -> 1241분 */ Arguments.of( - new TimeInfo(LocalTime.MIN, LocalTime.MAX), + new TimeInfo(LocalTime.MIN, MAX_END_TIME), LocalTime.of(1, 30), LocalTime.of(22, 11), 1241), @@ -118,7 +119,7 @@ static Stream getTimeInfos() { Arguments.of( new TimeInfo(LocalTime.of(21, 0), LocalTime.of(9, 0)), LocalTime.of(21, 0), - LocalTime.MAX, + MAX_END_TIME, 180), /* 무료 운영 시간 : 21:00 ~ 09:00 diff --git a/domain/src/test/java/com/parkingcomestrue/common/infra/converter/TimeInfoConverterTest.java b/domain/src/test/java/com/parkingcomestrue/common/infra/converter/TimeInfoConverterTest.java new file mode 100644 index 0000000..6ec22b7 --- /dev/null +++ b/domain/src/test/java/com/parkingcomestrue/common/infra/converter/TimeInfoConverterTest.java @@ -0,0 +1,36 @@ +package com.parkingcomestrue.common.infra.converter; + +import com.parkingcomestrue.common.domain.parking.TimeInfo; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class TimeInfoConverterTest { + + private TimeInfoConverter converter = new TimeInfoConverter(); + + @Test + void TimeInfo에서_컬럼으로_변경() { + //given + TimeInfo allDay = TimeInfo.ALL_DAY; + String expected = "00:00~23:59"; + + //when + String actual = converter.convertToDatabaseColumn(allDay); + + //then + Assertions.assertThat(actual).isEqualTo(expected); + } + + @Test + void 컬럼에서_TimeInfo로_변경() { + //given + String column = "00:00~23:59"; + TimeInfo expected = TimeInfo.ALL_DAY; + + //when + TimeInfo actual = converter.convertToEntityAttribute(column); + + //then + Assertions.assertThat(actual).isEqualTo(expected); + } +}