diff --git a/api/src/main/java/com/walking/api/converter/TrafficDetailConverter.java b/api/src/main/java/com/walking/api/converter/TrafficDetailConverter.java new file mode 100644 index 00000000..4196f561 --- /dev/null +++ b/api/src/main/java/com/walking/api/converter/TrafficDetailConverter.java @@ -0,0 +1,35 @@ +package com.walking.api.converter; + +import com.walking.api.service.dto.PredictedData; +import com.walking.api.web.dto.response.detail.PointDetail; +import com.walking.api.web.dto.response.detail.TrafficDetail; +import com.walking.data.entity.traffic.TrafficEntity; + +public final class TrafficDetailConverter { + + private TrafficDetailConverter() {} + + /** + * PredictedData를 기반으로 TrafficDetail를 생성합니다. + * + * @param predictedData 사이클 정보 와 현재 색상 및 잔여시간을 예측한 데이터 + * @return 예측 값을 바탕으로 만든 TrafficDetail + */ + public static TrafficDetail execute(PredictedData predictedData) { + + TrafficEntity trafficEntity = predictedData.getTraffic(); + + return TrafficDetail.builder() + .id(trafficEntity.getId()) + .color(predictedData.getCurrentColor().toString()) + .timeLeft(predictedData.getCurrentTimeLeft()) + .point( + PointDetail.builder().lng(trafficEntity.getLng()).lat(trafficEntity.getLat()).build()) + .redCycle(predictedData.getRedCycle()) + .greenCycle(predictedData.getGreenCycle()) + .detail(TrafficDetailInfoConverter.execute(trafficEntity)) + .isFavorite(false) + .viewName(trafficEntity.getName()) + .build(); + } +} diff --git a/api/src/main/java/com/walking/api/converter/TrafficDetailInfoConverter.java b/api/src/main/java/com/walking/api/converter/TrafficDetailInfoConverter.java new file mode 100644 index 00000000..18cb01aa --- /dev/null +++ b/api/src/main/java/com/walking/api/converter/TrafficDetailInfoConverter.java @@ -0,0 +1,34 @@ +package com.walking.api.converter; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.walking.api.web.dto.response.detail.TrafficDetailInfo; +import com.walking.data.entity.traffic.TrafficEntity; + +public final class TrafficDetailInfoConverter { + + private TrafficDetailInfoConverter() {} + + /** + * api.traffic_detail 의 detail 값(JSON)을 파싱하여 TrafficDetailInfo 로 변환합니다. + * + * @param trafficEntity + * @return + */ + public static TrafficDetailInfo execute(TrafficEntity trafficEntity) { + ObjectMapper objectMapper = new ObjectMapper(); + TrafficDetailInfo trafficDetailInfo = + TrafficDetailInfo.builder().trafficId(-1L).apiSource("ERROR").direction("ERROR").build(); + try { + trafficDetailInfo = + objectMapper.readValue(trafficEntity.getDetail(), TrafficDetailInfo.class); + } catch (JsonMappingException e) { + throw new RuntimeException("Convert to TrafficDetailInfo fail", e); + } catch (JsonProcessingException e) { + throw new RuntimeException("Convert to TrafficDetailInfo fail", e); + } + + return trafficDetailInfo; + } +} diff --git a/api/src/main/java/com/walking/api/repository/member/TrafficFavoritesRepository.java b/api/src/main/java/com/walking/api/repository/traffic/TrafficFavoritesRepository.java similarity index 52% rename from api/src/main/java/com/walking/api/repository/member/TrafficFavoritesRepository.java rename to api/src/main/java/com/walking/api/repository/traffic/TrafficFavoritesRepository.java index 15ea6239..20ca2a7d 100644 --- a/api/src/main/java/com/walking/api/repository/member/TrafficFavoritesRepository.java +++ b/api/src/main/java/com/walking/api/repository/traffic/TrafficFavoritesRepository.java @@ -1,8 +1,13 @@ -package com.walking.api.repository.member; +package com.walking.api.repository.traffic; +import com.walking.data.entity.member.MemberEntity; import com.walking.data.entity.member.TrafficFavoritesEntity; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository -public interface TrafficFavoritesRepository extends JpaRepository {} +public interface TrafficFavoritesRepository extends JpaRepository { + + List findByMemberFk(MemberEntity memberFk); +} diff --git a/api/src/main/java/com/walking/api/service/TrafficIntegrationPredictService.java b/api/src/main/java/com/walking/api/service/TrafficIntegrationPredictService.java index 0d3085c3..1fab3dd3 100644 --- a/api/src/main/java/com/walking/api/service/TrafficIntegrationPredictService.java +++ b/api/src/main/java/com/walking/api/service/TrafficIntegrationPredictService.java @@ -12,6 +12,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; /** 신호등의 현재 잔여 시간, 현재 색상, 각 색상별 사이클을 예측한 결과를 리턴합니다. */ @Service @@ -25,6 +26,7 @@ public class TrafficIntegrationPredictService { @Value("${walking.predict.dataInterval}") private int dataInterval; + @Transactional(readOnly = true) public IntegrationPredictResponseDto execute(IntegrationPredictRequestDto requestDto) { List trafficIds = requestDto.getTrafficIds(); CyclePredictionRequestDto cyclePredictionRequestDto = diff --git a/api/src/main/java/com/walking/api/service/dto/PredictedData.java b/api/src/main/java/com/walking/api/service/dto/PredictedData.java index c031c2ce..e3257ee6 100644 --- a/api/src/main/java/com/walking/api/service/dto/PredictedData.java +++ b/api/src/main/java/com/walking/api/service/dto/PredictedData.java @@ -65,6 +65,12 @@ public boolean isAllPredicted() { && currentTimeLeft > 0; } + /** + * 색상에 따른 사이클을 반환합니다. + * + * @param color 사이클을 알고자 하는 신호등의 색상 + * @return 파라미터로 전달받은 색상의 사이클 + */ public Float getCycleByColor(TrafficColor color) { if (color.isGreen()) { return greenCycle; diff --git a/api/src/main/java/com/walking/api/service/traffic/ReadTrafficFavoritesService.java b/api/src/main/java/com/walking/api/service/traffic/ReadTrafficFavoritesService.java new file mode 100644 index 00000000..5ec2b711 --- /dev/null +++ b/api/src/main/java/com/walking/api/service/traffic/ReadTrafficFavoritesService.java @@ -0,0 +1,19 @@ +package com.walking.api.service.traffic; + +import com.walking.api.repository.traffic.TrafficFavoritesRepository; +import com.walking.data.entity.member.MemberEntity; +import com.walking.data.entity.member.TrafficFavoritesEntity; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ReadTrafficFavoritesService { + + private final TrafficFavoritesRepository trafficFavoritesRepository; + + public List executeByMemberFk(MemberEntity member) { + return trafficFavoritesRepository.findByMemberFk(member); + } +} diff --git a/api/src/main/java/com/walking/api/service/traffic/ReadTrafficService.java b/api/src/main/java/com/walking/api/service/traffic/ReadTrafficService.java new file mode 100644 index 00000000..2bb8a842 --- /dev/null +++ b/api/src/main/java/com/walking/api/service/traffic/ReadTrafficService.java @@ -0,0 +1,22 @@ +package com.walking.api.service.traffic; + +import com.walking.api.repository.traffic.TrafficRepository; +import com.walking.data.entity.traffic.TrafficEntity; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ReadTrafficService { + + private final TrafficRepository trafficRepository; + + public TrafficEntity executeById(Long trafficId) { + return trafficRepository.findById(trafficId).orElseThrow(); + } + + public List executeByIds(List trafficIds) { + return trafficRepository.findByIds(trafficIds); + } +} diff --git a/api/src/main/java/com/walking/api/web/controller/path/PathController.java b/api/src/main/java/com/walking/api/web/controller/path/PathController.java index f99008c8..6058b22d 100644 --- a/api/src/main/java/com/walking/api/web/controller/path/PathController.java +++ b/api/src/main/java/com/walking/api/web/controller/path/PathController.java @@ -146,9 +146,9 @@ private static RouteDetailResponse getSampleRouteDetailResponse() { .viewName("후문") .point(PointDetail.builder().lat(BACK_DOOR_LAT).lng(BACK_DOOR_LNG).build()) .color("red") - .remainTime(10L) - .redCycle(30L) - .greenCycle(30L) + .timeLeft(10f) + .redCycle(30f) + .greenCycle(30f) .build())) .paths( List.of( diff --git a/api/src/main/java/com/walking/api/web/controller/traffic/TrafficController.java b/api/src/main/java/com/walking/api/web/controller/traffic/TrafficController.java index 24c3bb46..922d46a9 100644 --- a/api/src/main/java/com/walking/api/web/controller/traffic/TrafficController.java +++ b/api/src/main/java/com/walking/api/web/controller/traffic/TrafficController.java @@ -1,6 +1,11 @@ package com.walking.api.web.controller.traffic; +import com.walking.api.converter.TrafficDetailConverter; import com.walking.api.security.authentication.token.TokenUserDetails; +import com.walking.api.service.TrafficIntegrationPredictService; +import com.walking.api.service.dto.PredictedData; +import com.walking.api.service.dto.request.IntegrationPredictRequestDto; +import com.walking.api.service.dto.response.IntegrationPredictResponseDto; import com.walking.api.web.dto.request.point.OptionalTrafficPointParam; import com.walking.api.web.dto.request.point.OptionalViewPointParam; import com.walking.api.web.dto.request.traffic.FavoriteTrafficBody; @@ -17,6 +22,7 @@ import com.walking.api.web.support.MessageCode; import java.time.LocalDateTime; import java.util.List; +import java.util.Map; import java.util.Objects; import javax.validation.Valid; import javax.validation.constraints.Min; @@ -24,6 +30,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -41,6 +48,8 @@ @RequiredArgsConstructor public class TrafficController { + private final TrafficIntegrationPredictService integrationPredictService; + static double TF_BACK_DOOR_LAT = 35.178501; static double TF_BACK_DOOR_LNG = 126.912083; static double TF_BACK_THREE_LAT = 35.175841; @@ -76,11 +85,20 @@ public ApiResponse> searchTraffi } @GetMapping("/{trafficId}") + @Transactional public ApiResponse> browseTraffic( @PathVariable Long trafficId) { // todo implement log.info("Traffic browse trafficId: {}", trafficId); - BrowseTrafficsResponse response = getBrowseTrafficsResponse(); + IntegrationPredictResponseDto integrationPredictedResponse = + integrationPredictService.execute( + IntegrationPredictRequestDto.builder().trafficIds(List.of(trafficId)).build()); + + Map predictedDataMap = integrationPredictedResponse.getPredictedDataMap(); + PredictedData predictedData = predictedDataMap.get(trafficId); + TrafficDetail trafficDetail = TrafficDetailConverter.execute(predictedData); + BrowseTrafficsResponse response = + BrowseTrafficsResponse.builder().traffic(trafficDetail).build(); return ApiResponseGenerator.success(response, HttpStatus.OK, MessageCode.SUCCESS); } @@ -151,9 +169,9 @@ private static SearchTrafficsResponse getSearchViewTrafficsResponse() { .lng(TF_BACK_DOOR_LNG) .build()) .color("red") - .remainTime(10L) - .redCycle(30L) - .greenCycle(30L) + .timeLeft(10f) + .redCycle(30f) + .greenCycle(30f) .build(), TrafficDetail.builder() .id(2L) @@ -171,9 +189,9 @@ private static SearchTrafficsResponse getSearchViewTrafficsResponse() { .lng(TF_BACK_THREE_LNG) .build()) .color("green") - .remainTime(20L) - .redCycle(30L) - .greenCycle(30L) + .timeLeft(20f) + .redCycle(30f) + .greenCycle(30f) .build(), TrafficDetail.builder() .id(3L) @@ -188,9 +206,9 @@ private static SearchTrafficsResponse getSearchViewTrafficsResponse() { .point( PointDetail.builder().lat(TF_CHANPUNG_LAT).lng(TF_CHANPUNG_LNG).build()) .color("red") - .remainTime(10L) - .redCycle(30L) - .greenCycle(30L) + .timeLeft(10f) + .redCycle(30f) + .greenCycle(30f) .build(), TrafficDetail.builder() .id(4L) @@ -204,9 +222,9 @@ private static SearchTrafficsResponse getSearchViewTrafficsResponse() { .viewName("쿠쿠") .point(PointDetail.builder().lat(TF_CUCU_LAT).lng(TF_CUCU_LNG).build()) .color("green") - .remainTime(20L) - .redCycle(30L) - .greenCycle(30L) + .timeLeft(20f) + .redCycle(30f) + .greenCycle(30f) .build())) .build(); @@ -234,9 +252,9 @@ private static SearchTrafficsResponse getSearchTrafficsResponse() { .lng(TF_BACK_DOOR_LNG) .build()) .color("red") - .remainTime(10L) - .redCycle(30L) - .greenCycle(30L) + .timeLeft(10f) + .redCycle(30f) + .greenCycle(30f) .build())) .build(); @@ -260,9 +278,9 @@ private static BrowseTrafficsResponse getBrowseTrafficsResponse() { .point( PointDetail.builder().lat(TF_BACK_DOOR_LAT).lng(TF_BACK_DOOR_LNG).build()) .color("red") - .remainTime(10L) - .redCycle(30L) - .greenCycle(30L) + .timeLeft(10f) + .redCycle(30f) + .greenCycle(30f) .build()) .build(); diff --git a/api/src/main/java/com/walking/api/web/dto/response/detail/TrafficDetail.java b/api/src/main/java/com/walking/api/web/dto/response/detail/TrafficDetail.java index 073d3aa2..227f15b0 100644 --- a/api/src/main/java/com/walking/api/web/dto/response/detail/TrafficDetail.java +++ b/api/src/main/java/com/walking/api/web/dto/response/detail/TrafficDetail.java @@ -17,10 +17,9 @@ public class TrafficDetail { private Long id; - private String state; - private Long remainTime; - private Long greenCycle; - private Long redCycle; + private Float timeLeft; + private Float greenCycle; + private Float redCycle; private PointDetail point; private String color; private TrafficDetailInfo detail; diff --git a/api/src/test/java/com/walking/api/web/controller/traffic/TrafficControllerTest.java b/api/src/test/java/com/walking/api/web/controller/traffic/TrafficControllerTest.java index 712421e8..7c0e1cff 100644 --- a/api/src/test/java/com/walking/api/web/controller/traffic/TrafficControllerTest.java +++ b/api/src/test/java/com/walking/api/web/controller/traffic/TrafficControllerTest.java @@ -232,7 +232,7 @@ void searchTrafficsWithTrafficParam() throws Exception { @DisplayName("GET /{trafficId} 신호등 정보 조회 - 신호등 ID로 조회") void browseTraffic() throws Exception { mockMvc - .perform(get(BASE_URL + "/{trafficId}", 1).contentType(MediaType.APPLICATION_JSON)) + .perform(get(BASE_URL + "/{trafficId}", 3).contentType(MediaType.APPLICATION_JSON)) .andExpect(status().is2xxSuccessful()) .andDo( document( @@ -246,7 +246,7 @@ void browseTraffic() throws Exception { new ParameterDescriptorWithType("trafficId") .type(SimpleType.NUMBER) .description("신호등 ID") - .defaultValue(1L)) + .defaultValue(3L)) .responseSchema(Schema.schema("BrowseTrafficResponse")) .responseFields( Description.common(