diff --git a/README.md b/README.md new file mode 100644 index 0000000..dbb73ea --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# eapim-admin-kjb + +## 개요 +eapim-admin-kjb 는 eapim-admin 확장 모듈로 광주은행 커스터마이징에 필요한 요소들을 포함하고 있습니다. +eapim-admin 에서 포함하여 테스트 이용시 느린 부팅 속도 떄문에 별도 분리 + +## 주요 기능 +- EAI 배치 연계 +- 고객이메일발송 시스템 연계 \ No newline at end of file diff --git a/build.gradle b/build.gradle index 8cff9c4..a776fa2 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,7 @@ dependencies { // api "org.springframework.security.oauth:spring-security-oauth2:2.3.8.RELEASE" implementation "com.google.code.gson:gson:2.3.1" + implementation "org.apache.httpcomponents.client5:httpclient5:5.1" compileOnly 'org.slf4j:jul-to-slf4j:1.7.35' diff --git a/src/main/java/com/eactive/ext/kjb/ums/EmailProperty.java b/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java similarity index 74% rename from src/main/java/com/eactive/ext/kjb/ums/EmailProperty.java rename to src/main/java/com/eactive/ext/kjb/common/KjbProperty.java index 99a5338..439d526 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/EmailProperty.java +++ b/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java @@ -1,4 +1,4 @@ -package com.eactive.ext.kjb.ums; +package com.eactive.ext.kjb.common; import lombok.Data; import org.hibernate.validator.constraints.NotEmpty; @@ -6,9 +6,9 @@ import org.hibernate.validator.constraints.NotEmpty; import javax.validation.constraints.Pattern; @Data -public class EmailProperty { - @Pattern(regexp = "^$|^(https?)://[^\\s/$.?#].[^\\s]*$", message = "kjb.ums.eai_batch.url 은 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api") - private String eaiBatchUrl = ""; +public class KjbProperty { + @Pattern(regexp = "^(https?://[^\\s/$.?#][^\\s]*)|\\s*$", message = "kjb.ums.eai_batch.url 는 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api") + private String eaiBatchUrl; @NotEmpty(message = "kjb.ums.eai_batch.send_dir 는 필수값입니다.") @Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", @@ -21,6 +21,6 @@ public class EmailProperty { private String eaiBatchBackupDir; - @Pattern(regexp = "^[a-zA-Z0-9]{12}$", message = "kjb.ums.eai_batch.interface_id 는 12자리 영문대소문자 및 숫자 조합이어야 합니다.") + @Pattern(regexp = "^[a-zA-Z0-9]{13}$", message = "kjb.ums.eai_batch.interface_id 는 12자리 영문대소문자 및 숫자 조합이어야 합니다.") private String eaiBatchInterfaceId; } diff --git a/src/main/java/com/eactive/ext/kjb/eai/EaiBatchMessage.java b/src/main/java/com/eactive/ext/kjb/eai/EaiBatchMessage.java new file mode 100644 index 0000000..7ce84d2 --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/eai/EaiBatchMessage.java @@ -0,0 +1,112 @@ +package com.eactive.ext.kjb.eai; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.nio.ByteBuffer; + +@Slf4j +public class EaiBatchMessage { + @Getter + private String interfaceId = ""; + + @Getter + private String baseDate = ""; + + @Getter + private String count = ""; + + @Getter + private String resultCode = ""; + + @Getter + private String filename = ""; + + + private String rawMessage = ""; + + private boolean isReadOnly = false; + + + private EaiBatchMessage() {} + + + public static EaiBatchMessage createRequest(String interfaceId, String filename) { + EaiBatchMessage message = new EaiBatchMessage(); + message.interfaceId = interfaceId; + message.filename = filename; + + return message; + } + + public static EaiBatchMessage fromResponse(String rawMessage) { + EaiBatchMessage message = new EaiBatchMessage(); + message.rawMessage = rawMessage; + message.isReadOnly = true; + + if (rawMessage.length() >= 40) { + message.interfaceId = rawMessage.substring(0, 20).trim(); + message.baseDate = rawMessage.substring(20, 28).trim(); + message.count = rawMessage.substring(28, 38).trim(); + message.resultCode = rawMessage.substring(38, 39).trim(); + // filename 은 @@가 발견되면 이전 까지 trim / 발견되지 않으면 나머지 전체 그리고 trim + int endIdx = rawMessage.indexOf("@@", 39); + if (endIdx == -1) { + message.filename = rawMessage.substring(39).trim(); + } else { + message.filename = rawMessage.substring(39, endIdx).trim(); + } + } + + if (StringUtils.isEmpty(message.interfaceId)) { + log.error("Invalid EAI Batch response message: interfaceId is empty. rawMessage={}", rawMessage); + throw new IllegalArgumentException("Invalid EAI Batch response message: interfaceId is empty"); + } + + return message; + } + + + @Override + public String toString() { + if (isReadOnly) { + return rawMessage; + } else { + ByteBuffer buffer = ByteBuffer.allocate(1024); + // 0~19 : 인터페이스ID (20) + buffer.put(fixedString(interfaceId, 20)); + // 19-8: 기준일자 (8) + buffer.put(fixedString(baseDate, 8)); + // 27-10 : 건수 (10) + buffer.put(fixedString(count, 10)); + // 37-1 : 결과코드 (1) + buffer.put(fixedString(resultCode, 1)); + // 38-100 : 파일명 (100) + buffer.put(fixedString(filename, 100)); + + // 종료 구분자 (2) + buffer.put("@@".getBytes()); + + + return new String(buffer.array()).trim(); + } + } + + private byte[] fixedString(String str, int length) { + if (str == null) { + str = ""; + } + byte[] strBytes = str.getBytes(); + byte[] result = new byte[length]; + + int copyLength = Math.min(strBytes.length, length); + System.arraycopy(strBytes, 0, result, 0, copyLength); + + for (int i = copyLength; i < length; i++) { + result[i] = ' '; + } + + return result; + } +} diff --git a/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java b/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java new file mode 100644 index 0000000..ccd6774 --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java @@ -0,0 +1,57 @@ +package com.eactive.ext.kjb.eai; + +import com.eactive.ext.kjb.common.KjbProperty; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.io.entity.EntityUtils; + +import javax.validation.constraints.NotNull; +import java.io.IOException; + +@Slf4j +public class KjbEaiBatchModule { + private final KjbProperty property; + private final CloseableHttpClient httpClient; + + + public KjbEaiBatchModule(@NotNull KjbProperty property) { + if (property == null) { + throw new IllegalArgumentException("KjbProperty cannot be null"); + } + + this.property = property; + + if (StringUtils.isNotEmpty(property.getEaiBatchUrl())) { + this.httpClient = HttpClients.createDefault(); + } else { + this.httpClient = null; + log.warn("EAI_BATCH_URL 가 설정되지 않음. 실제 EAI 배치 연동 없이 동작함"); + } + } + + public EaiBatchMessage call(String interfaceId, String filename) throws IOException { + EaiBatchMessage reqMessage = EaiBatchMessage.createRequest(interfaceId, filename); + log.debug("EAI 배치 호출: {}", reqMessage); + + if (this.httpClient != null) { + String url = property.getEaiBatchUrl() + "?eaibatMsg=" + reqMessage.toString(); + final HttpGet httpGet = new HttpGet(url); + + String message = httpClient.execute(httpGet, response -> EntityUtils.toString(response.getEntity(), "EUC-KR")); + + EaiBatchMessage resMessage = EaiBatchMessage.fromResponse(message); + log.debug("EAI 배치 응답: {}", resMessage); + return resMessage; + } else { + log.info("EAI_BATCH_URL 이 설정되지 않아 실제 호출하지 않음. 요청 메시지: {}", reqMessage); + + // 정상응답 리턴 + EaiBatchMessage resMessage = EaiBatchMessage.fromResponse(String.format("%-20s%-8s%-10s%-1s%s@@", interfaceId, "", "0000000000", "1", "S:" + filename)); + log.debug("EAI 배치 응답(모의): {}", resMessage); + return resMessage; + } + } +} diff --git a/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java b/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java index 71d0bfe..9a92b1c 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java +++ b/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java @@ -3,6 +3,9 @@ package com.eactive.ext.kjb.ums; import com.eactive.apim.portal.portaluser.entity.PortalUser; import com.eactive.apim.portal.template.entity.MessageRequest; import com.eactive.apim.portal.template.repository.MessageRequestRepository; +import com.eactive.ext.kjb.common.KjbProperty; +import com.eactive.ext.kjb.eai.EaiBatchMessage; +import com.eactive.ext.kjb.eai.KjbEaiBatchModule; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import lombok.extern.slf4j.Slf4j; @@ -22,19 +25,19 @@ import java.util.Map; @Slf4j public class EmailJob { - private final EmailProperty prop; + private final KjbProperty prop; private final MessageRequest messageRequest; private final Path tempFilePath; private final Path eaiFilePath; private final Path backupFilePath; private final Path failedFilePath; - private MessageRequestRepository messageRequestRepository; + private final MessageRequestRepository messageRequestRepository; private final LocalDateTime now = LocalDateTime.now(); - private EmailJob(EmailProperty prop, MessageRequest messageRequest, MessageRequestRepository messageRequestRepository) { + private EmailJob(KjbProperty prop, MessageRequest messageRequest, MessageRequestRepository messageRequestRepository) { this.messageRequest = messageRequest; this.prop = prop; @@ -50,7 +53,7 @@ public class EmailJob { } - public static EmailJob create(EmailProperty prop, MessageRequest messageRequest, MessageRequestRepository repository) { + public static EmailJob create(KjbProperty prop, MessageRequest messageRequest, MessageRequestRepository repository) { if (prop == null) { throw new IllegalArgumentException("Property is null"); } @@ -120,13 +123,17 @@ public class EmailJob { messageRequestRepository.save(messageRequest); } - - try { + log.debug("이메일 파일 생성"); doSaveFile(); + + log.debug("EAI 배치 호출"); eaiBatchCall(); + + log.debug("발송 성공 처리"); doSuccess(); } catch (Exception e) { + log.error("이메일 발송 실패", e); doFail(); } finally { doDeleteEaiFile(); @@ -141,8 +148,16 @@ public class EmailJob { // TODO: EAI 발송 실패 처리 } - private void eaiBatchCall() { - // TODO: EAI 배치 호출 + private void eaiBatchCall() throws IOException { + KjbEaiBatchModule eaiBatchModule = new KjbEaiBatchModule(prop); + EaiBatchMessage result = eaiBatchModule.call(prop.getEaiBatchInterfaceId(), eaiFilePath.getFileName().toString()); + + if (!"1".equals(result.getResultCode())) { + log.error("EAI Batch call failed: {}", result); + throw new IllegalStateException("EAI Batch call failed: " + result); + } + + log.debug("EAI Batch call result: {}", result); } private void doSuccess() { diff --git a/src/main/java/com/eactive/ext/kjb/ums/EmailSendModule.java b/src/main/java/com/eactive/ext/kjb/ums/EmailSendModule.java index 320e849..fbfbb54 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/EmailSendModule.java +++ b/src/main/java/com/eactive/ext/kjb/ums/EmailSendModule.java @@ -2,7 +2,8 @@ package com.eactive.ext.kjb.ums; import com.eactive.apim.portal.template.entity.MessageRequest; import com.eactive.apim.portal.template.repository.MessageRequestRepository; -import com.eactive.ext.kjb.common.ValidationUtil; +import com.eactive.ext.kjb.common.KjbProperty; +import com.eactive.ext.kjb.utils.ValidationUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -14,12 +15,12 @@ import java.util.function.BiConsumer; @Slf4j public class EmailSendModule { private static EmailSendModule instance = null; - private static EmailProperty prop; + private static KjbProperty prop; private final MessageRequestRepository repo; - public static synchronized EmailSendModule getInstance(EmailProperty property, + public static synchronized EmailSendModule getInstance(KjbProperty property, MessageRequestRepository messageRequestRepository) { ValidationUtil.validateOrThrow(property); @@ -89,7 +90,7 @@ public class EmailSendModule { - private EmailSendModule(EmailProperty property, + private EmailSendModule(KjbProperty property, MessageRequestRepository repo) { this.repo = repo; @@ -103,14 +104,15 @@ public class EmailSendModule { public CompletableFuture sendEmail(MessageRequest messageRequest, BiConsumer callback) { return CompletableFuture .supplyAsync(() -> { - // TODO: MESSAGE_CODE를 통해 SERVICE_ID 생성 필요 - String serviceId; + String serviceId = "UNKNOWN"; - switch(messageRequest.getMessageCode()) { + serviceId = messageRequest.getMessageCode().getServiceId(); + messageRequest.setServiceId(serviceId); + if (StringUtils.isEmpty(serviceId)) { + throw new IllegalArgumentException("MessageRequest MessageCode serviceId is required"); } - messageRequest.setServiceId("TEMP_SERVICE"); if (repo != null) { repo.save(messageRequest); } @@ -123,26 +125,4 @@ public class EmailSendModule { }) .whenComplete(callback); } - - - private void validateMessageRequest(MessageRequest messageRequest) { - if (messageRequest == null) { - log.warn("MessageRequest is null"); - throw new IllegalArgumentException("MessageRequest is required"); - } - - if (StringUtils.isEmpty(messageRequest.getId())) { - log.warn("MessageRequest ID is empty"); - throw new IllegalArgumentException("MessageRequest ID is required"); - } - - if (StringUtils.isEmpty(messageRequest.getEmail())) { - log.warn("MessageRequest Email is empty for ID: {}", messageRequest.getId()); - throw new IllegalArgumentException("MessageRequest Email is required for ID: " + messageRequest.getId()); - } - } - - private void createMailFile() { - - } } diff --git a/src/main/java/com/eactive/ext/kjb/common/ValidationUtil.java b/src/main/java/com/eactive/ext/kjb/utils/ValidationUtil.java similarity index 97% rename from src/main/java/com/eactive/ext/kjb/common/ValidationUtil.java rename to src/main/java/com/eactive/ext/kjb/utils/ValidationUtil.java index 29e9662..dff8e99 100644 --- a/src/main/java/com/eactive/ext/kjb/common/ValidationUtil.java +++ b/src/main/java/com/eactive/ext/kjb/utils/ValidationUtil.java @@ -1,4 +1,4 @@ -package com.eactive.ext.kjb.common; +package com.eactive.ext.kjb.utils; import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator; diff --git a/src/test/java/com/eactive/ext/kjb/ums/test/KjbEmailJobSendModuleTests.java b/src/test/java/com/eactive/ext/kjb/ums/test/KjbEmailSendModuleTests.java similarity index 77% rename from src/test/java/com/eactive/ext/kjb/ums/test/KjbEmailJobSendModuleTests.java rename to src/test/java/com/eactive/ext/kjb/ums/test/KjbEmailSendModuleTests.java index 6bf739e..b197993 100644 --- a/src/test/java/com/eactive/ext/kjb/ums/test/KjbEmailJobSendModuleTests.java +++ b/src/test/java/com/eactive/ext/kjb/ums/test/KjbEmailSendModuleTests.java @@ -1,7 +1,8 @@ package com.eactive.ext.kjb.ums.test; import com.eactive.apim.portal.template.entity.MessageRequest; -import com.eactive.ext.kjb.ums.EmailProperty; +import com.eactive.apim.portal.template.entity.MessageRequestEnums; +import com.eactive.ext.kjb.common.KjbProperty; import com.eactive.ext.kjb.ums.EmailSendModule; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; @@ -11,18 +12,20 @@ import java.util.concurrent.CompletableFuture; @Slf4j -public class KjbEmailJobSendModuleTests { +public class KjbEmailSendModuleTests { @Test public void test() { - EmailProperty prop = new EmailProperty(); + KjbProperty prop = new KjbProperty(); prop.setEaiBatchUrl(null); prop.setEaiBatchSendDir("D:\\kjb-logs\\sendmail\\snd"); prop.setEaiBatchBackupDir("D:\\kjb-logs\\sendmail\\bak"); + prop.setEaiBatchInterfaceId("UAGFF00001UIE"); EmailSendModule service = EmailSendModule.getInstance(prop, null); MessageRequest messageRequest = new MessageRequest(); messageRequest.setId(UUID.randomUUID().toString()); + messageRequest.setMessageCode(MessageRequestEnums.MessageCode.USER_VERIFICATION_EMAIL); messageRequest.setEmail("dearmai@dearmai.net"); messageRequest.setUsername("김광은"); messageRequest.setSubject("제목 - test"); diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml new file mode 100644 index 0000000..c7a3a0d --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,22 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + \ No newline at end of file