init
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
build/
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'java-library'
|
||||||
|
id 'eclipse-wtp'
|
||||||
|
id 'idea'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'com.eactive.ext.kjb'
|
||||||
|
version = '1.0-SNAPSHOT'
|
||||||
|
|
||||||
|
def springVersion = "5.3.27"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
annotationProcessor "org.projectlombok:lombok:1.18.28"
|
||||||
|
|
||||||
|
api project(':elink-portal-common')
|
||||||
|
api project(":kjb-safedb")
|
||||||
|
|
||||||
|
api 'com.eactive.elink.common:elink-common-data:4.5.5'
|
||||||
|
|
||||||
|
// api "org.springframework:spring-context:${springVersion}"
|
||||||
|
// api "org.springframework:spring-webmvc:${springVersion}"
|
||||||
|
// api "org.springframework.security.oauth:spring-security-oauth2:2.3.8.RELEASE"
|
||||||
|
|
||||||
|
implementation "com.google.code.gson:gson:2.3.1"
|
||||||
|
|
||||||
|
|
||||||
|
compileOnly 'org.slf4j:jul-to-slf4j:1.7.35'
|
||||||
|
compileOnly 'org.slf4j:jcl-over-slf4j:1.7.35'
|
||||||
|
compileOnly 'org.slf4j:log4j-over-slf4j:1.7.35'
|
||||||
|
compileOnly 'org.slf4j:slf4j-api:1.7.35'
|
||||||
|
compileOnly fileTree(dir: "ext-libs/compileOnly", include: ["*.jar"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
testAnnotationProcessor "org.projectlombok:lombok:1.18.28"
|
||||||
|
|
||||||
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
|
||||||
|
testImplementation 'ch.qos.logback:logback-classic:1.2.10'
|
||||||
|
testImplementation 'ch.qos.logback:logback-access:1.2.10'
|
||||||
|
testImplementation 'ch.qos.logback:logback-core:1.2.10'
|
||||||
|
testImplementation 'org.slf4j:jul-to-slf4j:1.7.35'
|
||||||
|
testImplementation 'org.slf4j:jcl-over-slf4j:1.7.35'
|
||||||
|
testImplementation 'org.slf4j:log4j-over-slf4j:1.7.35'
|
||||||
|
testImplementation 'org.slf4j:slf4j-api:1.7.35'
|
||||||
|
|
||||||
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
|
||||||
|
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.8.2'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Compile
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
options.encoding = 'UTF-8'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Build
|
||||||
|
task sourcesJar(type: Jar, dependsOn: classes) {
|
||||||
|
archiveClassifier = 'sources'
|
||||||
|
from sourceSets.main.allSource
|
||||||
|
}
|
||||||
|
|
||||||
|
artifacts {
|
||||||
|
archives sourcesJar
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
archiveFileName = "eapim-admin-kjb.jar"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Test
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
|
systemProperty "kjb_safedb.mode", project.findProperty("kjb_safedb.mode") ?: ""
|
||||||
|
|
||||||
|
if (project.hasProperty("safedb")) {
|
||||||
|
systemProperty "kjb_safedb.mode", "real"
|
||||||
|
systemProperty "kjb_safedb.test.strict_mode", "true"
|
||||||
|
|
||||||
|
classpath += files("/safedb/JavaAPI/config")
|
||||||
|
classpath += fileTree(dir: "/safedb/JavaAPI/lib", include: ["*.jar"])
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
testLogging {
|
||||||
|
events "PASSED", "FAILED", "SKIPPED", "STANDARD_OUT", "STANDARD_ERROR"
|
||||||
|
exceptionFormat "full"
|
||||||
|
showStandardStreams = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
configurations.all {
|
||||||
|
resolutionStrategy {
|
||||||
|
// 동적 버전 탐색을 10분 캐시
|
||||||
|
cacheDynamicVersionsFor 10, 'minutes'
|
||||||
|
// 변하는 모듈(Changing Module)을 캐시하지 않음
|
||||||
|
cacheChangingModulesFor 0, 'seconds'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task settingEclipseEncoding {
|
||||||
|
if (project.file('.settings').exists()) {
|
||||||
|
File f = file('.settings/org.eclipse.core.resources.prefs')
|
||||||
|
f.write('eclipse.preferences.version=1\n')
|
||||||
|
f.append('encoding/<project>=utf-8')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eclipse {
|
||||||
|
synchronizationTasks settingEclipseEncoding
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.eactive.ext.kjb.common;
|
||||||
|
|
||||||
|
import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator;
|
||||||
|
|
||||||
|
import javax.validation.*;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ValidationUtil {
|
||||||
|
private static final ValidatorFactory factory;
|
||||||
|
private static final Validator validator;
|
||||||
|
|
||||||
|
static {
|
||||||
|
factory = Validation.byDefaultProvider()
|
||||||
|
.configure()
|
||||||
|
.messageInterpolator(new ParameterMessageInterpolator())
|
||||||
|
.buildValidatorFactory();
|
||||||
|
|
||||||
|
validator = factory.getValidator();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private ValidationUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static <T> Set<ConstraintViolation<T>> validate(T target) {
|
||||||
|
return validator.validate(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> void validateOrThrow(T target) {
|
||||||
|
Set<ConstraintViolation<T>> violations = validate(target);
|
||||||
|
if (!violations.isEmpty()) {
|
||||||
|
String message = violations.stream()
|
||||||
|
.map(v -> v.getPropertyPath() + " : " + v.getMessage())
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
throw new ConstraintViolationException("Validation failed: " + message, violations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> String getViolationMessage(T target) {
|
||||||
|
Set<ConstraintViolation<T>> violations = validate(target);
|
||||||
|
if (violations.isEmpty()) return null;
|
||||||
|
return violations.stream()
|
||||||
|
.map(v -> v.getPropertyPath() + " : " + v.getMessage())
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,225 @@
|
|||||||
|
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.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class EmailJob {
|
||||||
|
private final EmailProperty prop;
|
||||||
|
private final MessageRequest messageRequest;
|
||||||
|
|
||||||
|
private final Path tempFilePath;
|
||||||
|
private final Path eaiFilePath;
|
||||||
|
private final Path backupFilePath;
|
||||||
|
private final Path failedFilePath;
|
||||||
|
private MessageRequestRepository messageRequestRepository;
|
||||||
|
private final LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private EmailJob(EmailProperty prop, MessageRequest messageRequest, MessageRequestRepository messageRequestRepository) {
|
||||||
|
this.messageRequest = messageRequest;
|
||||||
|
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.messageRequestRepository = messageRequestRepository;
|
||||||
|
|
||||||
|
this.doCreateNewFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static EmailJob create(EmailProperty prop, MessageRequest messageRequest, MessageRequestRepository repository) {
|
||||||
|
if (prop == null) {
|
||||||
|
throw new IllegalArgumentException("Property is null");
|
||||||
|
}
|
||||||
|
if (messageRequest == null) {
|
||||||
|
throw new IllegalArgumentException("MessageRequest is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
validateMessageRequest(messageRequest, false, true);
|
||||||
|
|
||||||
|
if (messageRequest.getUser() != null) {
|
||||||
|
PortalUser user = messageRequest.getUser();
|
||||||
|
messageRequest.setUmsUid(UUIDBase62Util.uuidStringToBase62(user.getId()));
|
||||||
|
|
||||||
|
if (repository != null) {
|
||||||
|
repository.save(messageRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new EmailJob(prop, messageRequest, repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EmailValidationResult validateMessageRequest(MessageRequest messageRequest, boolean forSend, boolean isThrow) {
|
||||||
|
Map<String, String> errors = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(messageRequest.getId())) {
|
||||||
|
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.getServiceId())) {
|
||||||
|
errors.put("serviceId", "Service ID is required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 결과 리턴
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
if (isThrow) {
|
||||||
|
throw new IllegalArgumentException("Invalid MessageRequest: " + errors);
|
||||||
|
} else {
|
||||||
|
return EmailValidationResult.failed(errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EmailValidationResult.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void sendEmail() {
|
||||||
|
if (messageRequestRepository != null) {
|
||||||
|
PortalUser user = messageRequest.getUser();
|
||||||
|
if (user != null) {
|
||||||
|
messageRequest.setUmsUid(UUIDBase62Util.uuidStringToBase62(user.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
messageRequestRepository.save(messageRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
doSaveFile();
|
||||||
|
eaiBatchCall();
|
||||||
|
doSuccess();
|
||||||
|
} catch (Exception e) {
|
||||||
|
doFail();
|
||||||
|
} finally {
|
||||||
|
doDeleteEaiFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doDeleteEaiFile() {
|
||||||
|
// TODO: EAI 발송용 파일 삭제
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doFail() {
|
||||||
|
// TODO: EAI 발송 실패 처리
|
||||||
|
}
|
||||||
|
|
||||||
|
private void eaiBatchCall() {
|
||||||
|
// TODO: EAI 배치 호출
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
File mailFile = tempFilePath.toFile();
|
||||||
|
try {
|
||||||
|
boolean created = mailFile.createNewFile();
|
||||||
|
if (!created) {
|
||||||
|
log.error("Cannot create mail file for ID: {}", messageRequest.getId());
|
||||||
|
throw new IllegalStateException("Cannot create mail file for ID: " + messageRequest.getId());
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("Cannot create mail file for ID: {}", messageRequest.getId(), ex);
|
||||||
|
throw new IllegalStateException("Cannot create mail file for ID: " + messageRequest.getId(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doSaveFile() {
|
||||||
|
String jsonContent = generateJSON(true);
|
||||||
|
log.debug("Generated JSON: \n{}", jsonContent);
|
||||||
|
|
||||||
|
// 저장은 임시 파일에 euc-kr 인코딩으로 저장
|
||||||
|
byte[] data = null;
|
||||||
|
try {
|
||||||
|
data = jsonContent.getBytes("euc-kr");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
log.error("Cannot encode(euc-kr) JSON: \n{}", jsonContent, e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Files.write(tempFilePath, data);
|
||||||
|
Files.copy(tempFilePath, eaiFilePath);
|
||||||
|
Files.delete(tempFilePath);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generateJSON(boolean isPretty) {
|
||||||
|
validateMessageRequest(messageRequest, true, true);
|
||||||
|
|
||||||
|
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("NAME", messageRequest.getUsername());
|
||||||
|
jsonObject.addProperty("ENCKEY", ""); // 보안메일용으로 기입 불필요
|
||||||
|
|
||||||
|
|
||||||
|
jsonObject.addProperty("SUBJECT", messageRequest.getSubject());
|
||||||
|
jsonObject.addProperty("MESSAGE", messageRequest.getMessage());
|
||||||
|
|
||||||
|
if (isPretty) {
|
||||||
|
return new GsonBuilder().setPrettyPrinting().create().toJson(jsonObject);
|
||||||
|
} else {
|
||||||
|
return jsonObject.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.eactive.ext.kjb.ums;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.hibernate.validator.constraints.NotEmpty;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class EmailProperty {
|
||||||
|
@Pattern(regexp = "^$|^(https?)://[^\\s/$.?#].[^\\s]*$", message = "kjb.ums.eai_batch.url 은 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api")
|
||||||
|
private String eaiBatchUrl = "";
|
||||||
|
|
||||||
|
@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;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
|
||||||
|
|
||||||
|
@Pattern(regexp = "^[a-zA-Z0-9]{12}$", message = "kjb.ums.eai_batch.interface_id 는 12자리 영문대소문자 및 숫자 조합이어야 합니다.")
|
||||||
|
private String eaiBatchInterfaceId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
package com.eactive.ext.kjb.ums;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.template.entity.MessageRequest;
|
||||||
|
import com.eactive.apim.portal.template.repository.MessageRequestRepository;
|
||||||
|
import com.eactive.ext.kjb.common.ValidationUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class EmailSendModule {
|
||||||
|
private static EmailSendModule instance = null;
|
||||||
|
private static EmailProperty prop;
|
||||||
|
|
||||||
|
private final MessageRequestRepository repo;
|
||||||
|
|
||||||
|
|
||||||
|
public static synchronized EmailSendModule getInstance(EmailProperty property,
|
||||||
|
MessageRequestRepository messageRequestRepository) {
|
||||||
|
|
||||||
|
ValidationUtil.validateOrThrow(property);
|
||||||
|
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new EmailSendModule(property, messageRequestRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void environmentCheck() {
|
||||||
|
if (StringUtils.isEmpty(prop.getEaiBatchUrl())) {
|
||||||
|
log.warn("EAI_BATCH_URL 가 설정되지 않음. 실제 EAI 배치 연동 없이 동작함");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("EAI Batch URL: {}", prop.getEaiBatchUrl());
|
||||||
|
log.info("EAI Batch Send Dir: {}", prop.getEaiBatchSendDir());
|
||||||
|
|
||||||
|
|
||||||
|
// 디렉토리 검사(쓰기 권한 포함)
|
||||||
|
Path sendDirPath = Paths.get(prop.getEaiBatchSendDir());
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendDirPath.toFile().isDirectory()) {
|
||||||
|
log.error("EAI_BATCH_SEND_DIR 가 디렉토리가 아님: {}", prop.getEaiBatchSendDir());
|
||||||
|
throw new IllegalStateException("EAI_BATCH_SEND_DIR 가 디렉토리가 아님: " + prop.getEaiBatchSendDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendDirPath.toFile().canWrite()) {
|
||||||
|
log.error("EAI_BATCH_SEND_DIR 에 쓰기 권한이 없음: {}", prop.getEaiBatchSendDir());
|
||||||
|
throw new IllegalStateException("EAI_BATCH_SEND_DIR 에 쓰기 권한이 없음: " + prop.getEaiBatchSendDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 백업 디렉토리
|
||||||
|
Path backupDirPath = Paths.get(prop.getEaiBatchBackupDir());
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!backupDirPath.toFile().isDirectory()) {
|
||||||
|
log.error("EAI_BATCH_BACKUP_DIR 가 디렉토리가 아님: {}", prop.getEaiBatchBackupDir());
|
||||||
|
throw new IllegalStateException("EAI_BATCH_BACKUP_DIR 가 디렉토리가 아님: " + prop.getEaiBatchBackupDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!backupDirPath.toFile().canWrite()) {
|
||||||
|
log.error("EAI_BATCH_BACKUP_DIR 에 쓰기 권한이 없음: {}", prop.getEaiBatchBackupDir());
|
||||||
|
throw new IllegalStateException("EAI_BATCH_BACKUP_DIR 에 쓰기 권한이 없음: " + prop.getEaiBatchBackupDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("KjbEmailSendModule - Environment check passed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private EmailSendModule(EmailProperty property,
|
||||||
|
MessageRequestRepository repo) {
|
||||||
|
this.repo = repo;
|
||||||
|
|
||||||
|
if (property != null && prop == null) {
|
||||||
|
prop = property;
|
||||||
|
}
|
||||||
|
|
||||||
|
environmentCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Boolean> sendEmail(MessageRequest messageRequest, BiConsumer<Boolean, Throwable> callback) {
|
||||||
|
return CompletableFuture
|
||||||
|
.supplyAsync(() -> {
|
||||||
|
// TODO: MESSAGE_CODE를 통해 SERVICE_ID 생성 필요
|
||||||
|
String serviceId;
|
||||||
|
|
||||||
|
switch(messageRequest.getMessageCode()) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
messageRequest.setServiceId("TEMP_SERVICE");
|
||||||
|
if (repo != null) {
|
||||||
|
repo.save(messageRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
EmailJob job = EmailJob.create(prop, messageRequest, repo);
|
||||||
|
job.sendEmail();
|
||||||
|
|
||||||
|
log.debug("JSON : {}", job.generateJSON(true));
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.whenComplete(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void validateMessageRequest(MessageRequest messageRequest) {
|
||||||
|
if (messageRequest == null) {
|
||||||
|
log.warn("MessageRequest is null");
|
||||||
|
throw new IllegalArgumentException("MessageRequest is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(messageRequest.getId())) {
|
||||||
|
log.warn("MessageRequest ID is empty");
|
||||||
|
throw new IllegalArgumentException("MessageRequest ID is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(messageRequest.getEmail())) {
|
||||||
|
log.warn("MessageRequest Email is empty for ID: {}", messageRequest.getId());
|
||||||
|
throw new IllegalArgumentException("MessageRequest Email is required for ID: " + messageRequest.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createMailFile() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.eactive.ext.kjb.ums;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class EmailValidationResult {
|
||||||
|
private final boolean success;
|
||||||
|
private final Map<String, String> errors;
|
||||||
|
|
||||||
|
|
||||||
|
public EmailValidationResult(boolean success, Map<String, String> errors) {
|
||||||
|
this.success = success;
|
||||||
|
this.errors = errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EmailValidationResult ok() {
|
||||||
|
return new EmailValidationResult(true, Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EmailValidationResult failed(Map<String, String> errors) {
|
||||||
|
return new EmailValidationResult(false, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getErrors() {
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ValidationResult{" +
|
||||||
|
"success=" + success +
|
||||||
|
", errors=" + errors +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.eactive.ext.kjb.ums;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class UUIDBase62Util {
|
||||||
|
private static final String BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
|
public static String uuidStringToBase62(String uuidStr) {
|
||||||
|
UUID uuid = UUID.fromString(uuidStr);
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
|
||||||
|
bb.putLong(uuid.getMostSignificantBits());
|
||||||
|
bb.putLong(uuid.getLeastSignificantBits());
|
||||||
|
BigInteger bigInt = new BigInteger(1, bb.array());
|
||||||
|
return toBase62(bigInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String base62ToUuidString(String base62Str) {
|
||||||
|
BigInteger bigInt = fromBase62(base62Str);
|
||||||
|
byte[] bytes = toByteArray(bigInt, 16);
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(bytes);
|
||||||
|
long msb = bb.getLong();
|
||||||
|
long lsb = bb.getLong();
|
||||||
|
return new UUID(msb, lsb).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toBase62(BigInteger value) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
while (value.compareTo(BigInteger.ZERO) > 0) {
|
||||||
|
BigInteger[] divRem = value.divideAndRemainder(BigInteger.valueOf(62));
|
||||||
|
sb.append(BASE62.charAt(divRem[1].intValue()));
|
||||||
|
value = divRem[0];
|
||||||
|
}
|
||||||
|
// pad to 22 chars for consistent length
|
||||||
|
while (sb.length() < 22) sb.append('0');
|
||||||
|
return sb.reverse().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BigInteger fromBase62(String base62) {
|
||||||
|
BigInteger result = BigInteger.ZERO;
|
||||||
|
for (char c : base62.toCharArray()) {
|
||||||
|
int digit = BASE62.indexOf(c);
|
||||||
|
if (digit == -1) throw new IllegalArgumentException("Invalid Base62 character: " + c);
|
||||||
|
result = result.multiply(BigInteger.valueOf(62)).add(BigInteger.valueOf(digit));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] toByteArray(BigInteger bigInt, int size) {
|
||||||
|
byte[] src = bigInt.toByteArray();
|
||||||
|
byte[] dst = new byte[size];
|
||||||
|
int srcPos = Math.max(0, src.length - size);
|
||||||
|
int dstPos = Math.max(0, size - src.length);
|
||||||
|
int length = Math.min(size, src.length);
|
||||||
|
System.arraycopy(src, srcPos, dst, dstPos, length);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String uuid = UUID.randomUUID().toString();
|
||||||
|
String base62 = uuidStringToBase62(uuid);
|
||||||
|
String decoded = base62ToUuidString(base62);
|
||||||
|
|
||||||
|
System.out.println("UUID : " + uuid);
|
||||||
|
System.out.println("Base62: " + base62 + " (" + base62.length() + ")");
|
||||||
|
System.out.println("Decoded: " + decoded);
|
||||||
|
System.out.println("Same? " + uuid.equals(decoded));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.eactive.ext.kjb.ums.test;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.template.entity.MessageRequest;
|
||||||
|
import com.eactive.ext.kjb.ums.EmailProperty;
|
||||||
|
import com.eactive.ext.kjb.ums.EmailSendModule;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class KjbEmailJobSendModuleTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
EmailProperty prop = new EmailProperty();
|
||||||
|
prop.setEaiBatchUrl(null);
|
||||||
|
prop.setEaiBatchSendDir("D:\\kjb-logs\\sendmail\\snd");
|
||||||
|
prop.setEaiBatchBackupDir("D:\\kjb-logs\\sendmail\\bak");
|
||||||
|
|
||||||
|
EmailSendModule service = EmailSendModule.getInstance(prop, null);
|
||||||
|
MessageRequest messageRequest = new MessageRequest();
|
||||||
|
messageRequest.setId(UUID.randomUUID().toString());
|
||||||
|
messageRequest.setEmail("dearmai@dearmai.net");
|
||||||
|
messageRequest.setUsername("김광은");
|
||||||
|
messageRequest.setSubject("제목 - test");
|
||||||
|
messageRequest.setMessage("내용 - test");
|
||||||
|
|
||||||
|
|
||||||
|
CompletableFuture<Boolean> future = service.sendEmail(messageRequest, (result, ex) -> {
|
||||||
|
if (ex != null) {
|
||||||
|
log.error("failed", ex);
|
||||||
|
} else {
|
||||||
|
log.info("result: {}", result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
future.join();
|
||||||
|
|
||||||
|
log.info("done");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user