diff --git a/src/main/java/com/eactive/eai/custom/message/GUIDGeneratorDJB.java b/src/main/java/com/eactive/eai/custom/message/GUIDGeneratorDJB.java new file mode 100644 index 0000000..68a2a7d --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/message/GUIDGeneratorDJB.java @@ -0,0 +1,92 @@ +package com.eactive.eai.custom.message; + +import java.security.SecureRandom; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * DJBank(제주은행) 차세대 표준전문 GUID 생성기 + * + * GUID 구성 (40자): + * 일자(8) + 시간(6) + 밀리초(3) + 시스템코드(3) + 노드번호(2) + 세션ID(12) + Random(6) + * = 8 + 6 + 3 + 3 + 2 + 12 + 6 = 40자 + */ +public final class GUIDGeneratorDJB { + + public static final int GUID_LENGTH = 40; + + private static final DateTimeFormatter FMT_DATE = DateTimeFormatter.ofPattern("yyyyMMdd"); + private static final DateTimeFormatter FMT_TIME = DateTimeFormatter.ofPattern("HHmmss"); + private static final DateTimeFormatter FMT_MILLIS = DateTimeFormatter.ofPattern("SSS"); + + private static final AtomicInteger seq = new AtomicInteger(1); + private static final SecureRandom rnd = new SecureRandom(); + + private static final String NODE_NO; + private static final String SESSION_ID_BASE; + + static { + // 노드번호(2): 서버명 마지막 2자리, 없으면 "00" + String serverName = System.getProperty("server.key", ""); + if (serverName.length() >= 2) { + NODE_NO = serverName.substring(serverName.length() - 2); + } else { + NODE_NO = "00"; + } + + // 세션ID 기반(12): 호스트명 앞 12자 (부족 시 '_'로 우측패딩) + String hostname; + try { + hostname = java.net.InetAddress.getLocalHost().getHostName(); + } catch (Exception e) { + hostname = "____________"; + } + if (hostname.length() >= 12) { + SESSION_ID_BASE = hostname.substring(0, 12).toUpperCase(); + } else { + SESSION_ID_BASE = String.format("%-12s", hostname).toUpperCase().replace(' ', '_'); + } + } + + private GUIDGeneratorDJB() { + } + + /** + * @param systemCode 시스템구분코드 3자 (예: "EAI", "OPA") + */ + public static String getGUID(String systemCode) { + LocalDateTime now = LocalDateTime.now(); + + String date = now.format(FMT_DATE); // 8자 + String time = now.format(FMT_TIME); // 6자 + String millis = now.format(FMT_MILLIS); // 3자 + + // 시스템코드 3자 정규화 + String sysCode = normalizeCode(systemCode, 3); + + // 노드번호 2자 + String nodeNo = NODE_NO; + + // 세션ID 12자 + String sessionId = SESSION_ID_BASE; + + // Random 6자 (숫자) + int seqVal = seq.getAndUpdate(v -> v >= 999999 ? 1 : v + 1); + String random = String.format("%06d", seqVal); + + String guid = date + time + millis + sysCode + nodeNo + sessionId + random; + + if (guid.length() != GUID_LENGTH) { + throw new IllegalStateException("GUID length error: " + guid.length() + " [" + guid + "]"); + } + return guid; + } + + private static String normalizeCode(String code, int len) { + if (code == null) code = ""; + code = code.toUpperCase(); + if (code.length() >= len) return code.substring(0, len); + return String.format("%-" + len + "s", code).replace(' ', '_'); + } +} diff --git a/src/main/java/com/eactive/eai/custom/message/InterfaceMapperDJB.java b/src/main/java/com/eactive/eai/custom/message/InterfaceMapperDJB.java new file mode 100644 index 0000000..82bc95d --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/message/InterfaceMapperDJB.java @@ -0,0 +1,222 @@ +package com.eactive.eai.custom.message; + +import org.apache.commons.lang3.StringUtils; + +import com.eactive.eai.common.server.EAIServerManager; +import com.eactive.eai.message.StandardMessage; +import com.eactive.eai.message.service.DefaultInterfaceMapper; +import com.ext.eai.common.stdmessage.STDMessageKeys; + +/** + * DJBank(제주은행) 차세대 표준전문 InterfaceMapper + * + * HEAD 영역이 평면 구조로 변경됨에 따라 필드 접근 경로 및 값 변환 로직을 재정의. + * + * 주요 변경사항: + * - GUID: 40자 (기존 36자) + * - GUID 진행번호: HEAD.guid_prgs_no 별도 필드 (기존 GUID 문자열 내 포함) + * - 처리결과구분코드: S(성공)/F(오류) (기존 NR/ER) + * - 시스템환경구분코드: D/T/P (기존 O/S/D) + */ +@SuppressWarnings("serial") +public class InterfaceMapperDJB extends DefaultInterfaceMapper { + + private static final String PATH_MCI_NODE = "HEAD.mci_node_dvcd"; + private static final String PATH_EAI_NODE = "HEAD.eai_node_dvcd"; + private static final String PATH_MCI_SESN = "HEAD.mci_sesn_id"; + + // 처리결과구분코드 DJBank 값 + private static final String PROCS_RSLT_SUCCESS = "S"; + private static final String PROCS_RSLT_FAIL = "F"; + + @Override + public String getEaiSvcCode(StandardMessage standardMessage) { + String interfaceId = getInterfaceId(standardMessage); + String sendRecvDvcd = getSendRecvDivision(standardMessage); // S | R + String inExDivision = getInExDivision(standardMessage); // 1 | 2 + + if (interfaceId == null) interfaceId = ""; + interfaceId = interfaceId.trim(); + + return interfaceId + sendRecvDvcd + StringUtils.defaultString(inExDivision); + } + + // ---------------------------------------------------------------- + // GUID (40자 고정) + // ---------------------------------------------------------------- + + @Override + public void setGuid(StandardMessage standardMessage, String guid) { + if (guid == null) throw new RuntimeException("guid is null"); + if (guid.length() != GUIDGeneratorDJB.GUID_LENGTH) { + throw new RuntimeException("GUID length must be " + GUIDGeneratorDJB.GUID_LENGTH + ", actual=" + guid.length()); + } + setItemValue(standardMessage, getPath(GUID), guid); + } + + @Override + public String getGuid(StandardMessage standardMessage) { + return StringUtils.defaultString(getItemValue(standardMessage, getPath(GUID))); + } + + // ---------------------------------------------------------------- + // GUID 진행번호 — HEAD.guid_prgs_no 별도 필드 + // ---------------------------------------------------------------- + + @Override + public String getGuidSeq(StandardMessage standardMessage) { + String val = getItemValue(standardMessage, getPath(GUID_SEQ)); + if (StringUtils.isBlank(val)) return "001"; + try { + // NUMBER 타입은 내부적으로 선행 0 제거 → 조회 시 3자리로 복원 + return String.format("%03d", Integer.parseInt(val.trim())); + } catch (NumberFormatException e) { + return StringUtils.defaultString(val, "001"); + } + } + + @Override + public void setGuidSeq(StandardMessage standardMessage, String guidSeq) { + setItemValue(standardMessage, getPath(GUID_SEQ), guidSeq); + } + + @Override + public String nextGuidSeq(StandardMessage standardMessage) { + String current = getGuidSeq(standardMessage); + int seq = 1; + try { + seq = Integer.parseInt(current.trim()) + 1; + } catch (NumberFormatException e) { + seq = 2; + } + String next = String.format("%03d", seq); + setGuidSeq(standardMessage, next); + return next; + } + + // ---------------------------------------------------------------- + // 원거래 GUID — HEAD.ortr_guid (40자 그대로) + // ---------------------------------------------------------------- + + @Override + public String getOrgGuid(StandardMessage standardMessage) { + return StringUtils.defaultString(getItemValue(standardMessage, getPath(GUID_ORG))); + } + + @Override + public void setOrgGuid(StandardMessage standardMessage, String orgGuid) { + setItemValue(standardMessage, getPath(GUID_ORG), orgGuid); + } + + // ---------------------------------------------------------------- + // 처리결과구분코드: 엔진 내부(0=정상, 1=오류) ↔ DJBank(S/F) + // ---------------------------------------------------------------- + + @Override + public String getResponseType(StandardMessage standardMessage) { + String val = getItemValue(standardMessage, getPath(RESPONSE_TYPE)); + if (PROCS_RSLT_SUCCESS.equals(val)) return STDMessageKeys.RESPONSE_TYPE_CODE_N; + if (PROCS_RSLT_FAIL.equals(val)) return STDMessageKeys.RESPONSE_TYPE_CODE_E; + return val; + } + + @Override + public void setResponseType(StandardMessage standardMessage, String responseType) { + String siteVal; + if (STDMessageKeys.RESPONSE_TYPE_CODE_N.equals(responseType)) { + siteVal = PROCS_RSLT_SUCCESS; + } else if (STDMessageKeys.RESPONSE_TYPE_CODE_E.equals(responseType)) { + siteVal = PROCS_RSLT_FAIL; + } else { + siteVal = responseType; + } + setItemValue(standardMessage, getPath(RESPONSE_TYPE), siteVal); + } + + // ---------------------------------------------------------------- + // 원거래복원여부: Y/N 그대로 사용 (엔진 내부 1/0과 변환) + // ---------------------------------------------------------------- + + @Override + public String getRecoverYn(StandardMessage standardMessage) { + String val = getItemValue(standardMessage, getPath(RECOVER_YN)); + return "Y".equals(val) ? "1" : "0"; + } + + @Override + public void setRecoverYn(StandardMessage standardMessage, String recoverYn) { + setItemValue(standardMessage, getPath(RECOVER_YN), "1".equals(recoverYn) ? "Y" : "N"); + } + + // ---------------------------------------------------------------- + // 인터페이스ID (trim 처리) + // ---------------------------------------------------------------- + + @Override + public String getInterfaceId(StandardMessage standardMessage) { + String val = getItemValue(standardMessage, getPath(INTERFACE_ID)); + return val == null ? "" : val.trim(); + } + + // ---------------------------------------------------------------- + // 세션ID / 인스턴스코드 — MCI 여부에 따라 처리 + // ---------------------------------------------------------------- + + @Override + public String getSessionId(StandardMessage standardMessage) { + EAIServerManager server = EAIServerManager.getInstance(); + if (server != null && server.isMCI()) { + return StringUtils.defaultString(getItemValue(standardMessage, PATH_MCI_SESN)); + } + return ""; + } + + @Override + public void setSessionId(StandardMessage standardMessage, String sessionId) { + EAIServerManager server = EAIServerManager.getInstance(); + if (server != null && server.isMCI()) { + setItemValue(standardMessage, PATH_MCI_SESN, sessionId); + } + } + + @Override + public String getInstCode(StandardMessage standardMessage) { + EAIServerManager server = EAIServerManager.getInstance(); + if (server != null && server.isMCI()) { + return StringUtils.defaultString(getItemValue(standardMessage, PATH_MCI_NODE)); + } + return StringUtils.defaultString(getItemValue(standardMessage, PATH_EAI_NODE)); + } + + @Override + public void setInstCode(StandardMessage standardMessage, String instCode) { + EAIServerManager server = EAIServerManager.getInstance(); + if (server != null && server.isMCI()) { + setItemValue(standardMessage, PATH_MCI_NODE, instCode); + } else { + setItemValue(standardMessage, PATH_EAI_NODE, instCode); + } + } + + // ---------------------------------------------------------------- + // 요청시스템코드 — 채널유형코드(chnl_tycd)에서 추출 + // ---------------------------------------------------------------- + + @Override + public String getReqSysCode(StandardMessage standardMessage) { + String chnlTycd = getItemValue(standardMessage, getPath(CHANNEL_TYPE_CD)); + if (StringUtils.isNotBlank(chnlTycd)) { + return chnlTycd.trim(); + } + // fallback: tx_id(서비스ID) 경로에서 추출 + String txId = getItemValue(standardMessage, getPath(SERVICE_ID)); + if (txId != null) { + String[] split = txId.split("/"); + if (split.length > 1) { + String route = split[1].toUpperCase(); + return route.length() > 3 ? route.substring(0, 3) : route; + } + } + return null; + } +} diff --git a/src/main/java/com/eactive/eai/custom/message/StandardMessageCoordinatorDJB.java b/src/main/java/com/eactive/eai/custom/message/StandardMessageCoordinatorDJB.java new file mode 100644 index 0000000..f310c03 --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/message/StandardMessageCoordinatorDJB.java @@ -0,0 +1,257 @@ +package com.eactive.eai.custom.message; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.LinkedHashMap; +import java.util.Properties; + +import org.apache.commons.lang3.StringUtils; + +import com.eactive.eai.common.server.EAIServerManager; +import com.eactive.eai.common.util.Logger; +import com.eactive.eai.common.util.MessageUtil; +import com.eactive.eai.inbound.processor.ProcessVO; +import com.eactive.eai.message.StandardItem; +import com.eactive.eai.message.StandardMessage; +import com.eactive.eai.message.manager.StandardMessageManager; +import com.eactive.eai.message.service.DefaultStandardMessageCoordinator; +import com.eactive.eai.message.service.InterfaceMapper; +import com.ext.eai.common.stdmessage.STDMessageKeys; + +/** + * DJBank(제주은행) 차세대 표준전문 Coordinator + * + * 표준전문 구조 변경에 따른 각 처리 단계 후처리 정의: + * - HEAD 평면 구조, GUID 40자, guid_prgs_no 별도 필드 + * - MSG 영역: msg_dvcd(NM/EM) + MAIN_MSG + msg_list + * - 처리결과구분코드: S(성공) / F(오류) + * - 전문종료부: @JJ + */ +public class StandardMessageCoordinatorDJB extends DefaultStandardMessageCoordinator { + + private static final Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT); + + private static final DateTimeFormatter FMT_DATE = DateTimeFormatter.ofPattern("yyyyMMdd"); + private static final DateTimeFormatter FMT_TIME_MILLIS = DateTimeFormatter.ofPattern("HHmmssSSS"); + + // HEAD 필드 경로 + private static final String HEAD_FRST_MESG_DMAN_DT = "HEAD.frst_mesg_dman_dt"; + private static final String HEAD_FRST_MESG_DMAN_TIME = "HEAD.frst_mesg_dman_time"; + private static final String HEAD_FRST_TRNM_IPAD = "HEAD.frst_trnm_ipad"; + private static final String HEAD_FRST_TRNM_IPV4_ADDR = "HEAD.frst_trnm_ipv4_addr"; + private static final String HEAD_FRST_TRNM_MAC = "HEAD.frst_trnm_mac"; + private static final String HEAD_SYS_ENV_DVCD = "HEAD.sys_env_dvcd"; + private static final String HEAD_TRNM_SYS_DVCD = "HEAD.trnm_sys_dvcd"; + private static final String HEAD_MESG_RSPN_DT = "HEAD.mesg_rspn_dt"; + private static final String HEAD_MESG_RSPN_TIME = "HEAD.mesg_rspn_time"; + + // MSG 영역 경로 + private static final String MSG_DVCD = "MSG.msg_dvcd"; + private static final String MSG_SCOP_LEN = "MSG.msg_scop_len"; + private static final String MSG_MAIN_MSG = "MSG.MAIN_MSG"; + private static final String MSG_OUTP_ATRB_CD = "MSG.MAIN_MSG.outp_atrb_cd"; + private static final String MSG_OUTP_MSG_CD = "MSG.MAIN_MSG.outp_msg_cd"; + private static final String MSG_OUTP_MSG_CTNT = "MSG.MAIN_MSG.outp_msg_ctnt"; + private static final String MSG_LIST_ROWCNT = "MSG.MAIN_MSG.msg_list_rowcnt"; + private static final String MSG_LIST = "MSG.msg_list"; + + // DATA 영역 경로 + private static final String DATA_SCOP_LEN = "DATA.data_scop_len"; + + @Override + public void coordinateAfterParsing(StandardMessage standardMessage, Object paramObject, Properties prop) { + } + + @Override + public void coordinateAfterCreateMessageByRule(StandardMessage standardMessage, Object paramObject, Properties prop) { + StandardMessageManager manager = StandardMessageManager.getInstance(); + InterfaceMapper mapper = manager.getMapper(); + + // GUID 생성 (40자) 및 설정 + String guid = GUIDGeneratorDJB.getGUID("EAI"); + mapper.setGuid(standardMessage, guid); + + // GUID 진행번호 초기화 (최초=1) + mapper.setGuidSeq(standardMessage, "001"); + + // 원거래GUID = GUID 동일값 설정 (복원이 아닌 경우) + mapper.setOrgGuid(standardMessage, guid); + + // 최초전문요청일자/시각 설정 + LocalDateTime now = LocalDateTime.now(); + standardMessage.setData(HEAD_FRST_MESG_DMAN_DT, now.format(FMT_DATE)); + standardMessage.setData(HEAD_FRST_MESG_DMAN_TIME, now.format(FMT_TIME_MILLIS)); + + // 전송시스템구분코드 설정 + standardMessage.setData(HEAD_TRNM_SYS_DVCD, "EAI"); + + // 시스템환경구분코드 설정 (D/T/P) + EAIServerManager eaiServer = EAIServerManager.getInstance(); + standardMessage.setData(HEAD_SYS_ENV_DVCD, getSysEnvDvcd(eaiServer)); + + // 최초전송 IP / IPv4 / MAC 설정 + setNetworkInfo(standardMessage); + } + + @Override + public void coordinateBeforeStandardMessageSend(StandardMessage standardMessage, String msgType, String charset) { + makeupDataScopLen(standardMessage, charset); + } + + @Override + public void coordinateBeforeResponse(StandardMessage standardMessage, ProcessVO processVO, Properties prop, String charset) { + // 응답일자/시각 설정 + LocalDateTime now = LocalDateTime.now(); + standardMessage.setData(HEAD_MESG_RSPN_DT, now.format(FMT_DATE)); + standardMessage.setData(HEAD_MESG_RSPN_TIME, now.format(FMT_TIME_MILLIS)); + + makeupDataScopLen(standardMessage, charset); + } + + @Override + public boolean coordinateSetStandardMessageError(StandardMessage standardMessage, InterfaceMapper mapper, + String errCode, String errMsg) { + + // 처리결과 = 오류(F) + mapper.setResponseType(standardMessage, STDMessageKeys.RESPONSE_TYPE_CODE_E); + // 요청응답구분 = 응답(R) + mapper.setSendRecvDivision(standardMessage, STDMessageKeys.SEND_RECV_CD_RECV); + + // 에러코드/메시지 변환 + String siteErrCode = StringUtils.defaultString(MessageUtil.getTobeCode(errCode)); + String siteErrMsg = StringUtils.defaultString(MessageUtil.getTobeMessage(errCode)); + + // MSG 영역 활성화 + StandardItem msgItem = standardMessage.findItem("MSG"); + if (msgItem != null) { + msgItem.setHidden(false); + msgItem.setSize(1); + } + + // msg_dvcd = EM (에러메시지) + standardMessage.setData(MSG_DVCD, "EM"); + + // 출력속성코드 = 1(팝업) + standardMessage.setData(MSG_OUTP_ATRB_CD, "1"); + + // 대표 에러코드/메시지 + standardMessage.setData(MSG_OUTP_MSG_CD, siteErrCode); + standardMessage.setData(MSG_OUTP_MSG_CTNT, siteErrMsg); + + // msg_list 1건 구성 + standardMessage.setData(MSG_LIST_ROWCNT, "00001"); + StandardItem msgList = standardMessage.findItem(MSG_LIST); + if (msgList != null) { + msgList.setSize(1); + LinkedHashMap row = msgList.getArrayChilds(0, true); + if (row != null) { + if (row.containsKey("outp_msg_cd")) row.get("outp_msg_cd").setValue(siteErrCode); + if (row.containsKey("outp_msg_ctnt")) row.get("outp_msg_ctnt").setValue(siteErrMsg); + } + } + + // mapper 에러코드 설정 + mapper.setErrorCode(standardMessage, siteErrCode); + mapper.setErrorMsg(standardMessage, siteErrMsg); + + // msg_scop_len 계산 + makeMsgScopLen(standardMessage); + + // BIZ_DATA는 null 처리 (false 반환) + return false; + } + + @Override + public void coordinateAfterRecvNonStdSyncResponse(StandardMessage responseMessage) { + // 처리결과 = 성공(S) + StandardMessageManager manager = StandardMessageManager.getInstance(); + InterfaceMapper mapper = manager.getMapper(); + mapper.setResponseType(responseMessage, STDMessageKeys.RESPONSE_TYPE_CODE_N); + + // MSG 영역 활성화 + StandardItem msgItem = responseMessage.findItem("MSG"); + if (msgItem != null) { + msgItem.setHidden(false); + msgItem.setSize(1); + } + + // msg_dvcd = NM (정상메시지) + responseMessage.setData(MSG_DVCD, "NM"); + responseMessage.setData(MSG_OUTP_ATRB_CD, "0"); + responseMessage.setData(MSG_OUTP_MSG_CD, "000000000000"); + responseMessage.setData(MSG_OUTP_MSG_CTNT, "정상처리"); + responseMessage.setData(MSG_LIST_ROWCNT, "00001"); + + StandardItem msgList = responseMessage.findItem(MSG_LIST); + if (msgList != null) { + msgList.setSize(1); + LinkedHashMap row = msgList.getArrayChilds(0, true); + if (row != null) { + if (row.containsKey("outp_msg_cd")) row.get("outp_msg_cd").setValue("000000000000"); + if (row.containsKey("outp_msg_ctnt")) row.get("outp_msg_ctnt").setValue("정상처리"); + } + } + + makeMsgScopLen(responseMessage); + } + + // ---------------------------------------------------------------- + // private helpers + // ---------------------------------------------------------------- + + private String getSysEnvDvcd(EAIServerManager server) { + if (server.isPEAIServer()) return "P"; // 운영 + if (server.isSEAIServer()) return "T"; // 검증/테스트 + return "D"; // 개발 + } + + private void setNetworkInfo(StandardMessage standardMessage) { + try { + InetAddress localAddr = InetAddress.getLocalHost(); + String ipv4 = localAddr.getHostAddress(); + + // IPv4 주소 설정 + standardMessage.setData(HEAD_FRST_TRNM_IPAD, ipv4); + standardMessage.setData(HEAD_FRST_TRNM_IPV4_ADDR, ipv4); + + // MAC 주소 설정 + NetworkInterface ni = NetworkInterface.getByInetAddress(localAddr); + if (ni != null) { + byte[] mac = ni.getHardwareAddress(); + if (mac != null) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < mac.length; i++) { + if (i > 0) sb.append(":"); + sb.append(String.format("%02X", mac[i])); + } + standardMessage.setData(HEAD_FRST_TRNM_MAC, sb.toString()); + } + } + } catch (Exception e) { + if (logger.isWarn()) logger.warn("Failed to set network info", e); + } + } + + private void makeupDataScopLen(StandardMessage standardMessage, String charset) { + try { + int dataLen = standardMessage.getBizDataBytes(charset).length; + String lenStr = StringUtils.leftPad(String.valueOf(dataLen), 8, '0'); + standardMessage.setData(DATA_SCOP_LEN, lenStr); + } catch (Exception e) { + throw new RuntimeException("Failed to calculate data_scop_len", e); + } + } + + private void makeMsgScopLen(StandardMessage standardMessage) { + try { + StandardItem mainMsg = standardMessage.findItem(MSG_MAIN_MSG); + if (mainMsg == null) return; + int len = standardMessage.getBytesDataLengthForPath(MSG_MAIN_MSG); + standardMessage.setData(MSG_SCOP_LEN, StringUtils.leftPad(String.valueOf(len), 8, '0')); + } catch (Exception e) { + if (logger.isWarn()) logger.warn("Failed to calculate msg_scop_len", e); + } + } +} diff --git a/src/main/resources/standard-layout-djb.csv b/src/main/resources/standard-layout-djb.csv new file mode 100644 index 0000000..e76d376 --- /dev/null +++ b/src/main/resources/standard-layout-djb.csv @@ -0,0 +1,179 @@ +#----------------------------------------------------------------------------- +# comment start with # +# level; // 0 : root ~ n +# type; // 0: Message, 1: Field, 2:Group 3, Grid, 4 : Field Array, 9 : BIZ DATA +# size // 0 : variable, > 0 fixed +# fieldType; // 0: ELEMENT, 1: ATTRIBUTE, +# length; +# dataType; // 1: String, 2: Number, 10: ZZ String, 11: LL Number(LL prefix auto), 12: LL_NUMBER +#----------------------------------------------------------------------------- +# name ,level ,type ,size ,fieldType ,length ,dataType ,refPath, refValue, value +#----------------------------------------------------------------------------- +# ============================================================ +# HEAD - 헤더영역 (시스템헤더부 + 공통부) +# ============================================================ +HEAD,1,3,1,1,0,1,,, +stnd_mesg_len,2,2,1,1,8,12,,, +nxgn_stnd_idfr,2,2,1,1,4,1,,,JERA +guid,2,2,1,1,40,1,,, +guid_prgs_no,2,2,1,1,3,2,,,001 +stnd_mesg_ver,2,2,1,1,3,1,,,R10 +ortr_guid,2,2,1,1,40,1,,, +sect_ecrpt_yn,2,2,1,1,1,1,,,N +frst_trnm_ipad,2,2,1,1,40,1,,, +frst_trnm_ipv4_addr,2,2,1,1,15,1,,, +frst_trnm_mac,2,2,1,1,17,1,,, +frst_mesg_dman_dt,2,2,1,1,8,1,,, +frst_mesg_dman_time,2,2,1,1,9,1,,, +frst_trnm_sys_dvcd,2,2,1,1,3,1,,,OPA +trnm_sys_dvcd,2,2,1,1,3,1,,,EAI +mesg_rspn_dt,2,2,1,1,8,1,,, +mesg_rspn_time,2,2,1,1,9,1,,, +dman_rspn_dvcd,2,2,1,1,1,1,,,S +otsd_tmot_occu_rspn_yn,2,2,1,1,1,1,,, +sys_env_dvcd,2,2,1,1,1,1,,,D +tx_tycd,2,2,1,1,1,1,,,O +synz_dvcd,2,2,1,1,1,1,,,S +tx_id,2,2,1,1,12,1,,, +procs_rslt_dvcd,2,2,1,1,1,1,,,S +procs_rslt_romsg_tx_id,2,2,1,1,12,1,,, +chnl_tycd,2,2,1,1,3,1,,,EAI +chnl_dtls_clcd,2,2,1,1,3,1,,, +xtis_cd,2,2,1,1,3,1,,, +osdch_dvcd,2,2,1,1,3,1,,, +lnkd_seqno,2,2,1,1,3,2,,, +if_id,2,2,1,1,30,1,,, +hmab_dvcd,2,2,1,1,1,1,,, +mci_sesn_id,2,2,1,1,8,1,,, +mci_node_dvcd,2,2,1,1,2,1,,, +eai_node_dvcd,2,2,1,1,2,1,,, +fep_node_dvcd,2,2,1,1,2,1,,, +fep_sesn_id,2,2,1,1,8,1,,, +ortr_restr_yn,2,2,1,1,1,1,,, +filler,2,2,1,1,40,1,,, +input_mesg_tycd,2,2,1,1,1,1,,, +outp_mesg_tycd,2,2,1,1,1,1,,, +mesg_cnty_seqno,2,2,1,1,3,2,,, +msg_trnm_tycd,2,2,1,1,1,1,,, +msg_trnm_brcd,2,2,1,1,3,1,,, +msg_trnm_tlrno,2,2,1,1,6,1,,, +tx_cmgr_cd,2,2,1,1,4,1,,, +mgmt_brcd,2,2,1,1,3,1,,, +tx_brcd,2,2,1,1,3,1,,, +insl_brcd,2,2,1,1,3,1,,, +blng_brcd,2,2,1,1,3,1,,, +corp_tlwn_virt_brcd,2,2,1,1,3,1,,, +tlrno,2,2,1,1,6,1,,, +telr_jcls_cd,2,2,1,1,2,1,,, +clsn_bfaf_dvcd,2,2,1,1,1,1,,, +tlwn_dvcd,2,2,1,1,1,1,,, +scfc_tmnl_dvcd,2,2,1,1,2,1,,, +telr_cnfr_trgt_yn,2,2,1,1,1,1,,, +telr_cnfr_yn,2,2,1,1,1,1,,, +prcg_aprv_typ_dvcd,2,2,1,1,1,1,,, +sq1_aprv_media_dvcd,2,2,1,1,1,1,,, +sq1_aprv_prcg_empno,2,2,1,1,6,1,,, +sq1_aprv_prcg_athn_no,2,2,1,1,32,1,,, +sq2_aprv_media_dvcd,2,2,1,1,1,1,,, +sq2_aprv_prcg_empno,2,2,1,1,6,1,,, +sq2_aprv_prcg_athn_no,2,2,1,1,32,1,,, +main_scn_id,2,2,1,1,12,1,,, +tx_scn_id,2,2,1,1,12,1,,, +pb_seqno,2,2,1,1,5,2,,, +pb_ms_read_yn,2,2,1,1,1,1,,, +pb_print_conn_yn,2,2,1,1,1,1,,, +dgtl_tlwn_dvcd,2,2,1,1,1,1,,, +idc_scan_rnno,2,2,1,1,32,1,,, +idc_scan_no,2,2,1,1,25,1,,, +ctsed_dt,2,2,1,1,8,1,,, +reckn_dt,2,2,1,1,8,1,,, +slip_no,2,2,1,1,5,2,,, +gnrz_tmot_tktm,2,2,1,1,3,2,,,35 +tx_tmot_tktm,2,2,1,1,3,2,,,30 +idvd_info_iqry_prps_dvcd,2,2,1,1,3,1,,, +idvd_info_iqry_rsn,2,2,1,1,100,1,,, +rprs_msg_cd,2,2,1,1,12,1,,, +# ============================================================ +# EZDATA - 전자금융공통영역 (corp_tlwn_virt_brcd=ERP 조건부) +# ============================================================ +EZDATA,1,3,1,1,0,1,HEAD.corp_tlwn_virt_brcd,ERP, +login_mhod_dvcd,2,2,1,1,1,1,,, +elfn_bnkn_dvcd,2,2,1,1,2,1,,,01 +usr_no,2,2,1,1,16,1,,, +rgsr_id,2,2,1,1,16,1,,, +svr_tx_seqno,2,2,1,1,10,2,,, +tx_seqno,2,2,1,1,10,2,,, +priqr_tx_seqno,2,2,1,1,10,2,,, +secu_media_dvcd,2,2,1,1,2,1,,,00 +secu_media_sta_dvcd,2,2,1,1,2,1,,,00 +brll_srcd_yn,2,2,1,1,1,1,,,N +sq1_srcd_indc_no,2,2,1,1,2,2,,, +sq1_srcd_pswd,2,2,1,1,32,1,,, +sq2_srcd_indc_no,2,2,1,1,2,2,,, +sq2_srcd_pswd,2,2,1,1,32,1,,, +otp_athn_val,2,2,1,1,6,1,,, +secu_media_seqno_dman_val,2,2,1,1,4,1,,, +secu_media_seqno_athn_val,2,2,1,1,4,1,,, +next_data_exis_yn,2,2,1,1,1,1,,, +page_no,2,2,1,1,5,2,,, +page_size,2,2,1,1,5,2,,, +ttcnt,2,2,1,1,5,2,,, +iqry_ccnt,2,2,1,1,5,2,,, +next_data_info,2,2,1,1,100,1,,, +cstno,2,2,1,1,10,2,,, +otp_expr_gdnc_yn,2,2,1,1,1,1,,,N +dup_tx_yn,2,2,1,1,1,1,,,N +cpu_id_no,2,2,1,1,64,1,,, +elfn_mac,2,2,1,1,17,1,,, +hdd_srial_no,2,2,1,1,64,1,,, +main_board_srial_no,2,2,1,1,64,1,,, +pc_ofcl_ipad,2,2,1,1,40,1,,, +pc_prvat_ipad,2,2,1,1,40,1,,, +smph_old_unq_no,2,2,1,1,32,1,,, +smph_nw_unq_no,2,2,1,1,32,1,,, +smph_mac,2,2,1,1,12,1,,, +smph_modl_no,2,2,1,1,20,1,,, +ostp_cd,2,2,1,1,1,1,,, +mcco_dvcd,2,2,1,1,1,1,,, +elfn_use_tlno,2,2,1,1,12,1,,, +addt_athn_yn,2,2,1,1,1,1,,, +addt_athn_meth_dvcd,2,2,1,1,1,1,,, +abr_ip_yn,2,2,1,1,1,1,,,N +login_dt,2,2,1,1,8,1,,, +login_time,2,2,1,1,6,1,,, +cst_nm,2,2,1,1,100,1,,, +aprv_aprvl_rol_dvcd,2,2,1,1,1,1,,, +elfn_spr_fild,2,2,1,1,698,1,,, +# ============================================================ +# MSG - 메시지영역 (dman_rspn_dvcd != S 조건부, 응답 시) +# ============================================================ +MSG,1,3,0,1,0,1,HEAD.dman_rspn_dvcd,!S, +msg_dvcd,2,2,0,1,2,1,,,NM +msg_scop_len,2,2,0,1,8,2,,, +MAIN_MSG,2,3,0,1,0,1,,, +outp_atrb_cd,3,2,1,1,1,1,,,0 +outp_msg_cd,3,2,1,1,12,1,,, +outp_msg_ctnt,3,2,1,1,200,1,,, +outp_msg_desc,3,2,1,1,300,1,,, +mngm_msg_cd,3,2,1,1,12,1,,, +msg_list_rowcnt,3,2,0,1,5,2,,,00000 +msg_list,2,4,0,1,0,1,MSG.MAIN_MSG.msg_list_rowcnt,, +outp_msg_cd,3,2,0,1,12,1,,, +outp_msg_ctnt,3,2,0,1,200,1,,, +outp_msg_desc,3,2,0,1,300,1,,, +mngm_msg_cd,3,2,0,1,12,1,,, +err_occu_item_nm,3,2,0,1,50,1,,, +err_occu_loct,3,2,0,1,300,1,,, +err_chgr_nm,3,2,0,1,10,1,,, +err_chrg_cnpl_no,3,2,0,1,20,1,,, +# ============================================================ +# DATA - 데이터부 +# ============================================================ +DATA,1,3,1,1,0,1,,, +data_dvcd,2,2,1,1,2,1,,,IO +data_scop_len,2,2,1,1,8,2,,, +BIZ_DATA,2,9,1,1,0,1,,, +# ============================================================ +# 전문종료부 (@JJ, 3자 고정) +# ============================================================ +MESG_END_IDFR_VAL,1,2,1,1,3,10,,,@JJ diff --git a/src/main/resources/standard-message-config.properties b/src/main/resources/standard-message-config.properties index 9704d20..793a208 100644 --- a/src/main/resources/standard-message-config.properties +++ b/src/main/resources/standard-message-config.properties @@ -1,18 +1,23 @@ # layout : StandardMessage layout definition #layout.file.type=CSV -layout.file.type=DB +#layout.file.type=DB +layout.file.type=CSV #layout.file.path=standard-layout-kjb.csv #layout.file.path=standard-layout-elk.csv +layout.file.path=standard-layout-djb.csv #layout.filter.FLAT=com.eactive.eai.custom.message.FlatMessageFilterSBI layout.filter.FLAT=com.eactive.eai.message.filter.FlatMessageFilter # mapper : StandardMessage's fields getter/setter interface #mapper.class=com.eactive.eai.custom.message.InterfaceMapperSBI -mapper.class=com.eactive.eai.custom.message.InterfaceMapperKJB +#mapper.class=com.eactive.eai.custom.message.InterfaceMapperKJB +mapper.class=com.eactive.eai.custom.message.InterfaceMapperDJB #mapper.definition=standard-message-mapping-config-sbi.properties #mapper.definition=standard-message-mapping-config-elk.properties -mapper.definition=standard-message-mapping-config-kjb.properties +#mapper.definition=standard-message-mapping-config-kjb.properties +mapper.definition=standard-message-mapping-config-djb.properties # StandardMessage Coordinator implementation -message.coordinator.class=com.eactive.eai.custom.message.StandardMessageCoordinatorKJB +#message.coordinator.class=com.eactive.eai.custom.message.StandardMessageCoordinatorKJB +message.coordinator.class=com.eactive.eai.custom.message.StandardMessageCoordinatorDJB # RequestProcessor implementation #requestProcessor.class=com.eactive.eai.inbound.processor.RequestProcessor # reader : parsing input data to StandardMessage diff --git a/src/main/resources/standard-message-mapping-config-djb.properties b/src/main/resources/standard-message-mapping-config-djb.properties new file mode 100644 index 0000000..0538faa --- /dev/null +++ b/src/main/resources/standard-message-mapping-config-djb.properties @@ -0,0 +1,30 @@ +# DJBank(제주은행) 표준전문 필드 매핑 설정 +# InterfaceMapper 논리키 = 표준전문 필드 경로 +# HEAD 영역 (평면 구조) +GUID=HEAD.guid +GUID_SEQ=HEAD.guid_prgs_no +GUID_ORG=HEAD.ortr_guid +SERVICE_ID=HEAD.tx_id +INTERFACE_ID=HEAD.if_id +SEND_RECV_DIVISION=HEAD.dman_rspn_dvcd +IN_EX_DIVISION=HEAD.hmab_dvcd +OPERATION_ENV=HEAD.sys_env_dvcd +FIRST_REQ_IP=HEAD.frst_trnm_ipad +RECOVER_YN=HEAD.ortr_restr_yn +SESSION_ID=HEAD.mci_sesn_id +SYNC_ASYNC_TYPE=HEAD.synz_dvcd +CHANNEL_TYPE_CD=HEAD.chnl_tycd +CHANNEL_DETAIL_CD=HEAD.chnl_dtls_clcd +SEND_TIME=HEAD.frst_mesg_dman_time +RECV_TIME=HEAD.mesg_rspn_time +SYSTEM_TYPE=HEAD.trnm_sys_dvcd +TIMEOUT=HEAD.gnrz_tmot_tktm +INST_CODE=HEAD.eai_node_dvcd +USER_ID=HEAD.tlrno +REQ_SYS_CODE=HEAD.chnl_tycd +# MSG 영역 (응답 시 활성화) +ERROR_CODE=MSG.MAIN_MSG.outp_msg_cd +ERROR_MSG=MSG.MAIN_MSG.outp_msg_ctnt +RESPONSE_TYPE=HEAD.procs_rslt_dvcd +RETURN_VALUE= +RETURN_SERVICE_ID= diff --git a/src/test/java/com/eactive/eai/custom/message/GUIDGeneratorDJBTest.java b/src/test/java/com/eactive/eai/custom/message/GUIDGeneratorDJBTest.java new file mode 100644 index 0000000..84d24c4 --- /dev/null +++ b/src/test/java/com/eactive/eai/custom/message/GUIDGeneratorDJBTest.java @@ -0,0 +1,82 @@ +package com.eactive.eai.custom.message; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +class GUIDGeneratorDJBTest { + + @Test + void guid_길이는_40자여야_한다() { + String guid = GUIDGeneratorDJB.getGUID("EAI"); + assertEquals(40, guid.length(), "GUID 길이는 40자: " + guid); + } + + @Test + void guid_시스템코드_3자로_정규화된다() { + // 짧은 코드 + String guid1 = GUIDGeneratorDJB.getGUID("EA"); + assertEquals(40, guid1.length()); + + // 긴 코드 (잘림) + String guid2 = GUIDGeneratorDJB.getGUID("EAIFEP"); + assertEquals(40, guid2.length()); + + // null + String guid3 = GUIDGeneratorDJB.getGUID(null); + assertEquals(40, guid3.length()); + } + + @Test + void guid_구성_형식_검증() { + String guid = GUIDGeneratorDJB.getGUID("EAI"); + // 일자(8) + 시간(6) + 밀리초(3) + 시스템코드(3) + 노드번호(2) + 세션ID(12) + Random(6) + String date = guid.substring(0, 8); + String time = guid.substring(8, 14); + String millis = guid.substring(14, 17); + String sysCode = guid.substring(17, 20); + String nodeNo = guid.substring(20, 22); + String sessId = guid.substring(22, 34); + String random = guid.substring(34, 40); + + // 일자: 숫자 8자리 + assertDoesNotThrow(() -> Long.parseLong(date), "일자 부분이 숫자가 아님: " + date); + assertTrue(date.matches("\\d{8}"), "일자 형식 오류: " + date); + + // 시간: 숫자 6자리 + assertTrue(time.matches("\\d{6}"), "시간 형식 오류: " + time); + + // 밀리초: 숫자 3자리 + assertTrue(millis.matches("\\d{3}"), "밀리초 형식 오류: " + millis); + + // 시스템코드: 3자리 영문/숫자 + assertEquals(3, sysCode.length(), "시스템코드 길이 오류: " + sysCode); + + // 노드번호: 2자리 + assertEquals(2, nodeNo.length(), "노드번호 길이 오류: " + nodeNo); + + // 세션ID: 12자리 + assertEquals(12, sessId.length(), "세션ID 길이 오류: " + sessId); + + // Random: 숫자 6자리 + assertTrue(random.matches("\\d{6}"), "Random 형식 오류: " + random); + } + + @Test + void guid_연속_생성시_유니크해야_한다() { + Set guids = new HashSet<>(); + int count = 1000; + for (int i = 0; i < count; i++) { + guids.add(GUIDGeneratorDJB.getGUID("EAI")); + } + assertEquals(count, guids.size(), "GUID 중복 발생"); + } + + @Test + void guid_상수_길이값_확인() { + assertEquals(40, GUIDGeneratorDJB.GUID_LENGTH); + } +} diff --git a/src/test/java/com/eactive/eai/custom/message/InterfaceMapperDJBTest.java b/src/test/java/com/eactive/eai/custom/message/InterfaceMapperDJBTest.java new file mode 100644 index 0000000..28b939a --- /dev/null +++ b/src/test/java/com/eactive/eai/custom/message/InterfaceMapperDJBTest.java @@ -0,0 +1,218 @@ +package com.eactive.eai.custom.message; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.HashMap; +import java.util.Properties; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.eactive.eai.message.StandardMessage; +import com.eactive.eai.message.StandardMessageUtil; +import com.ext.eai.common.stdmessage.STDMessageKeys; + +class InterfaceMapperDJBTest { + + private InterfaceMapperDJB mapper; + private StandardMessage message; + + @BeforeEach + void setUp() throws Exception { + // CSV에서 표준전문 레이아웃 로드 + message = StandardMessageUtil.generateMessageFromCsvFile( + "src/main/resources/standard-layout-djb.csv"); + assertNotNull(message, "standard-layout-djb.csv 로드 실패"); + message.setReadPosition(0); + + // 매핑 설정 로드 + Properties props = new Properties(); + try (java.io.InputStream in = getClass().getClassLoader() + .getResourceAsStream("standard-message-mapping-config-djb.properties")) { + assertNotNull(in, "standard-message-mapping-config-djb.properties 로드 실패"); + props.load(in); + } + + HashMap pathMap = new HashMap<>(); + for (String key : props.stringPropertyNames()) { + pathMap.put(key.trim(), props.getProperty(key).trim()); + } + + mapper = new InterfaceMapperDJB(); + mapper.initPathMap(pathMap); + } + + // ---------------------------------------------------------------- + // GUID + // ---------------------------------------------------------------- + + @Test + void setGuid_40자_정상_설정() { + String guid = GUIDGeneratorDJB.getGUID("EAI"); + mapper.setGuid(message, guid); + assertEquals(guid, mapper.getGuid(message)); + } + + @Test + void setGuid_40자_아닌경우_예외발생() { + assertThrows(RuntimeException.class, () -> mapper.setGuid(message, "SHORT")); + } + + @Test + void setGuid_null이면_예외발생() { + assertThrows(RuntimeException.class, () -> mapper.setGuid(message, null)); + } + + // ---------------------------------------------------------------- + // GUID 진행번호 (guid_prgs_no - 별도 필드) + // ---------------------------------------------------------------- + + @Test + void guidSeq_초기값_및_nextGuidSeq_증가() { + // 초기값 설정 + mapper.setGuidSeq(message, "001"); + assertEquals("001", mapper.getGuidSeq(message)); + + // +1 증가 + String next = mapper.nextGuidSeq(message); + assertEquals("002", next); + assertEquals("002", mapper.getGuidSeq(message)); + } + + @Test + void nextGuidSeq_3자리_zero_padding() { + mapper.setGuidSeq(message, "009"); + assertEquals("010", mapper.nextGuidSeq(message)); + + mapper.setGuidSeq(message, "099"); + assertEquals("100", mapper.nextGuidSeq(message)); + } + + // ---------------------------------------------------------------- + // 원거래 GUID (ortr_guid) + // ---------------------------------------------------------------- + + @Test + void orgGuid_설정_조회() { + String guid = GUIDGeneratorDJB.getGUID("EAI"); + mapper.setOrgGuid(message, guid); + assertEquals(guid, mapper.getOrgGuid(message)); + } + + // ---------------------------------------------------------------- + // 처리결과구분코드 (S/F ↔ 엔진 내부 NR/ER) + // ---------------------------------------------------------------- + + @Test + void setResponseType_정상_NR_to_S() { + mapper.setResponseType(message, STDMessageKeys.RESPONSE_TYPE_CODE_N); // "NR" + assertEquals("S", message.findItemValue("HEAD.procs_rslt_dvcd")); + } + + @Test + void setResponseType_오류_ER_to_F() { + mapper.setResponseType(message, STDMessageKeys.RESPONSE_TYPE_CODE_E); // "ER" + assertEquals("F", message.findItemValue("HEAD.procs_rslt_dvcd")); + } + + @Test + void getResponseType_S_to_NR() { + message.setData("HEAD.procs_rslt_dvcd", "S"); + assertEquals(STDMessageKeys.RESPONSE_TYPE_CODE_N, mapper.getResponseType(message)); + } + + @Test + void getResponseType_F_to_ER() { + message.setData("HEAD.procs_rslt_dvcd", "F"); + assertEquals(STDMessageKeys.RESPONSE_TYPE_CODE_E, mapper.getResponseType(message)); + } + + // ---------------------------------------------------------------- + // 원거래복원여부 (Y/N ↔ 엔진 내부 1/0) + // ---------------------------------------------------------------- + + @Test + void recoverYn_Y면_1반환() { + message.setData("HEAD.ortr_restr_yn", "Y"); + assertEquals("1", mapper.getRecoverYn(message)); + } + + @Test + void recoverYn_N면_0반환() { + message.setData("HEAD.ortr_restr_yn", "N"); + assertEquals("0", mapper.getRecoverYn(message)); + } + + @Test + void setRecoverYn_1이면_Y설정() { + mapper.setRecoverYn(message, "1"); + assertEquals("Y", message.findItemValue("HEAD.ortr_restr_yn")); + } + + @Test + void setRecoverYn_0이면_N설정() { + mapper.setRecoverYn(message, "0"); + assertEquals("N", message.findItemValue("HEAD.ortr_restr_yn")); + } + + // ---------------------------------------------------------------- + // 인터페이스ID (trim) + // ---------------------------------------------------------------- + + @Test + void getInterfaceId_공백_trim() { + message.setData("HEAD.if_id", " IF001 "); + assertEquals("IF001", mapper.getInterfaceId(message)); + } + + @Test + void getInterfaceId_null이면_빈문자열() { + // 초기 공백상태 + String ifId = mapper.getInterfaceId(message); + assertNotNull(ifId); + assertEquals("", ifId.trim()); + } + + // ---------------------------------------------------------------- + // EAI 서비스코드 조합 + // ---------------------------------------------------------------- + + @Test + void getEaiSvcCode_인터페이스ID_요청응답_내외구분_조합() { + message.setData("HEAD.if_id", "IF_TEST_001"); + message.setData("HEAD.dman_rspn_dvcd", "S"); + message.setData("HEAD.hmab_dvcd", "1"); + + String svcCode = mapper.getEaiSvcCode(message); + assertTrue(svcCode.startsWith("IF_TEST_001"), "EAI 서비스코드: " + svcCode); + assertTrue(svcCode.contains("S"), "요청응답구분 포함: " + svcCode); + } + + // ---------------------------------------------------------------- + // 필드 경로 기본 동작 확인 (매핑 설정 유효성) + // ---------------------------------------------------------------- + + @Test + void sendRecvDivision_설정_조회() { + mapper.setSendRecvDivision(message, "R"); + assertEquals("R", mapper.getSendRecvDivision(message)); + } + + @Test + void inExDivision_설정_조회() { + mapper.setInExDivision(message, "1"); + assertEquals("1", mapper.getInExDivision(message)); + } + + @Test + void errorCode_설정_조회() { + mapper.setErrorCode(message, "ERR001"); + assertEquals("ERR001", mapper.getErrorCode(message)); + } + + @Test + void errorMsg_설정_조회() { + mapper.setErrorMsg(message, "오류가 발생했습니다."); + assertEquals("오류가 발생했습니다.", mapper.getErrorMsg(message)); + } +} diff --git a/src/test/java/com/eactive/eai/custom/message/StandardLayoutDJBTest.java b/src/test/java/com/eactive/eai/custom/message/StandardLayoutDJBTest.java new file mode 100644 index 0000000..f0cf81f --- /dev/null +++ b/src/test/java/com/eactive/eai/custom/message/StandardLayoutDJBTest.java @@ -0,0 +1,163 @@ +package com.eactive.eai.custom.message; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.eactive.eai.message.StandardItem; +import com.eactive.eai.message.StandardMessage; +import com.eactive.eai.message.StandardMessageUtil; + +/** + * standard-layout-djb.csv 로드 및 전문 구조 검증 + */ +class StandardLayoutDJBTest { + + private StandardMessage message; + + @BeforeEach + void setUp() { + message = StandardMessageUtil.generateMessageFromCsvFile( + "src/main/resources/standard-layout-djb.csv"); + assertNotNull(message, "standard-layout-djb.csv 로드 실패"); + message.setReadPosition(0); + } + + // ---------------------------------------------------------------- + // 최상위 블록 존재 여부 + // ---------------------------------------------------------------- + + @Test + void HEAD_블록_존재() { + assertNotNull(message.findItem("HEAD"), "HEAD 블록 없음"); + } + + @Test + void EZDATA_블록_존재() { + assertNotNull(message.findItem("EZDATA"), "EZDATA 블록 없음"); + } + + @Test + void MSG_블록_존재() { + assertNotNull(message.findItem("MSG"), "MSG 블록 없음"); + } + + @Test + void DATA_블록_존재() { + assertNotNull(message.findItem("DATA"), "DATA 블록 없음"); + } + + @Test + void 전문종료부_존재() { + assertNotNull(message.findItem("MESG_END_IDFR_VAL"), "전문종료부 없음"); + } + + // ---------------------------------------------------------------- + // HEAD 핵심 필드 검증 + // ---------------------------------------------------------------- + + @Test + void HEAD_guid_필드_존재_및_길이40() { + StandardItem guidItem = message.findItem("HEAD.guid"); + assertNotNull(guidItem, "HEAD.guid 필드 없음"); + assertEquals(40, guidItem.getLength(), "HEAD.guid 길이는 40자여야 함"); + } + + @Test + void HEAD_guid_prgs_no_필드_존재() { + StandardItem item = message.findItem("HEAD.guid_prgs_no"); + assertNotNull(item, "HEAD.guid_prgs_no 필드 없음"); + assertEquals(3, item.getLength()); + } + + @Test + void HEAD_nxgn_stnd_idfr_기본값_JERA() { + StandardItem item = message.findItem("HEAD.nxgn_stnd_idfr"); + assertNotNull(item, "HEAD.nxgn_stnd_idfr 필드 없음"); + assertEquals(4, item.getLength()); + assertEquals("JERA", item.getValue()); + } + + @Test + void HEAD_ortr_guid_필드_존재_및_길이40() { + StandardItem item = message.findItem("HEAD.ortr_guid"); + assertNotNull(item, "HEAD.ortr_guid 필드 없음"); + assertEquals(40, item.getLength()); + } + + @Test + void HEAD_if_id_필드_길이30() { + StandardItem item = message.findItem("HEAD.if_id"); + assertNotNull(item, "HEAD.if_id 필드 없음"); + assertEquals(30, item.getLength(), "HEAD.if_id 길이는 30자여야 함 (기존 20자에서 변경)"); + } + + @Test + void HEAD_dman_rspn_dvcd_기본값_S() { + StandardItem item = message.findItem("HEAD.dman_rspn_dvcd"); + assertNotNull(item, "HEAD.dman_rspn_dvcd 필드 없음"); + assertEquals("S", item.getValue()); + } + + @Test + void HEAD_procs_rslt_dvcd_기본값_S() { + StandardItem item = message.findItem("HEAD.procs_rslt_dvcd"); + assertNotNull(item, "HEAD.procs_rslt_dvcd 필드 없음"); + assertEquals("S", item.getValue()); + } + + @Test + void HEAD_trnm_sys_dvcd_기본값_EAI() { + StandardItem item = message.findItem("HEAD.trnm_sys_dvcd"); + assertNotNull(item, "HEAD.trnm_sys_dvcd 필드 없음"); + assertEquals("EAI", item.getValue()); + } + + // ---------------------------------------------------------------- + // MSG 영역 구조 검증 + // ---------------------------------------------------------------- + + @Test + void MSG_MAIN_MSG_블록_존재() { + assertNotNull(message.findItem("MSG.MAIN_MSG"), "MSG.MAIN_MSG 블록 없음"); + } + + @Test + void MSG_outp_msg_cd_길이12() { + StandardItem item = message.findItem("MSG.MAIN_MSG.outp_msg_cd"); + assertNotNull(item, "MSG.MAIN_MSG.outp_msg_cd 필드 없음"); + assertEquals(12, item.getLength()); + } + + @Test + void MSG_msg_list_Grid_존재() { + assertNotNull(message.findItem("MSG.msg_list"), "MSG.msg_list Grid 없음"); + } + + // ---------------------------------------------------------------- + // DATA 영역 구조 검증 + // ---------------------------------------------------------------- + + @Test + void DATA_BIZ_DATA_존재() { + assertNotNull(message.findItem("DATA.BIZ_DATA"), "DATA.BIZ_DATA 없음"); + } + + // ---------------------------------------------------------------- + // setData / findItemValue 기본 동작 + // ---------------------------------------------------------------- + + @Test + void HEAD_필드_setData_findItemValue() { + String guid40 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; // 40자 + message.setData("HEAD.guid", guid40); + assertEquals(guid40, message.findItemValue("HEAD.guid")); + } + + @Test + void MSG_필드_setData_findItemValue() { + message.setData("MSG.MAIN_MSG.outp_msg_ctnt", "테스트오류메시지"); + assertEquals("테스트오류메시지", message.findItemValue("MSG.MAIN_MSG.outp_msg_ctnt")); + } +}