From 798f4e7acea4f34af0d7728399feadb5993ed597 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Tue, 21 Oct 2025 17:53:50 +0900 Subject: [PATCH 01/16] =?UTF-8?q?=EB=A1=9C=EC=BB=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + bin/.gitignore | 2 + .../eactive/ext/kjb/common/KjbProperty.java | 24 ++- .../ext/kjb/eai/KjbEaiBatchModule.java | 24 ++- .../com/eactive/ext/kjb/ums/EmailJob.java | 18 +- .../eactive/ext/kjb/ums/EmailSendModule.java | 6 + .../eactive/ext/kjb/ums/KjbUmsService.java | 167 ++++++++++++++++++ .../eactive/ext/kjb/ums/UmsCallResult.java | 18 ++ .../eactive/ext/kjb/ums/UmzBizWorkCode.java | 19 ++ .../ext/kjb/ums/gson/IUmsGsonObject.java | 7 + .../ums/gson/ObpMonitoringUmsTemplate.java | 8 + .../ext/kjb/ums/gson/UmsRequestPayload.java | 97 ++++++++++ .../kjb/ums/gson/VerifyPhoneUmsTemplate.java | 12 ++ .../kjb/ums/test/KjbEmailSendModuleTests.java | 38 +++- 14 files changed, 421 insertions(+), 22 deletions(-) create mode 100644 bin/.gitignore create mode 100644 src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java create mode 100644 src/main/java/com/eactive/ext/kjb/ums/UmsCallResult.java create mode 100644 src/main/java/com/eactive/ext/kjb/ums/UmzBizWorkCode.java create mode 100644 src/main/java/com/eactive/ext/kjb/ums/gson/IUmsGsonObject.java create mode 100644 src/main/java/com/eactive/ext/kjb/ums/gson/ObpMonitoringUmsTemplate.java create mode 100644 src/main/java/com/eactive/ext/kjb/ums/gson/UmsRequestPayload.java create mode 100644 src/main/java/com/eactive/ext/kjb/ums/gson/VerifyPhoneUmsTemplate.java diff --git a/.gitignore b/.gitignore index 567609b..25c7ff0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ build/ +.settings +.classpath +.project diff --git a/bin/.gitignore b/bin/.gitignore new file mode 100644 index 0000000..7eed456 --- /dev/null +++ b/bin/.gitignore @@ -0,0 +1,2 @@ +/main/ +/test/ diff --git a/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java b/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java index 439d526..ddb084a 100644 --- a/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java +++ b/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java @@ -3,13 +3,21 @@ package com.eactive.ext.kjb.common; import lombok.Data; import org.hibernate.validator.constraints.NotEmpty; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; import javax.validation.constraints.Pattern; @Data public class KjbProperty { - @Pattern(regexp = "^(https?://[^\\s/$.?#][^\\s]*)|\\s*$", message = "kjb.ums.eai_batch.url 는 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api") + @Pattern(regexp = "^(https?://[^\\s/$.?#][^\\s]*)|\\s*$", message = "kjb.eai_batch.url 는 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api") private String eaiBatchUrl; + @Min(1) @Max(10) + private int eaiBatchConnectionTimeout = 5; + + @Min(1) @Max(30) + private int eaiBatchResponseTimeout = 10; + @NotEmpty(message = "kjb.ums.eai_batch.send_dir 는 필수값입니다.") @Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", message = "kjb.ums.eai_batch.send_dir 은 절대 경로여야 합니다. 예: /Data/eapim/portal/sendmail/snd 또는 C:\\Data\\eapim\\portal\\sendmail\\snd") @@ -20,7 +28,19 @@ public class KjbProperty { message = "kjb.ums.eai_batch.backup_dir 은 절대 경로여야 합니다. 예: /Data/eapim/portal/sendmail/bak 또는 C:\\Data\\eapim\\portal\\sendmail\\bak") private String eaiBatchBackupDir; - @Pattern(regexp = "^[a-zA-Z0-9]{13}$", message = "kjb.ums.eai_batch.interface_id 는 12자리 영문대소문자 및 숫자 조합이어야 합니다.") private String eaiBatchInterfaceId; + + + @Pattern(regexp = "^(https?://[^\\s/$.?#][^\\s]*)|\\s*$", message = "kjb.ums.url 는 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api") + private String umsUrl; + + @Pattern(regexp = "^[a-zA-Z0-9_-]{36}$", message = "kjb.ums.api_key 입력 형식이 맞지 않습니다.") + private String umsApiKey; + + @Min(1) @Max(10) + private int umsConnectionTimeout = 5; + + @Min(1) @Max(30) + private int umsResponseTimeout = 10; } diff --git a/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java b/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java index ccd6774..39133f1 100644 --- a/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java +++ b/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java @@ -4,17 +4,23 @@ 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.config.RequestConfig; 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 org.apache.hc.core5.net.URIBuilder; +import org.apache.hc.core5.util.Timeout; import javax.validation.constraints.NotNull; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; @Slf4j public class KjbEaiBatchModule { private final KjbProperty property; private final CloseableHttpClient httpClient; + private final RequestConfig requestConfig; public KjbEaiBatchModule(@NotNull KjbProperty property) { @@ -23,6 +29,10 @@ public class KjbEaiBatchModule { } this.property = property; + requestConfig = RequestConfig.custom() + .setConnectTimeout(Timeout.ofSeconds(property.getEaiBatchConnectionTimeout())) + .setResponseTimeout(Timeout.ofSeconds(property.getEaiBatchResponseTimeout())) + .build(); if (StringUtils.isNotEmpty(property.getEaiBatchUrl())) { this.httpClient = HttpClients.createDefault(); @@ -37,8 +47,18 @@ public class KjbEaiBatchModule { log.debug("EAI 배치 호출: {}", reqMessage); if (this.httpClient != null) { - String url = property.getEaiBatchUrl() + "?eaibatMsg=" + reqMessage.toString(); - final HttpGet httpGet = new HttpGet(url); + URI uri = null; + try { + uri = new URIBuilder(property.getEaiBatchUrl()) + .addParameter("eaibatMsg", reqMessage.toString()) + .build(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + log.debug("EAI Batch call - {}", uri.toASCIIString()); + + final HttpGet httpGet = new HttpGet(uri); + httpGet.setConfig(requestConfig); String message = httpClient.execute(httpGet, response -> EntityUtils.toString(response.getEntity(), "EUC-KR")); 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 9a92b1c..3e818cd 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java +++ b/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java @@ -83,17 +83,15 @@ public class EmailJob { errors.put("id", "ID is required"); } - if (StringUtils.isEmpty(messageRequest.getEmail())) { - errors.put("email", "Email is required"); - } - - if (StringUtils.isEmpty(messageRequest.getUsername())) { - errors.put("username", "Username is required"); - } - - if (forSend) { - // TODO: 발송용 체크 + if (StringUtils.isEmpty(messageRequest.getEmail())) { + errors.put("email", "Email is required"); + } + + if (StringUtils.isEmpty(messageRequest.getUsername())) { + errors.put("username", "Username is required"); + } + if (StringUtils.isEmpty(messageRequest.getServiceId())) { errors.put("serviceId", "Service ID is required"); } 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 fbfbb54..bdc2a73 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/EmailSendModule.java +++ b/src/main/java/com/eactive/ext/kjb/ums/EmailSendModule.java @@ -1,5 +1,6 @@ package com.eactive.ext.kjb.ums; +import com.eactive.apim.portal.template.entity.MessageCode; import com.eactive.apim.portal.template.entity.MessageRequest; import com.eactive.apim.portal.template.repository.MessageRequestRepository; import com.eactive.ext.kjb.common.KjbProperty; @@ -113,6 +114,11 @@ public class EmailSendModule { throw new IllegalArgumentException("MessageRequest MessageCode serviceId is required"); } + if (MessageCode.Channels.EMAIL.getValue() != (messageRequest.getMessageCode().getChannels() & MessageCode.Channels.EMAIL.getValue())) { + log.error("지원하지 않는 발송 채널 - {}", messageRequest.getMessageCode()); + throw new IllegalArgumentException("지원하지 않는 발송 채널 - " + messageRequest.getMessageCode()); + } + if (repo != null) { repo.save(messageRequest); } diff --git a/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java b/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java new file mode 100644 index 0000000..853fb6a --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java @@ -0,0 +1,167 @@ +package com.eactive.ext.kjb.ums; + +import com.eactive.apim.portal.template.entity.MessageCode; +import com.eactive.apim.portal.template.entity.MessageRequest; +import com.eactive.ext.kjb.common.KjbProperty; +import com.eactive.ext.kjb.ums.gson.IUmsGsonObject; +import com.eactive.ext.kjb.ums.gson.UmsRequestPayload; +import com.eactive.ext.kjb.ums.gson.VerifyPhoneUmsTemplate; +import com.eactive.ext.kjb.utils.ValidationUtil; +import com.google.gson.Gson; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.entity.EntityBuilder; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.util.Timeout; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; + +@Slf4j +public class KjbUmsService { + private final static String SEND_URL = "/ums/openapi/send"; + private final static String CONTENT_TYPE = "application/json; charset=utf-8"; + private final static String AUTHORIZATION_FMT = "Bearer %s"; + + private final KjbProperty property; + private final boolean isRealMode; + private final CloseableHttpClient httpClient; + private final RequestConfig requestConfig; + + + public KjbUmsService(KjbProperty property) { + if (property == null) { + log.error("[KJB-UMS] 파라미터 설정 되지 않음"); + throw new IllegalArgumentException("property is null"); + } + + isRealMode = property.getUmsUrl() != null && StringUtils.isNotEmpty(property.getUmsUrl().trim()); + + if (isRealMode) { + ValidationUtil.validateOrThrow(property); + + httpClient = HttpClients.createDefault(); + requestConfig = RequestConfig.custom() + .setConnectTimeout(Timeout.ofSeconds(property.getEaiBatchConnectionTimeout())) + .setResponseTimeout(Timeout.ofSeconds(property.getEaiBatchResponseTimeout())) + .build(); + } else { + httpClient = HttpClients.createDefault(); + requestConfig = null; + } + + this.property = property; + } + + public boolean send(MessageRequest messageRequest) { + if (messageRequest == null) { + throw new IllegalArgumentException("messageRequest is null"); + } + + if (MessageCode.Channels.KAKAO_ALIMTALK.getValue() != (messageRequest.getMessageCode().getChannels() & MessageCode.Channels.KAKAO_ALIMTALK.getValue())) { + log.error("지원하지 않는 발송 채널 - {}", messageRequest.getMessageCode().toString()); + throw new IllegalArgumentException("지원하지 않는 발송 채널 - " + messageRequest.getMessageCode().toString()); + } + + IUmsGsonObject content = null; + content = VerifyPhoneUmsTemplate.builder().authNumber(messageRequest.getMessage()).build(); + + String umsMessageId = this.sendKakaoAlimTalk(messageRequest.getId(), + UmzBizWorkCode.VERIFY_PHONE, + messageRequest.getPhone(), + content + ); + + log.info("MessageRequest sent - {}", umsMessageId); + + return true; + } + + public String sendKakaoAlimTalk(String guid, UmzBizWorkCode bizWorkCode, String cpno, IUmsGsonObject content) { + if (content == null) { + throw new IllegalArgumentException("content is null"); + } + + if (StringUtils.isEmpty(guid)) { + guid = String.format("OBP-%s", DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(LocalDateTime.now())); + } + + UmsRequestPayload payload = UmsRequestPayload.builder() + .messageIdentityNo(guid) + .msgBizWorkCode(bizWorkCode.getCode()) + .content(content) + .cellphone(cpno) + .msgSndChnlCd(UmsRequestPayload.ChannelCode.KM) + .build(); + + return callUms(SEND_URL, payload, (messageId, ex) -> { + if (ex != null) { + log.error("UMS Call error", ex); + throw new RuntimeException(ex); + } + + log.info("UMS Call success. ({})", messageId); + }).join().getUmsMessageId(); + } + + private CompletableFuture callUms(@SuppressWarnings("SameParameterValue") String url, UmsRequestPayload requestPayload, + BiConsumer callback) { + return CompletableFuture + .supplyAsync(() -> { + Gson gson = new Gson(); + String json = gson.toJson(requestPayload); + + HttpPost httpPost = new HttpPost(property.getUmsUrl() + url); + httpPost.setHeader("Content-Type", CONTENT_TYPE); + httpPost.setHeader("Authorization", String.format(AUTHORIZATION_FMT, property.getUmsApiKey())); + httpPost.setEntity(EntityBuilder.create().setText(json).setContentType(ContentType.APPLICATION_JSON).build()); + + httpPost.setConfig(requestConfig); + + AtomicReference result = new AtomicReference<>(); + if (isRealMode) { + try { + httpClient.execute(httpPost, res -> { + String body = EntityUtils.toString(res.getEntity()); + result.set(gson.fromJson(body, UmsCallResult.class)); + result.get().setUmsMessageId(requestPayload.getMessageIdentityNo()); + result.get().setSuccess(true); + return result; + }); + } catch (IOException e) { + log.error("UMS Call error", e); + result.set( + UmsCallResult.builder() + .umsMessageId(requestPayload.getMessageIdentityNo()) + .isSuccess(false) + .throwable(e) + .build() + ); + } + } else { + log.warn("UMS URL 미지정으로 실제 호출을 진행하지 않음."); + result.set(UmsCallResult.builder().isSuccess(true).build()); + } + + try { + log.debug("UMS Call URI : {}", httpPost.getUri().toASCIIString()); + log.debug("UMS Call Request payload : {}", json); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + return result.get(); + }) + .whenComplete(callback); + } +} diff --git a/src/main/java/com/eactive/ext/kjb/ums/UmsCallResult.java b/src/main/java/com/eactive/ext/kjb/ums/UmsCallResult.java new file mode 100644 index 0000000..f510345 --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/ums/UmsCallResult.java @@ -0,0 +1,18 @@ +package com.eactive.ext.kjb.ums; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class UmsCallResult { + private String umsMessageId; + private boolean isSuccess; + + private String returnCode; + private String returnMessage; + private String returnPayload; + + private Throwable throwable; +} diff --git a/src/main/java/com/eactive/ext/kjb/ums/UmzBizWorkCode.java b/src/main/java/com/eactive/ext/kjb/ums/UmzBizWorkCode.java new file mode 100644 index 0000000..10c7f3b --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/ums/UmzBizWorkCode.java @@ -0,0 +1,19 @@ +package com.eactive.ext.kjb.ums; + +import lombok.Getter; + +public enum UmzBizWorkCode { + MONITORING("B_ELFN_09510", "OBP 모니터링"), + VERIFY_PHONE("S55_ICT_0006", "API Portal 인증번호"), + ; + + @Getter + private String code; + @Getter + private String displayName; + + UmzBizWorkCode(String code, String displayName) { + this.code = code; + this.displayName = displayName; + } +} diff --git a/src/main/java/com/eactive/ext/kjb/ums/gson/IUmsGsonObject.java b/src/main/java/com/eactive/ext/kjb/ums/gson/IUmsGsonObject.java new file mode 100644 index 0000000..bee5d2b --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/ums/gson/IUmsGsonObject.java @@ -0,0 +1,7 @@ +package com.eactive.ext.kjb.ums.gson; + +/** + * GSON Serialize 용 Object + */ +public interface IUmsGsonObject { +} diff --git a/src/main/java/com/eactive/ext/kjb/ums/gson/ObpMonitoringUmsTemplate.java b/src/main/java/com/eactive/ext/kjb/ums/gson/ObpMonitoringUmsTemplate.java new file mode 100644 index 0000000..b384155 --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/ums/gson/ObpMonitoringUmsTemplate.java @@ -0,0 +1,8 @@ +package com.eactive.ext.kjb.ums.gson; + +import com.google.gson.annotations.SerializedName; + +public class ObpMonitoringUmsTemplate implements IUmsGsonObject { + @SerializedName("AUTH_NO") + private String authNumber; +} diff --git a/src/main/java/com/eactive/ext/kjb/ums/gson/UmsRequestPayload.java b/src/main/java/com/eactive/ext/kjb/ums/gson/UmsRequestPayload.java new file mode 100644 index 0000000..31836d2 --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/ums/gson/UmsRequestPayload.java @@ -0,0 +1,97 @@ +package com.eactive.ext.kjb.ums.gson; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.NotEmpty; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +@Data +@Builder +@Getter +public class UmsRequestPayload { + // MSG_IDNT_NO string 메시지식별번호 50 O "발송 구분을 위한 거래 번호 (GUID 권고) + //GUID로 입력이 어려울시 + //어플리케이션코드(3자리)+난수 로 유니크한 ID로 채번 권고" + @Length(min = 1, max = 50) + @NotEmpty + @SerializedName("MSG_IDNT_NO") + private String messageIdentityNo; + + // MSG_BZWK_CD string 메시지업무코드 30 O UMS에 등록된 메시지업무코드(AS-IS 템플릿ID) + @NotEmpty + @Length(min = 1, max = 30) + @SerializedName("MSG_BZWK_CD") + private String msgBizWorkCode; + + // 메시지업무파라미터내용 / 4000 / JSONObject + // MSG_BZWK_PARAM_CTNT object 메시지업무파라미터내용 4000 메시지업무코드의 템플릿에서 사용하는 변수와 변수값 + @NotNull + @SerializedName("MSG_BZWK_PARAM_CTNT") + private IUmsGsonObject content; + + // 발송예약일시 / 14 / 실 발송 일자(yyyyMMddHHmmss) + // SND_RESR_DTTM string 발송예약일시 14 20240418111500 O 실 발송 일자(현재시간) + @Pattern(regexp = "^[0-9]{14}$") + @NotEmpty + @SerializedName("SND_RESR_DTTM") + private String sendReservationDatetime = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()); + + // 휴대전화번호 / 44 / 휴대폰번호(평문, 암호화 둘다가능) + // CPNO string 휴대전화번호 44 "암호화 또는 평문 (safedb not rrno) + //문자일 시 암호문 + //숫자일 시 전화번호 + //고객번호 또는 휴대전화번호 필수" + @NotEmpty + @Pattern(regexp = "^[0-9]{10,11}$") + @SerializedName("CPNO") + private String cellphone; + + // 발송부점코드 / 4 / + // SND_BRCD string 발송부점코드 4 O 0333 + @NotEmpty + @SerializedName("SND_BRCD") + private String sendBranchCode = "0990"; + + // 발송직원번호 / 7 / ECEB057(텔러번호?) + // SND_EMPNO string 발송직원번호 7 O ? 넣긴해야함 + @NotEmpty + @SerializedName("SND_EMPNO") + private String sendEmployeeNo = "ECEB057"; + + // SRVC_ID string 서비스ID 11 요청단 측 서비스ID + @NotEmpty + @SerializedName("SRVC_ID") + private String serviceId = "eAPIM"; + + // MSG_SND_CHNL_CD string 메시지발송채널코드 2 + @SerializedName("MSG_SND_CHNL_CD") + @NotEmpty + private ChannelCode msgSndChnlCd = ChannelCode.KM; + + // VALDTN_RSLT_CD string 검증결과코드 2 00으로 고정 + @SerializedName("VALDTN_RSLT_CD") + @NotEmpty + private String validationResultCode = "00"; + + public enum ChannelCode { + SM, // SMS + LM, // LMS + MM, // MMS + KM, // 카카오-알림톡 + FM, // 카카오-친구톡 + SR, // RCS-SMS + LR, // RCS-LMS + MR, // RCS-MMS + TR, // RCS-TEMPLATE + IR, // RCS-IMAGE Template + PM // Push + } +} + diff --git a/src/main/java/com/eactive/ext/kjb/ums/gson/VerifyPhoneUmsTemplate.java b/src/main/java/com/eactive/ext/kjb/ums/gson/VerifyPhoneUmsTemplate.java new file mode 100644 index 0000000..f6ecca8 --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/ums/gson/VerifyPhoneUmsTemplate.java @@ -0,0 +1,12 @@ +package com.eactive.ext.kjb.ums.gson; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class VerifyPhoneUmsTemplate implements IUmsGsonObject { + @SerializedName("ATHN_NO") + private String authNumber; +} diff --git a/src/test/java/com/eactive/ext/kjb/ums/test/KjbEmailSendModuleTests.java b/src/test/java/com/eactive/ext/kjb/ums/test/KjbEmailSendModuleTests.java index b197993..eb087f7 100644 --- a/src/test/java/com/eactive/ext/kjb/ums/test/KjbEmailSendModuleTests.java +++ b/src/test/java/com/eactive/ext/kjb/ums/test/KjbEmailSendModuleTests.java @@ -1,11 +1,11 @@ package com.eactive.ext.kjb.ums.test; +import com.eactive.apim.portal.template.entity.MessageCode; import com.eactive.apim.portal.template.entity.MessageRequest; -import com.eactive.apim.portal.template.entity.MessageRequestEnums; import com.eactive.ext.kjb.common.KjbProperty; import com.eactive.ext.kjb.ums.EmailSendModule; +import com.eactive.ext.kjb.ums.KjbUmsService; import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.Test; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -14,18 +14,40 @@ import java.util.concurrent.CompletableFuture; @Slf4j public class KjbEmailSendModuleTests { - @Test - public void test() { +// @Test + public void testSms() { KjbProperty prop = new KjbProperty(); - prop.setEaiBatchUrl(null); - prop.setEaiBatchSendDir("D:\\kjb-logs\\sendmail\\snd"); - prop.setEaiBatchBackupDir("D:\\kjb-logs\\sendmail\\bak"); + prop.setEaiBatchSendDir("c:\\kjb-logs\\sendmail\\snd"); + prop.setEaiBatchBackupDir("c:\\kjb-logs\\sendmail\\bak"); + prop.setEaiBatchInterfaceId("UAGFF00001UIE"); + + MessageRequest messageRequest = new MessageRequest(); + messageRequest.setId(UUID.randomUUID().toString()); + messageRequest.setMessageCode(MessageCode.USER_VERIFICATION_MOBILEPHONE); + messageRequest.setEmail("dearmai@dearmai.net"); + messageRequest.setUsername("김광은"); + messageRequest.setSubject("제목 - test"); + messageRequest.setMessage("내용 - test"); + + KjbUmsService umsService = new KjbUmsService(prop); + umsService.send(messageRequest); + + log.info("done"); + } + +// @Test + public void testEmail() { + KjbProperty prop = new KjbProperty(); +// prop.setEaiBatchUrl("http://172.31.32.111:10230/BATAppWeb/EaiBatCall"); + prop.setEaiBatchUrl(""); + prop.setEaiBatchSendDir("c:\\kjb-logs\\sendmail\\snd"); + prop.setEaiBatchBackupDir("c:\\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.setMessageCode(MessageCode.USER_VERIFICATION_EMAIL); messageRequest.setEmail("dearmai@dearmai.net"); messageRequest.setUsername("김광은"); messageRequest.setSubject("제목 - test"); From 3b7c4d4683e1dfecb5609d5e1f59c904e53deb3e Mon Sep 17 00:00:00 2001 From: Rinjae Date: Wed, 22 Oct 2025 11:32:36 +0900 Subject: [PATCH 02/16] =?UTF-8?q?=EC=84=9C=EB=B2=84=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- .../eactive/ext/kjb/common/KjbProperty.java | 6 +- .../com/eactive/ext/kjb/ums/EmailJob.java | 10 +-- .../eactive/ext/kjb/ums/EmailSendModule.java | 30 ++++---- .../eactive/ext/kjb/ums/KjbUmsService.java | 70 ++++++++++++++++--- .../ext/kjb/ums/gson/UmsRequestPayload.java | 8 ++- .../com/eactive/ext/kjb/utils/JsonUtil.java | 10 +++ .../com/eactive/ext/kjb/utils/OsUtil.java | 24 +++++++ ...EmailSendModuleTests.java => UmsTest.java} | 51 ++++++++++---- src/test/resources/logback-test.xml | 6 +- 10 files changed, 170 insertions(+), 48 deletions(-) create mode 100644 src/main/java/com/eactive/ext/kjb/utils/JsonUtil.java create mode 100644 src/main/java/com/eactive/ext/kjb/utils/OsUtil.java rename src/test/java/com/eactive/ext/kjb/ums/test/{KjbEmailSendModuleTests.java => UmsTest.java} (53%) diff --git a/README.md b/README.md index dbb73ea..38e9c81 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,5 @@ eapim-admin 에서 포함하여 테스트 이용시 느린 부팅 속도 떄문 ## 주요 기능 - EAI 배치 연계 -- 고객이메일발송 시스템 연계 \ No newline at end of file +- 고객이메일발송 시스템 연계 + diff --git a/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java b/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java index ddb084a..850d074 100644 --- a/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java +++ b/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java @@ -21,15 +21,15 @@ public class KjbProperty { @NotEmpty(message = "kjb.ums.eai_batch.send_dir 는 필수값입니다.") @Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", message = "kjb.ums.eai_batch.send_dir 은 절대 경로여야 합니다. 예: /Data/eapim/portal/sendmail/snd 또는 C:\\Data\\eapim\\portal\\sendmail\\snd") - private String eaiBatchSendDir; + private String umsEaiBatchSendDir; @NotEmpty(message = "kjb.ums.eai_batch.backup_dir 는 필수값입니다.") @Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", message = "kjb.ums.eai_batch.backup_dir 은 절대 경로여야 합니다. 예: /Data/eapim/portal/sendmail/bak 또는 C:\\Data\\eapim\\portal\\sendmail\\bak") - private String eaiBatchBackupDir; + private String umsEaiBatchBackupDir; @Pattern(regexp = "^[a-zA-Z0-9]{13}$", message = "kjb.ums.eai_batch.interface_id 는 12자리 영문대소문자 및 숫자 조합이어야 합니다.") - private String eaiBatchInterfaceId; + private String umsEaiBatchInterfaceId; @Pattern(regexp = "^(https?://[^\\s/$.?#][^\\s]*)|\\s*$", message = "kjb.ums.url 는 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api") 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 3e818cd..9f3cf8c 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java +++ b/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java @@ -42,10 +42,10 @@ public class EmailJob { this.prop = prop; String filename = messageRequest.getServiceId() + "_" + DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(now); - this.tempFilePath = Paths.get(prop.getEaiBatchSendDir(), filename + ".tmp"); - this.eaiFilePath = Paths.get(prop.getEaiBatchSendDir(), filename); - this.backupFilePath = Paths.get(prop.getEaiBatchBackupDir(), filename); - this.failedFilePath = Paths.get(prop.getEaiBatchBackupDir(), filename + ".failed"); + this.tempFilePath = Paths.get(prop.getUmsEaiBatchSendDir(), filename + ".tmp"); + this.eaiFilePath = Paths.get(prop.getUmsEaiBatchSendDir(), filename); + this.backupFilePath = Paths.get(prop.getUmsEaiBatchBackupDir(), filename); + this.failedFilePath = Paths.get(prop.getUmsEaiBatchBackupDir(), filename + ".failed"); this.messageRequestRepository = messageRequestRepository; @@ -148,7 +148,7 @@ public class EmailJob { private void eaiBatchCall() throws IOException { KjbEaiBatchModule eaiBatchModule = new KjbEaiBatchModule(prop); - EaiBatchMessage result = eaiBatchModule.call(prop.getEaiBatchInterfaceId(), eaiFilePath.getFileName().toString()); + EaiBatchMessage result = eaiBatchModule.call(prop.getUmsEaiBatchInterfaceId(), eaiFilePath.getFileName().toString()); if (!"1".equals(result.getResultCode())) { log.error("EAI Batch call failed: {}", result); 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 bdc2a73..4c40457 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/EmailSendModule.java +++ b/src/main/java/com/eactive/ext/kjb/ums/EmailSendModule.java @@ -40,50 +40,50 @@ public class EmailSendModule { } log.info("EAI Batch URL: {}", prop.getEaiBatchUrl()); - log.info("EAI Batch Send Dir: {}", prop.getEaiBatchSendDir()); + log.info("EAI Batch Send Dir: {}", prop.getUmsEaiBatchSendDir()); // 디렉토리 검사(쓰기 권한 포함) - Path sendDirPath = Paths.get(prop.getEaiBatchSendDir()); + Path sendDirPath = Paths.get(prop.getUmsEaiBatchSendDir()); if (!sendDirPath.toFile().exists()) { // 생성 시도 boolean created = sendDirPath.toFile().mkdirs(); if (!created) { - log.error("EAI_BATCH_SEND_DIR 디렉토리를 생성할 수 없음: {}", prop.getEaiBatchSendDir()); - throw new IllegalStateException("EAI_BATCH_SEND_DIR 디렉토리를 생성할 수 없음: " + prop.getEaiBatchSendDir()); + log.error("EAI_BATCH_SEND_DIR 디렉토리를 생성할 수 없음: {}", prop.getUmsEaiBatchSendDir()); + throw new IllegalStateException("EAI_BATCH_SEND_DIR 디렉토리를 생성할 수 없음: " + prop.getUmsEaiBatchSendDir()); } } if (!sendDirPath.toFile().isDirectory()) { - log.error("EAI_BATCH_SEND_DIR 가 디렉토리가 아님: {}", prop.getEaiBatchSendDir()); - throw new IllegalStateException("EAI_BATCH_SEND_DIR 가 디렉토리가 아님: " + prop.getEaiBatchSendDir()); + log.error("EAI_BATCH_SEND_DIR 가 디렉토리가 아님: {}", prop.getUmsEaiBatchSendDir()); + throw new IllegalStateException("EAI_BATCH_SEND_DIR 가 디렉토리가 아님: " + prop.getUmsEaiBatchSendDir()); } if (!sendDirPath.toFile().canWrite()) { - log.error("EAI_BATCH_SEND_DIR 에 쓰기 권한이 없음: {}", prop.getEaiBatchSendDir()); - throw new IllegalStateException("EAI_BATCH_SEND_DIR 에 쓰기 권한이 없음: " + prop.getEaiBatchSendDir()); + log.error("EAI_BATCH_SEND_DIR 에 쓰기 권한이 없음: {}", prop.getUmsEaiBatchSendDir()); + throw new IllegalStateException("EAI_BATCH_SEND_DIR 에 쓰기 권한이 없음: " + prop.getUmsEaiBatchSendDir()); } // 백업 디렉토리 - Path backupDirPath = Paths.get(prop.getEaiBatchBackupDir()); + Path backupDirPath = Paths.get(prop.getUmsEaiBatchBackupDir()); if (!backupDirPath.toFile().exists()) { // 생성 시도 boolean created = backupDirPath.toFile().mkdirs(); if (!created) { - log.error("EAI_BATCH_BACKUP_DIR 디렉토리를 생성할 수 없음: {}", prop.getEaiBatchBackupDir()); - throw new IllegalStateException("EAI_BATCH_BACKUP_DIR 디렉토리를 생성할 수 없음: " + prop.getEaiBatchBackupDir()); + log.error("EAI_BATCH_BACKUP_DIR 디렉토리를 생성할 수 없음: {}", prop.getUmsEaiBatchBackupDir()); + throw new IllegalStateException("EAI_BATCH_BACKUP_DIR 디렉토리를 생성할 수 없음: " + prop.getUmsEaiBatchBackupDir()); } } if (!backupDirPath.toFile().isDirectory()) { - log.error("EAI_BATCH_BACKUP_DIR 가 디렉토리가 아님: {}", prop.getEaiBatchBackupDir()); - throw new IllegalStateException("EAI_BATCH_BACKUP_DIR 가 디렉토리가 아님: " + prop.getEaiBatchBackupDir()); + log.error("EAI_BATCH_BACKUP_DIR 가 디렉토리가 아님: {}", prop.getUmsEaiBatchBackupDir()); + throw new IllegalStateException("EAI_BATCH_BACKUP_DIR 가 디렉토리가 아님: " + prop.getUmsEaiBatchBackupDir()); } if (!backupDirPath.toFile().canWrite()) { - log.error("EAI_BATCH_BACKUP_DIR 에 쓰기 권한이 없음: {}", prop.getEaiBatchBackupDir()); - throw new IllegalStateException("EAI_BATCH_BACKUP_DIR 에 쓰기 권한이 없음: " + prop.getEaiBatchBackupDir()); + log.error("EAI_BATCH_BACKUP_DIR 에 쓰기 권한이 없음: {}", prop.getUmsEaiBatchBackupDir()); + throw new IllegalStateException("EAI_BATCH_BACKUP_DIR 에 쓰기 권한이 없음: " + prop.getUmsEaiBatchBackupDir()); } log.info("KjbEmailSendModule - Environment check passed."); diff --git a/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java b/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java index 853fb6a..defe9e8 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java +++ b/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java @@ -6,9 +6,12 @@ import com.eactive.ext.kjb.common.KjbProperty; import com.eactive.ext.kjb.ums.gson.IUmsGsonObject; import com.eactive.ext.kjb.ums.gson.UmsRequestPayload; import com.eactive.ext.kjb.ums.gson.VerifyPhoneUmsTemplate; +import com.eactive.ext.kjb.utils.JsonUtil; import com.eactive.ext.kjb.utils.ValidationUtil; import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.config.RequestConfig; @@ -16,13 +19,17 @@ import org.apache.hc.client5.http.entity.EntityBuilder; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.util.Timeout; +import org.springframework.util.StreamUtils; import java.io.IOException; import java.net.URISyntaxException; +import java.nio.charset.Charset; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.Arrays; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; @@ -104,13 +111,25 @@ public class KjbUmsService { .msgSndChnlCd(UmsRequestPayload.ChannelCode.KM) .build(); - return callUms(SEND_URL, payload, (messageId, ex) -> { + try { + ValidationUtil.validateOrThrow(payload); + } catch (Exception ex) { + log.debug("UMS Request payload validation failed - {}", JsonUtil.toPrettyString(payload)); + throw ex; + } + + return callUms(SEND_URL, payload, (result, ex) -> { if (ex != null) { - log.error("UMS Call error", ex); + log.error("UMS 발송 실패", ex); throw new RuntimeException(ex); } - log.info("UMS Call success. ({})", messageId); + if (result == null || !result.isSuccess()) { + log.error("UMS 발송 실패(return) - {}", JsonUtil.toPrettyString(result)); + throw new RuntimeException("UMS 발송실패 - " + JsonUtil.toPrettyString(result != null ? result.getThrowable().getMessage() : "UMS 발송실패 - 알수 없는 오류")); + } + + log.info("UMS Call success. ({})", result); }).join().getUmsMessageId(); } @@ -126,19 +145,52 @@ public class KjbUmsService { httpPost.setHeader("Authorization", String.format(AUTHORIZATION_FMT, property.getUmsApiKey())); httpPost.setEntity(EntityBuilder.create().setText(json).setContentType(ContentType.APPLICATION_JSON).build()); + if (log.isDebugEnabled()) { + try { + log.debug("Header - {}", httpPost.getHeader("Content-Type")); + log.debug("Header - {}", httpPost.getHeader("Authorization")); + log.debug("Request Body : {}", StreamUtils.copyToString(httpPost.getEntity().getContent(), Charset.defaultCharset())); + } catch (ProtocolException | IOException e) { + throw new RuntimeException(e); + } + } + httpPost.setConfig(requestConfig); AtomicReference result = new AtomicReference<>(); if (isRealMode) { try { httpClient.execute(httpPost, res -> { - String body = EntityUtils.toString(res.getEntity()); - result.set(gson.fromJson(body, UmsCallResult.class)); - result.get().setUmsMessageId(requestPayload.getMessageIdentityNo()); - result.get().setSuccess(true); - return result; + try { + + String body = EntityUtils.toString(res.getEntity()); + if (log.isDebugEnabled()) { + log.debug("Response - {}", body); + } + result.set(gson.fromJson(body, UmsCallResult.class)); + result.get().setUmsMessageId(requestPayload.getMessageIdentityNo()); + result.get().setSuccess(true); + return result; + } finally { + log.debug("Response status - {}", res.getCode()); + + if (res.getCode() != 200) { + Arrays.asList(res.getHeaders()).forEach(o -> { + log.warn("[Header] {}: {}", o.getName(), o.getValue()); + }); + } + } }); - } catch (IOException e) { + } catch (JsonSyntaxException e) { + log.error("UMS Call error(JSON Syntax)", e); + result.set( + UmsCallResult.builder() + .umsMessageId(requestPayload.getMessageIdentityNo()) + .isSuccess(false) + .throwable(e) + .build() + ); + } catch (Exception e) { log.error("UMS Call error", e); result.set( UmsCallResult.builder() diff --git a/src/main/java/com/eactive/ext/kjb/ums/gson/UmsRequestPayload.java b/src/main/java/com/eactive/ext/kjb/ums/gson/UmsRequestPayload.java index 31836d2..9dade0e 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/gson/UmsRequestPayload.java +++ b/src/main/java/com/eactive/ext/kjb/ums/gson/UmsRequestPayload.java @@ -41,6 +41,7 @@ public class UmsRequestPayload { @Pattern(regexp = "^[0-9]{14}$") @NotEmpty @SerializedName("SND_RESR_DTTM") + @Builder.Default private String sendReservationDatetime = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()); // 휴대전화번호 / 44 / 휴대폰번호(평문, 암호화 둘다가능) @@ -57,27 +58,32 @@ public class UmsRequestPayload { // SND_BRCD string 발송부점코드 4 O 0333 @NotEmpty @SerializedName("SND_BRCD") + @Builder.Default private String sendBranchCode = "0990"; // 발송직원번호 / 7 / ECEB057(텔러번호?) // SND_EMPNO string 발송직원번호 7 O ? 넣긴해야함 @NotEmpty @SerializedName("SND_EMPNO") + @Builder.Default private String sendEmployeeNo = "ECEB057"; // SRVC_ID string 서비스ID 11 요청단 측 서비스ID @NotEmpty @SerializedName("SRVC_ID") + @Builder.Default private String serviceId = "eAPIM"; // MSG_SND_CHNL_CD string 메시지발송채널코드 2 @SerializedName("MSG_SND_CHNL_CD") - @NotEmpty + @NotNull + @Builder.Default private ChannelCode msgSndChnlCd = ChannelCode.KM; // VALDTN_RSLT_CD string 검증결과코드 2 00으로 고정 @SerializedName("VALDTN_RSLT_CD") @NotEmpty + @Builder.Default private String validationResultCode = "00"; public enum ChannelCode { diff --git a/src/main/java/com/eactive/ext/kjb/utils/JsonUtil.java b/src/main/java/com/eactive/ext/kjb/utils/JsonUtil.java new file mode 100644 index 0000000..bf2033e --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/utils/JsonUtil.java @@ -0,0 +1,10 @@ +package com.eactive.ext.kjb.utils; + +import com.google.gson.GsonBuilder; + +public class JsonUtil { + public static String toPrettyString(Object obj) { + if (obj == null) { return "null"; } + return new GsonBuilder().setPrettyPrinting().create().toJson(obj); + } +} diff --git a/src/main/java/com/eactive/ext/kjb/utils/OsUtil.java b/src/main/java/com/eactive/ext/kjb/utils/OsUtil.java new file mode 100644 index 0000000..de9679a --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/utils/OsUtil.java @@ -0,0 +1,24 @@ +package com.eactive.ext.kjb.utils; + +import java.net.InetAddress; + +public class OsUtil { + public static boolean isWindows() { + return System.getProperty("os.name").toLowerCase().contains("win"); + } + + public static boolean isLinux() { + String os = System.getProperty("os.name").toLowerCase(); + return os.contains("nux") || os.contains("nix") || os.contains("aix") ; + } + + public static String getHostName() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (Exception e) { + String name = System.getProperty("COMPUTERNAME"); + if (name == null) name = System.getenv("HOSTNAME"); + return name != null ? name : "unknown"; + } + } +} diff --git a/src/test/java/com/eactive/ext/kjb/ums/test/KjbEmailSendModuleTests.java b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java similarity index 53% rename from src/test/java/com/eactive/ext/kjb/ums/test/KjbEmailSendModuleTests.java rename to src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java index eb087f7..5c838a7 100644 --- a/src/test/java/com/eactive/ext/kjb/ums/test/KjbEmailSendModuleTests.java +++ b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java @@ -5,29 +5,53 @@ import com.eactive.apim.portal.template.entity.MessageRequest; import com.eactive.ext.kjb.common.KjbProperty; import com.eactive.ext.kjb.ums.EmailSendModule; import com.eactive.ext.kjb.ums.KjbUmsService; +import com.eactive.ext.kjb.utils.OsUtil; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.util.UUID; import java.util.concurrent.CompletableFuture; @Slf4j -public class KjbEmailSendModuleTests { +public class UmsTest { + private static KjbProperty prop = null; -// @Test + @BeforeAll + public static void init() { + prop = new KjbProperty(); + prop.setUmsEaiBatchInterfaceId("UAGFF00001UIE"); + + if (OsUtil.isWindows()) { + prop.setUmsEaiBatchSendDir("c:\\kjb-logs\\sendmail\\snd"); + prop.setUmsEaiBatchBackupDir("c:\\kjb-logs\\sendmail\\bak"); + } + + if (OsUtil.isLinux()) { + prop.setUmsEaiBatchSendDir("/Data/eapim/portal/sendmail/snd"); + prop.setUmsEaiBatchBackupDir("/Data/eapim/portal/sendmail/bak"); + } + } + + @Test public void testSms() { - KjbProperty prop = new KjbProperty(); - prop.setEaiBatchSendDir("c:\\kjb-logs\\sendmail\\snd"); - prop.setEaiBatchBackupDir("c:\\kjb-logs\\sendmail\\bak"); - prop.setEaiBatchInterfaceId("UAGFF00001UIE"); + Assumptions.assumeTrue( + "true".equalsIgnoreCase(System.getenv("KJB_EAPIM_TEST_ENABLED")), + "OS 환경변수 내 KJB_EAPIM_TEST_ENABLED=TRUE 설정되어 있어야 동작함"); + + prop.setUmsUrl("http://172.31.35.144:9021/ums/openapi/send"); + prop.setUmsApiKey("7cca6d58-e4f7-474d-9edd-525806bc99ff"); MessageRequest messageRequest = new MessageRequest(); messageRequest.setId(UUID.randomUUID().toString()); messageRequest.setMessageCode(MessageCode.USER_VERIFICATION_MOBILEPHONE); + messageRequest.setPhone("01099750188"); messageRequest.setEmail("dearmai@dearmai.net"); messageRequest.setUsername("김광은"); messageRequest.setSubject("제목 - test"); - messageRequest.setMessage("내용 - test"); + messageRequest.setMessage("junit test"); KjbUmsService umsService = new KjbUmsService(prop); umsService.send(messageRequest); @@ -35,14 +59,17 @@ public class KjbEmailSendModuleTests { log.info("done"); } -// @Test + @Test public void testEmail() { - KjbProperty prop = new KjbProperty(); + Assumptions.assumeTrue( + "true".equalsIgnoreCase(System.getenv("KJB_EAPIM_TEST_ENABLED")), + "OS 환경변수 내 KJB_EAPIM_TEST_ENABLED=TRUE 설정되어 있어야 동작함"); + // prop.setEaiBatchUrl("http://172.31.32.111:10230/BATAppWeb/EaiBatCall"); prop.setEaiBatchUrl(""); - prop.setEaiBatchSendDir("c:\\kjb-logs\\sendmail\\snd"); - prop.setEaiBatchBackupDir("c:\\kjb-logs\\sendmail\\bak"); - prop.setEaiBatchInterfaceId("UAGFF00001UIE"); + prop.setUmsEaiBatchSendDir("c:\\kjb-logs\\sendmail\\snd"); + prop.setUmsEaiBatchBackupDir("c:\\kjb-logs\\sendmail\\bak"); + prop.setUmsEaiBatchInterfaceId("UAGFF00001UIE"); EmailSendModule service = EmailSendModule.getInstance(prop, null); MessageRequest messageRequest = new MessageRequest(); diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index c7a3a0d..b4222d8 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -1,9 +1,10 @@ - + - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + %d{yyyy-MM-dd HH:mm:ss.SSS} %magenta([%thread]) %highlight([%-3level]) %logger - %msg %n @@ -13,6 +14,7 @@ + From 1928579717749d1f4d4ad334bfff8b898dbb419a Mon Sep 17 00:00:00 2001 From: Rinjae Date: Wed, 22 Oct 2025 21:10:18 +0900 Subject: [PATCH 03/16] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=8B=A4?= =?UTF-8?q?=ED=8C=A8=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/eactive/ext/kjb/common/KjbProperty.java | 4 ++-- src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java | 9 ++++----- .../kjb/ums/{UmzBizWorkCode.java => UmsBizWorkCode.java} | 4 ++-- src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) rename src/main/java/com/eactive/ext/kjb/ums/{UmzBizWorkCode.java => UmsBizWorkCode.java} (80%) diff --git a/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java b/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java index 850d074..167b3c2 100644 --- a/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java +++ b/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java @@ -32,8 +32,8 @@ public class KjbProperty { private String umsEaiBatchInterfaceId; - @Pattern(regexp = "^(https?://[^\\s/$.?#][^\\s]*)|\\s*$", message = "kjb.ums.url 는 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api") - private String umsUrl; + @Pattern(regexp = "^(https?://[^\\s/$.?#][^\\s]*)|\\s*$", message = "kjb.ums.host_url 는 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api") + private String umsHostUrl; @Pattern(regexp = "^[a-zA-Z0-9_-]{36}$", message = "kjb.ums.api_key 입력 형식이 맞지 않습니다.") private String umsApiKey; diff --git a/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java b/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java index defe9e8..0f321b1 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java +++ b/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java @@ -11,7 +11,6 @@ import com.eactive.ext.kjb.utils.ValidationUtil; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.config.RequestConfig; @@ -52,7 +51,7 @@ public class KjbUmsService { throw new IllegalArgumentException("property is null"); } - isRealMode = property.getUmsUrl() != null && StringUtils.isNotEmpty(property.getUmsUrl().trim()); + isRealMode = property.getUmsHostUrl() != null && StringUtils.isNotEmpty(property.getUmsHostUrl().trim()); if (isRealMode) { ValidationUtil.validateOrThrow(property); @@ -84,7 +83,7 @@ public class KjbUmsService { content = VerifyPhoneUmsTemplate.builder().authNumber(messageRequest.getMessage()).build(); String umsMessageId = this.sendKakaoAlimTalk(messageRequest.getId(), - UmzBizWorkCode.VERIFY_PHONE, + UmsBizWorkCode.VERIFY_PHONE, messageRequest.getPhone(), content ); @@ -94,7 +93,7 @@ public class KjbUmsService { return true; } - public String sendKakaoAlimTalk(String guid, UmzBizWorkCode bizWorkCode, String cpno, IUmsGsonObject content) { + public String sendKakaoAlimTalk(String guid, UmsBizWorkCode bizWorkCode, String cpno, IUmsGsonObject content) { if (content == null) { throw new IllegalArgumentException("content is null"); } @@ -140,7 +139,7 @@ public class KjbUmsService { Gson gson = new Gson(); String json = gson.toJson(requestPayload); - HttpPost httpPost = new HttpPost(property.getUmsUrl() + url); + HttpPost httpPost = new HttpPost(property.getUmsHostUrl() + url); httpPost.setHeader("Content-Type", CONTENT_TYPE); httpPost.setHeader("Authorization", String.format(AUTHORIZATION_FMT, property.getUmsApiKey())); httpPost.setEntity(EntityBuilder.create().setText(json).setContentType(ContentType.APPLICATION_JSON).build()); diff --git a/src/main/java/com/eactive/ext/kjb/ums/UmzBizWorkCode.java b/src/main/java/com/eactive/ext/kjb/ums/UmsBizWorkCode.java similarity index 80% rename from src/main/java/com/eactive/ext/kjb/ums/UmzBizWorkCode.java rename to src/main/java/com/eactive/ext/kjb/ums/UmsBizWorkCode.java index 10c7f3b..b56d628 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/UmzBizWorkCode.java +++ b/src/main/java/com/eactive/ext/kjb/ums/UmsBizWorkCode.java @@ -2,7 +2,7 @@ package com.eactive.ext.kjb.ums; import lombok.Getter; -public enum UmzBizWorkCode { +public enum UmsBizWorkCode { MONITORING("B_ELFN_09510", "OBP 모니터링"), VERIFY_PHONE("S55_ICT_0006", "API Portal 인증번호"), ; @@ -12,7 +12,7 @@ public enum UmzBizWorkCode { @Getter private String displayName; - UmzBizWorkCode(String code, String displayName) { + UmsBizWorkCode(String code, String displayName) { this.code = code; this.displayName = displayName; } diff --git a/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java index 5c838a7..527b377 100644 --- a/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java +++ b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java @@ -41,7 +41,7 @@ public class UmsTest { "true".equalsIgnoreCase(System.getenv("KJB_EAPIM_TEST_ENABLED")), "OS 환경변수 내 KJB_EAPIM_TEST_ENABLED=TRUE 설정되어 있어야 동작함"); - prop.setUmsUrl("http://172.31.35.144:9021/ums/openapi/send"); + prop.setUmsHostUrl("http://172.31.35.144:9021"); prop.setUmsApiKey("7cca6d58-e4f7-474d-9edd-525806bc99ff"); MessageRequest messageRequest = new MessageRequest(); From fd1110e98661313af26379eade277e353688c769 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Wed, 5 Nov 2025 13:16:21 +0900 Subject: [PATCH 04/16] =?UTF-8?q?ums=20=EC=97=B0=EB=8F=99=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/eactive/ext/kjb/ums/KjbUmsService.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java b/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java index 0f321b1..659e73b 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java +++ b/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java @@ -35,6 +35,8 @@ import java.util.function.BiConsumer; @Slf4j public class KjbUmsService { + public final static int[] ALLOW_CHANNELS = { MessageCode.Channels.KAKAO_ALIMTALK.getValue() }; + private final static String SEND_URL = "/ums/openapi/send"; private final static String CONTENT_TYPE = "application/json; charset=utf-8"; private final static String AUTHORIZATION_FMT = "Bearer %s"; @@ -69,6 +71,17 @@ public class KjbUmsService { this.property = property; } + + public static boolean isAllowChannel(MessageRequest message) { + for (int channel : ALLOW_CHANNELS) { + if ((message.getMessageCode().getChannels() & channel) == channel) { + return true; + } + } + return false; + } + + public boolean send(MessageRequest messageRequest) { if (messageRequest == null) { throw new IllegalArgumentException("messageRequest is null"); From 4283c8b3fca5580b625972e5c03da9d0c8e678f6 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Wed, 5 Nov 2025 19:28:19 +0900 Subject: [PATCH 05/16] =?UTF-8?q?ums=20guid=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java b/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java index 659e73b..f54db35 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java +++ b/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java @@ -112,7 +112,7 @@ public class KjbUmsService { } if (StringUtils.isEmpty(guid)) { - guid = String.format("OBP-%s", DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(LocalDateTime.now())); + guid = String.format("APM-%s", DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(LocalDateTime.now())); } UmsRequestPayload payload = UmsRequestPayload.builder() From 5b354cffb16e64c7aa7e5ab8cc5ee4058cd585d0 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Mon, 10 Nov 2025 11:00:37 +0900 Subject: [PATCH 06/16] =?UTF-8?q?=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EB=B0=9C?= =?UTF-8?q?=EC=86=A1=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../ext/kjb/common/KjbEaiBatchException.java | 11 +++ .../eactive/ext/kjb/common/KjbProperty.java | 25 +++++- .../ext/kjb/eai/EaiBatchInterface.java | 64 ++++++++++++++++ .../eactive/ext/kjb/eai/EaiBatchMessage.java | 51 ++++++++----- .../ext/kjb/eai/KjbEaiBatchModule.java | 46 +++++++++-- .../com/eactive/ext/kjb/ums/EmailJob.java | 76 ++++++++++++++----- ...endModule.java => KjbEmailSendModule.java} | 65 +++++++++------- .../com/eactive/ext/kjb/ums/test/UmsTest.java | 39 +++++++--- 9 files changed, 284 insertions(+), 95 deletions(-) create mode 100644 src/main/java/com/eactive/ext/kjb/common/KjbEaiBatchException.java create mode 100644 src/main/java/com/eactive/ext/kjb/eai/EaiBatchInterface.java rename src/main/java/com/eactive/ext/kjb/ums/{EmailSendModule.java => KjbEmailSendModule.java} (66%) diff --git a/build.gradle b/build.gradle index a776fa2..d9e6691 100644 --- a/build.gradle +++ b/build.gradle @@ -82,7 +82,7 @@ jar { test { useJUnitPlatform() - systemProperty "kjb_safedb.mode", project.findProperty("kjb_safedb.mode") ?: "" + systemProperty "kjb_safedb.mode", System.getProperty("kjb_safedb.mode") if (project.hasProperty("safedb")) { systemProperty "kjb_safedb.mode", "real" diff --git a/src/main/java/com/eactive/ext/kjb/common/KjbEaiBatchException.java b/src/main/java/com/eactive/ext/kjb/common/KjbEaiBatchException.java new file mode 100644 index 0000000..a28d1c8 --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/common/KjbEaiBatchException.java @@ -0,0 +1,11 @@ +package com.eactive.ext.kjb.common; + +public class KjbEaiBatchException extends Exception { + public KjbEaiBatchException(String message) { + super(message); + } + + public KjbEaiBatchException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java b/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java index 167b3c2..498bc08 100644 --- a/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java +++ b/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java @@ -16,21 +16,35 @@ public class KjbProperty { private int eaiBatchConnectionTimeout = 5; @Min(1) @Max(30) - private int eaiBatchResponseTimeout = 10; + private int eaiBatchResponseTimeout = 5; + + @Min(1) @Max(30) + private int eaiBatchTimeout = 10; + + @NotEmpty + private String eaiBatchCharset = "euc-kr"; @NotEmpty(message = "kjb.ums.eai_batch.send_dir 는 필수값입니다.") @Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", - message = "kjb.ums.eai_batch.send_dir 은 절대 경로여야 합니다. 예: /Data/eapim/portal/sendmail/snd 또는 C:\\Data\\eapim\\portal\\sendmail\\snd") + message = "kjb.ums.eai_batch.send_dir 은 절대 경로여야 합니다. 예: /Data/eapim/portal/sendmail/snd 또는 c:\\kjb-logs\\sendmail\\snd") private String umsEaiBatchSendDir; @NotEmpty(message = "kjb.ums.eai_batch.backup_dir 는 필수값입니다.") @Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", - message = "kjb.ums.eai_batch.backup_dir 은 절대 경로여야 합니다. 예: /Data/eapim/portal/sendmail/bak 또는 C:\\Data\\eapim\\portal\\sendmail\\bak") + message = "kjb.ums.eai_batch.backup_dir 은 절대 경로여야 합니다. 예: /Data/eapim/portal/sendmail/bak 또는 c:\\kjb-logs\\sendmail\\bak") private String umsEaiBatchBackupDir; + @NotEmpty(message = "kjb.ums.eai_batch.error_dir 는 필수값입니다.") + @Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", + message = "kjb.ums.eai_batch.error_dir 은 절대 경로여야 합니다. 예: /Data/eapim/portal/sendmail/errpr 또는 c:\\kjb-logs\\sendmail\\error") + private String umsEaiBatchErrorDir; + @Pattern(regexp = "^[a-zA-Z0-9]{13}$", message = "kjb.ums.eai_batch.interface_id 는 12자리 영문대소문자 및 숫자 조합이어야 합니다.") private String umsEaiBatchInterfaceId; + @Min(1) @Max(365) + private int umsEaiBatchRetentionDate = 60; + @Pattern(regexp = "^(https?://[^\\s/$.?#][^\\s]*)|\\s*$", message = "kjb.ums.host_url 는 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api") private String umsHostUrl; @@ -42,5 +56,8 @@ public class KjbProperty { private int umsConnectionTimeout = 5; @Min(1) @Max(30) - private int umsResponseTimeout = 10; + private int umsResponseTimeout = 5; + + @Min(1) @Max(30) + private int umsTimeout = 10; } diff --git a/src/main/java/com/eactive/ext/kjb/eai/EaiBatchInterface.java b/src/main/java/com/eactive/ext/kjb/eai/EaiBatchInterface.java new file mode 100644 index 0000000..6d9832e --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/eai/EaiBatchInterface.java @@ -0,0 +1,64 @@ +package com.eactive.ext.kjb.eai; + +import lombok.Builder; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.validator.constraints.NotEmpty; + +import javax.validation.constraints.Pattern; +import java.io.File; +import java.nio.file.Paths; + +@Data +@Builder +@Slf4j +public class EaiBatchInterface { + @Pattern(regexp = "^[a-zA-Z0-9]{13}$", message = "id는 12자리 영문대소문자 및 숫자 조합이어야 합니다.") + private String id; + + @NotEmpty + @Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", message = "sendDir은 절대경로로 지정되어야 합니다.") + private String sendDir; + + @NotEmpty + @Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", message = "backupDir은 절대경로로 지정되어야 합니다.") + private String backupDir; + + @NotEmpty + @Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", message = "errorDir은 절대경로로 지정되어야 합니다.") + private String errorDir; + + + public void moveError(String filename) { + move(filename, errorDir); + } + + public void moveBackup(String filename) { + move(filename, backupDir); + } + + private void move(String filename, String targetDir) { + File file = Paths.get(sendDir, filename).toFile(); + File targetDirFile = new File(targetDir); + File targetFile = new File(targetDir, file.getName()); + + log.debug("송신파일 이동 시작 - {} -> {}", filename, targetDir); + + if (!file.exists()) { + log.warn("송신 파일이 존재 하지 않습니다. ({})", file.getAbsolutePath()); + return; + } + + if (!targetDirFile.exists()) { + if (!targetDirFile.mkdirs()) { + log.warn("대상 디렉토리 생성 실패 ({})", file.getAbsolutePath()); + return; + } + } + + if (!file.renameTo(targetFile)) { + log.warn("송신 파일 이동 실패 - {}", targetFile.getAbsolutePath()); + return; + } + } +} diff --git a/src/main/java/com/eactive/ext/kjb/eai/EaiBatchMessage.java b/src/main/java/com/eactive/ext/kjb/eai/EaiBatchMessage.java index 7ce84d2..db6685a 100644 --- a/src/main/java/com/eactive/ext/kjb/eai/EaiBatchMessage.java +++ b/src/main/java/com/eactive/ext/kjb/eai/EaiBatchMessage.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; @Slf4j @@ -17,6 +18,9 @@ public class EaiBatchMessage { @Getter private String count = ""; + /** + * 요청시 사용 안함, 응답시 1: 성공, 2: 실패 + */ @Getter private String resultCode = ""; @@ -28,14 +32,17 @@ public class EaiBatchMessage { private boolean isReadOnly = false; + private String charset; + private EaiBatchMessage() {} - public static EaiBatchMessage createRequest(String interfaceId, String filename) { + public static EaiBatchMessage createRequest(String interfaceId, String filename, String charset) { EaiBatchMessage message = new EaiBatchMessage(); message.interfaceId = interfaceId; message.filename = filename; + message.charset = charset; return message; } @@ -46,16 +53,16 @@ public class EaiBatchMessage { 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(); + message.interfaceId = rawMessage.substring(0, 19).trim(); + message.baseDate = rawMessage.substring(19, 27).trim(); + message.count = rawMessage.substring(27, 37).trim(); + message.resultCode = rawMessage.substring(37, 38).trim(); // filename 은 @@가 발견되면 이전 까지 trim / 발견되지 않으면 나머지 전체 그리고 trim - int endIdx = rawMessage.indexOf("@@", 39); + int endIdx = rawMessage.indexOf("@@", 38); if (endIdx == -1) { - message.filename = rawMessage.substring(39).trim(); + message.filename = rawMessage.substring(38).trim(); } else { - message.filename = rawMessage.substring(39, endIdx).trim(); + message.filename = rawMessage.substring(38, endIdx).trim(); } } @@ -74,16 +81,20 @@ public class EaiBatchMessage { 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)); + try { + // 인터페이스ID (19) + buffer.put(fixedString(interfaceId, 19)); + // 기준일자 (8) + buffer.put(fixedString(baseDate, 8)); + // 건수 (10) + buffer.put(fixedString(count, 10)); + // 결과코드 (1) + buffer.put(fixedString(resultCode, 1)); + // 파일명 (100) + buffer.put(fixedString(filename, 100)); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } // 종료 구분자 (2) buffer.put("@@".getBytes()); @@ -93,11 +104,11 @@ public class EaiBatchMessage { } } - private byte[] fixedString(String str, int length) { + private byte[] fixedString(String str, int length) throws UnsupportedEncodingException { if (str == null) { str = ""; } - byte[] strBytes = str.getBytes(); + byte[] strBytes = str.getBytes(this.charset); byte[] result = new byte[length]; int copyLength = Math.min(strBytes.length, length); diff --git a/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java b/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java index 39133f1..d1f7a2e 100644 --- a/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java +++ b/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java @@ -1,5 +1,6 @@ package com.eactive.ext.kjb.eai; +import com.eactive.ext.kjb.common.KjbEaiBatchException; import com.eactive.ext.kjb.common.KjbProperty; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -11,10 +12,13 @@ import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.util.Timeout; +import javax.validation.Valid; import javax.validation.constraints.NotNull; -import java.io.IOException; +import java.io.File; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.util.concurrent.*; @Slf4j public class KjbEaiBatchModule { @@ -42,10 +46,16 @@ public class KjbEaiBatchModule { } } - public EaiBatchMessage call(String interfaceId, String filename) throws IOException { - EaiBatchMessage reqMessage = EaiBatchMessage.createRequest(interfaceId, filename); + public EaiBatchMessage call(@Valid EaiBatchInterface intf, String filename) throws KjbEaiBatchException { + EaiBatchMessage reqMessage = EaiBatchMessage.createRequest(intf.getId(), filename, property.getEaiBatchCharset()); log.debug("EAI 배치 호출: {}", reqMessage); + // 송신 파일 체크 + File sendFile = Paths.get(intf.getSendDir(), filename).toFile(); + if (!sendFile.exists() || !sendFile.isFile()) { + throw new KjbEaiBatchException("Not found send file - " + sendFile.toPath()); + } + if (this.httpClient != null) { URI uri = null; try { @@ -60,17 +70,37 @@ public class KjbEaiBatchModule { final HttpGet httpGet = new HttpGet(uri); httpGet.setConfig(requestConfig); - String message = httpClient.execute(httpGet, response -> EntityUtils.toString(response.getEntity(), "EUC-KR")); + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future future = executor.submit(() -> + httpClient.execute(httpGet, response -> EntityUtils.toString(response.getEntity(), "EUC-KR"))); - EaiBatchMessage resMessage = EaiBatchMessage.fromResponse(message); - log.debug("EAI 배치 응답: {}", resMessage); - return resMessage; + EaiBatchMessage resMessage = null; + try { + String message = future.get(property.getEaiBatchTimeout(), TimeUnit.SECONDS); + resMessage = EaiBatchMessage.fromResponse(message); + log.debug("EAI 배치 응답: {}", resMessage); + + return resMessage; + } catch (InterruptedException | ExecutionException | TimeoutException e) { + log.error("EAI Batch call exception", e); + throw new KjbEaiBatchException("EAI Batch call failed", e); + } finally { + executor.shutdown(); + + // EAI 송신 실패일 경우 에러 경로로 이동 + if (resMessage != null && "1".equals(resMessage.getResultCode())) { + intf.moveBackup(filename); + } else { + intf.moveError(filename); + } + } } else { log.info("EAI_BATCH_URL 이 설정되지 않아 실제 호출하지 않음. 요청 메시지: {}", reqMessage); // 정상응답 리턴 - EaiBatchMessage resMessage = EaiBatchMessage.fromResponse(String.format("%-20s%-8s%-10s%-1s%s@@", interfaceId, "", "0000000000", "1", "S:" + filename)); + EaiBatchMessage resMessage = EaiBatchMessage.fromResponse(String.format("%-19s%-8s%-10s%-1s%s@@", intf.getId(), "", "0000000000", "1", "S:" + filename)); log.debug("EAI 배치 응답(모의): {}", resMessage); + intf.moveBackup(filename); 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 9f3cf8c..c7de34a 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java +++ b/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java @@ -3,9 +3,13 @@ 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.KjbEaiBatchException; import com.eactive.ext.kjb.common.KjbProperty; import com.eactive.ext.kjb.eai.EaiBatchMessage; +import com.eactive.ext.kjb.eai.EaiBatchInterface; import com.eactive.ext.kjb.eai.KjbEaiBatchModule; +import com.eactive.ext.kjb.safedb.KjbSafedbWrapper; +import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import lombok.extern.slf4j.Slf4j; @@ -28,6 +32,10 @@ public class EmailJob { private final KjbProperty prop; private final MessageRequest messageRequest; + private static final int READY = 1; // Temp 파일 생성 단계 + private static final int BEFORE_SEND = 2; + private static final int SEND = 3; + private final Path tempFilePath; private final Path eaiFilePath; private final Path backupFilePath; @@ -35,13 +43,15 @@ public class EmailJob { private final MessageRequestRepository messageRequestRepository; private final LocalDateTime now = LocalDateTime.now(); + private int eaiPhase = 0; + private EmailJob(KjbProperty prop, MessageRequest messageRequest, MessageRequestRepository messageRequestRepository) { this.messageRequest = messageRequest; this.prop = prop; - String filename = messageRequest.getServiceId() + "_" + DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(now); + String filename = messageRequest.getServiceId() + "_" + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now); this.tempFilePath = Paths.get(prop.getUmsEaiBatchSendDir(), filename + ".tmp"); this.eaiFilePath = Paths.get(prop.getUmsEaiBatchSendDir(), filename); this.backupFilePath = Paths.get(prop.getUmsEaiBatchBackupDir(), filename); @@ -112,7 +122,7 @@ public class EmailJob { @Transactional - public void sendEmail() { + public void sendEmail() throws KjbEaiBatchException { if (messageRequestRepository != null) { PortalUser user = messageRequest.getUser(); if (user != null) { @@ -130,9 +140,11 @@ public class EmailJob { log.debug("발송 성공 처리"); doSuccess(); + } catch (KjbEaiBatchException e) { + doFail(e); } catch (Exception e) { - log.error("이메일 발송 실패", e); - doFail(); + log.error(e.getMessage(), e); + doFail(e); } finally { doDeleteEaiFile(); } @@ -142,13 +154,24 @@ public class EmailJob { // TODO: EAI 발송용 파일 삭제 } - private void doFail() { + private void doFail(Exception e) throws KjbEaiBatchException { // TODO: EAI 발송 실패 처리 + if (e instanceof KjbEaiBatchException) { + throw (KjbEaiBatchException) e; + } else { + throw new KjbEaiBatchException("이메일 발송 실패", e); + } } - private void eaiBatchCall() throws IOException { + private void eaiBatchCall() throws KjbEaiBatchException { KjbEaiBatchModule eaiBatchModule = new KjbEaiBatchModule(prop); - EaiBatchMessage result = eaiBatchModule.call(prop.getUmsEaiBatchInterfaceId(), eaiFilePath.getFileName().toString()); + EaiBatchInterface eaiBatchInterface = EaiBatchInterface.builder() + .id(prop.getUmsEaiBatchInterfaceId()) + .sendDir(prop.getUmsEaiBatchSendDir()) + .backupDir(prop.getUmsEaiBatchBackupDir()) + .errorDir(prop.getUmsEaiBatchErrorDir()) + .build(); + EaiBatchMessage result = eaiBatchModule.call(eaiBatchInterface, eaiFilePath.getFileName().toString()); if (!"1".equals(result.getResultCode())) { log.error("EAI Batch call failed: {}", result); @@ -159,19 +182,11 @@ public class EmailJob { } private void doSuccess() { - // 백업 디렉토리로 복사 - try { - Files.copy(eaiFilePath, backupFilePath); - log.debug("Copied EAI file to backup: {} -> {}", eaiFilePath.toString(), backupFilePath.toString()); - } catch (IOException e) { - log.error("Cannot copy EAI file to backup: {} -> {}", eaiFilePath.toString(), backupFilePath.toString(), e); - } + // 딱히 뭐 할 거 없음. } public void doCreateNewFile() { - final String filename = "EMAIL_" + messageRequest.getId() + ".json"; - if (eaiFilePath.toFile().exists()) { log.error("Mail File exists for ID: {}", messageRequest.getId()); throw new IllegalStateException("Mail File exists for ID: " + messageRequest.getId()); @@ -210,29 +225,48 @@ public class EmailJob { } catch (IOException e) { log.error("Cannot write JSON to file: {}", eaiFilePath.toString(), e); throw new RuntimeException(e); + } finally { + // 임시 파일이 남아 있다면 오류 상황이므로 에러 경로로 이동 + if (Files.exists(tempFilePath)) { + try { + Files.move(tempFilePath, Paths.get(prop.getUmsEaiBatchErrorDir(), tempFilePath.getFileName().toString())); + } catch (IOException e) { + log.warn("임시 파일 저장시 실패 하여 에러 경로로 옮기는 도중 에러 발생", e); + } + } } } public String generateJSON(boolean isPretty) { validateMessageRequest(messageRequest, true, true); + KjbSafedbWrapper safedb = KjbSafedbWrapper.getInstance(); JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("MAKE_DATE", DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDateTime.now())); jsonObject.addProperty("MAKE_SEQNO", "0001"); jsonObject.addProperty("CAMP_ID", messageRequest.getServiceId()); // 전자금융 고객이메일 발송 업무ID (SERVICE_ID) jsonObject.addProperty("ID", messageRequest.getUmsUid()); // 고객 ID - jsonObject.addProperty("EMAIL", messageRequest.getEmail()); + jsonObject.addProperty("EMAIL", safedb.encryptNotRnnoString(messageRequest.getEmail())); jsonObject.addProperty("NAME", messageRequest.getUsername()); jsonObject.addProperty("ENCKEY", ""); // 보안메일용으로 기입 불필요 + Gson gson = new Gson(); + log.debug("Template string to json: {}", messageRequest.getMessage()); + JsonObject contentJSon = gson.fromJson(messageRequest.getMessage(), JsonObject.class); + contentJSon.entrySet().forEach(entry -> { + jsonObject.add(entry.getKey(), entry.getValue()); + }); - jsonObject.addProperty("SUBJECT", messageRequest.getSubject()); - jsonObject.addProperty("MESSAGE", messageRequest.getMessage()); +// jsonObject.addProperty("SUBJECT", messageRequest.getSubject()); +// jsonObject.addProperty("MESSAGE", messageRequest.getMessage()); + String jsonString; if (isPretty) { - return new GsonBuilder().setPrettyPrinting().create().toJson(jsonObject); + jsonString = new GsonBuilder().setPrettyPrinting().create().toJson(jsonObject); } else { - return jsonObject.toString(); + jsonString = jsonObject.toString(); } + + return jsonString; } } diff --git a/src/main/java/com/eactive/ext/kjb/ums/EmailSendModule.java b/src/main/java/com/eactive/ext/kjb/ums/KjbEmailSendModule.java similarity index 66% rename from src/main/java/com/eactive/ext/kjb/ums/EmailSendModule.java rename to src/main/java/com/eactive/ext/kjb/ums/KjbEmailSendModule.java index 4c40457..159f554 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/EmailSendModule.java +++ b/src/main/java/com/eactive/ext/kjb/ums/KjbEmailSendModule.java @@ -3,6 +3,7 @@ package com.eactive.ext.kjb.ums; import com.eactive.apim.portal.template.entity.MessageCode; import com.eactive.apim.portal.template.entity.MessageRequest; import com.eactive.apim.portal.template.repository.MessageRequestRepository; +import com.eactive.ext.kjb.common.KjbEaiBatchException; import com.eactive.ext.kjb.common.KjbProperty; import com.eactive.ext.kjb.utils.ValidationUtil; import lombok.extern.slf4j.Slf4j; @@ -14,25 +15,36 @@ import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; @Slf4j -public class EmailSendModule { - private static EmailSendModule instance = null; +public class KjbEmailSendModule { + public final static int[] ALLOW_CHANNELS = { MessageCode.Channels.EMAIL.getValue() }; + + private static KjbEmailSendModule instance = null; private static KjbProperty prop; private final MessageRequestRepository repo; - public static synchronized EmailSendModule getInstance(KjbProperty property, - MessageRequestRepository messageRequestRepository) { + public static synchronized KjbEmailSendModule getInstance(KjbProperty property, + MessageRequestRepository messageRequestRepository) { ValidationUtil.validateOrThrow(property); if (instance == null) { - instance = new EmailSendModule(property, messageRequestRepository); + instance = new KjbEmailSendModule(property, messageRequestRepository); } return instance; } + public static boolean isAllowChannel(MessageRequest message) { + for (int channel : ALLOW_CHANNELS) { + if ((message.getMessageCode().getChannels() & channel) == channel) { + return true; + } + } + return false; + } + public static void environmentCheck() { if (StringUtils.isEmpty(prop.getEaiBatchUrl())) { @@ -91,8 +103,8 @@ public class EmailSendModule { - private EmailSendModule(KjbProperty property, - MessageRequestRepository repo) { + private KjbEmailSendModule(KjbProperty property, + MessageRequestRepository repo) { this.repo = repo; if (property != null && prop == null) { @@ -102,33 +114,28 @@ public class EmailSendModule { environmentCheck(); } - public CompletableFuture sendEmail(MessageRequest messageRequest, BiConsumer callback) { - return CompletableFuture - .supplyAsync(() -> { - String serviceId = "UNKNOWN"; + public void sendEmail(MessageRequest messageRequest) throws KjbEaiBatchException { + String serviceId = "UNKNOWN"; - serviceId = messageRequest.getMessageCode().getServiceId(); - messageRequest.setServiceId(serviceId); + serviceId = messageRequest.getMessageCode().getServiceId(); + messageRequest.setServiceId(serviceId); - if (StringUtils.isEmpty(serviceId)) { - throw new IllegalArgumentException("MessageRequest MessageCode serviceId is required"); - } + if (StringUtils.isEmpty(serviceId)) { + throw new IllegalArgumentException("MessageRequest MessageCode serviceId is required"); + } - if (MessageCode.Channels.EMAIL.getValue() != (messageRequest.getMessageCode().getChannels() & MessageCode.Channels.EMAIL.getValue())) { - log.error("지원하지 않는 발송 채널 - {}", messageRequest.getMessageCode()); - throw new IllegalArgumentException("지원하지 않는 발송 채널 - " + messageRequest.getMessageCode()); - } + if (MessageCode.Channels.EMAIL.getValue() != (messageRequest.getMessageCode().getChannels() & MessageCode.Channels.EMAIL.getValue())) { + log.error("지원하지 않는 발송 채널 - {}", messageRequest.getMessageCode()); + throw new IllegalArgumentException("지원하지 않는 발송 채널 - " + messageRequest.getMessageCode()); + } - if (repo != null) { - repo.save(messageRequest); - } + if (repo != null) { + repo.save(messageRequest); + } - EmailJob job = EmailJob.create(prop, messageRequest, repo); - job.sendEmail(); + EmailJob job = EmailJob.create(prop, messageRequest, repo); + job.sendEmail(); - log.debug("JSON : {}", job.generateJSON(true)); - return true; - }) - .whenComplete(callback); + log.debug("JSON : {}", job.generateJSON(true)); } } diff --git a/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java index 527b377..4f57a20 100644 --- a/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java +++ b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java @@ -2,8 +2,9 @@ package com.eactive.ext.kjb.ums.test; import com.eactive.apim.portal.template.entity.MessageCode; import com.eactive.apim.portal.template.entity.MessageRequest; +import com.eactive.ext.kjb.common.KjbEaiBatchException; import com.eactive.ext.kjb.common.KjbProperty; -import com.eactive.ext.kjb.ums.EmailSendModule; +import com.eactive.ext.kjb.ums.KjbEmailSendModule; import com.eactive.ext.kjb.ums.KjbUmsService; import com.eactive.ext.kjb.utils.OsUtil; import lombok.extern.slf4j.Slf4j; @@ -65,31 +66,45 @@ public class UmsTest { "true".equalsIgnoreCase(System.getenv("KJB_EAPIM_TEST_ENABLED")), "OS 환경변수 내 KJB_EAPIM_TEST_ENABLED=TRUE 설정되어 있어야 동작함"); -// prop.setEaiBatchUrl("http://172.31.32.111:10230/BATAppWeb/EaiBatCall"); + prop.setEaiBatchUrl("http://172.31.32.111:10230/BATAppWeb/EaiBatCall"); prop.setEaiBatchUrl(""); prop.setUmsEaiBatchSendDir("c:\\kjb-logs\\sendmail\\snd"); prop.setUmsEaiBatchBackupDir("c:\\kjb-logs\\sendmail\\bak"); + prop.setUmsEaiBatchErrorDir("c:\\kjb-logs\\sendmail\\error"); prop.setUmsEaiBatchInterfaceId("UAGFF00001UIE"); - EmailSendModule service = EmailSendModule.getInstance(prop, null); + + System.getProperties().keySet().forEach(key -> { + log.info("{}: {}", key, System.getProperty((String) key)); + }); + + KjbEmailSendModule service = KjbEmailSendModule.getInstance(prop, null); MessageRequest messageRequest = new MessageRequest(); messageRequest.setId(UUID.randomUUID().toString()); messageRequest.setMessageCode(MessageCode.USER_VERIFICATION_EMAIL); messageRequest.setEmail("dearmai@dearmai.net"); messageRequest.setUsername("김광은"); messageRequest.setSubject("제목 - test"); - messageRequest.setMessage("내용 - test"); + messageRequest.setMessage("{\"ATHN_NO\": \"543216\"}"); + messageRequest.setServiceId("FM_APIM001"); + log.info("MessageRequest - {}", messageRequest); - CompletableFuture future = service.sendEmail(messageRequest, (result, ex) -> { - if (ex != null) { - log.error("failed", ex); - } else { - log.info("result: {}", result); - } - }); + try { + service.sendEmail(messageRequest); + } catch (KjbEaiBatchException e) { + throw new RuntimeException(e); + } - future.join(); +// CompletableFuture future = service.sendEmail(messageRequest, (result, ex) -> { +// if (ex != null) { +// log.error("failed", ex); +// } else { +// log.info("result: {}", result); +// } +// }); +// +// future.join(); log.info("done"); } From ccc4a80c9920028a0d6c930dac4f81822fe5fe24 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Mon, 10 Nov 2025 11:23:26 +0900 Subject: [PATCH 07/16] =?UTF-8?q?=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EB=B0=9C?= =?UTF-8?q?=EC=86=A1=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 ++++ .../com/eactive/ext/kjb/ums/EmailJob.java | 17 +------ .../com/eactive/ext/kjb/ums/test/UmsTest.java | 46 +++++++++++++++---- 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 38e9c81..3d53777 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,12 @@ eapim-admin 에서 포함하여 테스트 이용시 느린 부팅 속도 떄문 - EAI 배치 연계 - 고객이메일발송 시스템 연계 +## 테스트 +### testEmailInServer +```bash +# git pull +GIT_SSH_COMMAND='ssh -i ~/.ssh/id_rsa-jenkins' git pull origin jenkins_with_weblogic + +# test run +KJB_EAPIM_TEST_ENABLED=TRUE kjb-gradle.sh :eapim-admin-kjb:test --tests "com.eactive.ext.kjb.ums.test.UmsTest.testEmailInServer" -Psafedb +``` \ No newline at end of file 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 c7de34a..dc4d4fe 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java +++ b/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java @@ -36,7 +36,6 @@ public class EmailJob { private static final int BEFORE_SEND = 2; private static final int SEND = 3; - private final Path tempFilePath; private final Path eaiFilePath; private final Path backupFilePath; private final Path failedFilePath; @@ -52,7 +51,6 @@ public class EmailJob { this.prop = prop; String filename = messageRequest.getServiceId() + "_" + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now); - this.tempFilePath = Paths.get(prop.getUmsEaiBatchSendDir(), filename + ".tmp"); this.eaiFilePath = Paths.get(prop.getUmsEaiBatchSendDir(), filename); this.backupFilePath = Paths.get(prop.getUmsEaiBatchBackupDir(), filename); this.failedFilePath = Paths.get(prop.getUmsEaiBatchBackupDir(), filename + ".failed"); @@ -192,7 +190,7 @@ public class EmailJob { throw new IllegalStateException("Mail File exists for ID: " + messageRequest.getId()); } - File mailFile = tempFilePath.toFile(); + File mailFile = eaiFilePath.toFile(); try { boolean created = mailFile.createNewFile(); if (!created) { @@ -218,22 +216,11 @@ public class EmailJob { throw new RuntimeException(e); } try { - Files.write(tempFilePath, data); - Files.copy(tempFilePath, eaiFilePath); - Files.delete(tempFilePath); + Files.write(eaiFilePath, data); log.debug("Wrote JSON to file: {}", eaiFilePath.toString()); } catch (IOException e) { log.error("Cannot write JSON to file: {}", eaiFilePath.toString(), e); throw new RuntimeException(e); - } finally { - // 임시 파일이 남아 있다면 오류 상황이므로 에러 경로로 이동 - if (Files.exists(tempFilePath)) { - try { - Files.move(tempFilePath, Paths.get(prop.getUmsEaiBatchErrorDir(), tempFilePath.getFileName().toString())); - } catch (IOException e) { - log.warn("임시 파일 저장시 실패 하여 에러 경로로 옮기는 도중 에러 발생", e); - } - } } } diff --git a/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java index 4f57a20..ac3a5f3 100644 --- a/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java +++ b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java @@ -96,15 +96,43 @@ public class UmsTest { throw new RuntimeException(e); } -// CompletableFuture future = service.sendEmail(messageRequest, (result, ex) -> { -// if (ex != null) { -// log.error("failed", ex); -// } else { -// log.info("result: {}", result); -// } -// }); -// -// future.join(); + log.info("done"); + } + + @Test + public void testEmailInServer() { + Assumptions.assumeTrue( + "true".equalsIgnoreCase(System.getenv("KJB_EAPIM_TEST_ENABLED")), + "OS 환경변수 내 KJB_EAPIM_TEST_ENABLED=TRUE 설정되어 있어야 동작함"); + + prop.setEaiBatchUrl("http://172.31.32.111:10230/BATAppWeb/EaiBatCall"); + prop.setUmsEaiBatchSendDir("/Data/eapim/portal/sendmail/snd"); + prop.setUmsEaiBatchBackupDir("/Data/eapim/portal/sendmail/bak"); + prop.setUmsEaiBatchErrorDir("/Data/eapim/portal/sendmail/ error"); + prop.setUmsEaiBatchInterfaceId("UAGFF00001UIE"); + + + System.getProperties().keySet().forEach(key -> { + log.info("{}: {}", key, System.getProperty((String) key)); + }); + + KjbEmailSendModule service = KjbEmailSendModule.getInstance(prop, null); + MessageRequest messageRequest = new MessageRequest(); + messageRequest.setId(UUID.randomUUID().toString()); + messageRequest.setMessageCode(MessageCode.USER_VERIFICATION_EMAIL); + messageRequest.setEmail("dearmai@dearmai.net"); + messageRequest.setUsername("김광은"); + messageRequest.setSubject("제목 - test"); + messageRequest.setMessage("{\"ATHN_NO\": \"543216\"}"); + messageRequest.setServiceId("FM_APIM001"); + + log.info("MessageRequest - {}", messageRequest); + + try { + service.sendEmail(messageRequest); + } catch (KjbEaiBatchException e) { + throw new RuntimeException(e); + } log.info("done"); } From af36e11170e1e12e64c80c16c8762fa6adf10860 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Mon, 10 Nov 2025 11:25:26 +0900 Subject: [PATCH 08/16] =?UTF-8?q?=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EB=B0=9C?= =?UTF-8?q?=EC=86=A1=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java index ac3a5f3..2b95fe5 100644 --- a/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java +++ b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java @@ -108,7 +108,7 @@ public class UmsTest { prop.setEaiBatchUrl("http://172.31.32.111:10230/BATAppWeb/EaiBatCall"); prop.setUmsEaiBatchSendDir("/Data/eapim/portal/sendmail/snd"); prop.setUmsEaiBatchBackupDir("/Data/eapim/portal/sendmail/bak"); - prop.setUmsEaiBatchErrorDir("/Data/eapim/portal/sendmail/ error"); + prop.setUmsEaiBatchErrorDir("/Data/eapim/portal/sendmail/error"); prop.setUmsEaiBatchInterfaceId("UAGFF00001UIE"); From 1a426270feed8bfd182d325eaf2a2fdf820dfe37 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Mon, 10 Nov 2025 11:28:04 +0900 Subject: [PATCH 09/16] =?UTF-8?q?=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EB=B0=9C?= =?UTF-8?q?=EC=86=A1=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/eactive/ext/kjb/common/KjbProperty.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java b/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java index 498bc08..a777568 100644 --- a/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java +++ b/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java @@ -25,18 +25,12 @@ public class KjbProperty { private String eaiBatchCharset = "euc-kr"; @NotEmpty(message = "kjb.ums.eai_batch.send_dir 는 필수값입니다.") - @Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", - message = "kjb.ums.eai_batch.send_dir 은 절대 경로여야 합니다. 예: /Data/eapim/portal/sendmail/snd 또는 c:\\kjb-logs\\sendmail\\snd") private String umsEaiBatchSendDir; @NotEmpty(message = "kjb.ums.eai_batch.backup_dir 는 필수값입니다.") - @Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", - message = "kjb.ums.eai_batch.backup_dir 은 절대 경로여야 합니다. 예: /Data/eapim/portal/sendmail/bak 또는 c:\\kjb-logs\\sendmail\\bak") private String umsEaiBatchBackupDir; @NotEmpty(message = "kjb.ums.eai_batch.error_dir 는 필수값입니다.") - @Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", - message = "kjb.ums.eai_batch.error_dir 은 절대 경로여야 합니다. 예: /Data/eapim/portal/sendmail/errpr 또는 c:\\kjb-logs\\sendmail\\error") private String umsEaiBatchErrorDir; @Pattern(regexp = "^[a-zA-Z0-9]{13}$", message = "kjb.ums.eai_batch.interface_id 는 12자리 영문대소문자 및 숫자 조합이어야 합니다.") From 9ec3d92ad65ca2380636a8cdaa5a0700f29ada7c Mon Sep 17 00:00:00 2001 From: Rinjae Date: Mon, 10 Nov 2025 13:45:55 +0900 Subject: [PATCH 10/16] =?UTF-8?q?UMS=20=EC=BD=94=EB=93=9C=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + .../eactive/ext/kjb/ums/KjbUmsService.java | 168 ++++++++---------- .../com/eactive/ext/kjb/ums/test/UmsTest.java | 36 ++-- 3 files changed, 92 insertions(+), 113 deletions(-) diff --git a/README.md b/README.md index 3d53777..59ffe10 100644 --- a/README.md +++ b/README.md @@ -15,5 +15,6 @@ eapim-admin 에서 포함하여 테스트 이용시 느린 부팅 속도 떄문 GIT_SSH_COMMAND='ssh -i ~/.ssh/id_rsa-jenkins' git pull origin jenkins_with_weblogic # test run +KJB_EAPIM_TEST_ENABLED=TRUE kjb-gradle.sh :eapim-admin-kjb:test --tests "com.eactive.ext.kjb.ums.test.UmsTest.testSms" -Psafedb KJB_EAPIM_TEST_ENABLED=TRUE kjb-gradle.sh :eapim-admin-kjb:test --tests "com.eactive.ext.kjb.ums.test.UmsTest.testEmailInServer" -Psafedb ``` \ No newline at end of file diff --git a/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java b/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java index f54db35..c849023 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java +++ b/src/main/java/com/eactive/ext/kjb/ums/KjbUmsService.java @@ -3,6 +3,7 @@ package com.eactive.ext.kjb.ums; import com.eactive.apim.portal.template.entity.MessageCode; import com.eactive.apim.portal.template.entity.MessageRequest; import com.eactive.ext.kjb.common.KjbProperty; +import com.eactive.ext.kjb.common.KjbUmsException; import com.eactive.ext.kjb.ums.gson.IUmsGsonObject; import com.eactive.ext.kjb.ums.gson.UmsRequestPayload; import com.eactive.ext.kjb.ums.gson.VerifyPhoneUmsTemplate; @@ -24,14 +25,11 @@ import org.apache.hc.core5.util.Timeout; import org.springframework.util.StreamUtils; import java.io.IOException; -import java.net.URISyntaxException; import java.nio.charset.Charset; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BiConsumer; +import java.util.concurrent.*; @Slf4j public class KjbUmsService { @@ -82,7 +80,7 @@ public class KjbUmsService { } - public boolean send(MessageRequest messageRequest) { + public boolean send(MessageRequest messageRequest) throws KjbUmsException { if (messageRequest == null) { throw new IllegalArgumentException("messageRequest is null"); } @@ -106,7 +104,7 @@ public class KjbUmsService { return true; } - public String sendKakaoAlimTalk(String guid, UmsBizWorkCode bizWorkCode, String cpno, IUmsGsonObject content) { + public String sendKakaoAlimTalk(String guid, UmsBizWorkCode bizWorkCode, String cpno, IUmsGsonObject content) throws KjbUmsException { if (content == null) { throw new IllegalArgumentException("content is null"); } @@ -130,102 +128,84 @@ public class KjbUmsService { throw ex; } - return callUms(SEND_URL, payload, (result, ex) -> { - if (ex != null) { - log.error("UMS 발송 실패", ex); - throw new RuntimeException(ex); - } + UmsCallResult result = call(SEND_URL, payload); + if (!result.isSuccess()) { + throw new KjbUmsException(result.getThrowable().getMessage()); + } - if (result == null || !result.isSuccess()) { - log.error("UMS 발송 실패(return) - {}", JsonUtil.toPrettyString(result)); - throw new RuntimeException("UMS 발송실패 - " + JsonUtil.toPrettyString(result != null ? result.getThrowable().getMessage() : "UMS 발송실패 - 알수 없는 오류")); - } - - log.info("UMS Call success. ({})", result); - }).join().getUmsMessageId(); + return result.getUmsMessageId(); } - private CompletableFuture callUms(@SuppressWarnings("SameParameterValue") String url, UmsRequestPayload requestPayload, - BiConsumer callback) { - return CompletableFuture - .supplyAsync(() -> { - Gson gson = new Gson(); - String json = gson.toJson(requestPayload); + private UmsCallResult call(String url, UmsRequestPayload requestPayload) throws KjbUmsException { + ExecutorService executor = Executors.newSingleThreadExecutor(); + Gson gson = new Gson(); + String json = gson.toJson(requestPayload); - HttpPost httpPost = new HttpPost(property.getUmsHostUrl() + url); - httpPost.setHeader("Content-Type", CONTENT_TYPE); - httpPost.setHeader("Authorization", String.format(AUTHORIZATION_FMT, property.getUmsApiKey())); - httpPost.setEntity(EntityBuilder.create().setText(json).setContentType(ContentType.APPLICATION_JSON).build()); + HttpPost httpPost = new HttpPost(property.getUmsHostUrl() + url); + httpPost.setHeader("Content-Type", CONTENT_TYPE); + httpPost.setHeader("Authorization", String.format(AUTHORIZATION_FMT, property.getUmsApiKey())); + httpPost.setEntity(EntityBuilder.create().setText(json).setContentType(ContentType.APPLICATION_JSON).build()); - if (log.isDebugEnabled()) { - try { - log.debug("Header - {}", httpPost.getHeader("Content-Type")); - log.debug("Header - {}", httpPost.getHeader("Authorization")); - log.debug("Request Body : {}", StreamUtils.copyToString(httpPost.getEntity().getContent(), Charset.defaultCharset())); - } catch (ProtocolException | IOException e) { - throw new RuntimeException(e); - } - } + if (log.isDebugEnabled()) { + try { + log.debug("Header - {}", httpPost.getHeader("Content-Type")); + log.debug("Header - {}", httpPost.getHeader("Authorization")); + log.debug("Request Body : {}", StreamUtils.copyToString(httpPost.getEntity().getContent(), Charset.defaultCharset())); + } catch (ProtocolException | IOException e) { + throw new RuntimeException(e); + } + } - httpPost.setConfig(requestConfig); - - AtomicReference result = new AtomicReference<>(); - if (isRealMode) { - try { - httpClient.execute(httpPost, res -> { - try { - - String body = EntityUtils.toString(res.getEntity()); - if (log.isDebugEnabled()) { - log.debug("Response - {}", body); - } - result.set(gson.fromJson(body, UmsCallResult.class)); - result.get().setUmsMessageId(requestPayload.getMessageIdentityNo()); - result.get().setSuccess(true); - return result; - } finally { - log.debug("Response status - {}", res.getCode()); - - if (res.getCode() != 200) { - Arrays.asList(res.getHeaders()).forEach(o -> { - log.warn("[Header] {}: {}", o.getName(), o.getValue()); - }); - } - } - }); - } catch (JsonSyntaxException e) { - log.error("UMS Call error(JSON Syntax)", e); - result.set( - UmsCallResult.builder() - .umsMessageId(requestPayload.getMessageIdentityNo()) - .isSuccess(false) - .throwable(e) - .build() - ); - } catch (Exception e) { - log.error("UMS Call error", e); - result.set( - UmsCallResult.builder() - .umsMessageId(requestPayload.getMessageIdentityNo()) - .isSuccess(false) - .throwable(e) - .build() - ); - } - } else { - log.warn("UMS URL 미지정으로 실제 호출을 진행하지 않음."); - result.set(UmsCallResult.builder().isSuccess(true).build()); - } + httpPost.setConfig(requestConfig); + if (isRealMode) { + Future future = executor.submit(() -> { + return httpClient.execute(httpPost, res -> { try { - log.debug("UMS Call URI : {}", httpPost.getUri().toASCIIString()); - log.debug("UMS Call Request payload : {}", json); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } + UmsCallResult result; + String body = EntityUtils.toString(res.getEntity()); + if (log.isDebugEnabled()) { + log.debug("Response - {}", body); + } + result = gson.fromJson(body, UmsCallResult.class); + result.setUmsMessageId(requestPayload.getMessageIdentityNo()); + result.setSuccess(true); + return result; + } catch (JsonSyntaxException e) { + log.error("UMS Call error(JSON Syntax)", e); + return UmsCallResult.builder() + .umsMessageId(requestPayload.getMessageIdentityNo()) + .isSuccess(false) + .throwable(e) + .build(); + } catch (Exception e) { + log.error("UMS Call error", e); + return UmsCallResult.builder() + .umsMessageId(requestPayload.getMessageIdentityNo()) + .isSuccess(false) + .throwable(e) + .build(); + } finally { + log.debug("Response status - {}", res.getCode()); - return result.get(); - }) - .whenComplete(callback); + if (res.getCode() != 200) { + Arrays.asList(res.getHeaders()).forEach(o -> { + log.warn("[Header] {}: {}", o.getName(), o.getValue()); + }); + } + } + }); + }); + + try { + return future.get(property.getUmsTimeout(), TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + log.error("EAI Batch call exception", e); + throw new KjbUmsException("EAI Ums call failed", e); + } + } else { + log.warn("UMS URL 미지정으로 실제 호출을 진행하지 않음."); + return UmsCallResult.builder().isSuccess(true).build(); + } } } diff --git a/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java index 2b95fe5..6f5e1ad 100644 --- a/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java +++ b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java @@ -4,10 +4,12 @@ import com.eactive.apim.portal.template.entity.MessageCode; import com.eactive.apim.portal.template.entity.MessageRequest; import com.eactive.ext.kjb.common.KjbEaiBatchException; import com.eactive.ext.kjb.common.KjbProperty; +import com.eactive.ext.kjb.common.KjbUmsException; import com.eactive.ext.kjb.ums.KjbEmailSendModule; import com.eactive.ext.kjb.ums.KjbUmsService; import com.eactive.ext.kjb.utils.OsUtil; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -23,16 +25,22 @@ public class UmsTest { @BeforeAll public static void init() { prop = new KjbProperty(); + prop.setEaiBatchUrl("http://172.31.32.111:10230/BATAppWeb/EaiBatCall"); prop.setUmsEaiBatchInterfaceId("UAGFF00001UIE"); + prop.setUmsHostUrl("http://172.31.35.144:9021"); + prop.setUmsApiKey("7cca6d58-e4f7-474d-9edd-525806bc99ff"); + if (OsUtil.isWindows()) { prop.setUmsEaiBatchSendDir("c:\\kjb-logs\\sendmail\\snd"); prop.setUmsEaiBatchBackupDir("c:\\kjb-logs\\sendmail\\bak"); + prop.setUmsEaiBatchErrorDir("c:\\kjb-logs\\sendmail\\error"); } if (OsUtil.isLinux()) { prop.setUmsEaiBatchSendDir("/Data/eapim/portal/sendmail/snd"); prop.setUmsEaiBatchBackupDir("/Data/eapim/portal/sendmail/bak"); + prop.setUmsEaiBatchErrorDir("/Data/eapim/portal/sendmail/error"); } } @@ -42,9 +50,6 @@ public class UmsTest { "true".equalsIgnoreCase(System.getenv("KJB_EAPIM_TEST_ENABLED")), "OS 환경변수 내 KJB_EAPIM_TEST_ENABLED=TRUE 설정되어 있어야 동작함"); - prop.setUmsHostUrl("http://172.31.35.144:9021"); - prop.setUmsApiKey("7cca6d58-e4f7-474d-9edd-525806bc99ff"); - MessageRequest messageRequest = new MessageRequest(); messageRequest.setId(UUID.randomUUID().toString()); messageRequest.setMessageCode(MessageCode.USER_VERIFICATION_MOBILEPHONE); @@ -52,10 +57,17 @@ public class UmsTest { messageRequest.setEmail("dearmai@dearmai.net"); messageRequest.setUsername("김광은"); messageRequest.setSubject("제목 - test"); - messageRequest.setMessage("junit test"); + messageRequest.setMessage("{\"ATHN_NO\"; \"369369\"}"); KjbUmsService umsService = new KjbUmsService(prop); - umsService.send(messageRequest); + try { + boolean result = umsService.send(messageRequest); + + } catch (KjbUmsException e) { + log.error(e.getMessage(), e); + Assertions.fail(e); + return; + } log.info("done"); } @@ -66,13 +78,6 @@ public class UmsTest { "true".equalsIgnoreCase(System.getenv("KJB_EAPIM_TEST_ENABLED")), "OS 환경변수 내 KJB_EAPIM_TEST_ENABLED=TRUE 설정되어 있어야 동작함"); - prop.setEaiBatchUrl("http://172.31.32.111:10230/BATAppWeb/EaiBatCall"); - prop.setEaiBatchUrl(""); - prop.setUmsEaiBatchSendDir("c:\\kjb-logs\\sendmail\\snd"); - prop.setUmsEaiBatchBackupDir("c:\\kjb-logs\\sendmail\\bak"); - prop.setUmsEaiBatchErrorDir("c:\\kjb-logs\\sendmail\\error"); - prop.setUmsEaiBatchInterfaceId("UAGFF00001UIE"); - System.getProperties().keySet().forEach(key -> { log.info("{}: {}", key, System.getProperty((String) key)); @@ -105,13 +110,6 @@ public class UmsTest { "true".equalsIgnoreCase(System.getenv("KJB_EAPIM_TEST_ENABLED")), "OS 환경변수 내 KJB_EAPIM_TEST_ENABLED=TRUE 설정되어 있어야 동작함"); - prop.setEaiBatchUrl("http://172.31.32.111:10230/BATAppWeb/EaiBatCall"); - prop.setUmsEaiBatchSendDir("/Data/eapim/portal/sendmail/snd"); - prop.setUmsEaiBatchBackupDir("/Data/eapim/portal/sendmail/bak"); - prop.setUmsEaiBatchErrorDir("/Data/eapim/portal/sendmail/error"); - prop.setUmsEaiBatchInterfaceId("UAGFF00001UIE"); - - System.getProperties().keySet().forEach(key -> { log.info("{}: {}", key, System.getProperty((String) key)); }); From 26a0cd39d116462c809adeb275aa33d4e98c71df Mon Sep 17 00:00:00 2001 From: Rinjae Date: Mon, 10 Nov 2025 13:50:29 +0900 Subject: [PATCH 11/16] =?UTF-8?q?UMS=20=EC=BD=94=EB=93=9C=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/eactive/ext/kjb/common/KjbUmsException.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/com/eactive/ext/kjb/common/KjbUmsException.java diff --git a/src/main/java/com/eactive/ext/kjb/common/KjbUmsException.java b/src/main/java/com/eactive/ext/kjb/common/KjbUmsException.java new file mode 100644 index 0000000..07a1082 --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/common/KjbUmsException.java @@ -0,0 +1,11 @@ +package com.eactive.ext.kjb.common; + +public class KjbUmsException extends Exception { + public KjbUmsException(String message) { + super(message); + } + + public KjbUmsException(String message, Throwable cause) { + super(message, cause); + } +} From 8575f6d94d4b82d487f9bb6b222e4c3faf3ff2bd Mon Sep 17 00:00:00 2001 From: Rinjae Date: Mon, 10 Nov 2025 14:18:16 +0900 Subject: [PATCH 12/16] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/eactive/ext/kjb/ums/test/UmsTest.java | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java index 6f5e1ad..3273a51 100644 --- a/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java +++ b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java @@ -15,7 +15,6 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.util.UUID; -import java.util.concurrent.CompletableFuture; @Slf4j @@ -103,35 +102,4 @@ public class UmsTest { log.info("done"); } - - @Test - public void testEmailInServer() { - Assumptions.assumeTrue( - "true".equalsIgnoreCase(System.getenv("KJB_EAPIM_TEST_ENABLED")), - "OS 환경변수 내 KJB_EAPIM_TEST_ENABLED=TRUE 설정되어 있어야 동작함"); - - System.getProperties().keySet().forEach(key -> { - log.info("{}: {}", key, System.getProperty((String) key)); - }); - - KjbEmailSendModule service = KjbEmailSendModule.getInstance(prop, null); - MessageRequest messageRequest = new MessageRequest(); - messageRequest.setId(UUID.randomUUID().toString()); - messageRequest.setMessageCode(MessageCode.USER_VERIFICATION_EMAIL); - messageRequest.setEmail("dearmai@dearmai.net"); - messageRequest.setUsername("김광은"); - messageRequest.setSubject("제목 - test"); - messageRequest.setMessage("{\"ATHN_NO\": \"543216\"}"); - messageRequest.setServiceId("FM_APIM001"); - - log.info("MessageRequest - {}", messageRequest); - - try { - service.sendEmail(messageRequest); - } catch (KjbEaiBatchException e) { - throw new RuntimeException(e); - } - - log.info("done"); - } } From 2c6b12b85de8fcbc00929d69467c9c9197d481b0 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Mon, 10 Nov 2025 14:23:24 +0900 Subject: [PATCH 13/16] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/eactive/ext/kjb/ums/EmailJob.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 dc4d4fe..fb27084 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java +++ b/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java @@ -50,7 +50,7 @@ public class EmailJob { this.messageRequest = messageRequest; this.prop = prop; - String filename = messageRequest.getServiceId() + "_" + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now); + String filename = messageRequest.getServiceId() + "_" + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now) + ".dat"; this.eaiFilePath = Paths.get(prop.getUmsEaiBatchSendDir(), filename); this.backupFilePath = Paths.get(prop.getUmsEaiBatchBackupDir(), filename); this.failedFilePath = Paths.get(prop.getUmsEaiBatchBackupDir(), filename + ".failed"); From 436ef89ca35df9abe0e4684162c62adfa0c0d8a9 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Mon, 10 Nov 2025 14:23:24 +0900 Subject: [PATCH 14/16] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +-- src/main/java/com/eactive/ext/kjb/ums/EmailJob.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 59ffe10..397e5d2 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,11 @@ eapim-admin 에서 포함하여 테스트 이용시 느린 부팅 속도 떄문 - 고객이메일발송 시스템 연계 ## 테스트 -### testEmailInServer ```bash # git pull GIT_SSH_COMMAND='ssh -i ~/.ssh/id_rsa-jenkins' git pull origin jenkins_with_weblogic # test run KJB_EAPIM_TEST_ENABLED=TRUE kjb-gradle.sh :eapim-admin-kjb:test --tests "com.eactive.ext.kjb.ums.test.UmsTest.testSms" -Psafedb -KJB_EAPIM_TEST_ENABLED=TRUE kjb-gradle.sh :eapim-admin-kjb:test --tests "com.eactive.ext.kjb.ums.test.UmsTest.testEmailInServer" -Psafedb +KJB_EAPIM_TEST_ENABLED=TRUE kjb-gradle.sh :eapim-admin-kjb:test --tests "com.eactive.ext.kjb.ums.test.UmsTest.testEmail" -Psafedb ``` \ No newline at end of file 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 dc4d4fe..fb27084 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java +++ b/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java @@ -50,7 +50,7 @@ public class EmailJob { this.messageRequest = messageRequest; this.prop = prop; - String filename = messageRequest.getServiceId() + "_" + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now); + String filename = messageRequest.getServiceId() + "_" + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now) + ".dat"; this.eaiFilePath = Paths.get(prop.getUmsEaiBatchSendDir(), filename); this.backupFilePath = Paths.get(prop.getUmsEaiBatchBackupDir(), filename); this.failedFilePath = Paths.get(prop.getUmsEaiBatchBackupDir(), filename + ".failed"); From 1ee8f34115912e33a48b16308294d93334f0dc1f Mon Sep 17 00:00:00 2001 From: Rinjae Date: Mon, 10 Nov 2025 14:35:23 +0900 Subject: [PATCH 15/16] =?UTF-8?q?ums=20id=20=EC=B2=B4=EA=B3=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/eactive/ext/kjb/ums/EmailJob.java | 10 ++++++---- .../java/com/eactive/ext/kjb/ums/test/UmsTest.java | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) 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 fb27084..7a38cde 100644 --- a/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java +++ b/src/main/java/com/eactive/ext/kjb/ums/EmailJob.java @@ -5,8 +5,8 @@ import com.eactive.apim.portal.template.entity.MessageRequest; import com.eactive.apim.portal.template.repository.MessageRequestRepository; import com.eactive.ext.kjb.common.KjbEaiBatchException; import com.eactive.ext.kjb.common.KjbProperty; -import com.eactive.ext.kjb.eai.EaiBatchMessage; import com.eactive.ext.kjb.eai.EaiBatchInterface; +import com.eactive.ext.kjb.eai.EaiBatchMessage; import com.eactive.ext.kjb.eai.KjbEaiBatchModule; import com.eactive.ext.kjb.safedb.KjbSafedbWrapper; import com.google.gson.Gson; @@ -73,7 +73,8 @@ public class EmailJob { if (messageRequest.getUser() != null) { PortalUser user = messageRequest.getUser(); - messageRequest.setUmsUid(UUIDBase62Util.uuidStringToBase62(user.getId())); +// messageRequest.setUmsUid(UUIDBase62Util.uuidStringToBase62(user.getId())); + messageRequest.setUmsUid(user.getId()); if (repository != null) { repository.save(messageRequest); @@ -124,7 +125,8 @@ public class EmailJob { if (messageRequestRepository != null) { PortalUser user = messageRequest.getUser(); if (user != null) { - messageRequest.setUmsUid(UUIDBase62Util.uuidStringToBase62(user.getId())); + // messageRequest.setUmsUid(UUIDBase62Util.uuidStringToBase62(user.getId())); + messageRequest.setUmsUid(user.getId()); } messageRequestRepository.save(messageRequest); @@ -230,7 +232,7 @@ public class EmailJob { JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("MAKE_DATE", DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDateTime.now())); - jsonObject.addProperty("MAKE_SEQNO", "0001"); + jsonObject.addProperty("MAKE_SEQNO", DateTimeFormatter.ofPattern("HHmmssSSS").format(LocalDateTime.now())); jsonObject.addProperty("CAMP_ID", messageRequest.getServiceId()); // 전자금융 고객이메일 발송 업무ID (SERVICE_ID) jsonObject.addProperty("ID", messageRequest.getUmsUid()); // 고객 ID jsonObject.addProperty("EMAIL", safedb.encryptNotRnnoString(messageRequest.getEmail())); diff --git a/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java index 3273a51..5015ef9 100644 --- a/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java +++ b/src/test/java/com/eactive/ext/kjb/ums/test/UmsTest.java @@ -87,6 +87,7 @@ public class UmsTest { messageRequest.setId(UUID.randomUUID().toString()); messageRequest.setMessageCode(MessageCode.USER_VERIFICATION_EMAIL); messageRequest.setEmail("dearmai@dearmai.net"); + messageRequest.setUmsUid(UUID.randomUUID().toString()); messageRequest.setUsername("김광은"); messageRequest.setSubject("제목 - test"); messageRequest.setMessage("{\"ATHN_NO\": \"543216\"}"); From 797529c774965a1b482cb17349d5ef44ca4088f6 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Mon, 10 Nov 2025 16:50:37 +0900 Subject: [PATCH 16/16] =?UTF-8?q?property=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eactive/ext/kjb/common/KjbProperty.java | 15 +++++ .../ext/kjb/common/KjbPropertyValue.java | 13 ++++ .../ext/kjb/eai/KjbEaiBatchModule.java | 59 +++++++++++++++++++ .../ext/kjb/utils/KjbPropertyUtil.java | 35 +++++++++++ 4 files changed, 122 insertions(+) create mode 100644 src/main/java/com/eactive/ext/kjb/common/KjbPropertyValue.java create mode 100644 src/main/java/com/eactive/ext/kjb/utils/KjbPropertyUtil.java diff --git a/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java b/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java index a777568..f848d93 100644 --- a/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java +++ b/src/main/java/com/eactive/ext/kjb/common/KjbProperty.java @@ -9,49 +9,64 @@ import javax.validation.constraints.Pattern; @Data public class KjbProperty { + @KjbPropertyValue(key = "kjb.eai.batch.url") @Pattern(regexp = "^(https?://[^\\s/$.?#][^\\s]*)|\\s*$", message = "kjb.eai_batch.url 는 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api") private String eaiBatchUrl; + @KjbPropertyValue(key = "kjb.eai.batch.connection_timeout", defaultValue = "5") @Min(1) @Max(10) private int eaiBatchConnectionTimeout = 5; + @KjbPropertyValue(key = "kjb.eai.batch.response_timeout", defaultValue = "5") @Min(1) @Max(30) private int eaiBatchResponseTimeout = 5; + @KjbPropertyValue(key = "kjb.eai.batch.timeout", defaultValue = "10") @Min(1) @Max(30) private int eaiBatchTimeout = 10; + @KjbPropertyValue(key = "kjb.eai.batch.charset", defaultValue = "euc-kr") @NotEmpty private String eaiBatchCharset = "euc-kr"; + @KjbPropertyValue(key = "kjb.ums.eai_batch.send_dir") @NotEmpty(message = "kjb.ums.eai_batch.send_dir 는 필수값입니다.") private String umsEaiBatchSendDir; + @KjbPropertyValue(key = "kjb.ums.eai_batch.backup_dir") @NotEmpty(message = "kjb.ums.eai_batch.backup_dir 는 필수값입니다.") private String umsEaiBatchBackupDir; + @KjbPropertyValue(key = "kjb.ums.eai_batch.error_dir") @NotEmpty(message = "kjb.ums.eai_batch.error_dir 는 필수값입니다.") private String umsEaiBatchErrorDir; + @KjbPropertyValue(key = "kjb.ums.eai_batch.interface_id") @Pattern(regexp = "^[a-zA-Z0-9]{13}$", message = "kjb.ums.eai_batch.interface_id 는 12자리 영문대소문자 및 숫자 조합이어야 합니다.") private String umsEaiBatchInterfaceId; + @KjbPropertyValue(key = "kjb.ums.eai_batch.retention_date", defaultValue = "60") @Min(1) @Max(365) private int umsEaiBatchRetentionDate = 60; + @KjbPropertyValue(key = "kjb.ums.host") @Pattern(regexp = "^(https?://[^\\s/$.?#][^\\s]*)|\\s*$", message = "kjb.ums.host_url 는 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api") private String umsHostUrl; + @KjbPropertyValue(key = "kjb.ums.api_key") @Pattern(regexp = "^[a-zA-Z0-9_-]{36}$", message = "kjb.ums.api_key 입력 형식이 맞지 않습니다.") private String umsApiKey; + @KjbPropertyValue(key = "kjb.ums.connection_timeout", defaultValue = "5") @Min(1) @Max(10) private int umsConnectionTimeout = 5; + @KjbPropertyValue(key = "kjb.ums.response_timeout", defaultValue = "5") @Min(1) @Max(30) private int umsResponseTimeout = 5; + @KjbPropertyValue(key = "kjb.ums.timeout", defaultValue = "10") @Min(1) @Max(30) private int umsTimeout = 10; } diff --git a/src/main/java/com/eactive/ext/kjb/common/KjbPropertyValue.java b/src/main/java/com/eactive/ext/kjb/common/KjbPropertyValue.java new file mode 100644 index 0000000..0522f7d --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/common/KjbPropertyValue.java @@ -0,0 +1,13 @@ +package com.eactive.ext.kjb.common; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface KjbPropertyValue { + String key(); + String defaultValue() default ""; +} diff --git a/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java b/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java index d1f7a2e..72c57f3 100644 --- a/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java +++ b/src/main/java/com/eactive/ext/kjb/eai/KjbEaiBatchModule.java @@ -15,16 +15,27 @@ import org.apache.hc.core5.util.Timeout; import javax.validation.Valid; import javax.validation.constraints.NotNull; import java.io.File; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Comparator; import java.util.concurrent.*; +import java.util.stream.Stream; @Slf4j public class KjbEaiBatchModule { private final KjbProperty property; private final CloseableHttpClient httpClient; private final RequestConfig requestConfig; + private LocalDateTime lastFileRetentionCheckTime; + public KjbEaiBatchModule(@NotNull KjbProperty property) { @@ -104,4 +115,52 @@ public class KjbEaiBatchModule { return resMessage; } } + + /** + * 1시간 주기로 파일 유지 및 삭제 처리 + */ + public void processFileRetentionDays() { + if (lastFileRetentionCheckTime == null) { + lastFileRetentionCheckTime = LocalDateTime.now(); + } + + // 현재 시간과 1시간 이상 차이 날때 동작 + LocalDateTime now = LocalDateTime.now(); + boolean isOver = Duration.between(lastFileRetentionCheckTime, now).abs().toHours() == 1; + + if (!isOver) return; + + this.walkDeletePath(property.getUmsEaiBatchSendDir()); + this.walkDeletePath(property.getUmsEaiBatchBackupDir()); + this.walkDeletePath(property.getUmsEaiBatchErrorDir()); + + lastFileRetentionCheckTime = now; + } + + private void walkDeletePath(String path) { + final int retentionDays = property.getUmsEaiBatchRetentionDate(); + log.info("WalkDelete - {} / Retention days : {}", path, retentionDays); + + Path dir = Paths.get(path); + LocalDateTime now = LocalDateTime.now(); + + try (Stream paths = Files.walk(dir)) { + paths.sorted(Comparator.reverseOrder()).forEach(p -> { + try { + BasicFileAttributes attrs = Files.readAttributes(p, BasicFileAttributes.class); + LocalDateTime created = attrs.creationTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + + long over = Math.abs(Duration.between(created, now).toDays()); + if (over >= retentionDays) { + Files.deleteIfExists(p); + log.info("Delete over {} days - {}", retentionDays, p); + } + } catch (Exception e) { + log.warn("Delete fail - {}", p.getFileName().toString(), e); + } + }); + } catch (IOException e) { + log.warn("Not found path : {}", path); + } + } } diff --git a/src/main/java/com/eactive/ext/kjb/utils/KjbPropertyUtil.java b/src/main/java/com/eactive/ext/kjb/utils/KjbPropertyUtil.java new file mode 100644 index 0000000..39f836a --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/utils/KjbPropertyUtil.java @@ -0,0 +1,35 @@ +package com.eactive.ext.kjb.utils; + +import com.eactive.ext.kjb.common.KjbProperty; +import com.eactive.ext.kjb.common.KjbPropertyValue; +import org.apache.commons.lang3.StringUtils; + +import java.lang.reflect.Field; + +public class KjbPropertyUtil { + public static void main(String[] args) { + Class clazz = KjbProperty.class; + + StringBuilder sb = new StringBuilder(); + + for (Field field : clazz.getDeclaredFields()) { + KjbPropertyValue anno = field.getAnnotation(KjbPropertyValue.class); + + if (anno != null) { + sb.append("insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', "); + + String key = anno.key(); + String defValue = anno.defaultValue(); + if (StringUtils.isEmpty(defValue)) defValue = ""; + + sb.append("'").append(key).append("', '").append(addQuote(defValue)).append("');\n"); + } + } + + System.out.println(sb); + } + + private static String addQuote(String str) { + return str.replace("'", "''"); + } +}