diff --git a/frontend/src/api/screeningGuideApi.ts b/frontend/src/api/screeningGuideApi.ts index 0609fa0..4be75d1 100644 --- a/frontend/src/api/screeningGuideApi.ts +++ b/frontend/src/api/screeningGuideApi.ts @@ -72,6 +72,8 @@ export interface ShipInfoResponse { imoNo: string; shipName: string; shipStatus: string; + nationalityCode: string; + nationalityIsoCode: string | null; nationality: string; shipType: string; dwt: string; @@ -87,11 +89,19 @@ export interface CompanyInfoResponse { companyCode: string; fullName: string; abbreviation: string; - country: string; - city: string; status: string; + parentCompanyCode: string | null; + parentCompanyName: string | null; registrationCountry: string; - address: string; + registrationCountryCode: string; + registrationCountryIsoCode: string | null; + controlCountry: string | null; + controlCountryCode: string | null; + controlCountryIsoCode: string | null; + foundedDate: string | null; + email: string | null; + phone: string | null; + website: string | null; } // 지표 현재 상태 diff --git a/frontend/src/components/screening/HistoryTab.tsx b/frontend/src/components/screening/HistoryTab.tsx index 68e2034..0507406 100644 --- a/frontend/src/components/screening/HistoryTab.tsx +++ b/frontend/src/components/screening/HistoryTab.tsx @@ -73,13 +73,11 @@ function StatusBadge({ value }: { value: string | null }) { ); } -function InfoField({ label, value }: { label: string; value: string | null | undefined }) { - return ( -
-
{label}
-
{value || '-'}
-
- ); +function countryFlag(code: string | null | undefined): string { + if (!code || code.length < 2) return ''; + const cc = code.slice(0, 2).toUpperCase(); + const codePoints = [...cc].map((c) => 0x1F1E6 + c.charCodeAt(0) - 65); + return String.fromCodePoint(...codePoints); } function RiskValueCell({ value, narrative }: { value: string | null; narrative?: string }) { @@ -547,29 +545,137 @@ export default function HistoryTab({ lang }: HistoryTabProps) { {expandedSections.has('info') && (
{shipInfo && ( -
- - - - - - - - - - +
+ {/* 좌측: 핵심 식별 정보 */} +
+
+
{shipInfo.shipName || '-'}
+
+
+
+ IMO + {shipInfo.imoNo} +
+
+ MMSI + {shipInfo.mmsiNo || '-'} +
+
+ Status + {shipInfo.shipStatus || '-'} +
+
+
+ + {/* 구분선 */} +
+ + {/* 우측: 스펙 정보 */} +
+
+ 국적 + + {countryFlag(shipInfo.nationalityIsoCode)} {shipInfo.nationality || '-'} + +
+
+ 선종 + {shipInfo.shipType || '-'} +
+
+ DWT + {shipInfo.dwt || '-'} +
+
+ GT + {shipInfo.gt || '-'} +
+
+ 건조연도 + {shipInfo.buildYear || '-'} +
+
)} {companyInfo && ( -
- - - - - - - - +
+ {/* 좌측: 핵심 식별 정보 */} +
+
+
{companyInfo.fullName || '-'}
+ {companyInfo.abbreviation && ( +
{companyInfo.abbreviation}
+ )} +
+
+
+ Code + {companyInfo.companyCode} +
+
+ Status + {companyInfo.status || '-'} +
+ {companyInfo.parentCompanyName && ( +
+ 모회사 + {companyInfo.parentCompanyName} +
+ )} +
+
+ + {/* 구분선 */} +
+ + {/* 우측: 상세 정보 */} +
+
+ 등록국가 + + {countryFlag(companyInfo.registrationCountryIsoCode)} {companyInfo.registrationCountry || '-'} + +
+ {companyInfo.controlCountry && ( +
+ 관리국가 + + {countryFlag(companyInfo.controlCountryIsoCode)} {companyInfo.controlCountry} + +
+ )} + {companyInfo.foundedDate && ( +
+ 설립일 + {companyInfo.foundedDate} +
+ )} + {companyInfo.email && ( +
+ 이메일 + {companyInfo.email} +
+ )} + {companyInfo.phone && ( +
+ 전화 + {companyInfo.phone} +
+ )} + {companyInfo.website && ( +
+ 웹사이트 + + {companyInfo.website} + +
+ )} +
)}
diff --git a/src/main/java/com/snp/batch/global/dto/screening/CompanyInfoResponse.java b/src/main/java/com/snp/batch/global/dto/screening/CompanyInfoResponse.java index cfe76fa..1033d9a 100644 --- a/src/main/java/com/snp/batch/global/dto/screening/CompanyInfoResponse.java +++ b/src/main/java/com/snp/batch/global/dto/screening/CompanyInfoResponse.java @@ -10,9 +10,17 @@ public class CompanyInfoResponse { private String companyCode; private String fullName; private String abbreviation; - private String country; - private String city; private String status; + private String parentCompanyCode; + private String parentCompanyName; private String registrationCountry; - private String address; + private String registrationCountryCode; + private String registrationCountryIsoCode; + private String controlCountry; + private String controlCountryCode; + private String controlCountryIsoCode; + private String foundedDate; + private String email; + private String phone; + private String website; } diff --git a/src/main/java/com/snp/batch/global/dto/screening/ShipInfoResponse.java b/src/main/java/com/snp/batch/global/dto/screening/ShipInfoResponse.java index d122d2a..b3c666d 100644 --- a/src/main/java/com/snp/batch/global/dto/screening/ShipInfoResponse.java +++ b/src/main/java/com/snp/batch/global/dto/screening/ShipInfoResponse.java @@ -10,6 +10,8 @@ public class ShipInfoResponse { private String imoNo; private String shipName; private String shipStatus; + private String nationalityCode; + private String nationalityIsoCode; private String nationality; private String shipType; private String dwt; diff --git a/src/main/java/com/snp/batch/global/model/screening/CompanyDetailInfo.java b/src/main/java/com/snp/batch/global/model/screening/CompanyDetailInfo.java index c1d7923..2d52875 100644 --- a/src/main/java/com/snp/batch/global/model/screening/CompanyDetailInfo.java +++ b/src/main/java/com/snp/batch/global/model/screening/CompanyDetailInfo.java @@ -34,4 +34,28 @@ public class CompanyDetailInfo { @Column(name = "oa_addr", length = 512) private String address; + + @Column(name = "prnt_company_cd", length = 14) + private String parentCompanyCode; + + @Column(name = "country_reg_cd", length = 6) + private String registrationCountryCode; + + @Column(name = "country_ctrl", length = 40) + private String controlCountry; + + @Column(name = "country_ctrl_cd", length = 6) + private String controlCountryCode; + + @Column(name = "company_fndn_ymd", length = 8) + private String foundedDate; + + @Column(name = "eml_addr", length = 320) + private String email; + + @Column(name = "tel", length = 60) + private String phone; + + @Column(name = "wbst_url", length = 120) + private String website; } diff --git a/src/main/java/com/snp/batch/global/model/screening/ShipCountryCode.java b/src/main/java/com/snp/batch/global/model/screening/ShipCountryCode.java new file mode 100644 index 0000000..dcd7df9 --- /dev/null +++ b/src/main/java/com/snp/batch/global/model/screening/ShipCountryCode.java @@ -0,0 +1,25 @@ +package com.snp.batch.global.model.screening; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Table(name = "tb_ship_country_cd", schema = "std_snp_svc") +@Getter +@NoArgsConstructor +public class ShipCountryCode { + + @Id + @Column(name = "ship_country_cd", length = 3) + private String shipCountryCode; + + @Column(name = "cd_nm", length = 100) + private String codeName; + + @Column(name = "iso_two_cd", length = 2) + private String isoTwoCode; + + @Column(name = "iso_thr_cd", length = 3) + private String isoThreeCode; +} diff --git a/src/main/java/com/snp/batch/global/model/screening/ShipInfoMaster.java b/src/main/java/com/snp/batch/global/model/screening/ShipInfoMaster.java index 942c4d0..4fb9950 100644 --- a/src/main/java/com/snp/batch/global/model/screening/ShipInfoMaster.java +++ b/src/main/java/com/snp/batch/global/model/screening/ShipInfoMaster.java @@ -20,6 +20,9 @@ public class ShipInfoMaster { @Column(name = "ship_status", length = 50) private String shipStatus; + @Column(name = "ntnlty_cd", length = 50) + private String nationalityCode; + @Column(name = "ship_ntnlty", length = 57) private String nationality; diff --git a/src/main/java/com/snp/batch/global/repository/screening/ShipCountryCodeRepository.java b/src/main/java/com/snp/batch/global/repository/screening/ShipCountryCodeRepository.java new file mode 100644 index 0000000..7d48f7e --- /dev/null +++ b/src/main/java/com/snp/batch/global/repository/screening/ShipCountryCodeRepository.java @@ -0,0 +1,12 @@ +package com.snp.batch.global.repository.screening; + +import com.snp.batch.global.model.screening.ShipCountryCode; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface ShipCountryCodeRepository extends JpaRepository { + Optional findByShipCountryCode(String shipCountryCode); +} diff --git a/src/main/java/com/snp/batch/service/ScreeningGuideService.java b/src/main/java/com/snp/batch/service/ScreeningGuideService.java index 6d2a378..1b8f296 100644 --- a/src/main/java/com/snp/batch/service/ScreeningGuideService.java +++ b/src/main/java/com/snp/batch/service/ScreeningGuideService.java @@ -10,6 +10,8 @@ import com.snp.batch.global.dto.screening.RiskCategoryResponse; import com.snp.batch.global.dto.screening.RiskIndicatorResponse; import com.snp.batch.global.dto.screening.ShipInfoResponse; import com.snp.batch.global.model.screening.ChangeTypeLang; +import com.snp.batch.global.model.screening.CompanyDetailInfo; +import com.snp.batch.global.model.screening.ShipCountryCode; import com.snp.batch.global.model.screening.CompanyComplianceHistory; import com.snp.batch.global.model.screening.ComplianceIndicator; import com.snp.batch.global.model.screening.ComplianceIndicatorLang; @@ -30,6 +32,7 @@ import com.snp.batch.global.repository.screening.RiskIndicatorCategoryLangReposi import com.snp.batch.global.repository.screening.RiskIndicatorLangRepository; import com.snp.batch.global.repository.screening.RiskIndicatorRepository; import com.snp.batch.global.repository.screening.ShipComplianceHistoryRepository; +import com.snp.batch.global.repository.screening.ShipCountryCodeRepository; import com.snp.batch.global.repository.screening.ShipInfoMasterRepository; import com.snp.batch.global.repository.screening.ShipRiskDetailHistoryRepository; import lombok.RequiredArgsConstructor; @@ -68,6 +71,7 @@ public class ScreeningGuideService { private final CompanyComplianceHistoryRepository companyComplianceHistoryRepo; private final ShipInfoMasterRepository shipInfoMasterRepo; private final CompanyDetailInfoRepository companyDetailInfoRepo; + private final ShipCountryCodeRepository shipCountryCodeRepo; private final JdbcTemplate jdbcTemplate; /** @@ -275,6 +279,8 @@ public class ScreeningGuideService { .imoNo(s.getImoNo()) .shipName(s.getShipName()) .shipStatus(s.getShipStatus()) + .nationalityCode(s.getNationalityCode()) + .nationalityIsoCode(resolveIsoTwoCode(s.getNationalityCode())) .nationality(s.getNationality()) .shipType(s.getShipType()) .dwt(s.getDwt()) @@ -293,16 +299,32 @@ public class ScreeningGuideService { @Transactional(readOnly = true) public CompanyInfoResponse getCompanyInfo(String companyCode) { return companyDetailInfoRepo.findByCompanyCode(companyCode) - .map(c -> CompanyInfoResponse.builder() - .companyCode(c.getCompanyCode()) - .fullName(c.getFullName()) - .abbreviation(c.getAbbreviation()) - .country(c.getCountry()) - .city(c.getCity()) - .status(c.getStatus()) - .registrationCountry(c.getRegistrationCountry()) - .address(c.getAddress()) - .build()) + .map(c -> { + String parentCompanyName = null; + if (c.getParentCompanyCode() != null && !c.getParentCompanyCode().isBlank()) { + parentCompanyName = companyDetailInfoRepo.findByCompanyCode(c.getParentCompanyCode()) + .map(CompanyDetailInfo::getFullName) + .orElse("UNKNOWN"); + } + return CompanyInfoResponse.builder() + .companyCode(c.getCompanyCode()) + .fullName(c.getFullName()) + .abbreviation(c.getAbbreviation()) + .status(c.getStatus()) + .parentCompanyCode(c.getParentCompanyCode()) + .parentCompanyName(parentCompanyName) + .registrationCountry(c.getRegistrationCountry()) + .registrationCountryCode(c.getRegistrationCountryCode()) + .registrationCountryIsoCode(resolveIsoTwoCode(c.getRegistrationCountryCode())) + .controlCountry(c.getControlCountry()) + .controlCountryCode(c.getControlCountryCode()) + .controlCountryIsoCode(resolveIsoTwoCode(c.getControlCountryCode())) + .foundedDate(c.getFoundedDate()) + .email(c.getEmail()) + .phone(c.getPhone()) + .website(c.getWebsite()) + .build(); + }) .orElse(null); } @@ -408,6 +430,13 @@ public class ScreeningGuideService { } } + private String resolveIsoTwoCode(String shipCountryCode) { + if (shipCountryCode == null || shipCountryCode.isBlank()) return null; + return shipCountryCodeRepo.findByShipCountryCode(shipCountryCode) + .map(ShipCountryCode::getIsoTwoCode) + .orElse(null); + } + /** * Risk 지표 컬럼명 → 필드명 매핑 조회 */