diff --git a/src/main/java/gc/mda/signal_batch/domain/ship/controller/ShipImageControllerV2.java b/src/main/java/gc/mda/signal_batch/domain/ship/controller/ShipImageControllerV2.java index 831b899..83c47fa 100644 --- a/src/main/java/gc/mda/signal_batch/domain/ship/controller/ShipImageControllerV2.java +++ b/src/main/java/gc/mda/signal_batch/domain/ship/controller/ShipImageControllerV2.java @@ -1,6 +1,7 @@ package gc.mda.signal_batch.domain.ship.controller; import gc.mda.signal_batch.domain.ship.dto.ShipImageDto; +import gc.mda.signal_batch.domain.ship.dto.ValidShipImageDto; import gc.mda.signal_batch.domain.ship.service.ShipImageService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -57,4 +58,26 @@ public class ShipImageControllerV2 { return ResponseEntity.ok(images); } + + @GetMapping("/valid-list") + @Operation( + summary = "선박 사진 보유 IMO 전체 목록", + description = "선박 사진이 등록된 모든 IMO 번호와 대표 썸네일 경로를 반환합니다. 초기 로딩 시 선박 썸네일 표시 용도입니다." + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "조회 성공", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ValidShipImageDto.class))) + ) + }) + public ResponseEntity> getValidShipImageList() { + long start = System.currentTimeMillis(); + + List result = shipImageService.getAllValidShipImages(); + + log.info("Valid ship image list: {} IMOs ({}ms)", result.size(), System.currentTimeMillis() - start); + + return ResponseEntity.ok(result); + } } diff --git a/src/main/java/gc/mda/signal_batch/domain/ship/dto/ValidShipImageDto.java b/src/main/java/gc/mda/signal_batch/domain/ship/dto/ValidShipImageDto.java new file mode 100644 index 0000000..38c1fd8 --- /dev/null +++ b/src/main/java/gc/mda/signal_batch/domain/ship/dto/ValidShipImageDto.java @@ -0,0 +1,21 @@ +package gc.mda.signal_batch.domain.ship.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "선박 사진 보유 IMO 정보 (썸네일 경로 포함)") +public class ValidShipImageDto { + + @Schema(description = "IMO 번호", example = "9141833") + private Integer imo; + + @Schema(description = "썸네일 이미지 경로", example = "/shipimg/8161/816100_1.jpg") + private String thumbnailPath; +} diff --git a/src/main/java/gc/mda/signal_batch/domain/ship/repository/ShipImageRepository.java b/src/main/java/gc/mda/signal_batch/domain/ship/repository/ShipImageRepository.java index a97989f..8e7540e 100644 --- a/src/main/java/gc/mda/signal_batch/domain/ship/repository/ShipImageRepository.java +++ b/src/main/java/gc/mda/signal_batch/domain/ship/repository/ShipImageRepository.java @@ -47,6 +47,26 @@ public class ShipImageRepository { return queryJdbcTemplate.query(FIND_BY_IMO_SQL, ROW_MAPPER, imo); } + private static final String FIND_ALL_VALID_SQL = """ + SELECT DISTINCT ON (lrno) lrno, pic_id + FROM signal.t_snp_ship_img + ORDER BY lrno, dateofphoto DESC NULLS LAST, pic_id DESC + """; + + private static final RowMapper VALID_ROW_MAPPER = (rs, rowNum) -> { + ValidShipImageData data = new ValidShipImageData(); + data.imo = rs.getInt("lrno"); + data.picId = rs.getInt("pic_id"); + return data; + }; + + /** + * 선박 사진이 있는 모든 IMO의 대표 이미지 조회 (IMO당 1건, 최신 사진 우선) + */ + public List findAllValidShipImages() { + return queryJdbcTemplate.query(FIND_ALL_VALID_SQL, VALID_ROW_MAPPER); + } + /** * Repository 내부에서 사용하는 Raw 데이터 클래스 */ @@ -55,4 +75,9 @@ public class ShipImageRepository { public String copyright; public java.time.LocalDate dateOfPhoto; } + + public static class ValidShipImageData { + public Integer imo; + public Integer picId; + } } diff --git a/src/main/java/gc/mda/signal_batch/domain/ship/service/ShipImageService.java b/src/main/java/gc/mda/signal_batch/domain/ship/service/ShipImageService.java index eb80200..9e8cd04 100644 --- a/src/main/java/gc/mda/signal_batch/domain/ship/service/ShipImageService.java +++ b/src/main/java/gc/mda/signal_batch/domain/ship/service/ShipImageService.java @@ -1,8 +1,10 @@ package gc.mda.signal_batch.domain.ship.service; import gc.mda.signal_batch.domain.ship.dto.ShipImageDto; +import gc.mda.signal_batch.domain.ship.dto.ValidShipImageDto; import gc.mda.signal_batch.domain.ship.repository.ShipImageRepository; import gc.mda.signal_batch.domain.ship.repository.ShipImageRepository.ShipImageRawData; +import gc.mda.signal_batch.domain.ship.repository.ShipImageRepository.ValidShipImageData; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -31,6 +33,20 @@ public class ShipImageService { .collect(Collectors.toList()); } + /** + * 선박 사진이 있는 전체 IMO 목록과 썸네일 경로 조회 + */ + public List getAllValidShipImages() { + List rawList = shipImageRepository.findAllValidShipImages(); + + return rawList.stream() + .map(raw -> ValidShipImageDto.builder() + .imo(raw.imo) + .thumbnailPath(buildImagePath(raw.picId) + "_1.jpg") + .build()) + .collect(Collectors.toList()); + } + /** * Raw 데이터를 DTO로 변환 */ diff --git a/src/main/java/gc/mda/signal_batch/domain/vessel/dto/RecentVesselPositionDto.java b/src/main/java/gc/mda/signal_batch/domain/vessel/dto/RecentVesselPositionDto.java index d3117d8..4a5e1d4 100644 --- a/src/main/java/gc/mda/signal_batch/domain/vessel/dto/RecentVesselPositionDto.java +++ b/src/main/java/gc/mda/signal_batch/domain/vessel/dto/RecentVesselPositionDto.java @@ -20,6 +20,9 @@ public class RecentVesselPositionDto { @Schema(description = "MMSI (9자리, 한국선박 440/441로 시작)", example = "440113620") private String mmsi; + @Schema(description = "IMO 번호 (0이면 미부여)", example = "9141833") + private Long imo; + @Schema(description = "경도 (WGS84)", example = "127.0638") private Double lon; diff --git a/src/main/java/gc/mda/signal_batch/domain/vessel/service/VesselPositionService.java b/src/main/java/gc/mda/signal_batch/domain/vessel/service/VesselPositionService.java index 9aa68ef..f41acff 100644 --- a/src/main/java/gc/mda/signal_batch/domain/vessel/service/VesselPositionService.java +++ b/src/main/java/gc/mda/signal_batch/domain/vessel/service/VesselPositionService.java @@ -84,6 +84,7 @@ public class VesselPositionService { String sql = """ SELECT mmsi, + imo, lon, lat, sog, @@ -118,8 +119,11 @@ public class VesselPositionService { String nationalCode = mmsi != null && mmsi.length() >= 3 ? mmsi.substring(0, 3) : "000"; + long imo = rs.getLong("imo"); + return RecentVesselPositionDto.builder() .mmsi(mmsi) + .imo(imo > 0 ? imo : null) .lon(rs.getDouble("lon")) .lat(rs.getDouble("lat")) .sog(rs.getBigDecimal("sog"))