Merge pull request 'feat: recent-positions IMO 필드 + 선박사진 보유 목록 API' (#54) from feature/dashboard-phase-1 into develop
This commit is contained in:
커밋
c2a0c43fd6
@ -1,6 +1,7 @@
|
|||||||
package gc.mda.signal_batch.domain.ship.controller;
|
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.ShipImageDto;
|
||||||
|
import gc.mda.signal_batch.domain.ship.dto.ValidShipImageDto;
|
||||||
import gc.mda.signal_batch.domain.ship.service.ShipImageService;
|
import gc.mda.signal_batch.domain.ship.service.ShipImageService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
@ -57,4 +58,26 @@ public class ShipImageControllerV2 {
|
|||||||
|
|
||||||
return ResponseEntity.ok(images);
|
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<List<ValidShipImageDto>> getValidShipImageList() {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
|
||||||
|
List<ValidShipImageDto> result = shipImageService.getAllValidShipImages();
|
||||||
|
|
||||||
|
log.info("Valid ship image list: {} IMOs ({}ms)", result.size(), System.currentTimeMillis() - start);
|
||||||
|
|
||||||
|
return ResponseEntity.ok(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
@ -47,6 +47,26 @@ public class ShipImageRepository {
|
|||||||
return queryJdbcTemplate.query(FIND_BY_IMO_SQL, ROW_MAPPER, imo);
|
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<ValidShipImageData> 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<ValidShipImageData> findAllValidShipImages() {
|
||||||
|
return queryJdbcTemplate.query(FIND_ALL_VALID_SQL, VALID_ROW_MAPPER);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository 내부에서 사용하는 Raw 데이터 클래스
|
* Repository 내부에서 사용하는 Raw 데이터 클래스
|
||||||
*/
|
*/
|
||||||
@ -55,4 +75,9 @@ public class ShipImageRepository {
|
|||||||
public String copyright;
|
public String copyright;
|
||||||
public java.time.LocalDate dateOfPhoto;
|
public java.time.LocalDate dateOfPhoto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ValidShipImageData {
|
||||||
|
public Integer imo;
|
||||||
|
public Integer picId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
package gc.mda.signal_batch.domain.ship.service;
|
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.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;
|
||||||
import gc.mda.signal_batch.domain.ship.repository.ShipImageRepository.ShipImageRawData;
|
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.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -31,6 +33,20 @@ public class ShipImageService {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 선박 사진이 있는 전체 IMO 목록과 썸네일 경로 조회
|
||||||
|
*/
|
||||||
|
public List<ValidShipImageDto> getAllValidShipImages() {
|
||||||
|
List<ValidShipImageData> 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로 변환
|
* Raw 데이터를 DTO로 변환
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -20,6 +20,9 @@ public class RecentVesselPositionDto {
|
|||||||
@Schema(description = "MMSI (9자리, 한국선박 440/441로 시작)", example = "440113620")
|
@Schema(description = "MMSI (9자리, 한국선박 440/441로 시작)", example = "440113620")
|
||||||
private String mmsi;
|
private String mmsi;
|
||||||
|
|
||||||
|
@Schema(description = "IMO 번호 (0이면 미부여)", example = "9141833")
|
||||||
|
private Long imo;
|
||||||
|
|
||||||
@Schema(description = "경도 (WGS84)", example = "127.0638")
|
@Schema(description = "경도 (WGS84)", example = "127.0638")
|
||||||
private Double lon;
|
private Double lon;
|
||||||
|
|
||||||
|
|||||||
@ -84,6 +84,7 @@ public class VesselPositionService {
|
|||||||
String sql = """
|
String sql = """
|
||||||
SELECT
|
SELECT
|
||||||
mmsi,
|
mmsi,
|
||||||
|
imo,
|
||||||
lon,
|
lon,
|
||||||
lat,
|
lat,
|
||||||
sog,
|
sog,
|
||||||
@ -118,8 +119,11 @@ public class VesselPositionService {
|
|||||||
String nationalCode = mmsi != null && mmsi.length() >= 3
|
String nationalCode = mmsi != null && mmsi.length() >= 3
|
||||||
? mmsi.substring(0, 3) : "000";
|
? mmsi.substring(0, 3) : "000";
|
||||||
|
|
||||||
|
long imo = rs.getLong("imo");
|
||||||
|
|
||||||
return RecentVesselPositionDto.builder()
|
return RecentVesselPositionDto.builder()
|
||||||
.mmsi(mmsi)
|
.mmsi(mmsi)
|
||||||
|
.imo(imo > 0 ? imo : null)
|
||||||
.lon(rs.getDouble("lon"))
|
.lon(rs.getDouble("lon"))
|
||||||
.lat(rs.getDouble("lat"))
|
.lat(rs.getDouble("lat"))
|
||||||
.sog(rs.getBigDecimal("sog"))
|
.sog(rs.getBigDecimal("sog"))
|
||||||
|
|||||||
불러오는 중...
Reference in New Issue
Block a user