feat: 중첩 JSON 구조 파싱 대응 및 DTO import 버그 수정 (#63)
백엔드:
- generateDtoCode에서 List/Map 타입 import 자동 추가
- JsonSchemaParser에 targetField 파라미터 추가 (특정 필드 내부만 파싱)
- 배열 내 객체 감지 시 List<{FieldName}Item> 타입으로 추론
프론트엔드:
- JSON 샘플 파싱 시 "전체 JSON / 특정 필드 내부" 선택 옵션 추가
- targetField 기본값 "data" (페이징 래퍼 대응)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
부모
6391683959
커밋
9667d3a0ac
@ -117,8 +117,9 @@ export const bypassApi = {
|
||||
deleteJson<ApiResponse<void>>(`${BASE}/${id}`),
|
||||
generateCode: (id: number, force = false) =>
|
||||
postJson<ApiResponse<CodeGenerationResult>>(`${BASE}/${id}/generate?force=${force}`),
|
||||
parseJson: async (jsonSample: string): Promise<ApiResponse<BypassFieldDto[]>> => {
|
||||
const res = await fetch(`${BASE}/parse-json`, {
|
||||
parseJson: async (jsonSample: string, targetField?: string): Promise<ApiResponse<BypassFieldDto[]>> => {
|
||||
const params = targetField ? `?targetField=${encodeURIComponent(targetField)}` : '';
|
||||
const res = await fetch(`${BASE}/parse-json${params}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: jsonSample, // 이미 JSON 문자열이므로 JSON.stringify 하지 않음
|
||||
|
||||
@ -26,6 +26,8 @@ export default function BypassStepFields({ fields, onChange }: BypassStepFieldsP
|
||||
const [jsonSample, setJsonSample] = useState('');
|
||||
const [parsing, setParsing] = useState(false);
|
||||
const [parseError, setParseError] = useState<string | null>(null);
|
||||
const [parseMode, setParseMode] = useState<'root' | 'field'>('root');
|
||||
const [targetField, setTargetField] = useState('data');
|
||||
|
||||
const handleAdd = () => {
|
||||
onChange([...fields, createEmptyField(fields.length)]);
|
||||
@ -53,7 +55,8 @@ export default function BypassStepFields({ fields, onChange }: BypassStepFieldsP
|
||||
setParseError(null);
|
||||
setParsing(true);
|
||||
try {
|
||||
const result = await bypassApi.parseJson(jsonSample);
|
||||
const tf = parseMode === 'field' ? targetField : undefined;
|
||||
const result = await bypassApi.parseJson(jsonSample.trim(), tf);
|
||||
if (result.success && result.data) {
|
||||
onChange(result.data);
|
||||
setActiveTab('manual');
|
||||
@ -220,6 +223,48 @@ export default function BypassStepFields({ fields, onChange }: BypassStepFieldsP
|
||||
placeholder={'{\n "riskScore": 85,\n "imoNumber": "1234567",\n "vesselName": "EXAMPLE SHIP"\n}'}
|
||||
className="w-full px-3 py-2 text-sm font-mono rounded-lg border border-wing-border bg-wing-card text-wing-text placeholder:text-wing-muted focus:outline-none focus:ring-2 focus:ring-wing-accent/50 resize-none"
|
||||
/>
|
||||
|
||||
{/* 파싱 옵션 */}
|
||||
<div className="mt-3 space-y-2">
|
||||
<label className="text-sm font-medium text-wing-text">파싱 대상</label>
|
||||
<div className="flex items-center gap-4">
|
||||
<label className="flex items-center gap-2 text-sm text-wing-text cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
name="parseMode"
|
||||
checked={parseMode === 'root'}
|
||||
onChange={() => setParseMode('root')}
|
||||
className="accent-wing-accent"
|
||||
/>
|
||||
전체 JSON
|
||||
</label>
|
||||
<label className="flex items-center gap-2 text-sm text-wing-text cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
name="parseMode"
|
||||
checked={parseMode === 'field'}
|
||||
onChange={() => setParseMode('field')}
|
||||
className="accent-wing-accent"
|
||||
/>
|
||||
특정 필드 내부
|
||||
</label>
|
||||
</div>
|
||||
{parseMode === 'field' && (
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="text"
|
||||
value={targetField}
|
||||
onChange={(e) => setTargetField(e.target.value)}
|
||||
placeholder="필드명 (예: data, results, items)"
|
||||
className="px-3 py-1.5 text-sm rounded-lg border border-wing-border bg-wing-surface text-wing-text placeholder:text-wing-muted focus:outline-none focus:ring-1 focus:ring-wing-accent/50"
|
||||
/>
|
||||
<span className="text-xs text-wing-muted">
|
||||
해당 필드가 배열이면 첫 번째 요소를, 객체면 그 내부를 파싱합니다.
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{parseError && (
|
||||
<p className="text-sm text-red-500">{parseError}</p>
|
||||
)}
|
||||
|
||||
@ -108,12 +108,14 @@ public class BypassConfigController {
|
||||
|
||||
@Operation(
|
||||
summary = "JSON 샘플 파싱",
|
||||
description = "JSON 샘플에서 DTO 필드 목록을 추출합니다."
|
||||
description = "JSON 샘플에서 DTO 필드 목록을 추출합니다. targetField를 지정하면 해당 필드 내부를 파싱합니다. 예: targetField=data → root.data[0] 내부 필드 추출."
|
||||
)
|
||||
@PostMapping("/parse-json")
|
||||
public ResponseEntity<ApiResponse<List<BypassFieldDto>>> parseJson(@RequestBody String jsonSample) {
|
||||
public ResponseEntity<ApiResponse<List<BypassFieldDto>>> parseJson(
|
||||
@RequestBody String jsonSample,
|
||||
@RequestParam(required = false) String targetField) {
|
||||
try {
|
||||
List<BypassFieldDto> fields = jsonSchemaParser.parse(jsonSample);
|
||||
List<BypassFieldDto> fields = jsonSchemaParser.parse(jsonSample, targetField);
|
||||
return ResponseEntity.ok(ApiResponse.success(fields));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().body(ApiResponse.error("JSON 파싱 실패: " + e.getMessage()));
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
package com.snp.batch.jobs.web.compliance.controller;
|
||||
|
||||
import com.snp.batch.common.web.ApiResponse;
|
||||
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 java.util.List;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import com.snp.batch.jobs.web.compliance.dto.CompliancesByImosDto;
|
||||
import com.snp.batch.jobs.web.compliance.service.CompliancesByImosService;
|
||||
import com.snp.batch.jobs.web.compliance.dto.UpdatedComplianceListDto;
|
||||
import com.snp.batch.jobs.web.compliance.service.UpdatedComplianceListService;
|
||||
import com.snp.batch.jobs.web.compliance.dto.ComplianceValuesMeaningDto;
|
||||
import com.snp.batch.jobs.web.compliance.service.ComplianceValuesMeaningService;
|
||||
import com.snp.batch.jobs.web.compliance.dto.PagedUpdatedComplianceListDto;
|
||||
import com.snp.batch.jobs.web.compliance.service.PagedUpdatedComplianceListService;
|
||||
|
||||
/**
|
||||
* Compliance bypass API
|
||||
* S&P Maritime API에서 데이터를 실시간 조회하여 그대로 반환
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/compliance")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "Compliance", description = "[Service API] Compliance bypass API")
|
||||
public class ComplianceController extends BaseBypassController {
|
||||
|
||||
private final CompliancesByImosService compliancesByImosService;
|
||||
private final UpdatedComplianceListService updatedComplianceListService;
|
||||
private final ComplianceValuesMeaningService complianceValuesMeaningService;
|
||||
private final PagedUpdatedComplianceListService pagedUpdatedComplianceListService;
|
||||
|
||||
@Operation(
|
||||
summary = "IMO 기반 Compliance 조회 조회",
|
||||
description = "S&P API에서 IMO 기반 Compliance 조회 데이터를 요청하고 응답을 그대로 반환합니다."
|
||||
)
|
||||
@GetMapping("/CompliancesByImos")
|
||||
public ResponseEntity<ApiResponse<List<CompliancesByImosDto>>> getCompliancesByImosData(@Parameter(description = "Comma separated IMOs up to a total of 100", example = "9876543")
|
||||
@RequestParam(required = true) String imos) {
|
||||
return execute(() -> compliancesByImosService.getCompliancesByImosData(imos));
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "기간 내 변경 Compliance 조회 조회",
|
||||
description = "S&P API에서 기간 내 변경 Compliance 조회 데이터를 요청하고 응답을 그대로 반환합니다."
|
||||
)
|
||||
@GetMapping("/UpdatedComplianceList")
|
||||
public ResponseEntity<ApiResponse<List<UpdatedComplianceListDto>>> getUpdatedComplianceListData(@Parameter(description = "Time/seconds are optional", example = "9876543")
|
||||
@RequestParam(required = true) String fromDate,
|
||||
@Parameter(description = "Time/seconds are optional. If unspecified, the current UTC date and time is used", example = "9876543")
|
||||
@RequestParam(required = true) String toDate) {
|
||||
return execute(() -> updatedComplianceListService.getUpdatedComplianceListData(fromDate, toDate));
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "모든 Compliance 지표 조회 조회",
|
||||
description = "S&P API에서 모든 Compliance 지표 조회 데이터를 요청하고 응답을 그대로 반환합니다."
|
||||
)
|
||||
@GetMapping("/ComplianceValuesMeaning")
|
||||
public ResponseEntity<ApiResponse<List<ComplianceValuesMeaningDto>>> getComplianceValuesMeaningData() {
|
||||
return execute(() -> complianceValuesMeaningService.getComplianceValuesMeaningData());
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "PagedUpdatedComplianceList 조회",
|
||||
description = "S&P API에서 PagedUpdatedComplianceList 데이터를 요청하고 응답을 그대로 반환합니다."
|
||||
)
|
||||
@GetMapping("/PagedUpdatedComplianceList")
|
||||
public ResponseEntity<ApiResponse<List<PagedUpdatedComplianceListDto>>> getPagedUpdatedComplianceListData(@Parameter(description = "Time/seconds are optional", example = "9876543")
|
||||
@RequestParam(required = true) String fromDate,
|
||||
@Parameter(description = "Time/seconds are optional. If unspecified, the current UTC date and time is used", example = "9876543")
|
||||
@RequestParam(required = true) String toDate,
|
||||
@Parameter(description = "Page number to display.", example = "9876543")
|
||||
@RequestParam(required = true) String pageNumber,
|
||||
@Parameter(description = "How many elements will be on the single page. Maximum allowed is 1000.", example = "9876543")
|
||||
@RequestParam(required = true) String pageSize) {
|
||||
return execute(() -> pagedUpdatedComplianceListService.getPagedUpdatedComplianceListData(fromDate, toDate, pageNumber, pageSize));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.snp.batch.jobs.web.compliance.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ComplianceValuesMeaningDto {
|
||||
|
||||
@JsonProperty("complianceValue")
|
||||
private Integer complianceValue;
|
||||
|
||||
@JsonProperty("meaning")
|
||||
private String meaning;
|
||||
|
||||
}
|
||||
@ -0,0 +1,122 @@
|
||||
package com.snp.batch.jobs.web.compliance.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CompliancesByImosDto {
|
||||
|
||||
@JsonProperty("lrimoShipNo")
|
||||
private String lrimoShipNo;
|
||||
|
||||
@JsonProperty("dateAmended")
|
||||
private String dateAmended;
|
||||
|
||||
@JsonProperty("legalOverall")
|
||||
private Integer legalOverall;
|
||||
|
||||
@JsonProperty("shipBESSanctionList")
|
||||
private Integer shipBESSanctionList;
|
||||
|
||||
@JsonProperty("shipDarkActivityIndicator")
|
||||
private Integer shipDarkActivityIndicator;
|
||||
|
||||
@JsonProperty("shipDetailsNoLongerMaintained")
|
||||
private Integer shipDetailsNoLongerMaintained;
|
||||
|
||||
@JsonProperty("shipEUSanctionList")
|
||||
private Integer shipEUSanctionList;
|
||||
|
||||
@JsonProperty("shipFlagDisputed")
|
||||
private Integer shipFlagDisputed;
|
||||
|
||||
@JsonProperty("shipFlagSanctionedCountry")
|
||||
private Integer shipFlagSanctionedCountry;
|
||||
|
||||
@JsonProperty("shipHistoricalFlagSanctionedCountry")
|
||||
private Integer shipHistoricalFlagSanctionedCountry;
|
||||
|
||||
@JsonProperty("shipOFACNonSDNSanctionList")
|
||||
private Integer shipOFACNonSDNSanctionList;
|
||||
|
||||
@JsonProperty("shipOFACSanctionList")
|
||||
private Integer shipOFACSanctionList;
|
||||
|
||||
@JsonProperty("shipOFACAdvisoryList")
|
||||
private Integer shipOFACAdvisoryList;
|
||||
|
||||
@JsonProperty("shipOwnerOFACSSIList")
|
||||
private Integer shipOwnerOFACSSIList;
|
||||
|
||||
@JsonProperty("shipOwnerAustralianSanctionList")
|
||||
private Integer shipOwnerAustralianSanctionList;
|
||||
|
||||
@JsonProperty("shipOwnerBESSanctionList")
|
||||
private Integer shipOwnerBESSanctionList;
|
||||
|
||||
@JsonProperty("shipOwnerCanadianSanctionList")
|
||||
private Integer shipOwnerCanadianSanctionList;
|
||||
|
||||
@JsonProperty("shipOwnerEUSanctionList")
|
||||
private Integer shipOwnerEUSanctionList;
|
||||
|
||||
@JsonProperty("shipOwnerFATFJurisdiction")
|
||||
private Integer shipOwnerFATFJurisdiction;
|
||||
|
||||
@JsonProperty("shipOwnerHistoricalOFACSanctionedCountry")
|
||||
private Integer shipOwnerHistoricalOFACSanctionedCountry;
|
||||
|
||||
@JsonProperty("shipOwnerOFACSanctionList")
|
||||
private Integer shipOwnerOFACSanctionList;
|
||||
|
||||
@JsonProperty("shipOwnerOFACSanctionedCountry")
|
||||
private Integer shipOwnerOFACSanctionedCountry;
|
||||
|
||||
@JsonProperty("shipOwnerParentCompanyNonCompliance")
|
||||
private Integer shipOwnerParentCompanyNonCompliance;
|
||||
|
||||
@JsonProperty("shipOwnerParentFATFJurisdiction")
|
||||
private String shipOwnerParentFATFJurisdiction;
|
||||
|
||||
@JsonProperty("shipOwnerParentOFACSanctionedCountry")
|
||||
private String shipOwnerParentOFACSanctionedCountry;
|
||||
|
||||
@JsonProperty("shipOwnerSwissSanctionList")
|
||||
private Integer shipOwnerSwissSanctionList;
|
||||
|
||||
@JsonProperty("shipOwnerUAESanctionList")
|
||||
private Integer shipOwnerUAESanctionList;
|
||||
|
||||
@JsonProperty("shipOwnerUNSanctionList")
|
||||
private Integer shipOwnerUNSanctionList;
|
||||
|
||||
@JsonProperty("shipSanctionedCountryPortCallLast12m")
|
||||
private Integer shipSanctionedCountryPortCallLast12m;
|
||||
|
||||
@JsonProperty("shipSanctionedCountryPortCallLast3m")
|
||||
private Integer shipSanctionedCountryPortCallLast3m;
|
||||
|
||||
@JsonProperty("shipSanctionedCountryPortCallLast6m")
|
||||
private Integer shipSanctionedCountryPortCallLast6m;
|
||||
|
||||
@JsonProperty("shipSecurityLegalDisputeEvent")
|
||||
private Integer shipSecurityLegalDisputeEvent;
|
||||
|
||||
@JsonProperty("shipSTSPartnerNonComplianceLast12m")
|
||||
private Integer shipSTSPartnerNonComplianceLast12m;
|
||||
|
||||
@JsonProperty("shipSwissSanctionList")
|
||||
private Integer shipSwissSanctionList;
|
||||
|
||||
@JsonProperty("shipUNSanctionList")
|
||||
private Integer shipUNSanctionList;
|
||||
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package com.snp.batch.jobs.web.compliance.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PagedUpdatedComplianceListDto {
|
||||
|
||||
@JsonProperty("pageSize")
|
||||
private Integer pageSize;
|
||||
|
||||
@JsonProperty("pageNumber")
|
||||
private Integer pageNumber;
|
||||
|
||||
@JsonProperty("firstPage")
|
||||
private String firstPage;
|
||||
|
||||
@JsonProperty("lastPage")
|
||||
private String lastPage;
|
||||
|
||||
@JsonProperty("totalPages")
|
||||
private Integer totalPages;
|
||||
|
||||
@JsonProperty("totalRecords")
|
||||
private Integer totalRecords;
|
||||
|
||||
@JsonProperty("nextPage")
|
||||
private String nextPage;
|
||||
|
||||
@JsonProperty("previousPage")
|
||||
private String previousPage;
|
||||
|
||||
@JsonProperty("data")
|
||||
private List<Object> data;
|
||||
|
||||
}
|
||||
@ -0,0 +1,122 @@
|
||||
package com.snp.batch.jobs.web.compliance.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UpdatedComplianceListDto {
|
||||
|
||||
@JsonProperty("lrimoShipNo")
|
||||
private String lrimoShipNo;
|
||||
|
||||
@JsonProperty("dateAmended")
|
||||
private String dateAmended;
|
||||
|
||||
@JsonProperty("legalOverall")
|
||||
private Integer legalOverall;
|
||||
|
||||
@JsonProperty("shipBESSanctionList")
|
||||
private Integer shipBESSanctionList;
|
||||
|
||||
@JsonProperty("shipDarkActivityIndicator")
|
||||
private Integer shipDarkActivityIndicator;
|
||||
|
||||
@JsonProperty("shipDetailsNoLongerMaintained")
|
||||
private Integer shipDetailsNoLongerMaintained;
|
||||
|
||||
@JsonProperty("shipEUSanctionList")
|
||||
private Integer shipEUSanctionList;
|
||||
|
||||
@JsonProperty("shipFlagDisputed")
|
||||
private Integer shipFlagDisputed;
|
||||
|
||||
@JsonProperty("shipFlagSanctionedCountry")
|
||||
private Integer shipFlagSanctionedCountry;
|
||||
|
||||
@JsonProperty("shipHistoricalFlagSanctionedCountry")
|
||||
private Integer shipHistoricalFlagSanctionedCountry;
|
||||
|
||||
@JsonProperty("shipOFACNonSDNSanctionList")
|
||||
private Integer shipOFACNonSDNSanctionList;
|
||||
|
||||
@JsonProperty("shipOFACSanctionList")
|
||||
private Integer shipOFACSanctionList;
|
||||
|
||||
@JsonProperty("shipOFACAdvisoryList")
|
||||
private Integer shipOFACAdvisoryList;
|
||||
|
||||
@JsonProperty("shipOwnerOFACSSIList")
|
||||
private Integer shipOwnerOFACSSIList;
|
||||
|
||||
@JsonProperty("shipOwnerAustralianSanctionList")
|
||||
private Integer shipOwnerAustralianSanctionList;
|
||||
|
||||
@JsonProperty("shipOwnerBESSanctionList")
|
||||
private Integer shipOwnerBESSanctionList;
|
||||
|
||||
@JsonProperty("shipOwnerCanadianSanctionList")
|
||||
private Integer shipOwnerCanadianSanctionList;
|
||||
|
||||
@JsonProperty("shipOwnerEUSanctionList")
|
||||
private Integer shipOwnerEUSanctionList;
|
||||
|
||||
@JsonProperty("shipOwnerFATFJurisdiction")
|
||||
private Integer shipOwnerFATFJurisdiction;
|
||||
|
||||
@JsonProperty("shipOwnerHistoricalOFACSanctionedCountry")
|
||||
private Integer shipOwnerHistoricalOFACSanctionedCountry;
|
||||
|
||||
@JsonProperty("shipOwnerOFACSanctionList")
|
||||
private Integer shipOwnerOFACSanctionList;
|
||||
|
||||
@JsonProperty("shipOwnerOFACSanctionedCountry")
|
||||
private Integer shipOwnerOFACSanctionedCountry;
|
||||
|
||||
@JsonProperty("shipOwnerParentCompanyNonCompliance")
|
||||
private Integer shipOwnerParentCompanyNonCompliance;
|
||||
|
||||
@JsonProperty("shipOwnerParentFATFJurisdiction")
|
||||
private Integer shipOwnerParentFATFJurisdiction;
|
||||
|
||||
@JsonProperty("shipOwnerParentOFACSanctionedCountry")
|
||||
private Integer shipOwnerParentOFACSanctionedCountry;
|
||||
|
||||
@JsonProperty("shipOwnerSwissSanctionList")
|
||||
private Integer shipOwnerSwissSanctionList;
|
||||
|
||||
@JsonProperty("shipOwnerUAESanctionList")
|
||||
private Integer shipOwnerUAESanctionList;
|
||||
|
||||
@JsonProperty("shipOwnerUNSanctionList")
|
||||
private Integer shipOwnerUNSanctionList;
|
||||
|
||||
@JsonProperty("shipSanctionedCountryPortCallLast12m")
|
||||
private Integer shipSanctionedCountryPortCallLast12m;
|
||||
|
||||
@JsonProperty("shipSanctionedCountryPortCallLast3m")
|
||||
private Integer shipSanctionedCountryPortCallLast3m;
|
||||
|
||||
@JsonProperty("shipSanctionedCountryPortCallLast6m")
|
||||
private Integer shipSanctionedCountryPortCallLast6m;
|
||||
|
||||
@JsonProperty("shipSecurityLegalDisputeEvent")
|
||||
private Integer shipSecurityLegalDisputeEvent;
|
||||
|
||||
@JsonProperty("shipSTSPartnerNonComplianceLast12m")
|
||||
private Integer shipSTSPartnerNonComplianceLast12m;
|
||||
|
||||
@JsonProperty("shipSwissSanctionList")
|
||||
private Integer shipSwissSanctionList;
|
||||
|
||||
@JsonProperty("shipUNSanctionList")
|
||||
private Integer shipUNSanctionList;
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.snp.batch.jobs.web.compliance.service;
|
||||
|
||||
import com.snp.batch.jobs.web.compliance.dto.ComplianceValuesMeaningDto;
|
||||
import com.snp.batch.common.web.service.BaseBypassService;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 모든 Compliance 지표 조회 bypass 서비스
|
||||
* 외부 Maritime API에서 데이터를 실시간 조회하여 그대로 반환
|
||||
*/
|
||||
@Service
|
||||
public class ComplianceValuesMeaningService extends BaseBypassService<ComplianceValuesMeaningDto> {
|
||||
|
||||
public ComplianceValuesMeaningService(
|
||||
@Qualifier("maritimeServiceApiWebClient") WebClient webClient) {
|
||||
super(webClient, "/RiskAndCompliance/ComplianceValuesMeaning", "모든 Compliance 지표 조회",
|
||||
new ParameterizedTypeReference<>() {},
|
||||
new ParameterizedTypeReference<>() {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 모든 Compliance 지표 조회 데이터를 조회합니다.
|
||||
*
|
||||
* @return 모든 Compliance 지표 조회
|
||||
*/
|
||||
public List<ComplianceValuesMeaningDto> getComplianceValuesMeaningData() {
|
||||
return fetchGetList(uri -> uri.path(getApiPath())
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.snp.batch.jobs.web.compliance.service;
|
||||
|
||||
import com.snp.batch.jobs.web.compliance.dto.CompliancesByImosDto;
|
||||
import com.snp.batch.common.web.service.BaseBypassService;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
/**
|
||||
* IMO 기반 Compliance 조회 bypass 서비스
|
||||
* 외부 Maritime API에서 데이터를 실시간 조회하여 그대로 반환
|
||||
*/
|
||||
@Service
|
||||
public class CompliancesByImosService extends BaseBypassService<CompliancesByImosDto> {
|
||||
|
||||
public CompliancesByImosService(
|
||||
@Qualifier("maritimeServiceApiWebClient") WebClient webClient) {
|
||||
super(webClient, "/RiskAndCompliance/CompliancesByImos", "IMO 기반 Compliance 조회",
|
||||
new ParameterizedTypeReference<>() {},
|
||||
new ParameterizedTypeReference<>() {});
|
||||
}
|
||||
|
||||
/**
|
||||
* IMO 기반 Compliance 조회 데이터를 조회합니다.
|
||||
*
|
||||
* @return IMO 기반 Compliance 조회
|
||||
*/
|
||||
public List<CompliancesByImosDto> getCompliancesByImosData(String imos) {
|
||||
return fetchGetList(uri -> uri.path(getApiPath())
|
||||
.queryParam("imos", imos)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package com.snp.batch.jobs.web.compliance.service;
|
||||
|
||||
import com.snp.batch.jobs.web.compliance.dto.PagedUpdatedComplianceListDto;
|
||||
import com.snp.batch.common.web.service.BaseBypassService;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
/**
|
||||
* PagedUpdatedComplianceList bypass 서비스
|
||||
* 외부 Maritime API에서 데이터를 실시간 조회하여 그대로 반환
|
||||
*/
|
||||
@Service
|
||||
public class PagedUpdatedComplianceListService extends BaseBypassService<PagedUpdatedComplianceListDto> {
|
||||
|
||||
public PagedUpdatedComplianceListService(
|
||||
@Qualifier("maritimeServiceApiWebClient") WebClient webClient) {
|
||||
super(webClient, "/RiskAndCompliance/PagedUpdatedComplianceList", "PagedUpdatedComplianceList",
|
||||
new ParameterizedTypeReference<>() {},
|
||||
new ParameterizedTypeReference<>() {});
|
||||
}
|
||||
|
||||
/**
|
||||
* PagedUpdatedComplianceList 데이터를 조회합니다.
|
||||
*
|
||||
* @return PagedUpdatedComplianceList
|
||||
*/
|
||||
public List<PagedUpdatedComplianceListDto> getPagedUpdatedComplianceListData(String fromDate, String toDate, String pageNumber, String pageSize) {
|
||||
return fetchGetList(uri -> uri.path(getApiPath())
|
||||
.queryParam("fromDate", fromDate)
|
||||
.queryParam("toDate", toDate)
|
||||
.queryParam("pageNumber", pageNumber)
|
||||
.queryParam("pageSize", pageSize)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package com.snp.batch.jobs.web.compliance.service;
|
||||
|
||||
import com.snp.batch.jobs.web.compliance.dto.UpdatedComplianceListDto;
|
||||
import com.snp.batch.common.web.service.BaseBypassService;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 기간 내 변경 Compliance 조회 bypass 서비스
|
||||
* 외부 Maritime API에서 데이터를 실시간 조회하여 그대로 반환
|
||||
*/
|
||||
@Service
|
||||
public class UpdatedComplianceListService extends BaseBypassService<UpdatedComplianceListDto> {
|
||||
|
||||
public UpdatedComplianceListService(
|
||||
@Qualifier("maritimeServiceApiWebClient") WebClient webClient) {
|
||||
super(webClient, "/RiskAndCompliance/UpdatedComplianceList", "기간 내 변경 Compliance 조회",
|
||||
new ParameterizedTypeReference<>() {},
|
||||
new ParameterizedTypeReference<>() {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 기간 내 변경 Compliance 조회 데이터를 조회합니다.
|
||||
*
|
||||
* @return 기간 내 변경 Compliance 조회
|
||||
*/
|
||||
public List<UpdatedComplianceListDto> getUpdatedComplianceListData(String fromDate, String toDate) {
|
||||
return fetchGetList(uri -> uri.path(getApiPath())
|
||||
.queryParam("fromDate", fromDate)
|
||||
.queryParam("toDate", toDate)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@ -99,6 +99,11 @@ public class BypassCodeGenerator {
|
||||
boolean needsLocalDateTime = fields.stream()
|
||||
.anyMatch(f -> "LocalDateTime".equals(f.getFieldType()));
|
||||
|
||||
boolean needsList = fields.stream()
|
||||
.anyMatch(f -> f.getFieldType() != null && f.getFieldType().startsWith("List"));
|
||||
boolean needsMap = fields.stream()
|
||||
.anyMatch(f -> f.getFieldType() != null && f.getFieldType().startsWith("Map"));
|
||||
|
||||
StringBuilder imports = new StringBuilder();
|
||||
imports.append("import com.fasterxml.jackson.annotation.JsonProperty;\n");
|
||||
imports.append("import lombok.AllArgsConstructor;\n");
|
||||
@ -109,6 +114,12 @@ public class BypassCodeGenerator {
|
||||
if (needsLocalDateTime) {
|
||||
imports.append("import java.time.LocalDateTime;\n");
|
||||
}
|
||||
if (needsList) {
|
||||
imports.append("import java.util.List;\n");
|
||||
}
|
||||
if (needsMap) {
|
||||
imports.append("import java.util.Map;\n");
|
||||
}
|
||||
|
||||
StringBuilder fieldLines = new StringBuilder();
|
||||
for (BypassApiField field : fields) {
|
||||
|
||||
@ -32,6 +32,20 @@ public class JsonSchemaParser {
|
||||
* @return 필드 목록 (파싱 실패 시 빈 목록 반환)
|
||||
*/
|
||||
public List<BypassFieldDto> parse(String jsonSample) {
|
||||
return parse(jsonSample, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON 샘플 문자열에서 필드 목록을 추출합니다.
|
||||
* targetField가 지정된 경우 해당 필드 내부를 파싱 대상으로 사용합니다.
|
||||
* 배열이면 첫 번째 요소를 파싱합니다.
|
||||
*
|
||||
* @param jsonSample JSON 샘플 문자열
|
||||
* @param targetField 파싱할 대상 필드명 (null이면 루트 파싱)
|
||||
* 예: "data" → root.data[0] 내부를 파싱
|
||||
* @return 필드 목록 (파싱 실패 시 빈 목록 반환)
|
||||
*/
|
||||
public List<BypassFieldDto> parse(String jsonSample, String targetField) {
|
||||
List<BypassFieldDto> result = new ArrayList<>();
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(jsonSample);
|
||||
@ -43,13 +57,35 @@ public class JsonSchemaParser {
|
||||
return result;
|
||||
}
|
||||
|
||||
// targetField가 지정된 경우 해당 필드 내부를 파싱 대상으로 사용
|
||||
if (targetField != null && !targetField.isEmpty()) {
|
||||
JsonNode nested = target.get(targetField);
|
||||
if (nested != null) {
|
||||
if (nested.isArray() && nested.size() > 0) {
|
||||
target = nested.get(0);
|
||||
} else if (nested.isObject()) {
|
||||
target = nested;
|
||||
} else {
|
||||
log.warn("JSON 파싱: targetField='{}' 가 객체나 배열이 아닙니다.", targetField);
|
||||
return result;
|
||||
}
|
||||
if (!target.isObject()) {
|
||||
log.warn("JSON 파싱: targetField='{}' 내부에서 유효한 객체를 찾을 수 없습니다.", targetField);
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
log.warn("JSON 파싱: targetField='{}' 를 찾을 수 없습니다.", targetField);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
int sortOrder = 0;
|
||||
var fields = target.fields();
|
||||
while (fields.hasNext()) {
|
||||
var entry = fields.next();
|
||||
String originalKey = entry.getKey();
|
||||
String fieldName = toCamelCase(originalKey);
|
||||
String fieldType = inferType(entry.getValue());
|
||||
String fieldType = inferType(entry.getValue(), fieldName);
|
||||
|
||||
// jsonProperty: fieldName과 원본 키가 다를 때만 설정
|
||||
String jsonProperty = fieldName.equals(originalKey) ? null : originalKey;
|
||||
@ -82,9 +118,13 @@ public class JsonSchemaParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* JsonNode에서 Java 타입 추론
|
||||
* JsonNode에서 Java 타입 추론.
|
||||
* 배열 내 첫 번째 요소가 ObjectNode인 경우 "{FieldName}Item" 타입으로 처리합니다.
|
||||
*
|
||||
* @param node 파싱할 노드
|
||||
* @param fieldName 필드명 (배열 내 객체 타입명 생성에 사용)
|
||||
*/
|
||||
private String inferType(JsonNode node) {
|
||||
private String inferType(JsonNode node, String fieldName) {
|
||||
if (node.isTextual()) {
|
||||
return "String";
|
||||
}
|
||||
@ -104,6 +144,10 @@ public class JsonSchemaParser {
|
||||
return "String";
|
||||
}
|
||||
if (node.isArray()) {
|
||||
if (node.size() > 0 && node.get(0).isObject()) {
|
||||
String itemTypeName = capitalize(fieldName) + "Item";
|
||||
return "List<" + itemTypeName + ">";
|
||||
}
|
||||
return "List<Object>";
|
||||
}
|
||||
if (node.isObject()) {
|
||||
@ -111,4 +155,11 @@ public class JsonSchemaParser {
|
||||
}
|
||||
return "String";
|
||||
}
|
||||
|
||||
private String capitalize(String s) {
|
||||
if (s == null || s.isEmpty()) {
|
||||
return s;
|
||||
}
|
||||
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
불러오는 중...
Reference in New Issue
Block a user