diff --git a/docker-compose.yml b/docker-compose.yml index 034a2936..cb601975 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,10 +14,24 @@ services: - rabbitmq_data:/var/lib/rabbitmq restart: unless-stopped healthcheck: - test: ["CMD", "rabbitmq-diagnostics", "check_port_connectivity"] + test: [ "CMD", "rabbitmq-diagnostics", "check_port_connectivity" ] interval: 30s timeout: 10s retries: 3 + mysql: + image: mysql:8.0 + container_name: tracky-db + restart: always + environment: + MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} + MYSQL_DATABASE: ${DB_NAME} + MYSQL_USER: ${DB_USER} + MYSQL_PASSWORD: ${DB_PASSWORD} + ports: + - "3307:3306" + volumes: + - mysql_data:/var/lib/mysql volumes: - rabbitmq_data: \ No newline at end of file + rabbitmq_data: + mysql_data: diff --git a/tracky-core/src/main/resources/application-common.yml b/tracky-core/src/main/resources/application-common.yml index 6f07dd47..f4795e3a 100644 --- a/tracky-core/src/main/resources/application-common.yml +++ b/tracky-core/src/main/resources/application-common.yml @@ -28,7 +28,7 @@ spring: sql: init: - mode: never + mode: always encoding: UTF-8 schema-locations: classpath:schema.sql data-locations: classpath:data.sql @@ -39,7 +39,7 @@ management: exposure: include: - "*" - + logging: level: # RabbitMQ ConnectionFactory INFO 로그 끄기 diff --git a/tracky-hub/src/main/java/kernel360trackybe/trackyhub/common/config/RabbitMQConfig.java b/tracky-hub/src/main/java/kernel360trackybe/trackyhub/common/config/RabbitMQConfig.java index 3d2132bd..6fc21635 100644 --- a/tracky-hub/src/main/java/kernel360trackybe/trackyhub/common/config/RabbitMQConfig.java +++ b/tracky-hub/src/main/java/kernel360trackybe/trackyhub/common/config/RabbitMQConfig.java @@ -13,7 +13,9 @@ import org.springframework.context.annotation.Configuration; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Configuration @RequiredArgsConstructor public class RabbitMQConfig { @@ -32,6 +34,7 @@ public FanoutExchange fanoutExchange() { @Bean public Queue gpsQueue() { + log.info("Queue 생성: gpsQueue"); return QueueBuilder.durable(properties.getQueue().getGps()) .withArgument("x-dead-letter-exchange", properties.getExchange().getDlx()) .withArgument("x-dead-letter-routing-key", properties.getRouting().getDeadLetterKey()) diff --git a/tracky-web/src/main/java/kernel360/trackyweb/common/filter/HttpLoggingFilter.java b/tracky-web/src/main/java/kernel360/trackyweb/common/filter/HttpLoggingFilter.java index 8778bd43..6c0e2670 100644 --- a/tracky-web/src/main/java/kernel360/trackyweb/common/filter/HttpLoggingFilter.java +++ b/tracky-web/src/main/java/kernel360/trackyweb/common/filter/HttpLoggingFilter.java @@ -4,6 +4,7 @@ import java.nio.charset.StandardCharsets; import java.util.Iterator; +import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.util.ContentCachingResponseWrapper; @@ -19,6 +20,7 @@ @Slf4j @Component +@Order(1) public class HttpLoggingFilter implements Filter { @Override public void doFilter( @@ -29,6 +31,11 @@ public void doFilter( HttpServletRequest httpRequest = (HttpServletRequest)request; HttpServletResponse httpResponse = (HttpServletResponse)response; + if (isSseRequest(httpRequest)) { + chain.doFilter(request, response); + return; + } + ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(httpRequest); ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(httpResponse); @@ -89,5 +96,9 @@ private String getHeaders(HttpServletResponse response) { return headerString.toString(); } -} + private boolean isSseRequest(HttpServletRequest request) { + String accept = request.getHeader("Accept"); + return accept != null && accept.contains("text/event-stream"); + } +} diff --git a/tracky-web/src/main/java/kernel360/trackyweb/drive/domain/provider/DriveDomainProvider.java b/tracky-web/src/main/java/kernel360/trackyweb/drive/domain/provider/DriveDomainProvider.java index b27fdd58..699090eb 100644 --- a/tracky-web/src/main/java/kernel360/trackyweb/drive/domain/provider/DriveDomainProvider.java +++ b/tracky-web/src/main/java/kernel360/trackyweb/drive/domain/provider/DriveDomainProvider.java @@ -81,6 +81,11 @@ public Map countDailyTotalOperation(LocalDate targetDate) { return OperationTotalCount.toMap(driveDomainRepository.getDailyTotalOperation(targetDate)); } + public Long findActiveDriveIdByMdn(String mdn) { + return driveDomainRepository.findRunningDriveIdByMdn(mdn) + .orElseThrow(() -> GlobalException.throwError(ErrorCode.NOT_REALTIME_DRIVE)); + } + //월별 통계 - 미운행 차량 수 public Map getNonOperatedCars(LocalDate targetDate) { return NonOperatedCar.toMap(driveDomainRepository.getNonOperatedCars(targetDate)); diff --git a/tracky-web/src/main/java/kernel360/trackyweb/drive/infrastructure/repository/DriveDomainRepositoryCustom.java b/tracky-web/src/main/java/kernel360/trackyweb/drive/infrastructure/repository/DriveDomainRepositoryCustom.java index 18de9f07..63d9de4f 100644 --- a/tracky-web/src/main/java/kernel360/trackyweb/drive/infrastructure/repository/DriveDomainRepositoryCustom.java +++ b/tracky-web/src/main/java/kernel360/trackyweb/drive/infrastructure/repository/DriveDomainRepositoryCustom.java @@ -40,6 +40,8 @@ Page findRunningDriveListAdmin( Optional findByDriveId(Long driveId); + Optional findRunningDriveIdByMdn(String mdn); + List getDailyOperationCar(LocalDate targetDate); List getDailyTotalOperation(LocalDate targetDate); diff --git a/tracky-web/src/main/java/kernel360/trackyweb/drive/infrastructure/repository/DriveDomainRepositoryImpl.java b/tracky-web/src/main/java/kernel360/trackyweb/drive/infrastructure/repository/DriveDomainRepositoryCustomImpl.java similarity index 95% rename from tracky-web/src/main/java/kernel360/trackyweb/drive/infrastructure/repository/DriveDomainRepositoryImpl.java rename to tracky-web/src/main/java/kernel360/trackyweb/drive/infrastructure/repository/DriveDomainRepositoryCustomImpl.java index 76c65228..a9e8589d 100644 --- a/tracky-web/src/main/java/kernel360/trackyweb/drive/infrastructure/repository/DriveDomainRepositoryImpl.java +++ b/tracky-web/src/main/java/kernel360/trackyweb/drive/infrastructure/repository/DriveDomainRepositoryCustomImpl.java @@ -40,7 +40,7 @@ @Repository @RequiredArgsConstructor @Slf4j -public class DriveDomainRepositoryImpl implements DriveDomainRepositoryCustom { +public class DriveDomainRepositoryCustomImpl implements DriveDomainRepositoryCustom { private final JPAQueryFactory queryFactory; @@ -208,6 +208,7 @@ public Optional findByDriveId(Long driveId) { return Optional.of(driveHistory); } + //일일 통계 - target Date에 운행한 차량 수 @Override public List getDailyOperationCar(LocalDate targetDate) { LocalDateTime start = targetDate.atStartOfDay(); @@ -308,6 +309,20 @@ private long fetchTotalCount(BooleanBuilder condition) { ).orElse(0L); } + @Override + public Optional findRunningDriveIdByMdn(String mdn) { + return Optional.ofNullable( + queryFactory + .select(driveEntity.id) + .from(driveEntity) + .where( + driveEntity.car.mdn.eq(mdn), + driveEntity.driveOffTime.isNull() + ) + .fetchOne() + ); + } + //검색 조건 private BooleanExpression isEqualMdnContainsRenterName(String mdn, String search) { if (StringUtils.isBlank(search)) { @@ -326,3 +341,4 @@ private BooleanExpression isContainsBizName(String bizSearch) { } } + diff --git a/tracky-web/src/main/java/kernel360/trackyweb/drive/infrastructure/repository/GpsHistoryDomainRepository.java b/tracky-web/src/main/java/kernel360/trackyweb/drive/infrastructure/repository/GpsHistoryDomainRepository.java index 7969f960..33880e5c 100644 --- a/tracky-web/src/main/java/kernel360/trackyweb/drive/infrastructure/repository/GpsHistoryDomainRepository.java +++ b/tracky-web/src/main/java/kernel360/trackyweb/drive/infrastructure/repository/GpsHistoryDomainRepository.java @@ -3,13 +3,14 @@ import java.util.List; import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import kernel360.trackycore.core.domain.entity.GpsHistoryEntity; +import kernel360.trackycore.core.infrastructure.repository.GpsHistoryRepository; +import kernel360.trackyweb.realtime.infrastructure.repository.GpsHistoryRepositoryCustom; -public interface GpsHistoryDomainRepository extends JpaRepository { +public interface GpsHistoryDomainRepository extends GpsHistoryRepository, GpsHistoryRepositoryCustom { @Query(value = """ SELECT * FROM gpshistory diff --git a/tracky-web/src/main/java/kernel360/trackyweb/emitter/EventEmitterService.java b/tracky-web/src/main/java/kernel360/trackyweb/emitter/EventEmitterService.java index 1456bef5..9e2dc707 100644 --- a/tracky-web/src/main/java/kernel360/trackyweb/emitter/EventEmitterService.java +++ b/tracky-web/src/main/java/kernel360/trackyweb/emitter/EventEmitterService.java @@ -15,53 +15,73 @@ public class EventEmitterService { private final Map emitters = new ConcurrentHashMap<>(); - public SseEmitter subscribe(String clientId) { + public SseEmitter subscribe(String driveId) { + + SseEmitter oldEmitter = emitters.get(driveId); + if (oldEmitter != null) { + oldEmitter.complete(); // 연결 종료 + emitters.remove(driveId); + log.info("기존 SSE 연결 종료: driveId = {}", driveId); + } + SseEmitter emitter = new SseEmitter(30 * 60 * 1000L); - emitters.put(clientId, emitter); - log.info("SSE 구독 연결: clientId = {}", clientId); + emitters.put(driveId, emitter); + + log.info("SSE 연결: driveId = {}, emitter={}", driveId, emitter); + + emitter.onCompletion(() -> { + log.info("SSE 연결 종료: driveId = {}", driveId); + emitters.remove(driveId); + }); - emitter.onCompletion(() -> emitters.remove(clientId)); - emitter.onTimeout(() -> emitters.remove(clientId)); - emitter.onError((e) -> emitters.remove(clientId)); + emitter.onTimeout(() -> emitters.remove(driveId)); + emitter.onError((e) -> emitters.remove(driveId)); try { + emitter.send(SseEmitter.event().comment("connected")); + emitter.send(SseEmitter.event() .name("init") .data("SSE 연결 성공")); } catch (Exception e) { - log.error("초기 SSE 메시지 전송 실패: clientId = {}", clientId, e); + log.error("초기 SSE 메시지 전송 실패: driveId = {}", driveId, e); emitter.completeWithError(e); - emitters.remove(clientId); + emitters.remove(driveId); } return emitter; } - public void sendEvent(String eventName, Object data) { - emitters.forEach((key, emitter) -> { - try { - emitter.send(SseEmitter.event() - .name(eventName) - .data(data)); - } catch (IOException e) { - log.warn("SSE 연결 실패 (eventName: {}): {}", eventName, e.getMessage()); - emitter.completeWithError(e); - emitters.remove(key); - } - }); + public void sendToDriveId(String driveId, String eventName, Object data) { + SseEmitter emitter = emitters.get(driveId); + log.info("emitters.get({}) 결과: {}", driveId, emitter); + + if (emitter == null) { + log.warn("SSE 전송 실패: driveId={}, event={}", driveId, eventName); + return; + } + + try { + emitter.send(SseEmitter.event().name(eventName).data(data)); + log.info("emitter.send 성공"); + } catch (Exception e) { + log.error("emitter.send 실패: {}", e.getMessage(), e); + emitter.completeWithError(e); + emitters.remove(driveId); + } } // keep-alive 메시지를 15초마다 전송 @Scheduled(fixedRate = 15000) public void sendKeepAlive() { - emitters.forEach((clientId, emitter) -> { + emitters.forEach((driveId, emitter) -> { try { emitter.send(SseEmitter.event().comment("keep-alive")); } catch (IOException e) { - log.warn("SSE keep-alive 실패: clientId = {}", clientId); + log.warn("SSE keep-alive 실패: driveId = {}", driveId); emitter.completeWithError(e); - emitters.remove(clientId); + emitters.remove(driveId); } }); } diff --git a/tracky-web/src/main/java/kernel360/trackyweb/emitter/EventSseController.java b/tracky-web/src/main/java/kernel360/trackyweb/emitter/EventSseController.java index 29912f2a..8241a451 100644 --- a/tracky-web/src/main/java/kernel360/trackyweb/emitter/EventSseController.java +++ b/tracky-web/src/main/java/kernel360/trackyweb/emitter/EventSseController.java @@ -1,5 +1,6 @@ package kernel360.trackyweb.emitter; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -7,6 +8,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @RestController @RequiredArgsConstructor @@ -15,9 +17,8 @@ public class EventSseController { private final EventEmitterService eventEmitterService; - @GetMapping("/subscribe") - public SseEmitter subscribe(@RequestParam String clientId) { - return eventEmitterService.subscribe(clientId); + @GetMapping(value = "/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public SseEmitter subscribe(@RequestParam String driveId) { + return eventEmitterService.subscribe(driveId); } - } diff --git a/tracky-web/src/main/java/kernel360/trackyweb/emitter/GpsPointResponse.java b/tracky-web/src/main/java/kernel360/trackyweb/emitter/GpsPointResponse.java new file mode 100644 index 00000000..5aaed1ad --- /dev/null +++ b/tracky-web/src/main/java/kernel360/trackyweb/emitter/GpsPointResponse.java @@ -0,0 +1,23 @@ +package kernel360.trackyweb.emitter; + +import kernel360.trackyweb.realtime.application.dto.request.CycleGpsRequest; + +public record GpsPointResponse( + double lat, + double lon, + int ang, + int spd, + String oTime, + double sum +) { + public static GpsPointResponse from(CycleGpsRequest req) { + return new GpsPointResponse( + req.gpsInfo().getLat(), + req.gpsInfo().getLon(), + req.gpsInfo().getAng(), + req.gpsInfo().getSpd(), + req.oTime().toString(), + req.gpsInfo().getSum() + ); + } +} diff --git a/tracky-web/src/main/java/kernel360/trackyweb/realtime/application/dto/RealTimeService.java b/tracky-web/src/main/java/kernel360/trackyweb/realtime/application/RealTimeService.java similarity index 78% rename from tracky-web/src/main/java/kernel360/trackyweb/realtime/application/dto/RealTimeService.java rename to tracky-web/src/main/java/kernel360/trackyweb/realtime/application/RealTimeService.java index a9406058..ab77b842 100644 --- a/tracky-web/src/main/java/kernel360/trackyweb/realtime/application/dto/RealTimeService.java +++ b/tracky-web/src/main/java/kernel360/trackyweb/realtime/application/RealTimeService.java @@ -15,16 +15,17 @@ import kernel360.trackyweb.realtime.application.dto.response.GpsDataResponse; import kernel360.trackyweb.realtime.application.dto.response.RunningCarDetailResponse; import kernel360.trackyweb.realtime.application.dto.response.RunningCarResponse; -// import kernel360.trackyweb.realtime.domain.provider.GpsHistoryDomainProvider; +import kernel360.trackyweb.realtime.domain.provider.GpsHistoryDomainProvider; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @Service @RequiredArgsConstructor - +@Slf4j public class RealTimeService { private final DriveDomainProvider driveDomainProvider; - // private final GpsHistoryDomainProvider gpsHistoryDomainProvider; + private final GpsHistoryDomainProvider gpsHistoryDomainProvider; @Transactional(readOnly = true) public ApiResponse> getRunningCars( @@ -52,15 +53,13 @@ public ApiResponse getRunningCarDetailById(Long id) { return ApiResponse.success(RunningCarDetailResponse.from(drive, sum)); } - // @Transactional(readOnly = true) - // public GpsDataResponse getOneGps(Long id) { - // return gpsHistoryDomainProvider.getOneGpsByDriveId(id); - // } - // - // public List getNowGpsPath(Long id, LocalDateTime nowTime) { - // return gpsHistoryDomainProvider.getGpsListAfterTime(id, nowTime).stream() - // .map(GpsDataResponse::from) - // .toList(); - // } + @Transactional(readOnly = true) + public List getBeforeGpsPath(Long id, LocalDateTime nowTime) { + log.info("nowTime{}", nowTime); + return gpsHistoryDomainProvider.getGpsPathBeforeTime(id, nowTime) + .stream() + .map(GpsDataResponse::from) + .toList(); + } } diff --git a/tracky-web/src/main/java/kernel360/trackyweb/realtime/application/dto/response/GpsDataResponse.java b/tracky-web/src/main/java/kernel360/trackyweb/realtime/application/dto/response/GpsDataResponse.java index ba3b5ffb..b0035627 100644 --- a/tracky-web/src/main/java/kernel360/trackyweb/realtime/application/dto/response/GpsDataResponse.java +++ b/tracky-web/src/main/java/kernel360/trackyweb/realtime/application/dto/response/GpsDataResponse.java @@ -9,7 +9,8 @@ public record GpsDataResponse( int lat, int lon, int ang, - int spd + int spd, + double sum ) { public static GpsDataResponse from(GpsHistoryEntity entity) { return new GpsDataResponse( @@ -17,7 +18,8 @@ public static GpsDataResponse from(GpsHistoryEntity entity) { entity.getLat(), entity.getLon(), entity.getAng(), - entity.getSpd() + entity.getSpd(), + entity.getSum() ); } } diff --git a/tracky-web/src/main/java/kernel360/trackyweb/realtime/application/service/MessageListener.java b/tracky-web/src/main/java/kernel360/trackyweb/realtime/application/service/MessageListener.java index dcf17d7e..7d98d033 100644 --- a/tracky-web/src/main/java/kernel360/trackyweb/realtime/application/service/MessageListener.java +++ b/tracky-web/src/main/java/kernel360/trackyweb/realtime/application/service/MessageListener.java @@ -1,26 +1,41 @@ package kernel360.trackyweb.realtime.application.service; +import java.util.List; + import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Service; -import kernel360.trackyweb.common.sse.GlobalSseEvent; +import kernel360.trackyweb.drive.domain.provider.DriveDomainProvider; import kernel360.trackyweb.emitter.EventEmitterService; +import kernel360.trackyweb.emitter.GpsPointResponse; import kernel360.trackyweb.realtime.application.dto.request.GpsHistoryMessage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +@Slf4j @Service @RequiredArgsConstructor -@Slf4j public class MessageListener { - private final GlobalSseEvent globalSseEvent; + private final DriveDomainProvider driveDomainProvider; private final EventEmitterService eventEmitterService; // GPS 정보 처리 큐 @RabbitListener(queues = "web-queue") public void receiveCarMessages(GpsHistoryMessage messages) { + log.info("Web-Queue 메시지 수신: {}", messages); + + try { + Long driveId = driveDomainProvider.findActiveDriveIdByMdn(messages.mdn()); + + List gpsList = messages.cList().stream() + .map(GpsPointResponse::from) + .toList(); + + eventEmitterService.sendToDriveId(driveId.toString(), "drive_path", gpsList); - eventEmitterService.sendEvent("gps_data", messages); - log.info("Web-Queue 메시지 SSE 전송: {}", messages.toString()); + log.info("SSE 전송 시도: driveId={}, gpsCount={}", driveId, gpsList.size()); + } catch (Exception e) { + log.error("GPS 메시지 처리 실패: {}", messages, e); + } } } diff --git a/tracky-web/src/main/java/kernel360/trackyweb/realtime/domain/provider/GpsHistoryDomainProvider.java b/tracky-web/src/main/java/kernel360/trackyweb/realtime/domain/provider/GpsHistoryDomainProvider.java new file mode 100644 index 00000000..b0e7a32c --- /dev/null +++ b/tracky-web/src/main/java/kernel360/trackyweb/realtime/domain/provider/GpsHistoryDomainProvider.java @@ -0,0 +1,24 @@ +package kernel360.trackyweb.realtime.domain.provider; + +import java.time.LocalDateTime; +import java.util.List; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import kernel360.trackycore.core.domain.entity.GpsHistoryEntity; +import kernel360.trackyweb.drive.infrastructure.repository.GpsHistoryDomainRepository; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class GpsHistoryDomainProvider { + + private final GpsHistoryDomainRepository gpsHistoryDomainRepository; + + @Transactional(readOnly = true) + public List getGpsPathBeforeTime(Long driveId, LocalDateTime nowTime) { + return gpsHistoryDomainRepository.findGpsPathBeforeTime(driveId, nowTime); + } + +} diff --git a/tracky-web/src/main/java/kernel360/trackyweb/realtime/infrastructure/repository/GpsHistoryRepositoryCustom.java b/tracky-web/src/main/java/kernel360/trackyweb/realtime/infrastructure/repository/GpsHistoryRepositoryCustom.java new file mode 100644 index 00000000..00798f5e --- /dev/null +++ b/tracky-web/src/main/java/kernel360/trackyweb/realtime/infrastructure/repository/GpsHistoryRepositoryCustom.java @@ -0,0 +1,14 @@ +package kernel360.trackyweb.realtime.infrastructure.repository; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +import kernel360.trackycore.core.domain.entity.GpsHistoryEntity; + +public interface GpsHistoryRepositoryCustom { + Optional findOneGpsByDriveId(Long id); + + List findGpsPathBeforeTime(Long driveId, LocalDateTime nowTime); + +} diff --git a/tracky-web/src/main/java/kernel360/trackyweb/realtime/infrastructure/repository/GpsHistoryRepositoryCustomImpl.java b/tracky-web/src/main/java/kernel360/trackyweb/realtime/infrastructure/repository/GpsHistoryRepositoryCustomImpl.java new file mode 100644 index 00000000..46ee6678 --- /dev/null +++ b/tracky-web/src/main/java/kernel360/trackyweb/realtime/infrastructure/repository/GpsHistoryRepositoryCustomImpl.java @@ -0,0 +1,49 @@ +package kernel360.trackyweb.realtime.infrastructure.repository; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Repository; + +import com.querydsl.jpa.impl.JPAQueryFactory; + +import kernel360.trackycore.core.domain.entity.GpsHistoryEntity; +import kernel360.trackycore.core.domain.entity.QGpsHistoryEntity; +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class GpsHistoryRepositoryCustomImpl implements GpsHistoryRepositoryCustom { + + private final JPAQueryFactory queryFactory; + + @Override + public Optional findOneGpsByDriveId(Long id) { + QGpsHistoryEntity gps = QGpsHistoryEntity.gpsHistoryEntity; + + GpsHistoryEntity result = queryFactory + .selectFrom(gps) + .where(gps.drive.id.eq(id)) + .orderBy(gps.oTime.desc()) + .limit(1) + .fetchFirst(); + + return Optional.ofNullable(result); + } + + @Override + public List findGpsPathBeforeTime(Long driveId, LocalDateTime nowTime) { + QGpsHistoryEntity gps = QGpsHistoryEntity.gpsHistoryEntity; + + return queryFactory + .selectFrom(gps) + .where( + gps.drive.id.eq(driveId), + gps.oTime.loe(nowTime) + ) + .orderBy(gps.oTime.asc()) + .fetch(); + } + +} diff --git a/tracky-web/src/main/java/kernel360/trackyweb/realtime/presentation/RealTimeController.java b/tracky-web/src/main/java/kernel360/trackyweb/realtime/presentation/RealTimeController.java index f17b6e9b..0e5b9ab5 100644 --- a/tracky-web/src/main/java/kernel360/trackyweb/realtime/presentation/RealTimeController.java +++ b/tracky-web/src/main/java/kernel360/trackyweb/realtime/presentation/RealTimeController.java @@ -1,18 +1,22 @@ package kernel360.trackyweb.realtime.presentation; +import java.time.LocalDateTime; import java.util.List; +import org.springframework.format.annotation.DateTimeFormat; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import io.swagger.v3.oas.annotations.media.Schema; import kernel360.trackycore.core.common.api.ApiResponse; import kernel360.trackyweb.realtime.application.dto.RealTimeService; import kernel360.trackyweb.realtime.application.dto.request.RealTimeCarListRequest; +import kernel360.trackyweb.realtime.application.dto.response.GpsDataResponse; import kernel360.trackyweb.realtime.application.dto.response.RunningCarDetailResponse; import kernel360.trackyweb.realtime.application.dto.response.RunningCarResponse; import kernel360.trackyweb.sign.infrastructure.security.principal.MemberPrincipal; @@ -38,4 +42,12 @@ public ApiResponse getRunningCarDetail(@PathVariable L return realTimeService.getRunningCarDetailById(id); } + @GetMapping("/gps/beforepath/{id}") + public ApiResponse> getBeforeGpsPath( + @PathVariable Long id, + @RequestParam("nowTime") @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") LocalDateTime nowTime + ) { + return ApiResponse.success(realTimeService.getBeforeGpsPath(id, nowTime)); + } + }