snp-batch-validation/src/main/java/com/snp/batch/service/BypassConfigService.java
HYOJIN ce27c60985 feat: 같은 도메인에 여러 BYPASS API 엔드포인트 등록 지원 (#63)
- BypassApiConfig에 endpointName 필드 추가 (externalPath에서 자동 추출)
- domainName unique 제약 → domainName + endpointName 복합 unique로 변경
- 코드 생성 시 같은 도메인의 모든 설정을 합쳐 Controller 1개 생성
- Service/DTO는 엔드포인트별로 별도 생성
- CodeGenerationResult에 servicePaths/dtoPaths 목록 반환
- 프론트엔드 타입/UI 업데이트

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 08:25:03 +09:00

245 lines
9.0 KiB
Java

package com.snp.batch.service;
import com.snp.batch.global.dto.BypassConfigRequest;
import com.snp.batch.global.dto.BypassConfigResponse;
import com.snp.batch.global.dto.BypassFieldDto;
import com.snp.batch.global.dto.BypassParamDto;
import com.snp.batch.global.model.BypassApiConfig;
import com.snp.batch.global.model.BypassApiField;
import com.snp.batch.global.model.BypassApiParam;
import com.snp.batch.global.repository.BypassApiConfigRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
/**
* BYPASS API 설정 CRUD 비즈니스 로직
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class BypassConfigService {
private final BypassApiConfigRepository configRepository;
/**
* 설정 목록 조회
*
* @return 전체 BYPASS API 설정 목록
*/
public List<BypassConfigResponse> getConfigs() {
return configRepository.findAll().stream()
.map(this::toResponse)
.toList();
}
/**
* 설정 상세 조회
*
* @param id 설정 ID
* @return 설정 상세 정보
*/
public BypassConfigResponse getConfig(Long id) {
BypassApiConfig config = configRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("설정을 찾을 수 없습니다: " + id));
return toResponse(config);
}
/**
* 설정 등록
*
* @param request 등록 요청 DTO
* @return 등록된 설정 정보
*/
@Transactional
public BypassConfigResponse createConfig(BypassConfigRequest request) {
String endpointName = extractEndpointName(request.getExternalPath());
if (configRepository.existsByDomainNameAndEndpointName(request.getDomainName(), endpointName)) {
throw new IllegalArgumentException(
"이미 존재하는 도메인+엔드포인트 조합입니다: " + request.getDomainName() + "/" + endpointName);
}
BypassApiConfig config = toEntity(request);
if (request.getParams() != null) {
request.getParams().forEach(p -> {
BypassApiParam param = toParamEntity(p);
param.setConfig(config);
config.getParams().add(param);
});
}
if (request.getFields() != null) {
request.getFields().forEach(f -> {
BypassApiField field = toFieldEntity(f);
field.setConfig(config);
config.getFields().add(field);
});
}
return toResponse(configRepository.save(config));
}
/**
* 설정 수정
*
* @param id 설정 ID
* @param request 수정 요청 DTO
* @return 수정된 설정 정보
*/
@Transactional
public BypassConfigResponse updateConfig(Long id, BypassConfigRequest request) {
BypassApiConfig config = configRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("설정을 찾을 수 없습니다: " + id));
config.setDomainName(request.getDomainName());
config.setDisplayName(request.getDisplayName());
config.setWebclientBean(request.getWebclientBean());
config.setExternalPath(request.getExternalPath());
config.setHttpMethod(request.getHttpMethod());
config.setResponseType(request.getResponseType());
config.setDescription(request.getDescription());
// params 교체 (orphanRemoval로 기존 자동 삭제)
config.getParams().clear();
if (request.getParams() != null) {
request.getParams().forEach(p -> {
BypassApiParam param = toParamEntity(p);
param.setConfig(config);
config.getParams().add(param);
});
}
// fields 교체
config.getFields().clear();
if (request.getFields() != null) {
request.getFields().forEach(f -> {
BypassApiField field = toFieldEntity(f);
field.setConfig(config);
config.getFields().add(field);
});
}
return toResponse(configRepository.save(config));
}
/**
* 설정 삭제
*
* @param id 설정 ID
*/
@Transactional
public void deleteConfig(Long id) {
if (!configRepository.existsById(id)) {
throw new IllegalArgumentException("설정을 찾을 수 없습니다: " + id);
}
configRepository.deleteById(id);
}
/**
* 코드 생성 완료 마킹
*
* @param id 설정 ID
*/
@Transactional
public void markAsGenerated(Long id) {
BypassApiConfig config = configRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("설정을 찾을 수 없습니다: " + id));
config.setGenerated(true);
config.setGeneratedAt(LocalDateTime.now());
configRepository.save(config);
}
// ---- private 헬퍼 메서드 ----
private BypassConfigResponse toResponse(BypassApiConfig config) {
return BypassConfigResponse.builder()
.id(config.getId())
.domainName(config.getDomainName())
.endpointName(config.getEndpointName())
.displayName(config.getDisplayName())
.webclientBean(config.getWebclientBean())
.externalPath(config.getExternalPath())
.httpMethod(config.getHttpMethod())
.responseType(config.getResponseType())
.description(config.getDescription())
.generated(config.getGenerated())
.generatedAt(config.getGeneratedAt())
.createdAt(config.getCreatedAt())
.updatedAt(config.getUpdatedAt())
.params(config.getParams().stream().map(this::toParamDto).toList())
.fields(config.getFields().stream().map(this::toFieldDto).toList())
.build();
}
private BypassApiConfig toEntity(BypassConfigRequest request) {
return BypassApiConfig.builder()
.domainName(request.getDomainName())
.endpointName(extractEndpointName(request.getExternalPath()))
.displayName(request.getDisplayName())
.webclientBean(request.getWebclientBean())
.externalPath(request.getExternalPath())
.httpMethod(request.getHttpMethod() != null ? request.getHttpMethod() : "GET")
.responseType(request.getResponseType() != null ? request.getResponseType() : "LIST")
.description(request.getDescription())
.build();
}
/**
* externalPath의 마지막 세그먼트를 endpointName으로 추출
* 예: "/RiskAndCompliance/CompliancesByImos" → "CompliancesByImos"
*/
private String extractEndpointName(String externalPath) {
if (externalPath == null || externalPath.isEmpty()) {
return "";
}
String[] segments = externalPath.split("/");
return segments[segments.length - 1];
}
private BypassApiParam toParamEntity(BypassParamDto dto) {
return BypassApiParam.builder()
.paramName(dto.getParamName())
.paramType(dto.getParamType() != null ? dto.getParamType() : "STRING")
.paramIn(dto.getParamIn() != null ? dto.getParamIn() : "QUERY")
.required(dto.getRequired() != null ? dto.getRequired() : true)
.description(dto.getDescription())
.sortOrder(dto.getSortOrder() != null ? dto.getSortOrder() : 0)
.build();
}
private BypassApiField toFieldEntity(BypassFieldDto dto) {
return BypassApiField.builder()
.fieldName(dto.getFieldName())
.jsonProperty(dto.getJsonProperty())
.fieldType(dto.getFieldType() != null ? dto.getFieldType() : "String")
.description(dto.getDescription())
.sortOrder(dto.getSortOrder() != null ? dto.getSortOrder() : 0)
.build();
}
private BypassParamDto toParamDto(BypassApiParam param) {
return BypassParamDto.builder()
.id(param.getId())
.paramName(param.getParamName())
.paramType(param.getParamType())
.paramIn(param.getParamIn())
.required(param.getRequired())
.description(param.getDescription())
.sortOrder(param.getSortOrder())
.build();
}
private BypassFieldDto toFieldDto(BypassApiField field) {
return BypassFieldDto.builder()
.id(field.getId())
.fieldName(field.getFieldName())
.jsonProperty(field.getJsonProperty())
.fieldType(field.getFieldType())
.description(field.getDescription())
.sortOrder(field.getSortOrder())
.build();
}
}