Compare commits
No commits in common. "49bfc416bd3b4a10e65d166e96be83f4bc495ca4" and "2fab1b56a36b240d2e8a677e92e771d4fd455132" have entirely different histories.
49bfc416bd
...
2fab1b56a3
@ -4,24 +4,13 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2026-04-09]
|
||||
|
||||
### 추가
|
||||
- swagger.json 기반 Swagger 응답 스키마 DTO 자동 생성 (#14)
|
||||
- POST 요청 시 requestBody 스키마 자동 생성
|
||||
- POST BODY 파라미터 미등록 시 JsonNode body 자동 추가
|
||||
|
||||
### 변경
|
||||
- Swagger API 그룹을 WebClient 종류별 3개로 분리 (Ships/AIS/Web Services)
|
||||
- Swagger/코드 생성기에서 bypass 용어 제거
|
||||
- Screening Guide API를 운영환경에서도 노출
|
||||
- API 카탈로그 Swagger 링크를 WebClient 종류별 그룹으로 연결
|
||||
|
||||
## [2026-04-08]
|
||||
## [2026-04-08.2]
|
||||
|
||||
### 추가
|
||||
- UpdatedRiskList 엔드포인트 변경 및 Ship 데이터 조회 모듈 추가
|
||||
|
||||
## [2026-04-08]
|
||||
|
||||
### 변경
|
||||
- Basic Auth 인증 비활성화 (프록시 서버로 인증 이관)
|
||||
- Swagger Basic Auth 보안 스키마 제거
|
||||
|
||||
@ -39,7 +39,6 @@ export interface BypassConfigResponse {
|
||||
description: string;
|
||||
generated: boolean;
|
||||
generatedAt: string | null;
|
||||
responseSchemaClass: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
params: BypassParamDto[];
|
||||
|
||||
@ -14,7 +14,6 @@ interface BypassConfig {
|
||||
domainName: string;
|
||||
endpointName: string;
|
||||
displayName: string;
|
||||
webclientBean: string;
|
||||
httpMethod: string;
|
||||
externalPath: string;
|
||||
description: string;
|
||||
@ -37,18 +36,15 @@ const METHOD_COLORS: Record<string, string> = {
|
||||
DELETE: 'bg-red-100 text-red-700',
|
||||
};
|
||||
|
||||
const SWAGGER_GROUP_MAP: Record<string, string> = {
|
||||
maritimeApiWebClient: '3-1.%20Ships%20API',
|
||||
maritimeAisApiWebClient: '3-2.%20AIS%20API',
|
||||
maritimeServiceApiWebClient: '3-3.%20Web%20Services%20API',
|
||||
};
|
||||
const SWAGGER_BASE = '/snp-global/swagger-ui/index.html?urls.primaryName=3.%20Bypass%20API';
|
||||
|
||||
function buildSwaggerDeepLink(config: BypassConfig): string {
|
||||
const group = SWAGGER_GROUP_MAP[config.webclientBean] ?? '3-1.%20Ships%20API';
|
||||
const base = `/snp-global/swagger-ui/index.html?urls.primaryName=${group}`;
|
||||
// Swagger UI deep link: #/{Tag}/{operationId}
|
||||
// Tag = domainName 첫글자 대문자 (예: compliance → Compliance)
|
||||
// operationId = get{EndpointName}Data (SpringDoc 기본 패턴)
|
||||
const tag = config.domainName.charAt(0).toUpperCase() + config.domainName.slice(1);
|
||||
const operationId = `get${config.endpointName}Data`;
|
||||
return `${base}#/${tag}/${operationId}`;
|
||||
return `${SWAGGER_BASE}#/${tag}/${operationId}`;
|
||||
}
|
||||
|
||||
export default function BypassCatalog() {
|
||||
@ -102,7 +98,7 @@ export default function BypassCatalog() {
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="/snp-global/swagger-ui/index.html"
|
||||
href={SWAGGER_BASE}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="px-4 py-2 text-sm font-medium text-white bg-wing-accent hover:bg-wing-accent/80 rounded-lg transition-colors no-underline"
|
||||
|
||||
@ -5,7 +5,6 @@ import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.info.License;
|
||||
import io.swagger.v3.oas.models.servers.Server;
|
||||
import io.swagger.v3.oas.models.tags.Tag;
|
||||
import org.springdoc.core.models.GroupedOpenApi;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
@ -13,8 +12,6 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Swagger/OpenAPI 3.0 설정
|
||||
@ -44,16 +41,17 @@ public class SwaggerConfig {
|
||||
@ConditionalOnProperty(name = "app.environment", havingValue = "dev", matchIfMissing = true)
|
||||
public GroupedOpenApi bypassConfigApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("2. API Config")
|
||||
.group("2. Bypass Config")
|
||||
.pathsToMatch("/api/bypass-config/**")
|
||||
.addOpenApiCustomizer(openApi -> openApi.info(new Info()
|
||||
.title("API Config")
|
||||
.description("API 설정 및 코드 생성 관리")
|
||||
.title("Bypass Config API")
|
||||
.description("Bypass API 설정 및 코드 생성 관리 API")
|
||||
.version("v1.0.0")))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "app.environment", havingValue = "dev", matchIfMissing = true)
|
||||
public GroupedOpenApi screeningGuideApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("4. Screening Guide")
|
||||
@ -66,59 +64,16 @@ public class SwaggerConfig {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi shipsBypassApi() {
|
||||
return createBypassApiGroup("3-1. Ships API", "[Ship API]",
|
||||
"S&P Global Ships/Events/PSC 데이터를 제공합니다.");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi aisBypassApi() {
|
||||
return createBypassApiGroup("3-2. AIS API", "[AIS API]",
|
||||
"S&P Global AIS(선박위치추적) 데이터를 제공합니다.");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi servicesBypassApi() {
|
||||
return createBypassApiGroup("3-3. Web Services API", "[Service API]",
|
||||
"S&P Global Maritime Web Services 데이터를 제공합니다.");
|
||||
}
|
||||
|
||||
private GroupedOpenApi createBypassApiGroup(String group, String tagPrefix, String description) {
|
||||
public GroupedOpenApi bypassApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group(group)
|
||||
.group("3. Bypass API")
|
||||
.pathsToMatch("/api/**")
|
||||
.pathsToExclude("/api/bypass-config/**", "/api/screening-guide/**", "/api/bypass-account/**")
|
||||
.addOpenApiCustomizer(openApi -> {
|
||||
openApi.info(new Info()
|
||||
.title(group)
|
||||
.description(description)
|
||||
.title("Bypass API")
|
||||
.description("S&P Global 선박/해운 데이터를 제공합니다.")
|
||||
.version("v1.0.0"));
|
||||
|
||||
// Tag description에 prefix가 포함된 것만 필터링
|
||||
if (openApi.getTags() != null) {
|
||||
Set<String> matchingTags = openApi.getTags().stream()
|
||||
.filter(tag -> tag.getDescription() != null
|
||||
&& tag.getDescription().startsWith(tagPrefix))
|
||||
.map(Tag::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (openApi.getPaths() != null) {
|
||||
if (matchingTags.isEmpty()) {
|
||||
// 매칭 태그가 없으면 모든 경로 제거
|
||||
openApi.getPaths().clear();
|
||||
} else {
|
||||
openApi.getPaths().entrySet().removeIf(entry -> {
|
||||
var pathItem = entry.getValue();
|
||||
return pathItem.readOperations().stream()
|
||||
.noneMatch(op -> op.getTags() != null
|
||||
&& op.getTags().stream().anyMatch(matchingTags::contains));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 매칭되지 않는 태그 제거
|
||||
openApi.getTags().removeIf(tag -> !matchingTags.contains(tag.getName()));
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
@ -127,11 +82,11 @@ public class SwaggerConfig {
|
||||
@ConditionalOnProperty(name = "app.environment", havingValue = "dev", matchIfMissing = true)
|
||||
public GroupedOpenApi bypassAccountApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("5. Account Management")
|
||||
.group("5. Bypass Account")
|
||||
.pathsToMatch("/api/bypass-account/**")
|
||||
.addOpenApiCustomizer(openApi -> openApi.info(new Info()
|
||||
.title("Account Management API")
|
||||
.description("API 계정 및 신청 관리")
|
||||
.title("Bypass Account Management API")
|
||||
.description("Bypass API 계정 및 신청 관리 API")
|
||||
.version("v1.0.0")))
|
||||
.build();
|
||||
}
|
||||
@ -168,10 +123,10 @@ public class SwaggerConfig {
|
||||
S&P Global Maritime 데이터 서비스 REST API 문서입니다.
|
||||
|
||||
### 제공 API
|
||||
- **Ships / AIS / Web Services API**: S&P Global 선박/해운 데이터 조회
|
||||
- **API Config**: API 설정 관리
|
||||
- **Bypass API**: S&P Global 선박/해운 데이터 조회
|
||||
- **Bypass Config API**: Bypass API 설정 관리
|
||||
- **Screening Guide API**: Risk & Compliance 스크리닝 가이드
|
||||
- **Account Management API**: API 계정 관리
|
||||
- **Bypass Account API**: API 계정 관리
|
||||
|
||||
### 버전 정보
|
||||
- API Version: v1.0.0
|
||||
|
||||
@ -34,9 +34,6 @@ public class BypassConfigRequest {
|
||||
/** 설명 */
|
||||
private String description;
|
||||
|
||||
/** Swagger 응답 스키마 DTO 클래스 (FQCN) */
|
||||
private String responseSchemaClass;
|
||||
|
||||
/** 파라미터 목록 */
|
||||
private List<BypassParamDto> params;
|
||||
}
|
||||
|
||||
@ -40,9 +40,6 @@ public class BypassConfigResponse {
|
||||
/** 설명 */
|
||||
private String description;
|
||||
|
||||
/** Swagger 응답 스키마 DTO 클래스 (FQCN) */
|
||||
private String responseSchemaClass;
|
||||
|
||||
/** 코드 생성 완료 여부 */
|
||||
private Boolean generated;
|
||||
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
package com.snp.batch.global.dto.bypass;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* S&P Global API 응답 스키마 (Swagger 문서용)
|
||||
* 이 클래스는 자동 생성되었습니다. 직접 수정하지 마세요.
|
||||
*/
|
||||
@Getter
|
||||
@Schema(description = "APSShipResult")
|
||||
public class APSShipResult {
|
||||
@Schema(description = "apsStatus", example = "")
|
||||
private String apsStatus;
|
||||
@Schema(description = "shipCount", example = "0")
|
||||
private Integer shipCount;
|
||||
@Schema(description = "apsShipDetail", example = "")
|
||||
private String apsShipDetail;
|
||||
}
|
||||
@ -1,83 +0,0 @@
|
||||
package com.snp.batch.global.dto.bypass;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* S&P Global API 응답 스키마 (Swagger 문서용)
|
||||
* 이 클래스는 자동 생성되었습니다. 직접 수정하지 마세요.
|
||||
*/
|
||||
@Getter
|
||||
@Schema(description = "ComplianceDetails")
|
||||
public class ComplianceDetails {
|
||||
@Schema(description = "lrimoShipNo", example = "")
|
||||
private String lrimoShipNo;
|
||||
@Schema(description = "dateAmended", example = "")
|
||||
private String dateAmended;
|
||||
@Schema(description = "legalOverall", example = "0")
|
||||
private Integer legalOverall;
|
||||
@Schema(description = "shipBESSanctionList", example = "0")
|
||||
private Integer shipBESSanctionList;
|
||||
@Schema(description = "shipDarkActivityIndicator", example = "0")
|
||||
private Integer shipDarkActivityIndicator;
|
||||
@Schema(description = "shipDetailsNoLongerMaintained", example = "0")
|
||||
private Integer shipDetailsNoLongerMaintained;
|
||||
@Schema(description = "shipEUSanctionList", example = "0")
|
||||
private Integer shipEUSanctionList;
|
||||
@Schema(description = "shipFlagDisputed", example = "0")
|
||||
private Integer shipFlagDisputed;
|
||||
@Schema(description = "shipFlagSanctionedCountry", example = "0")
|
||||
private Integer shipFlagSanctionedCountry;
|
||||
@Schema(description = "shipHistoricalFlagSanctionedCountry", example = "0")
|
||||
private Integer shipHistoricalFlagSanctionedCountry;
|
||||
@Schema(description = "shipOFACNonSDNSanctionList", example = "0")
|
||||
private Integer shipOFACNonSDNSanctionList;
|
||||
@Schema(description = "shipOFACSanctionList", example = "0")
|
||||
private Integer shipOFACSanctionList;
|
||||
@Schema(description = "shipOFACAdvisoryList", example = "0")
|
||||
private Integer shipOFACAdvisoryList;
|
||||
@Schema(description = "shipOwnerOFACSSIList", example = "0")
|
||||
private Integer shipOwnerOFACSSIList;
|
||||
@Schema(description = "shipOwnerAustralianSanctionList", example = "0")
|
||||
private Integer shipOwnerAustralianSanctionList;
|
||||
@Schema(description = "shipOwnerBESSanctionList", example = "0")
|
||||
private Integer shipOwnerBESSanctionList;
|
||||
@Schema(description = "shipOwnerCanadianSanctionList", example = "0")
|
||||
private Integer shipOwnerCanadianSanctionList;
|
||||
@Schema(description = "shipOwnerEUSanctionList", example = "0")
|
||||
private Integer shipOwnerEUSanctionList;
|
||||
@Schema(description = "shipOwnerFATFJurisdiction", example = "0")
|
||||
private Integer shipOwnerFATFJurisdiction;
|
||||
@Schema(description = "shipOwnerHistoricalOFACSanctionedCountry", example = "0")
|
||||
private Integer shipOwnerHistoricalOFACSanctionedCountry;
|
||||
@Schema(description = "shipOwnerOFACSanctionList", example = "0")
|
||||
private Integer shipOwnerOFACSanctionList;
|
||||
@Schema(description = "shipOwnerOFACSanctionedCountry", example = "0")
|
||||
private Integer shipOwnerOFACSanctionedCountry;
|
||||
@Schema(description = "shipOwnerParentCompanyNonCompliance", example = "0")
|
||||
private Integer shipOwnerParentCompanyNonCompliance;
|
||||
@Schema(description = "shipOwnerParentFATFJurisdiction", example = "0")
|
||||
private Integer shipOwnerParentFATFJurisdiction;
|
||||
@Schema(description = "shipOwnerParentOFACSanctionedCountry", example = "0")
|
||||
private Integer shipOwnerParentOFACSanctionedCountry;
|
||||
@Schema(description = "shipOwnerSwissSanctionList", example = "0")
|
||||
private Integer shipOwnerSwissSanctionList;
|
||||
@Schema(description = "shipOwnerUAESanctionList", example = "0")
|
||||
private Integer shipOwnerUAESanctionList;
|
||||
@Schema(description = "shipOwnerUNSanctionList", example = "0")
|
||||
private Integer shipOwnerUNSanctionList;
|
||||
@Schema(description = "shipSanctionedCountryPortCallLast12m", example = "0")
|
||||
private Integer shipSanctionedCountryPortCallLast12m;
|
||||
@Schema(description = "shipSanctionedCountryPortCallLast3m", example = "0")
|
||||
private Integer shipSanctionedCountryPortCallLast3m;
|
||||
@Schema(description = "shipSanctionedCountryPortCallLast6m", example = "0")
|
||||
private Integer shipSanctionedCountryPortCallLast6m;
|
||||
@Schema(description = "shipSecurityLegalDisputeEvent", example = "0")
|
||||
private Integer shipSecurityLegalDisputeEvent;
|
||||
@Schema(description = "shipSTSPartnerNonComplianceLast12m", example = "0")
|
||||
private Integer shipSTSPartnerNonComplianceLast12m;
|
||||
@Schema(description = "shipSwissSanctionList", example = "0")
|
||||
private Integer shipSwissSanctionList;
|
||||
@Schema(description = "shipUNSanctionList", example = "0")
|
||||
private Integer shipUNSanctionList;
|
||||
}
|
||||
@ -1,99 +0,0 @@
|
||||
package com.snp.batch.global.dto.bypass;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* S&P Global API 응답 스키마 (Swagger 문서용)
|
||||
* 이 클래스는 자동 생성되었습니다. 직접 수정하지 마세요.
|
||||
*/
|
||||
@Getter
|
||||
@Schema(description = "PortFacility")
|
||||
public class PortFacility {
|
||||
@Schema(description = "port_ID", example = "0")
|
||||
private Integer port_ID;
|
||||
@Schema(description = "old_ID", example = "")
|
||||
private String old_ID;
|
||||
@Schema(description = "status", example = "")
|
||||
private String status;
|
||||
@Schema(description = "port_Name", example = "")
|
||||
private String port_Name;
|
||||
@Schema(description = "unlocode", example = "")
|
||||
private String unlocode;
|
||||
@Schema(description = "countryCode", example = "")
|
||||
private String countryCode;
|
||||
@Schema(description = "country_Name", example = "")
|
||||
private String country_Name;
|
||||
@Schema(description = "dec_Lat", example = "0.0")
|
||||
private Double dec_Lat;
|
||||
@Schema(description = "dec_Long", example = "0.0")
|
||||
private Double dec_Long;
|
||||
@Schema(description = "position", example = "")
|
||||
private String position;
|
||||
@Schema(description = "time_Zone", example = "")
|
||||
private String time_Zone;
|
||||
@Schema(description = "dayLight_Saving_Time", example = "false")
|
||||
private Boolean dayLight_Saving_Time;
|
||||
@Schema(description = "maximum_Draft", example = "0.0")
|
||||
private Double maximum_Draft;
|
||||
@Schema(description = "breakbulk_Facilities", example = "false")
|
||||
private Boolean breakbulk_Facilities;
|
||||
@Schema(description = "container_Facilities", example = "false")
|
||||
private Boolean container_Facilities;
|
||||
@Schema(description = "dry_Bulk_Facilities", example = "false")
|
||||
private Boolean dry_Bulk_Facilities;
|
||||
@Schema(description = "liquid_Facilities", example = "false")
|
||||
private Boolean liquid_Facilities;
|
||||
@Schema(description = "roRo_Facilities", example = "false")
|
||||
private Boolean roRo_Facilities;
|
||||
@Schema(description = "passenger_Facilities", example = "false")
|
||||
private Boolean passenger_Facilities;
|
||||
@Schema(description = "dry_Dock_Facilities", example = "false")
|
||||
private Boolean dry_Dock_Facilities;
|
||||
@Schema(description = "lpG_Facilities", example = "0")
|
||||
private Integer lpG_Facilities;
|
||||
@Schema(description = "lnG_Facilities", example = "0")
|
||||
private Integer lnG_Facilities;
|
||||
@Schema(description = "ispS_Compliant", example = "false")
|
||||
private Boolean ispS_Compliant;
|
||||
@Schema(description = "csI_Compliant", example = "false")
|
||||
private Boolean csI_Compliant;
|
||||
@Schema(description = "last_Update", example = "")
|
||||
private String last_Update;
|
||||
@Schema(description = "entry_Date", example = "")
|
||||
private String entry_Date;
|
||||
@Schema(description = "region_Name", example = "")
|
||||
private String region_Name;
|
||||
@Schema(description = "continent_Name", example = "")
|
||||
private String continent_Name;
|
||||
@Schema(description = "master_POID", example = "")
|
||||
private String master_POID;
|
||||
@Schema(description = "wS_Port", example = "0")
|
||||
private Integer wS_Port;
|
||||
@Schema(description = "max_LOA", example = "0.0")
|
||||
private Double max_LOA;
|
||||
@Schema(description = "max_Beam", example = "0.0")
|
||||
private Double max_Beam;
|
||||
@Schema(description = "max_DWT", example = "0")
|
||||
private Integer max_DWT;
|
||||
@Schema(description = "max_Offshore_Draught", example = "0.0")
|
||||
private Double max_Offshore_Draught;
|
||||
@Schema(description = "max_Offshore_LOA", example = "0.0")
|
||||
private Double max_Offshore_LOA;
|
||||
@Schema(description = "max_Offshore_BCM", example = "0.0")
|
||||
private Double max_Offshore_BCM;
|
||||
@Schema(description = "max_Offshore_DWT", example = "0.0")
|
||||
private Double max_Offshore_DWT;
|
||||
@Schema(description = "lnG_Bunker", example = "false")
|
||||
private Boolean lnG_Bunker;
|
||||
@Schema(description = "dO_Bunker", example = "false")
|
||||
private Boolean dO_Bunker;
|
||||
@Schema(description = "fO_Bunker", example = "false")
|
||||
private Boolean fO_Bunker;
|
||||
@Schema(description = "free_Trade_Zone", example = "false")
|
||||
private Boolean free_Trade_Zone;
|
||||
@Schema(description = "ecO_Port", example = "false")
|
||||
private Boolean ecO_Port;
|
||||
@Schema(description = "emission_Control_Area", example = "false")
|
||||
private Boolean emission_Control_Area;
|
||||
}
|
||||
@ -1,97 +0,0 @@
|
||||
package com.snp.batch.global.dto.bypass;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* S&P Global API 응답 스키마 (Swagger 문서용)
|
||||
* 이 클래스는 자동 생성되었습니다. 직접 수정하지 마세요.
|
||||
*/
|
||||
@Getter
|
||||
@Schema(description = "RiskDetails")
|
||||
public class RiskDetails {
|
||||
@Schema(description = "lrno", example = "")
|
||||
private String lrno;
|
||||
@Schema(description = "lastUpdated", example = "")
|
||||
private String lastUpdated;
|
||||
@Schema(description = "riskDataMaintained", example = "0")
|
||||
private Integer riskDataMaintained;
|
||||
@Schema(description = "daysSinceLastSeenOnAIS", example = "0")
|
||||
private Integer daysSinceLastSeenOnAIS;
|
||||
@Schema(description = "daysUnderAIS", example = "0")
|
||||
private Integer daysUnderAIS;
|
||||
@Schema(description = "imoCorrectOnAIS", example = "0")
|
||||
private Integer imoCorrectOnAIS;
|
||||
@Schema(description = "sailingUnderName", example = "0")
|
||||
private Integer sailingUnderName;
|
||||
@Schema(description = "anomalousMessagesFromMMSI", example = "0")
|
||||
private Integer anomalousMessagesFromMMSI;
|
||||
@Schema(description = "mostRecentDarkActivity", example = "0")
|
||||
private Integer mostRecentDarkActivity;
|
||||
@Schema(description = "portCalls", example = "0")
|
||||
private Integer portCalls;
|
||||
@Schema(description = "portRisk", example = "0")
|
||||
private Integer portRisk;
|
||||
@Schema(description = "stsOperations", example = "0")
|
||||
private Integer stsOperations;
|
||||
@Schema(description = "driftingHighSeas", example = "0")
|
||||
private Integer driftingHighSeas;
|
||||
@Schema(description = "riskEvents", example = "0")
|
||||
private Integer riskEvents;
|
||||
@Schema(description = "flagChanges", example = "0")
|
||||
private Integer flagChanges;
|
||||
@Schema(description = "flagParisMOUPerformance", example = "0")
|
||||
private Integer flagParisMOUPerformance;
|
||||
@Schema(description = "flagTokyoMOUPeformance", example = "0")
|
||||
private Integer flagTokyoMOUPeformance;
|
||||
@Schema(description = "flagUSCGMOUPerformance", example = "0")
|
||||
private Integer flagUSCGMOUPerformance;
|
||||
@Schema(description = "uscgQualship21", example = "0")
|
||||
private Integer uscgQualship21;
|
||||
@Schema(description = "timeSincePSCInspection", example = "0")
|
||||
private Integer timeSincePSCInspection;
|
||||
@Schema(description = "pscInspections", example = "0")
|
||||
private Integer pscInspections;
|
||||
@Schema(description = "pscDefects", example = "0")
|
||||
private Integer pscDefects;
|
||||
@Schema(description = "pscDetentions", example = "0")
|
||||
private Integer pscDetentions;
|
||||
@Schema(description = "currentSMCCertificate", example = "0")
|
||||
private Integer currentSMCCertificate;
|
||||
@Schema(description = "docChanges", example = "0")
|
||||
private Integer docChanges;
|
||||
@Schema(description = "currentClass", example = "0")
|
||||
private Integer currentClass;
|
||||
@Schema(description = "classStatusChanges", example = "0")
|
||||
private Integer classStatusChanges;
|
||||
@Schema(description = "pandICoverage", example = "0")
|
||||
private Integer pandICoverage;
|
||||
@Schema(description = "nameChanges", example = "0")
|
||||
private Integer nameChanges;
|
||||
@Schema(description = "gboChanges", example = "0")
|
||||
private Integer gboChanges;
|
||||
@Schema(description = "ageOfShip", example = "0")
|
||||
private Integer ageOfShip;
|
||||
@Schema(description = "iuuFishingViolation", example = "0")
|
||||
private Integer iuuFishingViolation;
|
||||
@Schema(description = "draughtChanges", example = "0")
|
||||
private Integer draughtChanges;
|
||||
@Schema(description = "mostRecentSanctionedPortCall", example = "0")
|
||||
private Integer mostRecentSanctionedPortCall;
|
||||
@Schema(description = "singleShipOperation", example = "0")
|
||||
private Integer singleShipOperation;
|
||||
@Schema(description = "fleetSafety", example = "0")
|
||||
private Integer fleetSafety;
|
||||
@Schema(description = "fleetPSC", example = "0")
|
||||
private Integer fleetPSC;
|
||||
@Schema(description = "specialSurveyOverdue", example = "0")
|
||||
private Integer specialSurveyOverdue;
|
||||
@Schema(description = "ownerUnknown", example = "0")
|
||||
private Integer ownerUnknown;
|
||||
@Schema(description = "russianPortCall", example = "0")
|
||||
private Integer russianPortCall;
|
||||
@Schema(description = "russianOwnerRegistration", example = "0")
|
||||
private Integer russianOwnerRegistration;
|
||||
@Schema(description = "russianSTS", example = "0")
|
||||
private Integer russianSTS;
|
||||
}
|
||||
@ -1,181 +0,0 @@
|
||||
package com.snp.batch.global.dto.bypass;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* S&P Global API 응답 스키마 (Swagger 문서용)
|
||||
* 이 클래스는 자동 생성되었습니다. 직접 수정하지 마세요.
|
||||
*/
|
||||
@Getter
|
||||
@Schema(description = "RiskWithNarrativesDetails")
|
||||
public class RiskWithNarrativesDetails {
|
||||
@Schema(description = "lrno", example = "")
|
||||
private String lrno;
|
||||
@Schema(description = "lastUpdated", example = "")
|
||||
private String lastUpdated;
|
||||
@Schema(description = "riskDataMaintained", example = "0")
|
||||
private Integer riskDataMaintained;
|
||||
@Schema(description = "daysSinceLastSeenOnAIS", example = "0")
|
||||
private Integer daysSinceLastSeenOnAIS;
|
||||
@Schema(description = "daysSinceLastSeenOnAISNarrative", example = "")
|
||||
private String daysSinceLastSeenOnAISNarrative;
|
||||
@Schema(description = "daysUnderAIS", example = "0")
|
||||
private Integer daysUnderAIS;
|
||||
@Schema(description = "daysUnderAISNarrative", example = "")
|
||||
private String daysUnderAISNarrative;
|
||||
@Schema(description = "imoCorrectOnAIS", example = "0")
|
||||
private Integer imoCorrectOnAIS;
|
||||
@Schema(description = "imoCorrectOnAISNarrative", example = "")
|
||||
private String imoCorrectOnAISNarrative;
|
||||
@Schema(description = "sailingUnderName", example = "0")
|
||||
private Integer sailingUnderName;
|
||||
@Schema(description = "sailingUnderNameNarrative", example = "")
|
||||
private String sailingUnderNameNarrative;
|
||||
@Schema(description = "anomalousMessagesFromMMSI", example = "0")
|
||||
private Integer anomalousMessagesFromMMSI;
|
||||
@Schema(description = "anomalousMessagesFromMMSINarrative", example = "")
|
||||
private String anomalousMessagesFromMMSINarrative;
|
||||
@Schema(description = "mostRecentDarkActivity", example = "0")
|
||||
private Integer mostRecentDarkActivity;
|
||||
@Schema(description = "mostRecentDarkActivityNarrative", example = "")
|
||||
private String mostRecentDarkActivityNarrative;
|
||||
@Schema(description = "portCalls", example = "0")
|
||||
private Integer portCalls;
|
||||
@Schema(description = "portCallsNarrative", example = "")
|
||||
private String portCallsNarrative;
|
||||
@Schema(description = "portRisk", example = "0")
|
||||
private Integer portRisk;
|
||||
@Schema(description = "portRiskNarrative", example = "")
|
||||
private String portRiskNarrative;
|
||||
@Schema(description = "stsOperations", example = "0")
|
||||
private Integer stsOperations;
|
||||
@Schema(description = "stsOperationsNarrative", example = "")
|
||||
private String stsOperationsNarrative;
|
||||
@Schema(description = "driftingHighSeas", example = "0")
|
||||
private Integer driftingHighSeas;
|
||||
@Schema(description = "driftingHighSeasNarrative", example = "")
|
||||
private String driftingHighSeasNarrative;
|
||||
@Schema(description = "riskEvents", example = "0")
|
||||
private Integer riskEvents;
|
||||
@Schema(description = "riskEventNarrative", example = "")
|
||||
private String riskEventNarrative;
|
||||
@Schema(description = "riskEventNarrativeExtended", example = "")
|
||||
private String riskEventNarrativeExtended;
|
||||
@Schema(description = "flagChanges", example = "0")
|
||||
private Integer flagChanges;
|
||||
@Schema(description = "flagChangeNarrative", example = "")
|
||||
private String flagChangeNarrative;
|
||||
@Schema(description = "flagParisMOUPerformance", example = "0")
|
||||
private Integer flagParisMOUPerformance;
|
||||
@Schema(description = "flagParisMOUPerformanceNarrative", example = "")
|
||||
private String flagParisMOUPerformanceNarrative;
|
||||
@Schema(description = "flagTokyoMOUPeformance", example = "0")
|
||||
private Integer flagTokyoMOUPeformance;
|
||||
@Schema(description = "flagTokyoMOUPeformanceNarrative", example = "")
|
||||
private String flagTokyoMOUPeformanceNarrative;
|
||||
@Schema(description = "flagUSCGMOUPerformance", example = "0")
|
||||
private Integer flagUSCGMOUPerformance;
|
||||
@Schema(description = "flagUSCGMOUPerformanceNarrative", example = "")
|
||||
private String flagUSCGMOUPerformanceNarrative;
|
||||
@Schema(description = "uscgQualship21", example = "0")
|
||||
private Integer uscgQualship21;
|
||||
@Schema(description = "uscgQualship21Narrative", example = "")
|
||||
private String uscgQualship21Narrative;
|
||||
@Schema(description = "timeSincePSCInspection", example = "0")
|
||||
private Integer timeSincePSCInspection;
|
||||
@Schema(description = "timeSincePSCInspectionNarrative", example = "")
|
||||
private String timeSincePSCInspectionNarrative;
|
||||
@Schema(description = "pscInspections", example = "0")
|
||||
private Integer pscInspections;
|
||||
@Schema(description = "pscInspectionNarrative", example = "")
|
||||
private String pscInspectionNarrative;
|
||||
@Schema(description = "pscDefects", example = "0")
|
||||
private Integer pscDefects;
|
||||
@Schema(description = "pscDefectsNarrative", example = "")
|
||||
private String pscDefectsNarrative;
|
||||
@Schema(description = "pscDetentions", example = "0")
|
||||
private Integer pscDetentions;
|
||||
@Schema(description = "pscDetentionsNarrative", example = "")
|
||||
private String pscDetentionsNarrative;
|
||||
@Schema(description = "currentSMCCertificate", example = "0")
|
||||
private Integer currentSMCCertificate;
|
||||
@Schema(description = "currentSMCCertificateNarrative", example = "")
|
||||
private String currentSMCCertificateNarrative;
|
||||
@Schema(description = "docChanges", example = "0")
|
||||
private Integer docChanges;
|
||||
@Schema(description = "docChangesNarrative", example = "")
|
||||
private String docChangesNarrative;
|
||||
@Schema(description = "currentClass", example = "0")
|
||||
private Integer currentClass;
|
||||
@Schema(description = "currentClassNarrative", example = "")
|
||||
private String currentClassNarrative;
|
||||
@Schema(description = "currentClassNarrativeExtended", example = "")
|
||||
private String currentClassNarrativeExtended;
|
||||
@Schema(description = "classStatusChanges", example = "0")
|
||||
private Integer classStatusChanges;
|
||||
@Schema(description = "classStatusChangesNarrative", example = "")
|
||||
private String classStatusChangesNarrative;
|
||||
@Schema(description = "pandICoverage", example = "0")
|
||||
private Integer pandICoverage;
|
||||
@Schema(description = "pandICoverageNarrative", example = "")
|
||||
private String pandICoverageNarrative;
|
||||
@Schema(description = "pandICoverageNarrativeExtended", example = "")
|
||||
private String pandICoverageNarrativeExtended;
|
||||
@Schema(description = "nameChanges", example = "0")
|
||||
private Integer nameChanges;
|
||||
@Schema(description = "nameChangesNarrative", example = "")
|
||||
private String nameChangesNarrative;
|
||||
@Schema(description = "gboChanges", example = "0")
|
||||
private Integer gboChanges;
|
||||
@Schema(description = "gboChangesNarrative", example = "")
|
||||
private String gboChangesNarrative;
|
||||
@Schema(description = "ageOfShip", example = "0")
|
||||
private Integer ageOfShip;
|
||||
@Schema(description = "ageofShipNarrative", example = "")
|
||||
private String ageofShipNarrative;
|
||||
@Schema(description = "iuuFishingViolation", example = "0")
|
||||
private Integer iuuFishingViolation;
|
||||
@Schema(description = "iuuFishingNarrative", example = "")
|
||||
private String iuuFishingNarrative;
|
||||
@Schema(description = "draughtChanges", example = "0")
|
||||
private Integer draughtChanges;
|
||||
@Schema(description = "draughtChangesNarrative", example = "")
|
||||
private String draughtChangesNarrative;
|
||||
@Schema(description = "mostRecentSanctionedPortCall", example = "0")
|
||||
private Integer mostRecentSanctionedPortCall;
|
||||
@Schema(description = "mostRecentSanctionedPortCallNarrative", example = "")
|
||||
private String mostRecentSanctionedPortCallNarrative;
|
||||
@Schema(description = "singleShipOperation", example = "0")
|
||||
private Integer singleShipOperation;
|
||||
@Schema(description = "singleShipOperationNarrative", example = "")
|
||||
private String singleShipOperationNarrative;
|
||||
@Schema(description = "fleetSafety", example = "0")
|
||||
private Integer fleetSafety;
|
||||
@Schema(description = "fleetSafetyNarrative", example = "")
|
||||
private String fleetSafetyNarrative;
|
||||
@Schema(description = "fleetPSC", example = "0")
|
||||
private Integer fleetPSC;
|
||||
@Schema(description = "fleetPSCNarrative", example = "")
|
||||
private String fleetPSCNarrative;
|
||||
@Schema(description = "specialSurveyOverdue", example = "0")
|
||||
private Integer specialSurveyOverdue;
|
||||
@Schema(description = "specialSurveyOverdueNarrative", example = "")
|
||||
private String specialSurveyOverdueNarrative;
|
||||
@Schema(description = "ownerUnknown", example = "0")
|
||||
private Integer ownerUnknown;
|
||||
@Schema(description = "ownerUnknownNarrative", example = "")
|
||||
private String ownerUnknownNarrative;
|
||||
@Schema(description = "russianPortCall", example = "0")
|
||||
private Integer russianPortCall;
|
||||
@Schema(description = "russianPortCallNarrative", example = "")
|
||||
private String russianPortCallNarrative;
|
||||
@Schema(description = "russianOwnerRegistration", example = "0")
|
||||
private Integer russianOwnerRegistration;
|
||||
@Schema(description = "russianOwnerRegistrationNarrative", example = "")
|
||||
private String russianOwnerRegistrationNarrative;
|
||||
@Schema(description = "russianSTS", example = "0")
|
||||
private Integer russianSTS;
|
||||
@Schema(description = "russianSTSNarrative", example = "")
|
||||
private String russianSTSNarrative;
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
package com.snp.batch.global.dto.bypass;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* S&P Global API 응답 스키마 (Swagger 문서용)
|
||||
* 이 클래스는 자동 생성되었습니다. 직접 수정하지 마세요.
|
||||
*/
|
||||
@Getter
|
||||
@Schema(description = "TargetsParameters")
|
||||
public class TargetsParameters {
|
||||
@Schema(description = "sinceSeconds", example = "0")
|
||||
private Integer sinceSeconds;
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
package com.snp.batch.global.dto.bypass;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* S&P Global API 응답 스키마 (Swagger 문서용)
|
||||
* 이 클래스는 자동 생성되었습니다. 직접 수정하지 마세요.
|
||||
*/
|
||||
@Getter
|
||||
@Schema(description = "sTargetCount")
|
||||
public class sTargetCount {
|
||||
@Schema(description = "apsStatus", example = "")
|
||||
private String apsStatus;
|
||||
@Schema(description = "targetCount", example = "0")
|
||||
private Integer targetCount;
|
||||
}
|
||||
@ -95,12 +95,6 @@ public class BypassApiConfig {
|
||||
@Column(name = "generated_at")
|
||||
private LocalDateTime generatedAt;
|
||||
|
||||
/**
|
||||
* Swagger 응답 스키마 DTO 클래스 (FQCN, 코드 생성 시 자동 설정)
|
||||
*/
|
||||
@Column(name = "response_schema_class", length = 300)
|
||||
private String responseSchemaClass;
|
||||
|
||||
/**
|
||||
* 생성 일시 (감사 필드)
|
||||
*/
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
package com.snp.batch.jobs.web.ais.controller;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.snp.batch.common.web.controller.BaseBypassController;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import com.snp.batch.jobs.web.ais.service.GetTargetCountService;
|
||||
|
||||
/**
|
||||
* Ais API
|
||||
* S&P Maritime API에서 데이터를 실시간 조회하여 JSON을 그대로 반환
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/ais")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "Ais", description = "[AIS API] Ais API")
|
||||
public class AisController extends BaseBypassController {
|
||||
|
||||
private final GetTargetCountService getTargetCountService;
|
||||
|
||||
@Operation(
|
||||
summary = "선박 AIS 대상 수 조회",
|
||||
description = "선박 AIS 대상 수 조회"
|
||||
)
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "선박 AIS 대상 수 조회",
|
||||
content = @io.swagger.v3.oas.annotations.media.Content(
|
||||
mediaType = "application/json",
|
||||
array = @io.swagger.v3.oas.annotations.media.ArraySchema(
|
||||
schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = com.snp.batch.global.dto.bypass.sTargetCount.class)
|
||||
)
|
||||
)
|
||||
)
|
||||
@PostMapping("/GetTargetCount")
|
||||
public ResponseEntity<JsonNode> getGetTargetCountData(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "",
|
||||
content = @io.swagger.v3.oas.annotations.media.Content(
|
||||
mediaType = "application/json",
|
||||
schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = com.snp.batch.global.dto.bypass.TargetsParameters.class)))
|
||||
@RequestBody JsonNode sinceSeconds) {
|
||||
return executeRaw(() -> getTargetCountService.getGetTargetCountData(sinceSeconds));
|
||||
}
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
package com.snp.batch.jobs.web.ais.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.snp.batch.common.web.service.BaseBypassService;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* 선박 AIS 대상 수 조회 서비스
|
||||
* 외부 Maritime API에서 데이터를 실시간 조회하여 JSON을 그대로 반환
|
||||
*/
|
||||
@Service
|
||||
public class GetTargetCountService extends BaseBypassService<JsonNode> {
|
||||
|
||||
public GetTargetCountService(
|
||||
@Qualifier("maritimeAisApiWebClient") WebClient webClient) {
|
||||
super(webClient, "/AisSvc.svc/AIS/GetTargetCount", "선박 AIS 대상 수 조회",
|
||||
new ParameterizedTypeReference<>() {},
|
||||
new ParameterizedTypeReference<>() {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 선박 AIS 대상 수 조회 데이터를 조회합니다.
|
||||
*/
|
||||
public JsonNode getGetTargetCountData(JsonNode sinceSeconds) {
|
||||
return fetchRawPost(sinceSeconds, uri -> uri.path(getApiPath())
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@ -14,13 +14,13 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import com.snp.batch.jobs.web.compliance.service.CompliancesByImosService;
|
||||
|
||||
/**
|
||||
* Compliance API
|
||||
* Compliance bypass API
|
||||
* S&P Maritime API에서 데이터를 실시간 조회하여 JSON을 그대로 반환
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/compliance")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "Compliance", description = "[Service API] Compliance API")
|
||||
@Tag(name = "Compliance", description = "[Service API] Compliance bypass API")
|
||||
public class ComplianceController extends BaseBypassController {
|
||||
|
||||
private final CompliancesByImosService compliancesByImosService;
|
||||
@ -29,16 +29,6 @@ public class ComplianceController extends BaseBypassController {
|
||||
summary = "IMO 기반 선박 규정준수 조회",
|
||||
description = "Gets details of the IMOs of ships with full compliance details that match given IMOs"
|
||||
)
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "IMO 기반 선박 규정준수 조회",
|
||||
content = @io.swagger.v3.oas.annotations.media.Content(
|
||||
mediaType = "application/json",
|
||||
array = @io.swagger.v3.oas.annotations.media.ArraySchema(
|
||||
schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = com.snp.batch.global.dto.bypass.ComplianceDetails.class)
|
||||
)
|
||||
)
|
||||
)
|
||||
@GetMapping("/CompliancesByImos")
|
||||
public ResponseEntity<JsonNode> getCompliancesByImosData(@Parameter(description = "Comma separated IMOs up to a total of 100", example = "9876543")
|
||||
@RequestParam(required = true) String imos) {
|
||||
|
||||
@ -8,7 +8,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* IMO 기반 선박 규정준수 조회 서비스
|
||||
* IMO 기반 선박 규정준수 조회 bypass 서비스
|
||||
* 외부 Maritime API에서 데이터를 실시간 조회하여 JSON을 그대로 반환
|
||||
*/
|
||||
@Service
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
package com.snp.batch.jobs.web.facility.controller;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.snp.batch.common.web.controller.BaseBypassController;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import com.snp.batch.jobs.web.facility.service.PortsService;
|
||||
|
||||
/**
|
||||
* Facility API
|
||||
* S&P Maritime API에서 데이터를 실시간 조회하여 JSON을 그대로 반환
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/facility")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "Facility", description = "[Service API] Facility API")
|
||||
public class FacilityController extends BaseBypassController {
|
||||
|
||||
private final PortsService portsService;
|
||||
|
||||
@Operation(
|
||||
summary = "항구 시설 조회",
|
||||
description = "항구 시설 조회"
|
||||
)
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "항구 시설 조회",
|
||||
content = @io.swagger.v3.oas.annotations.media.Content(
|
||||
mediaType = "application/json",
|
||||
array = @io.swagger.v3.oas.annotations.media.ArraySchema(
|
||||
schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = com.snp.batch.global.dto.bypass.PortFacility.class)
|
||||
)
|
||||
)
|
||||
)
|
||||
@GetMapping("/Ports")
|
||||
public ResponseEntity<JsonNode> getPortsData() {
|
||||
return executeRaw(() -> portsService.getPortsData());
|
||||
}
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
package com.snp.batch.jobs.web.facility.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.snp.batch.common.web.service.BaseBypassService;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* 항구 시설 조회 서비스
|
||||
* 외부 Maritime API에서 데이터를 실시간 조회하여 JSON을 그대로 반환
|
||||
*/
|
||||
@Service
|
||||
public class PortsService extends BaseBypassService<JsonNode> {
|
||||
|
||||
public PortsService(
|
||||
@Qualifier("maritimeServiceApiWebClient") WebClient webClient) {
|
||||
super(webClient, "/Facilities/Ports", "항구 시설 조회",
|
||||
new ParameterizedTypeReference<>() {},
|
||||
new ParameterizedTypeReference<>() {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 항구 시설 조회 데이터를 조회합니다.
|
||||
*/
|
||||
public JsonNode getPortsData() {
|
||||
return fetchRawGet(uri -> uri.path(getApiPath())
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@ -15,13 +15,13 @@ import com.snp.batch.jobs.web.risk.service.RisksByImosService;
|
||||
import com.snp.batch.jobs.web.risk.service.UpdatedComplianceListService;
|
||||
|
||||
/**
|
||||
* Risk API
|
||||
* Risk bypass API
|
||||
* S&P Maritime API에서 데이터를 실시간 조회하여 JSON을 그대로 반환
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/risk")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "Risk", description = "[Service API] Risk API")
|
||||
@Tag(name = "Risk", description = "[Service API] Risk bypass API")
|
||||
public class RiskController extends BaseBypassController {
|
||||
|
||||
private final RisksByImosService risksByImosService;
|
||||
@ -31,16 +31,6 @@ public class RiskController extends BaseBypassController {
|
||||
summary = "IMO 기반 선박 위험지표 조회",
|
||||
description = "Gets details of the IMOs of all ships with risk updates as a collection"
|
||||
)
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "IMO 기반 선박 위험지표 조회",
|
||||
content = @io.swagger.v3.oas.annotations.media.Content(
|
||||
mediaType = "application/json",
|
||||
array = @io.swagger.v3.oas.annotations.media.ArraySchema(
|
||||
schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = com.snp.batch.global.dto.bypass.RiskWithNarrativesDetails.class)
|
||||
)
|
||||
)
|
||||
)
|
||||
@GetMapping("/RisksByImos")
|
||||
public ResponseEntity<JsonNode> getRisksByImosData(@Parameter(description = "Comma separated IMOs up to a total of 100", example = "9876543")
|
||||
@RequestParam(required = true) String imos) {
|
||||
@ -51,16 +41,6 @@ public class RiskController extends BaseBypassController {
|
||||
summary = "기간 내 변경된 위험지표 조회",
|
||||
description = "Gets details of the IMOs of all ships with risk updates"
|
||||
)
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "기간 내 변경된 위험지표 조회",
|
||||
content = @io.swagger.v3.oas.annotations.media.Content(
|
||||
mediaType = "application/json",
|
||||
array = @io.swagger.v3.oas.annotations.media.ArraySchema(
|
||||
schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = com.snp.batch.global.dto.bypass.RiskDetails.class)
|
||||
)
|
||||
)
|
||||
)
|
||||
@GetMapping("/UpdatedRiskList")
|
||||
public ResponseEntity<JsonNode> getUpdatedComplianceListData(@Parameter(description = "Time/seconds are optional", example = "2026-03-30T07:01:27.000Z")
|
||||
@RequestParam(required = true) String fromDate,
|
||||
|
||||
@ -8,7 +8,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* IMO 기반 선박 위험지표 조회 서비스
|
||||
* IMO 기반 선박 위험지표 조회 bypass 서비스
|
||||
* 외부 Maritime API에서 데이터를 실시간 조회하여 JSON을 그대로 반환
|
||||
*/
|
||||
@Service
|
||||
|
||||
@ -8,7 +8,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* 기간 내 변경된 위험지표 조회 서비스
|
||||
* 기간 내 변경된 위험지표 조회 bypass 서비스
|
||||
* 외부 Maritime API에서 데이터를 실시간 조회하여 JSON을 그대로 반환
|
||||
*/
|
||||
@Service
|
||||
|
||||
@ -14,13 +14,13 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import com.snp.batch.jobs.web.ship.service.GetShipDataByIHSLRorIMOService;
|
||||
|
||||
/**
|
||||
* Ship API
|
||||
* Ship bypass API
|
||||
* S&P Maritime API에서 데이터를 실시간 조회하여 JSON을 그대로 반환
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/ship")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "Ship", description = "[Ship API] Ship API")
|
||||
@Tag(name = "Ship", description = "[Ship API] Ship bypass API")
|
||||
public class ShipController extends BaseBypassController {
|
||||
|
||||
private final GetShipDataByIHSLRorIMOService getShipDataByIHSLRorIMOService;
|
||||
@ -29,16 +29,6 @@ public class ShipController extends BaseBypassController {
|
||||
summary = "IMO 기반 선박제원정보 조회",
|
||||
description = "IMO 기반 선박제원정보 조회"
|
||||
)
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "IMO 기반 선박제원정보 조회",
|
||||
content = @io.swagger.v3.oas.annotations.media.Content(
|
||||
mediaType = "application/json",
|
||||
array = @io.swagger.v3.oas.annotations.media.ArraySchema(
|
||||
schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = com.snp.batch.global.dto.bypass.APSShipResult.class)
|
||||
)
|
||||
)
|
||||
)
|
||||
@GetMapping("/GetShipDataByIHSLRorIMO")
|
||||
public ResponseEntity<JsonNode> getGetShipDataByIHSLRorIMOData(@Parameter(description = "", example = "9876543")
|
||||
@RequestParam(required = true) String ihslrOrImo) {
|
||||
|
||||
@ -8,7 +8,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* IMO 기반 선박제원정보 조회 서비스
|
||||
* IMO 기반 선박제원정보 조회 bypass 서비스
|
||||
* 외부 Maritime API에서 데이터를 실시간 조회하여 JSON을 그대로 반환
|
||||
*/
|
||||
@Service
|
||||
|
||||
@ -3,7 +3,6 @@ package com.snp.batch.service;
|
||||
import com.snp.batch.global.dto.CodeGenerationResult;
|
||||
import com.snp.batch.global.model.BypassApiConfig;
|
||||
import com.snp.batch.global.model.BypassApiParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -12,10 +11,8 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -26,11 +23,8 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class BypassCodeGenerator {
|
||||
|
||||
private final SwaggerSchemaGenerator swaggerSchemaGenerator;
|
||||
|
||||
private static final String BASE_PACKAGE = "com.snp.batch.jobs.web";
|
||||
|
||||
/**
|
||||
@ -49,26 +43,6 @@ public class BypassCodeGenerator {
|
||||
|
||||
List<String> servicePaths = new ArrayList<>();
|
||||
|
||||
// Generate response/request DTOs from swagger.json
|
||||
Map<Long, String> responseSchemaMap = new HashMap<>();
|
||||
Map<Long, String> requestBodySchemaMap = new HashMap<>();
|
||||
for (BypassApiConfig config : configs) {
|
||||
String fqcn = swaggerSchemaGenerator.generateResponseDto(
|
||||
config.getWebclientBean(), config.getExternalPath(), force);
|
||||
if (fqcn != null) {
|
||||
responseSchemaMap.put(config.getId(), fqcn);
|
||||
config.setResponseSchemaClass(fqcn);
|
||||
}
|
||||
// POST 엔드포인트의 requestBody DTO 생성
|
||||
if ("POST".equalsIgnoreCase(config.getHttpMethod())) {
|
||||
String reqFqcn = swaggerSchemaGenerator.generateRequestBodyDto(
|
||||
config.getWebclientBean(), config.getExternalPath(), force);
|
||||
if (reqFqcn != null) {
|
||||
requestBodySchemaMap.put(config.getId(), reqFqcn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (BypassApiConfig config : configs) {
|
||||
String endpointName = config.getEndpointName();
|
||||
String servicePath = basePath + "/service/" + endpointName + "Service.java";
|
||||
@ -84,7 +58,7 @@ public class BypassCodeGenerator {
|
||||
}
|
||||
|
||||
// Controller: 모든 엔드포인트를 합치므로 항상 재생성
|
||||
String controllerCode = generateControllerCode(domain, configs, responseSchemaMap, requestBodySchemaMap);
|
||||
String controllerCode = generateControllerCode(domain, configs);
|
||||
String domainCapitalized = capitalize(domain);
|
||||
Path controllerFilePath = writeFile(
|
||||
basePath + "/controller/" + domainCapitalized + "Controller.java", controllerCode, true);
|
||||
@ -109,23 +83,9 @@ public class BypassCodeGenerator {
|
||||
String serviceClass = endpointName + "Service";
|
||||
boolean isPost = "POST".equalsIgnoreCase(config.getHttpMethod());
|
||||
|
||||
// POST인데 BODY 파라미터가 없으면 자동 추가
|
||||
List<BypassApiParam> effectiveParams = new ArrayList<>(params);
|
||||
if (isPost && effectiveParams.stream().noneMatch(p -> "BODY".equalsIgnoreCase(p.getParamIn()))) {
|
||||
BypassApiParam autoBody = new BypassApiParam();
|
||||
autoBody.setParamName("body");
|
||||
autoBody.setParamType("STRING");
|
||||
autoBody.setParamIn("BODY");
|
||||
autoBody.setRequired(false);
|
||||
autoBody.setDescription("요청 본문 (JSON)");
|
||||
autoBody.setExample("{}");
|
||||
autoBody.setSortOrder(999);
|
||||
effectiveParams.add(autoBody);
|
||||
}
|
||||
|
||||
String methodName = "get" + endpointName + "Data";
|
||||
String fetchMethod = buildFetchMethodCall(config, effectiveParams, isPost);
|
||||
String methodParams = buildMethodParams(effectiveParams);
|
||||
String fetchMethod = buildFetchMethodCall(config, params, isPost);
|
||||
String methodParams = buildMethodParams(params);
|
||||
|
||||
return """
|
||||
package {{PACKAGE}};
|
||||
@ -138,7 +98,7 @@ public class BypassCodeGenerator {
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* {{DISPLAY_NAME}} 서비스
|
||||
* {{DISPLAY_NAME}} bypass 서비스
|
||||
* 외부 Maritime API에서 데이터를 실시간 조회하여 JSON을 그대로 반환
|
||||
*/
|
||||
@Service
|
||||
@ -173,7 +133,7 @@ public class BypassCodeGenerator {
|
||||
* Controller 코드 생성 (RAW 모드).
|
||||
* 모든 엔드포인트가 ResponseEntity<JsonNode>를 반환합니다 (외부 API 원본 JSON 그대로).
|
||||
*/
|
||||
private String generateControllerCode(String domain, List<BypassApiConfig> configs, Map<Long, String> responseSchemaMap, Map<Long, String> requestBodySchemaMap) {
|
||||
private String generateControllerCode(String domain, List<BypassApiConfig> configs) {
|
||||
String packageName = BASE_PACKAGE + "." + domain + ".controller";
|
||||
String servicePackage = BASE_PACKAGE + "." + domain + ".service";
|
||||
String domainCap = capitalize(domain);
|
||||
@ -197,7 +157,7 @@ public class BypassCodeGenerator {
|
||||
.anyMatch(c -> c.getParams().stream().anyMatch(p -> "PATH".equalsIgnoreCase(p.getParamIn())));
|
||||
boolean anyQuery = configs.stream()
|
||||
.anyMatch(c -> c.getParams().stream().anyMatch(p -> "QUERY".equalsIgnoreCase(p.getParamIn())));
|
||||
boolean anyBody = anyPost || configs.stream()
|
||||
boolean anyBody = configs.stream()
|
||||
.anyMatch(c -> c.getParams().stream().anyMatch(p -> "BODY".equalsIgnoreCase(p.getParamIn())));
|
||||
|
||||
if (anyPost) importSet.add("import org.springframework.web.bind.annotation.PostMapping;");
|
||||
@ -221,7 +181,7 @@ public class BypassCodeGenerator {
|
||||
}
|
||||
|
||||
String tagPrefix = getTagPrefix(configs.get(0).getWebclientBean());
|
||||
String tagDescription = tagPrefix + " " + domainCap + " API";
|
||||
String tagDescription = tagPrefix + " " + domainCap + " bypass API";
|
||||
|
||||
// 엔드포인트 메서드 목록
|
||||
StringBuilder methods = new StringBuilder();
|
||||
@ -231,25 +191,10 @@ public class BypassCodeGenerator {
|
||||
String serviceField = Character.toLowerCase(serviceClass.charAt(0)) + serviceClass.substring(1);
|
||||
boolean isPost = "POST".equalsIgnoreCase(config.getHttpMethod());
|
||||
|
||||
// POST인데 BODY 파라미터가 없으면 자동 추가
|
||||
List<BypassApiParam> ctrlParams = new ArrayList<>(config.getParams());
|
||||
if (isPost && ctrlParams.stream().noneMatch(p -> "BODY".equalsIgnoreCase(p.getParamIn()))) {
|
||||
BypassApiParam autoBody = new BypassApiParam();
|
||||
autoBody.setParamName("body");
|
||||
autoBody.setParamType("STRING");
|
||||
autoBody.setParamIn("BODY");
|
||||
autoBody.setRequired(false);
|
||||
autoBody.setDescription("요청 본문 (JSON)");
|
||||
autoBody.setExample("{}");
|
||||
autoBody.setSortOrder(999);
|
||||
ctrlParams.add(autoBody);
|
||||
}
|
||||
|
||||
String mappingAnnotation = isPost ? "@PostMapping" : "@GetMapping";
|
||||
String mappingPath = buildMappingPath(ctrlParams, config.getExternalPath());
|
||||
String reqBodySchema = requestBodySchemaMap.get(config.getId());
|
||||
String paramAnnotations = buildControllerParamAnnotations(ctrlParams, reqBodySchema);
|
||||
String serviceCallArgs = buildServiceCallArgs(ctrlParams);
|
||||
String mappingPath = buildMappingPath(config.getParams(), config.getExternalPath());
|
||||
String paramAnnotations = buildControllerParamAnnotations(config.getParams());
|
||||
String serviceCallArgs = buildServiceCallArgs(config.getParams());
|
||||
String methodName = "get" + endpointName + "Data";
|
||||
|
||||
methods.append("\n");
|
||||
@ -260,20 +205,6 @@ public class BypassCodeGenerator {
|
||||
: config.getDisplayName() + " 데이터를 요청하고 응답을 그대로 반환합니다.";
|
||||
methods.append(" description = \"").append(opDescription).append("\"\n");
|
||||
methods.append(" )\n");
|
||||
// @ApiResponse with schema (if available)
|
||||
String schemaClass = responseSchemaMap.get(config.getId());
|
||||
if (schemaClass != null && !schemaClass.isEmpty()) {
|
||||
methods.append(" @io.swagger.v3.oas.annotations.responses.ApiResponse(\n");
|
||||
methods.append(" responseCode = \"200\",\n");
|
||||
methods.append(" description = \"").append(config.getDisplayName()).append("\",\n");
|
||||
methods.append(" content = @io.swagger.v3.oas.annotations.media.Content(\n");
|
||||
methods.append(" mediaType = \"application/json\",\n");
|
||||
methods.append(" array = @io.swagger.v3.oas.annotations.media.ArraySchema(\n");
|
||||
methods.append(" schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = ").append(schemaClass).append(".class)\n");
|
||||
methods.append(" )\n");
|
||||
methods.append(" )\n");
|
||||
methods.append(" )\n");
|
||||
}
|
||||
methods.append(" ").append(mappingAnnotation).append(mappingPath).append("\n");
|
||||
methods.append(" public ResponseEntity<JsonNode> ").append(methodName).append("(");
|
||||
if (!paramAnnotations.isEmpty()) {
|
||||
@ -288,7 +219,7 @@ public class BypassCodeGenerator {
|
||||
return "package " + packageName + ";\n\n"
|
||||
+ importsStr + "\n\n"
|
||||
+ "/**\n"
|
||||
+ " * " + domainCap + " API\n"
|
||||
+ " * " + domainCap + " bypass API\n"
|
||||
+ " * S&P Maritime API에서 데이터를 실시간 조회하여 JSON을 그대로 반환\n"
|
||||
+ " */\n"
|
||||
+ "@RestController\n"
|
||||
@ -332,10 +263,7 @@ public class BypassCodeGenerator {
|
||||
private String buildMethodParams(List<BypassApiParam> params) {
|
||||
return params.stream()
|
||||
.sorted((a, b) -> Integer.compare(a.getSortOrder(), b.getSortOrder()))
|
||||
.map(p -> {
|
||||
String type = "BODY".equalsIgnoreCase(p.getParamIn()) ? "JsonNode" : toJavaType(p.getParamType());
|
||||
return type + " " + p.getParamName();
|
||||
})
|
||||
.map(p -> toJavaType(p.getParamType()) + " " + p.getParamName())
|
||||
.collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
@ -346,7 +274,7 @@ public class BypassCodeGenerator {
|
||||
.collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
private String buildControllerParamAnnotations(List<BypassApiParam> params, String requestBodySchemaClass) {
|
||||
private String buildControllerParamAnnotations(List<BypassApiParam> params) {
|
||||
if (params.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
@ -362,17 +290,8 @@ public class BypassCodeGenerator {
|
||||
return switch (p.getParamIn().toUpperCase()) {
|
||||
case "PATH" -> "@Parameter(description = \"" + description + "\", example = \"" + example + "\")\n"
|
||||
+ " @PathVariable " + javaType + " " + paramName;
|
||||
case "BODY" -> {
|
||||
StringBuilder bodyAnno = new StringBuilder();
|
||||
bodyAnno.append("@io.swagger.v3.oas.annotations.parameters.RequestBody(description = \"").append(description).append("\"");
|
||||
if (requestBodySchemaClass != null && !requestBodySchemaClass.isEmpty()) {
|
||||
bodyAnno.append(",\n content = @io.swagger.v3.oas.annotations.media.Content(\n");
|
||||
bodyAnno.append(" mediaType = \"application/json\",\n");
|
||||
bodyAnno.append(" schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = ").append(requestBodySchemaClass).append(".class))");
|
||||
}
|
||||
bodyAnno.append(")\n @RequestBody JsonNode ").append(paramName);
|
||||
yield bodyAnno.toString();
|
||||
}
|
||||
case "BODY" -> "@Parameter(description = \"" + description + "\", example = \"" + example + "\")\n"
|
||||
+ " @RequestBody " + javaType + " " + paramName;
|
||||
default -> {
|
||||
String required = Boolean.TRUE.equals(p.getRequired()) ? "true" : "false";
|
||||
yield "@Parameter(description = \"" + description + "\", example = \"" + example + "\")\n"
|
||||
|
||||
@ -80,7 +80,6 @@ public class BypassConfigService {
|
||||
config.setExternalPath(request.getExternalPath());
|
||||
config.setHttpMethod(request.getHttpMethod());
|
||||
config.setDescription(request.getDescription());
|
||||
config.setResponseSchemaClass(request.getResponseSchemaClass());
|
||||
|
||||
// params 교체: clear → flush(DELETE 실행) → 새로 추가
|
||||
config.getParams().clear();
|
||||
@ -132,7 +131,6 @@ public class BypassConfigService {
|
||||
.externalPath(config.getExternalPath())
|
||||
.httpMethod(config.getHttpMethod())
|
||||
.description(config.getDescription())
|
||||
.responseSchemaClass(config.getResponseSchemaClass())
|
||||
.generated(config.getGenerated())
|
||||
.generatedAt(config.getGeneratedAt())
|
||||
.createdAt(config.getCreatedAt())
|
||||
@ -150,7 +148,6 @@ public class BypassConfigService {
|
||||
.externalPath(request.getExternalPath())
|
||||
.httpMethod(request.getHttpMethod() != null ? request.getHttpMethod() : "GET")
|
||||
.description(request.getDescription())
|
||||
.responseSchemaClass(request.getResponseSchemaClass())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@ -1,252 +0,0 @@
|
||||
package com.snp.batch.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SwaggerSchemaGenerator {
|
||||
|
||||
private static final String DTO_PACKAGE = "com.snp.batch.global.dto.bypass";
|
||||
private static final String DTO_BASE_PATH = "src/main/java/com/snp/batch/global/dto/bypass";
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private static final Map<String, String> SWAGGER_FILE_MAP = Map.of(
|
||||
"maritimeAisApiWebClient", "swagger/swagger_aisapi.json",
|
||||
"maritimeApiWebClient", "swagger/swagger_shipsapi.json",
|
||||
"maritimeServiceApiWebClient", "swagger/swagger_webservices.json"
|
||||
);
|
||||
|
||||
/**
|
||||
* swagger.json에서 응답 스키마를 추출하여 DTO Java 클래스를 자동 생성합니다.
|
||||
* @return 생성된 DTO의 FQCN, 스키마를 찾을 수 없으면 null
|
||||
*/
|
||||
public String generateResponseDto(String webclientBean, String externalPath, boolean force) {
|
||||
String swaggerFile = SWAGGER_FILE_MAP.get(webclientBean);
|
||||
if (swaggerFile == null) {
|
||||
log.warn("Unknown webclientBean: {}, skipping DTO generation", webclientBean);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
JsonNode swagger = loadSwaggerJson(swaggerFile);
|
||||
if (swagger == null) return null;
|
||||
|
||||
// Find path in swagger
|
||||
JsonNode paths = swagger.get("paths");
|
||||
if (paths == null || !paths.has(externalPath)) {
|
||||
log.info("Path {} not found in {}, skipping DTO generation", externalPath, swaggerFile);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get 200 response schema $ref
|
||||
JsonNode pathItem = paths.get(externalPath);
|
||||
// Try GET first, then POST
|
||||
JsonNode operation = pathItem.has("get") ? pathItem.get("get") : pathItem.has("post") ? pathItem.get("post") : null;
|
||||
if (operation == null) return null;
|
||||
|
||||
JsonNode responses = operation.get("responses");
|
||||
if (responses == null || !responses.has("200")) return null;
|
||||
|
||||
JsonNode response200 = responses.get("200");
|
||||
JsonNode content = response200.get("content");
|
||||
if (content == null) return null;
|
||||
|
||||
JsonNode jsonContent = content.has("application/json") ? content.get("application/json") : content.has("text/json") ? content.get("text/json") : null;
|
||||
if (jsonContent == null) return null;
|
||||
|
||||
JsonNode schema = jsonContent.get("schema");
|
||||
if (schema == null) return null;
|
||||
|
||||
// Resolve $ref - could be direct or array items
|
||||
String schemaRef = null;
|
||||
boolean isArray = "array".equals(schema.has("type") ? schema.get("type").asText() : "");
|
||||
if (isArray && schema.has("items") && schema.get("items").has("$ref")) {
|
||||
schemaRef = schema.get("items").get("$ref").asText();
|
||||
} else if (schema.has("$ref")) {
|
||||
schemaRef = schema.get("$ref").asText();
|
||||
}
|
||||
|
||||
if (schemaRef == null) return null;
|
||||
|
||||
// Extract schema name from $ref (e.g., "#/components/schemas/Foo.Bar.Baz" -> "Baz")
|
||||
String fullSchemaName = schemaRef.substring(schemaRef.lastIndexOf("/") + 1);
|
||||
String className = fullSchemaName.contains(".")
|
||||
? fullSchemaName.substring(fullSchemaName.lastIndexOf(".") + 1)
|
||||
: fullSchemaName;
|
||||
|
||||
// Get schema definition
|
||||
JsonNode schemas = swagger.at("/components/schemas/" + fullSchemaName);
|
||||
if (schemas.isMissingNode()) return null;
|
||||
|
||||
// Generate DTO file
|
||||
String projectRoot = System.getProperty("user.dir");
|
||||
String filePath = projectRoot + "/" + DTO_BASE_PATH + "/" + className + ".java";
|
||||
|
||||
if (!force && Files.exists(Path.of(filePath))) {
|
||||
log.info("DTO already exists, skipping: {}", filePath);
|
||||
return DTO_PACKAGE + "." + className;
|
||||
}
|
||||
|
||||
String javaCode = generateDtoCode(className, schemas);
|
||||
Files.createDirectories(Path.of(filePath).getParent());
|
||||
Files.writeString(Path.of(filePath), javaCode, StandardCharsets.UTF_8);
|
||||
log.info("DTO generated: {}", filePath);
|
||||
|
||||
return DTO_PACKAGE + "." + className;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to generate response DTO for {} {}: {}", webclientBean, externalPath, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* swagger.json에서 requestBody 스키마를 추출하여 DTO Java 클래스를 자동 생성합니다.
|
||||
* @return 생성된 DTO의 FQCN, 스키마를 찾을 수 없으면 null
|
||||
*/
|
||||
public String generateRequestBodyDto(String webclientBean, String externalPath, boolean force) {
|
||||
String swaggerFile = SWAGGER_FILE_MAP.get(webclientBean);
|
||||
if (swaggerFile == null) return null;
|
||||
|
||||
try {
|
||||
JsonNode swagger = loadSwaggerJson(swaggerFile);
|
||||
if (swagger == null) return null;
|
||||
|
||||
JsonNode paths = swagger.get("paths");
|
||||
if (paths == null || !paths.has(externalPath)) return null;
|
||||
|
||||
JsonNode pathItem = paths.get(externalPath);
|
||||
JsonNode operation = pathItem.has("post") ? pathItem.get("post") : null;
|
||||
if (operation == null) return null;
|
||||
|
||||
JsonNode requestBody = operation.get("requestBody");
|
||||
if (requestBody == null) return null;
|
||||
|
||||
JsonNode content = requestBody.get("content");
|
||||
if (content == null) return null;
|
||||
|
||||
JsonNode jsonContent = content.has("application/json") ? content.get("application/json")
|
||||
: content.has("application/json-patch+json") ? content.get("application/json-patch+json")
|
||||
: content.has("text/json") ? content.get("text/json") : null;
|
||||
if (jsonContent == null) return null;
|
||||
|
||||
JsonNode schema = jsonContent.get("schema");
|
||||
if (schema == null || !schema.has("$ref")) return null;
|
||||
|
||||
String schemaRef = schema.get("$ref").asText();
|
||||
String fullSchemaName = schemaRef.substring(schemaRef.lastIndexOf("/") + 1);
|
||||
String className = fullSchemaName.contains(".")
|
||||
? fullSchemaName.substring(fullSchemaName.lastIndexOf(".") + 1)
|
||||
: fullSchemaName;
|
||||
|
||||
JsonNode schemaDef = swagger.at("/components/schemas/" + fullSchemaName);
|
||||
if (schemaDef.isMissingNode()) return null;
|
||||
|
||||
String projectRoot = System.getProperty("user.dir");
|
||||
String filePath = projectRoot + "/" + DTO_BASE_PATH + "/" + className + ".java";
|
||||
|
||||
if (!force && Files.exists(Path.of(filePath))) {
|
||||
log.info("Request DTO already exists, skipping: {}", filePath);
|
||||
return DTO_PACKAGE + "." + className;
|
||||
}
|
||||
|
||||
String javaCode = generateDtoCode(className, schemaDef);
|
||||
Files.createDirectories(Path.of(filePath).getParent());
|
||||
Files.writeString(Path.of(filePath), javaCode, StandardCharsets.UTF_8);
|
||||
log.info("Request DTO generated: {}", filePath);
|
||||
|
||||
return DTO_PACKAGE + "." + className;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to generate request body DTO for {} {}: {}", webclientBean, externalPath, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private JsonNode loadSwaggerJson(String resourcePath) {
|
||||
try {
|
||||
ClassPathResource resource = new ClassPathResource(resourcePath);
|
||||
try (InputStream is = resource.getInputStream()) {
|
||||
return objectMapper.readTree(is);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to load swagger file: {}", resourcePath, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String generateDtoCode(String className, JsonNode schema) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("package ").append(DTO_PACKAGE).append(";\n\n");
|
||||
sb.append("import io.swagger.v3.oas.annotations.media.Schema;\n");
|
||||
sb.append("import lombok.Getter;\n\n");
|
||||
|
||||
String title = schema.has("title") ? schema.get("title").asText() : className;
|
||||
sb.append("/**\n");
|
||||
sb.append(" * S&P Global API 응답 스키마 (Swagger 문서용)\n");
|
||||
sb.append(" * 이 클래스는 자동 생성되었습니다. 직접 수정하지 마세요.\n");
|
||||
sb.append(" */\n");
|
||||
sb.append("@Getter\n");
|
||||
sb.append("@Schema(description = \"").append(title).append("\")\n");
|
||||
sb.append("public class ").append(className).append(" {\n");
|
||||
|
||||
JsonNode properties = schema.get("properties");
|
||||
if (properties != null) {
|
||||
Iterator<Map.Entry<String, JsonNode>> fields = properties.fields();
|
||||
while (fields.hasNext()) {
|
||||
Map.Entry<String, JsonNode> field = fields.next();
|
||||
String fieldName = field.getKey();
|
||||
JsonNode fieldSchema = field.getValue();
|
||||
|
||||
String javaType = resolveJavaType(fieldSchema);
|
||||
String example = getExampleValue(fieldSchema);
|
||||
|
||||
sb.append(" @Schema(description = \"").append(fieldName).append("\"");
|
||||
if (example != null) {
|
||||
sb.append(", example = \"").append(example).append("\"");
|
||||
}
|
||||
sb.append(")\n");
|
||||
sb.append(" private ").append(javaType).append(" ").append(fieldName).append(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("}\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String resolveJavaType(JsonNode fieldSchema) {
|
||||
String type = fieldSchema.has("type") ? fieldSchema.get("type").asText() : "string";
|
||||
String format = fieldSchema.has("format") ? fieldSchema.get("format").asText() : "";
|
||||
|
||||
return switch (type) {
|
||||
case "integer" -> "int64".equals(format) ? "Long" : "Integer";
|
||||
case "number" -> "Double";
|
||||
case "boolean" -> "Boolean";
|
||||
case "array" -> "java.util.List<Object>";
|
||||
default -> "String";
|
||||
};
|
||||
}
|
||||
|
||||
private String getExampleValue(JsonNode fieldSchema) {
|
||||
String type = fieldSchema.has("type") ? fieldSchema.get("type").asText() : "string";
|
||||
return switch (type) {
|
||||
case "integer" -> "0";
|
||||
case "number" -> "0.0";
|
||||
case "boolean" -> "false";
|
||||
case "array" -> null;
|
||||
default -> "";
|
||||
};
|
||||
}
|
||||
}
|
||||
파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
Load Diff
파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
Load Diff
파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
Load Diff
불러오는 중...
Reference in New Issue
Block a user