From c2f1e57f93adeb02b117c299e6cd3def57cce2da Mon Sep 17 00:00:00 2001 From: Photogrammer Date: Mon, 26 Aug 2024 15:42:41 +0900 Subject: [PATCH 01/10] =?UTF-8?q?feat:=20BusArrival=20API=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/datagg/dto/BusArrivalBodyDto.java | 38 +++++++++++++++++++ .../api/datagg/dto/BusArrivalRespDto.java | 15 ++++++++ .../api/datagg/service/BusApiService.java | 5 ++- .../datagg/service/SimpleBusApiService.java | 26 +++++++++++-- 4 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 server/src/main/java/com/talkka/server/api/datagg/dto/BusArrivalBodyDto.java create mode 100644 server/src/main/java/com/talkka/server/api/datagg/dto/BusArrivalRespDto.java diff --git a/server/src/main/java/com/talkka/server/api/datagg/dto/BusArrivalBodyDto.java b/server/src/main/java/com/talkka/server/api/datagg/dto/BusArrivalBodyDto.java new file mode 100644 index 00000000..a4592276 --- /dev/null +++ b/server/src/main/java/com/talkka/server/api/datagg/dto/BusArrivalBodyDto.java @@ -0,0 +1,38 @@ +package com.talkka.server.api.datagg.dto; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; + +import jakarta.validation.constraints.NotNull; + +@JacksonXmlRootElement(localName = "busArrivalList") +public record BusArrivalBodyDto( + @NotNull + Long stationId, // 정류소아이디 + @NotNull + Long routeId, // 노선아이디 + @NotNull + Integer locationNo1, // 첫번째차량 위치 정보 + @NotNull + Integer predictTime1, // 첫번째차량 도착예상시간 + @NotNull + Integer lowPlate1, // 첫번째차량 저상버스여부 + @NotNull + String plateNo1, // 첫번째차량 차량번호 + @NotNull + Integer remainSeatCnt1, // 첫번째차량 빈자리 수 + @NotNull + Integer locationNo2, // 두번째차량 위치 정보 + @NotNull + Integer predictTime2, // 두번째차량 도착예상시간 + @NotNull + Integer lowPlate2, // 두번째차량 저상버스여부 + @NotNull + String plateNo2, // 두번째차량 차량번호 + @NotNull + Integer remainSeatCnt2, // 두번째차량 빈자리 수 + @NotNull + Integer staOrder, // 정류소 순번 + @NotNull + String flag // 상태구분 +) { +} diff --git a/server/src/main/java/com/talkka/server/api/datagg/dto/BusArrivalRespDto.java b/server/src/main/java/com/talkka/server/api/datagg/dto/BusArrivalRespDto.java new file mode 100644 index 00000000..a3ae6775 --- /dev/null +++ b/server/src/main/java/com/talkka/server/api/datagg/dto/BusArrivalRespDto.java @@ -0,0 +1,15 @@ +package com.talkka.server.api.datagg.dto; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; + +@JacksonXmlRootElement(localName = "response") +public record BusArrivalRespDto( + @JacksonXmlProperty(localName = "comMsgHeader") Map comMsgHeader, + @JacksonXmlProperty(localName = "msgHeader") Map msgHeader, + @JacksonXmlProperty(localName = "msgBody") List msgBody +) implements PublicBusApiResp { +} \ No newline at end of file diff --git a/server/src/main/java/com/talkka/server/api/datagg/service/BusApiService.java b/server/src/main/java/com/talkka/server/api/datagg/service/BusApiService.java index aeeab52e..ec27733d 100644 --- a/server/src/main/java/com/talkka/server/api/datagg/service/BusApiService.java +++ b/server/src/main/java/com/talkka/server/api/datagg/service/BusApiService.java @@ -1,8 +1,10 @@ package com.talkka.server.api.datagg.service; import java.util.List; +import java.util.Optional; import com.talkka.server.api.core.exception.ApiClientException; +import com.talkka.server.api.datagg.dto.BusArrivalBodyDto; import com.talkka.server.api.datagg.dto.BusLocationBodyDto; import com.talkka.server.api.datagg.dto.BusRouteInfoBodyDto; import com.talkka.server.api.datagg.dto.BusRouteSearchBodyDto; @@ -17,5 +19,6 @@ public interface BusApiService { List getBusLocationInfo(String apiRouteId) throws ApiClientException; - // List getBusStationArrivalInfo(String routeId); + Optional getBusArrival(String apiRouteId, String apiStationId) throws + ApiClientException; } diff --git a/server/src/main/java/com/talkka/server/api/datagg/service/SimpleBusApiService.java b/server/src/main/java/com/talkka/server/api/datagg/service/SimpleBusApiService.java index 4d9108bf..748361cd 100644 --- a/server/src/main/java/com/talkka/server/api/datagg/service/SimpleBusApiService.java +++ b/server/src/main/java/com/talkka/server/api/datagg/service/SimpleBusApiService.java @@ -2,6 +2,7 @@ import java.net.URI; import java.util.List; +import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,6 +15,8 @@ import com.talkka.server.api.core.exception.ApiClientException; import com.talkka.server.api.datagg.config.BusApiKeyProperty; +import com.talkka.server.api.datagg.dto.BusArrivalBodyDto; +import com.talkka.server.api.datagg.dto.BusArrivalRespDto; import com.talkka.server.api.datagg.dto.BusLocationBodyDto; import com.talkka.server.api.datagg.dto.BusLocationRespDto; import com.talkka.server.api.datagg.dto.BusRouteInfoBodyDto; @@ -105,10 +108,25 @@ public List getBusLocationInfo(String apiRouteId) throws Api } } - // @Override - // public List getBusStationArrivalInfo(String routeId) { - // return null; - // } + @Override + public Optional getBusArrival(String apiRouteId, String apiStationId) throws + ApiClientException { + final String path = "/6410000/busarrivalservice/getBusArrivalItem"; + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("routeId", apiRouteId); + params.add("stationId", apiStationId); + try { + URI uri = this.getOpenApiUri(path, params); + ResponseEntity resp = restTemplate.getForEntity(uri, BusArrivalRespDto.class); + var body = resp.getBody().msgBody(); + if (body == null || body.isEmpty()) { + return Optional.empty(); + } + return Optional.of(body.get(0)); + } catch (Exception exception) { + throw new ApiClientException(exception.getMessage()); + } + } private URI getOpenApiUri(String path, MultiValueMap params) { final var builder = new DefaultUriBuilderFactory(); From 33488380b0eba9a9680d4de0f96a78bab79740bf Mon Sep 17 00:00:00 2001 From: Photogrammer Date: Mon, 26 Aug 2024 15:43:25 +0900 Subject: [PATCH 02/10] =?UTF-8?q?feat:=20CachedStorage=20=EB=B0=8F=20Memor?= =?UTF-8?q?yCachedStorage=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/common/util/CachedStorage.java | 11 ++++ .../common/util/MemoryCachedStorage.java | 52 +++++++++++++++++++ .../server/common/util/TimeExpiredValue.java | 15 ++++++ 3 files changed, 78 insertions(+) create mode 100644 server/src/main/java/com/talkka/server/common/util/CachedStorage.java create mode 100644 server/src/main/java/com/talkka/server/common/util/MemoryCachedStorage.java create mode 100644 server/src/main/java/com/talkka/server/common/util/TimeExpiredValue.java diff --git a/server/src/main/java/com/talkka/server/common/util/CachedStorage.java b/server/src/main/java/com/talkka/server/common/util/CachedStorage.java new file mode 100644 index 00000000..0f09443e --- /dev/null +++ b/server/src/main/java/com/talkka/server/common/util/CachedStorage.java @@ -0,0 +1,11 @@ +package com.talkka.server.common.util; + +import java.util.Optional; + +public interface CachedStorage { + Optional get(K key); + + void put(K key, V value); + + void remove(K key); +} diff --git a/server/src/main/java/com/talkka/server/common/util/MemoryCachedStorage.java b/server/src/main/java/com/talkka/server/common/util/MemoryCachedStorage.java new file mode 100644 index 00000000..74d41161 --- /dev/null +++ b/server/src/main/java/com/talkka/server/common/util/MemoryCachedStorage.java @@ -0,0 +1,52 @@ +package com.talkka.server.common.util; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * MemoryCachedStorage + * Redis 사용 이전에 메모리에 캐시를 저장하는 클래스 + * 시간에 따라 Expire 되지 않아 메모리가 쌓이는 문제가 발생할 수 있음. + * @param + * @param + */ +public abstract class MemoryCachedStorage implements CachedStorage { + private final Map> cache = new ConcurrentHashMap<>(); + private final Integer expireTimeSeconds; + + public MemoryCachedStorage(Integer expireTimeSeconds) { + this.expireTimeSeconds = expireTimeSeconds; + } + + @Override + public void put(K key, V value) { + TimeExpiredValue timeExpiredValue = TimeExpiredValue.create(value, + System.currentTimeMillis() + expireTimeSeconds * 1000L); + cache.put(key, timeExpiredValue); + removeExpired(); + } + + @Override + public Optional get(K key) { + TimeExpiredValue timeExpiredValue = cache.get(key); + + if (timeExpiredValue == null) { + return Optional.empty(); + } + if (timeExpiredValue.isExpired()) { + cache.remove(key); + return Optional.empty(); + } + return Optional.of(timeExpiredValue.value()); + } + + @Override + public void remove(K key) { + cache.remove(key); + } + + private void removeExpired() { + cache.entrySet().removeIf(entry -> entry.getValue().isExpired()); + } +} diff --git a/server/src/main/java/com/talkka/server/common/util/TimeExpiredValue.java b/server/src/main/java/com/talkka/server/common/util/TimeExpiredValue.java new file mode 100644 index 00000000..74f6a251 --- /dev/null +++ b/server/src/main/java/com/talkka/server/common/util/TimeExpiredValue.java @@ -0,0 +1,15 @@ +package com.talkka.server.common.util; + +public record TimeExpiredValue(long expiredTime, T value) { + public static TimeExpiredValue create(T value, long expiredTime) { + return new TimeExpiredValue<>(expiredTime, value); + } + + public static TimeExpiredValue create(T value) { + return new TimeExpiredValue<>(System.currentTimeMillis(), value); + } + + public boolean isExpired() { + return System.currentTimeMillis() > expiredTime; + } +} From 0eb13f881e6fc59081844ba32ed3a3592c53eece Mon Sep 17 00:00:00 2001 From: Photogrammer Date: Mon, 26 Aug 2024 15:44:39 +0900 Subject: [PATCH 03/10] =?UTF-8?q?feat:=20=EB=B2=84=EC=8A=A4=20=EB=8F=84?= =?UTF-8?q?=EC=B0=A9=EC=A0=95=EB=B3=B4=20Interface,=20DTO,=20Exception=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/bus/dto/BusLiveArrivalRespDto.java | 46 +++++++++++++++++++ .../server/bus/dto/BusLiveInfoRespDto.java | 18 ++++++++ .../server/bus/enums/RunningStatus.java | 13 ++++++ .../GetBusLiveArrivalInfoFailedException.java | 9 ++++ .../server/bus/service/BusArrivalService.java | 12 +++++ .../bus/service/BusLiveInfoService.java | 12 +++++ 6 files changed, 110 insertions(+) create mode 100644 server/src/main/java/com/talkka/server/bus/dto/BusLiveArrivalRespDto.java create mode 100644 server/src/main/java/com/talkka/server/bus/dto/BusLiveInfoRespDto.java create mode 100644 server/src/main/java/com/talkka/server/bus/enums/RunningStatus.java create mode 100644 server/src/main/java/com/talkka/server/bus/exception/GetBusLiveArrivalInfoFailedException.java create mode 100644 server/src/main/java/com/talkka/server/bus/service/BusArrivalService.java create mode 100644 server/src/main/java/com/talkka/server/bus/service/BusLiveInfoService.java diff --git a/server/src/main/java/com/talkka/server/bus/dto/BusLiveArrivalRespDto.java b/server/src/main/java/com/talkka/server/bus/dto/BusLiveArrivalRespDto.java new file mode 100644 index 00000000..d8a5c1a3 --- /dev/null +++ b/server/src/main/java/com/talkka/server/bus/dto/BusLiveArrivalRespDto.java @@ -0,0 +1,46 @@ +package com.talkka.server.bus.dto; + +import java.util.Optional; + +import com.talkka.server.api.datagg.dto.BusArrivalBodyDto; +import com.talkka.server.bus.enums.RunningStatus; + +import jakarta.validation.constraints.NotNull; + +public record BusLiveArrivalRespDto( + @NotNull + Integer locationNo1, // 첫번째차량 위치 정보 + @NotNull + Integer predictTime1, // 첫번째차량 도착예상시간 + @NotNull + String plateNo1, // 첫번째차량 차량번호 + @NotNull + Integer remainSeatCnt1, // 첫번째차량 빈자리 수 + @NotNull + Integer locationNo2, // 두번째차량 위치 정보 + @NotNull + Integer predictTime2, // 두번째차량 도착예상시간 + @NotNull + String plateNo2, // 두번째차량 차량번호 + @NotNull + Integer remainSeatCnt2, // 두번째차량 빈자리 수 + @NotNull + RunningStatus flag // 상태구분 +) { + public static Optional of(BusArrivalBodyDto busArrivalBodyDto) { + if (busArrivalBodyDto == null) { + return Optional.empty(); + } + return Optional.of(new BusLiveArrivalRespDto( + busArrivalBodyDto.locationNo1(), + busArrivalBodyDto.predictTime1(), + busArrivalBodyDto.plateNo1(), + busArrivalBodyDto.remainSeatCnt1(), + busArrivalBodyDto.locationNo2(), + busArrivalBodyDto.predictTime2(), + busArrivalBodyDto.plateNo2(), + busArrivalBodyDto.remainSeatCnt2(), + RunningStatus.valueOf(busArrivalBodyDto.flag()) + )); + } +} diff --git a/server/src/main/java/com/talkka/server/bus/dto/BusLiveInfoRespDto.java b/server/src/main/java/com/talkka/server/bus/dto/BusLiveInfoRespDto.java new file mode 100644 index 00000000..bdff9cbe --- /dev/null +++ b/server/src/main/java/com/talkka/server/bus/dto/BusLiveInfoRespDto.java @@ -0,0 +1,18 @@ +package com.talkka.server.bus.dto; + +import jakarta.validation.constraints.NotNull; + +public record BusLiveInfoRespDto( + @NotNull + Short seq, + @NotNull + BusRouteStationRespDto routeStation, + BusLiveArrivalRespDto arrivalInfo) { + + public static BusLiveInfoRespDto of( + Short seq, + BusRouteStationRespDto routeStation, + BusLiveArrivalRespDto arrivalInfo) { + return new BusLiveInfoRespDto(seq, routeStation, arrivalInfo); + } +} diff --git a/server/src/main/java/com/talkka/server/bus/enums/RunningStatus.java b/server/src/main/java/com/talkka/server/bus/enums/RunningStatus.java new file mode 100644 index 00000000..8e142bcf --- /dev/null +++ b/server/src/main/java/com/talkka/server/bus/enums/RunningStatus.java @@ -0,0 +1,13 @@ +package com.talkka.server.bus.enums; + +public enum RunningStatus { + RUN, PASS, STOP, WAIT, UNKNOWN; + + public static RunningStatus fromString(String status) { + try { + return RunningStatus.valueOf(status); + } catch (Exception e) { + return UNKNOWN; + } + } +} diff --git a/server/src/main/java/com/talkka/server/bus/exception/GetBusLiveArrivalInfoFailedException.java b/server/src/main/java/com/talkka/server/bus/exception/GetBusLiveArrivalInfoFailedException.java new file mode 100644 index 00000000..8e97a564 --- /dev/null +++ b/server/src/main/java/com/talkka/server/bus/exception/GetBusLiveArrivalInfoFailedException.java @@ -0,0 +1,9 @@ +package com.talkka.server.bus.exception; + +public class GetBusLiveArrivalInfoFailedException extends RuntimeException { + private static final String MESSAGE = "버스 도착 정보 획득에 실패하였습니다."; + + public GetBusLiveArrivalInfoFailedException() { + super(MESSAGE); + } +} diff --git a/server/src/main/java/com/talkka/server/bus/service/BusArrivalService.java b/server/src/main/java/com/talkka/server/bus/service/BusArrivalService.java new file mode 100644 index 00000000..a6be00bf --- /dev/null +++ b/server/src/main/java/com/talkka/server/bus/service/BusArrivalService.java @@ -0,0 +1,12 @@ +package com.talkka.server.bus.service; + +import java.util.Optional; + +import com.talkka.server.bus.dto.BusLiveArrivalRespDto; +import com.talkka.server.bus.exception.GetBusLiveArrivalInfoFailedException; + +public interface BusArrivalService { + Optional getBusArrivalInfo(Long routeStationId, String apiRouteId, + String apiStationId) throws + GetBusLiveArrivalInfoFailedException; +} diff --git a/server/src/main/java/com/talkka/server/bus/service/BusLiveInfoService.java b/server/src/main/java/com/talkka/server/bus/service/BusLiveInfoService.java new file mode 100644 index 00000000..98d7a163 --- /dev/null +++ b/server/src/main/java/com/talkka/server/bus/service/BusLiveInfoService.java @@ -0,0 +1,12 @@ +package com.talkka.server.bus.service; + +import com.talkka.server.bus.dto.BusLiveInfoRespDto; +import com.talkka.server.bus.exception.BusRouteStationNotFoundException; +import com.talkka.server.bus.exception.GetBusLiveArrivalInfoFailedException; + +public interface BusLiveInfoService { + + BusLiveInfoRespDto getBusLiveInfo(Long routeStationId) throws + BusRouteStationNotFoundException, + GetBusLiveArrivalInfoFailedException; +} From baba23eb7b5700be53db44a43f067ff0cbe2ba12 Mon Sep 17 00:00:00 2001 From: Photogrammer Date: Mon, 26 Aug 2024 15:45:56 +0900 Subject: [PATCH 04/10] =?UTF-8?q?feat:=20=EB=B2=84=EC=8A=A4=20=EC=8B=A4?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=A0=95=EB=B3=B4=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bus/service/BusLiveInfoServiceImpl.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 server/src/main/java/com/talkka/server/bus/service/BusLiveInfoServiceImpl.java diff --git a/server/src/main/java/com/talkka/server/bus/service/BusLiveInfoServiceImpl.java b/server/src/main/java/com/talkka/server/bus/service/BusLiveInfoServiceImpl.java new file mode 100644 index 00000000..fa6f1787 --- /dev/null +++ b/server/src/main/java/com/talkka/server/bus/service/BusLiveInfoServiceImpl.java @@ -0,0 +1,34 @@ +package com.talkka.server.bus.service; + +import org.springframework.stereotype.Service; + +import com.talkka.server.bus.dao.BusRouteStationRepository; +import com.talkka.server.bus.dto.BusLiveInfoRespDto; +import com.talkka.server.bus.dto.BusRouteStationRespDto; +import com.talkka.server.bus.exception.BusRouteStationNotFoundException; +import com.talkka.server.bus.exception.GetBusLiveArrivalInfoFailedException; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class BusLiveInfoServiceImpl implements BusLiveInfoService { + private final BusArrivalService busArrivalService; + private final BusRouteStationRepository busRouteStationRepository; + + @Override + public BusLiveInfoRespDto getBusLiveInfo(Long routeStationId) + throws BusRouteStationNotFoundException, GetBusLiveArrivalInfoFailedException { + var busRouteStation = busRouteStationRepository.findById(routeStationId) + .orElseThrow(() -> new BusRouteStationNotFoundException(routeStationId)); + var apiRouteId = busRouteStation.getRoute().getApiRouteId(); + var apiStationId = busRouteStation.getStation().getApiStationId(); + var arrivalInfo = busArrivalService.getBusArrivalInfo(routeStationId, apiRouteId, apiStationId) + .orElse(null); + + return BusLiveInfoRespDto.of( + busRouteStation.getStationSeq(), + BusRouteStationRespDto.of(busRouteStation), + arrivalInfo); + } +} From 5d565bedd3d226eac1c0a7508cb4e1514165737b Mon Sep 17 00:00:00 2001 From: Photogrammer Date: Mon, 26 Aug 2024 15:46:06 +0900 Subject: [PATCH 05/10] =?UTF-8?q?feat:=20=EB=8F=84=EC=B0=A9=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=BA=90=EC=8B=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/talkka/server/bus/util/ArrivalCache.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 server/src/main/java/com/talkka/server/bus/util/ArrivalCache.java diff --git a/server/src/main/java/com/talkka/server/bus/util/ArrivalCache.java b/server/src/main/java/com/talkka/server/bus/util/ArrivalCache.java new file mode 100644 index 00000000..35f273dc --- /dev/null +++ b/server/src/main/java/com/talkka/server/bus/util/ArrivalCache.java @@ -0,0 +1,13 @@ +package com.talkka.server.bus.util; + +import org.springframework.stereotype.Component; + +import com.talkka.server.bus.dto.BusLiveArrivalRespDto; +import com.talkka.server.common.util.MemoryCachedStorage; + +@Component +public class ArrivalCache extends MemoryCachedStorage { + ArrivalCache() { + super(60); // 60초 주기로 캐싱 무효화 + } +} From 6c763532759d7417b2421d4ed948175439346242 Mon Sep 17 00:00:00 2001 From: Photogrammer Date: Mon, 26 Aug 2024 15:46:14 +0900 Subject: [PATCH 06/10] =?UTF-8?q?feat:=20=EB=8F=84=EC=B0=A9=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bus/service/BusArrivalServiceImpl.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 server/src/main/java/com/talkka/server/bus/service/BusArrivalServiceImpl.java diff --git a/server/src/main/java/com/talkka/server/bus/service/BusArrivalServiceImpl.java b/server/src/main/java/com/talkka/server/bus/service/BusArrivalServiceImpl.java new file mode 100644 index 00000000..0a7ef69e --- /dev/null +++ b/server/src/main/java/com/talkka/server/bus/service/BusArrivalServiceImpl.java @@ -0,0 +1,39 @@ +package com.talkka.server.bus.service; + +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import com.talkka.server.api.core.exception.ApiClientException; +import com.talkka.server.api.datagg.service.BusApiService; +import com.talkka.server.bus.dto.BusLiveArrivalRespDto; +import com.talkka.server.bus.exception.GetBusLiveArrivalInfoFailedException; +import com.talkka.server.common.util.CachedStorage; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class BusArrivalServiceImpl implements BusArrivalService { + private final BusApiService busApiService; + private final CachedStorage arrivalCache; + + @Override + public Optional getBusArrivalInfo(Long routeStationId, String apiRouteId, + String apiStationId) + throws GetBusLiveArrivalInfoFailedException { + try { + var cached = arrivalCache.get(routeStationId); + if (cached.isPresent()) { + return cached; + } + + var arrivalInfo = busApiService.getBusArrival(apiRouteId, apiStationId) + .flatMap(BusLiveArrivalRespDto::of); + arrivalInfo.ifPresent(busLiveArrivalRespDto -> arrivalCache.put(routeStationId, busLiveArrivalRespDto)); + return arrivalInfo; + } catch (ApiClientException exception) { + throw new GetBusLiveArrivalInfoFailedException(); + } + } +} From af3a386e4df4babf5543c6f1d25bfa9277eb4db2 Mon Sep 17 00:00:00 2001 From: Photogrammer Date: Mon, 26 Aug 2024 15:46:26 +0900 Subject: [PATCH 07/10] =?UTF-8?q?feat:=20=EB=B2=84=EC=8A=A4=20=EC=8B=A4?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=A0=95=EB=B3=B4=20API=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 --- .../server/bus/controller/BusLiveInfoApi.java | 28 +++++++++++++++ .../bus/controller/BusLiveInfoController.java | 34 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 server/src/main/java/com/talkka/server/bus/controller/BusLiveInfoApi.java create mode 100644 server/src/main/java/com/talkka/server/bus/controller/BusLiveInfoController.java diff --git a/server/src/main/java/com/talkka/server/bus/controller/BusLiveInfoApi.java b/server/src/main/java/com/talkka/server/bus/controller/BusLiveInfoApi.java new file mode 100644 index 00000000..e3bf8872 --- /dev/null +++ b/server/src/main/java/com/talkka/server/bus/controller/BusLiveInfoApi.java @@ -0,0 +1,28 @@ +package com.talkka.server.bus.controller; + +import org.springframework.http.ResponseEntity; + +import com.talkka.server.bus.dto.BusLiveInfoRespDto; +import com.talkka.server.common.dto.ErrorRespDto; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; + +@Tag(name = "버스 실시간 정보", description = "버스 실시간 정보 조회 API") +public interface BusLiveInfoApi { + @Operation(summary = "버스 실시간 정보 조회", description = "버스 실시간 정보 조회 API") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = @Content(schema = @Schema(implementation = BusLiveInfoRespDto.class))), + @ApiResponse(responseCode = "400", description = "잘못된 요청", + content = @Content(schema = @Schema(implementation = ErrorRespDto.class))) + }) + ResponseEntity getBusLiveInfo( + @Parameter(description = "노선 정류장 ID", required = true) + Long routeStationId); +} diff --git a/server/src/main/java/com/talkka/server/bus/controller/BusLiveInfoController.java b/server/src/main/java/com/talkka/server/bus/controller/BusLiveInfoController.java new file mode 100644 index 00000000..f5ee2fc1 --- /dev/null +++ b/server/src/main/java/com/talkka/server/bus/controller/BusLiveInfoController.java @@ -0,0 +1,34 @@ +package com.talkka.server.bus.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.talkka.server.bus.exception.BusRouteStationNotFoundException; +import com.talkka.server.bus.exception.GetBusLiveArrivalInfoFailedException; +import com.talkka.server.bus.service.BusLiveInfoService; +import com.talkka.server.common.dto.ErrorRespDto; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/bus/live") +public class BusLiveInfoController implements BusLiveInfoApi { + private final BusLiveInfoService busLiveInfoService; + + @Override + @GetMapping("/{routeStationId}") + public ResponseEntity getBusLiveInfo(@PathVariable Long routeStationId) { + ResponseEntity response; + try { + var busLiveInfo = busLiveInfoService.getBusLiveInfo(routeStationId); + response = ResponseEntity.ok(busLiveInfo); + } catch (BusRouteStationNotFoundException | GetBusLiveArrivalInfoFailedException exception) { + response = ResponseEntity.badRequest().body(ErrorRespDto.of(exception)); + } + return response; + } +} From 0f4cf88d0b6af64692c5b9c1760851c568f1fc6f Mon Sep 17 00:00:00 2001 From: Photogrammer Date: Mon, 26 Aug 2024 16:08:31 +0900 Subject: [PATCH 08/10] =?UTF-8?q?feat:=20LiveInfo=20=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=B2=84=EC=8A=A4=20=EC=A0=95=EB=B3=B4=EB=8F=84=20=ED=95=A8?= =?UTF-8?q?=EA=BB=98=20=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD,=20BusArrivalRespDto=20=EC=9D=B4=EB=A6=84=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 --- ...iveArrivalRespDto.java => BusArrivalRespDto.java} | 6 +++--- .../talkka/server/bus/dto/BusLiveInfoRespDto.java | 12 +++++++++--- .../talkka/server/bus/service/BusArrivalService.java | 4 ++-- .../server/bus/service/BusArrivalServiceImpl.java | 8 ++++---- .../server/bus/service/BusLiveInfoServiceImpl.java | 10 ++++++++-- .../com/talkka/server/bus/util/ArrivalCache.java | 4 ++-- 6 files changed, 28 insertions(+), 16 deletions(-) rename server/src/main/java/com/talkka/server/bus/dto/{BusLiveArrivalRespDto.java => BusArrivalRespDto.java} (88%) diff --git a/server/src/main/java/com/talkka/server/bus/dto/BusLiveArrivalRespDto.java b/server/src/main/java/com/talkka/server/bus/dto/BusArrivalRespDto.java similarity index 88% rename from server/src/main/java/com/talkka/server/bus/dto/BusLiveArrivalRespDto.java rename to server/src/main/java/com/talkka/server/bus/dto/BusArrivalRespDto.java index d8a5c1a3..1624cfea 100644 --- a/server/src/main/java/com/talkka/server/bus/dto/BusLiveArrivalRespDto.java +++ b/server/src/main/java/com/talkka/server/bus/dto/BusArrivalRespDto.java @@ -7,7 +7,7 @@ import jakarta.validation.constraints.NotNull; -public record BusLiveArrivalRespDto( +public record BusArrivalRespDto( @NotNull Integer locationNo1, // 첫번째차량 위치 정보 @NotNull @@ -27,11 +27,11 @@ public record BusLiveArrivalRespDto( @NotNull RunningStatus flag // 상태구분 ) { - public static Optional of(BusArrivalBodyDto busArrivalBodyDto) { + public static Optional of(BusArrivalBodyDto busArrivalBodyDto) { if (busArrivalBodyDto == null) { return Optional.empty(); } - return Optional.of(new BusLiveArrivalRespDto( + return Optional.of(new BusArrivalRespDto( busArrivalBodyDto.locationNo1(), busArrivalBodyDto.predictTime1(), busArrivalBodyDto.plateNo1(), diff --git a/server/src/main/java/com/talkka/server/bus/dto/BusLiveInfoRespDto.java b/server/src/main/java/com/talkka/server/bus/dto/BusLiveInfoRespDto.java index bdff9cbe..2431f38c 100644 --- a/server/src/main/java/com/talkka/server/bus/dto/BusLiveInfoRespDto.java +++ b/server/src/main/java/com/talkka/server/bus/dto/BusLiveInfoRespDto.java @@ -6,13 +6,19 @@ public record BusLiveInfoRespDto( @NotNull Short seq, @NotNull + Long routeId, + @NotNull + String routeName, + @NotNull BusRouteStationRespDto routeStation, - BusLiveArrivalRespDto arrivalInfo) { + BusArrivalRespDto arrivalInfo) { public static BusLiveInfoRespDto of( Short seq, + Long routeId, + String routeName, BusRouteStationRespDto routeStation, - BusLiveArrivalRespDto arrivalInfo) { - return new BusLiveInfoRespDto(seq, routeStation, arrivalInfo); + BusArrivalRespDto arrivalInfo) { + return new BusLiveInfoRespDto(seq, routeId, routeName, routeStation, arrivalInfo); } } diff --git a/server/src/main/java/com/talkka/server/bus/service/BusArrivalService.java b/server/src/main/java/com/talkka/server/bus/service/BusArrivalService.java index a6be00bf..f971d1b9 100644 --- a/server/src/main/java/com/talkka/server/bus/service/BusArrivalService.java +++ b/server/src/main/java/com/talkka/server/bus/service/BusArrivalService.java @@ -2,11 +2,11 @@ import java.util.Optional; -import com.talkka.server.bus.dto.BusLiveArrivalRespDto; +import com.talkka.server.bus.dto.BusArrivalRespDto; import com.talkka.server.bus.exception.GetBusLiveArrivalInfoFailedException; public interface BusArrivalService { - Optional getBusArrivalInfo(Long routeStationId, String apiRouteId, + Optional getBusArrivalInfo(Long routeStationId, String apiRouteId, String apiStationId) throws GetBusLiveArrivalInfoFailedException; } diff --git a/server/src/main/java/com/talkka/server/bus/service/BusArrivalServiceImpl.java b/server/src/main/java/com/talkka/server/bus/service/BusArrivalServiceImpl.java index 0a7ef69e..7546ec06 100644 --- a/server/src/main/java/com/talkka/server/bus/service/BusArrivalServiceImpl.java +++ b/server/src/main/java/com/talkka/server/bus/service/BusArrivalServiceImpl.java @@ -6,7 +6,7 @@ import com.talkka.server.api.core.exception.ApiClientException; import com.talkka.server.api.datagg.service.BusApiService; -import com.talkka.server.bus.dto.BusLiveArrivalRespDto; +import com.talkka.server.bus.dto.BusArrivalRespDto; import com.talkka.server.bus.exception.GetBusLiveArrivalInfoFailedException; import com.talkka.server.common.util.CachedStorage; @@ -16,10 +16,10 @@ @RequiredArgsConstructor public class BusArrivalServiceImpl implements BusArrivalService { private final BusApiService busApiService; - private final CachedStorage arrivalCache; + private final CachedStorage arrivalCache; @Override - public Optional getBusArrivalInfo(Long routeStationId, String apiRouteId, + public Optional getBusArrivalInfo(Long routeStationId, String apiRouteId, String apiStationId) throws GetBusLiveArrivalInfoFailedException { try { @@ -29,7 +29,7 @@ public Optional getBusArrivalInfo(Long routeStationId, St } var arrivalInfo = busApiService.getBusArrival(apiRouteId, apiStationId) - .flatMap(BusLiveArrivalRespDto::of); + .flatMap(BusArrivalRespDto::of); arrivalInfo.ifPresent(busLiveArrivalRespDto -> arrivalCache.put(routeStationId, busLiveArrivalRespDto)); return arrivalInfo; } catch (ApiClientException exception) { diff --git a/server/src/main/java/com/talkka/server/bus/service/BusLiveInfoServiceImpl.java b/server/src/main/java/com/talkka/server/bus/service/BusLiveInfoServiceImpl.java index fa6f1787..73746787 100644 --- a/server/src/main/java/com/talkka/server/bus/service/BusLiveInfoServiceImpl.java +++ b/server/src/main/java/com/talkka/server/bus/service/BusLiveInfoServiceImpl.java @@ -21,13 +21,19 @@ public BusLiveInfoRespDto getBusLiveInfo(Long routeStationId) throws BusRouteStationNotFoundException, GetBusLiveArrivalInfoFailedException { var busRouteStation = busRouteStationRepository.findById(routeStationId) .orElseThrow(() -> new BusRouteStationNotFoundException(routeStationId)); - var apiRouteId = busRouteStation.getRoute().getApiRouteId(); - var apiStationId = busRouteStation.getStation().getApiStationId(); + var route = busRouteStation.getRoute(); + var station = busRouteStation.getStation(); + var routeId = route.getId(); + var routeName = route.getRouteName(); + var apiRouteId = route.getApiRouteId(); + var apiStationId = station.getApiStationId(); var arrivalInfo = busArrivalService.getBusArrivalInfo(routeStationId, apiRouteId, apiStationId) .orElse(null); return BusLiveInfoRespDto.of( busRouteStation.getStationSeq(), + routeId, + routeName, BusRouteStationRespDto.of(busRouteStation), arrivalInfo); } diff --git a/server/src/main/java/com/talkka/server/bus/util/ArrivalCache.java b/server/src/main/java/com/talkka/server/bus/util/ArrivalCache.java index 35f273dc..c614319a 100644 --- a/server/src/main/java/com/talkka/server/bus/util/ArrivalCache.java +++ b/server/src/main/java/com/talkka/server/bus/util/ArrivalCache.java @@ -2,11 +2,11 @@ import org.springframework.stereotype.Component; -import com.talkka.server.bus.dto.BusLiveArrivalRespDto; +import com.talkka.server.bus.dto.BusArrivalRespDto; import com.talkka.server.common.util.MemoryCachedStorage; @Component -public class ArrivalCache extends MemoryCachedStorage { +public class ArrivalCache extends MemoryCachedStorage { ArrivalCache() { super(60); // 60초 주기로 캐싱 무효화 } From 9e8f8634170ad3cee127646bede9c3e9e1fce52e Mon Sep 17 00:00:00 2001 From: Photogrammer Date: Mon, 26 Aug 2024 16:11:16 +0900 Subject: [PATCH 09/10] =?UTF-8?q?refactor:=20BusArrivalService=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=EC=B2=B4=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...{BusArrivalServiceImpl.java => CachedBusArrivalService.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename server/src/main/java/com/talkka/server/bus/service/{BusArrivalServiceImpl.java => CachedBusArrivalService.java} (94%) diff --git a/server/src/main/java/com/talkka/server/bus/service/BusArrivalServiceImpl.java b/server/src/main/java/com/talkka/server/bus/service/CachedBusArrivalService.java similarity index 94% rename from server/src/main/java/com/talkka/server/bus/service/BusArrivalServiceImpl.java rename to server/src/main/java/com/talkka/server/bus/service/CachedBusArrivalService.java index 7546ec06..57847161 100644 --- a/server/src/main/java/com/talkka/server/bus/service/BusArrivalServiceImpl.java +++ b/server/src/main/java/com/talkka/server/bus/service/CachedBusArrivalService.java @@ -14,7 +14,7 @@ @Service @RequiredArgsConstructor -public class BusArrivalServiceImpl implements BusArrivalService { +public class CachedBusArrivalService implements BusArrivalService { private final BusApiService busApiService; private final CachedStorage arrivalCache; From d7b42fd1bed0e54d243a7b1debf224ef3c5ffe9c Mon Sep 17 00:00:00 2001 From: Photogrammer Date: Mon, 26 Aug 2024 16:18:26 +0900 Subject: [PATCH 10/10] =?UTF-8?q?chore:=20MemoryCachedStorage=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/talkka/server/common/util/MemoryCachedStorage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/talkka/server/common/util/MemoryCachedStorage.java b/server/src/main/java/com/talkka/server/common/util/MemoryCachedStorage.java index 74d41161..072af984 100644 --- a/server/src/main/java/com/talkka/server/common/util/MemoryCachedStorage.java +++ b/server/src/main/java/com/talkka/server/common/util/MemoryCachedStorage.java @@ -7,7 +7,8 @@ /** * MemoryCachedStorage * Redis 사용 이전에 메모리에 캐시를 저장하는 클래스 - * 시간에 따라 Expire 되지 않아 메모리가 쌓이는 문제가 발생할 수 있음. + * 사용량이 증가할 경우 put 에서 removeExpired 를 호출하여 메모리를 정리에 시간이 크게 소요될 수 있음 + * (임시방편으로 사용하고 있으며 추후 Redis 로 대체할 예정) * @param * @param */