친구톡 발송 완료

This commit is contained in:
hehihoho3@gmail.com 2025-09-02 17:42:19 +09:00
parent f5029faba2
commit 2376446be3
10 changed files with 285 additions and 145 deletions

View File

@ -351,9 +351,12 @@ public class LogAspect {
case "MjKakaoProfileInfoVO":
case "BizTemplateRequest":
case "MsgAtRequestVO":
case "MsgFtRequestVO":
case "String":
case "ArrayList":
return ApiObjectUtil.toJson(returnValue);
default:
log.info("데이터를 추가해 주세요");
log.info("데이터를 추가해 주세요 [{}]", classNm);
return "데이터를 추가해 주세요";
}
}

View File

@ -145,6 +145,9 @@ public class ApiService <T> {
if (msgFtRequestVO.getSendKind() != null) {
body.add("sendKind", msgFtRequestVO.getSendKind());
}
if ( msgFtRequestVO.getTemplateMsgType() != null) {
body.add("imageType", msgFtRequestVO.getTemplateMsgType());
}
if (msgFtRequestVO.getTemplateCode() != null) {
body.add("templateCode", msgFtRequestVO.getTemplateCode());
}
@ -159,7 +162,7 @@ public class ApiService <T> {
StatusResponse.class
);
log.info("Upload response :: [{}]", response.getBody());
// log.info("Upload response :: [{}]", response.getBody());
return response.getBody();
}

View File

@ -50,6 +50,7 @@ public enum StatMsg {
, STAT_2040("2040","본문 데이터 오류")
, STAT_2041("2041","타이틀 데이터 오류")
, STAT_2042("2042","대체문자 데이터 오류")
, STAT_2043("2043","대체문자 발송 시 발신번호가 필요합니다.")
, STAT_2050("2050","지원하지 않는 이미지 형식입니다.")
, STAT_2051("2051","이미지 용량은 5MB 이내여야 합니다.")
@ -60,6 +61,8 @@ public enum StatMsg {
, STAT_2056("2056","비율 2:1 이상 또는 3:4 이하만 허용됩니다.")
, STAT_2057("2057","대체문자(MMS) 이미지는 10MB를 초과할 수 없습니다.")
, STAT_2080("2080","친구톡은 20시 50분부터 익일 08시까지 발송이 제한됩니다.")
, STAT_2099("2099","기타 시스템 오류")

View File

@ -0,0 +1,44 @@
package com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain;
import lombok.*;
import java.util.ArrayList;
import java.util.List;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
@Getter
@Setter
public class KakaoFTSendVO {
// 카카오 발신 관련
private String mberId; // mberId
private String senderKey; // 카카오 발신 (필수)
private String adFlag; // 카카오 발신 (필수)
// 이미지 관련
private String imageFileName; // 이미지 파일명
private String imageType; // 이미지 타입 (I: 이미지, null: 텍스트만)
private String atchFileId; // 첨부파일 ID
private String templateImageUrl; // 친구톡 이미지 URL
// 메시지 내용
private String templateContent; // 친구톡 발송 내용 (개별 수신자별)
private String subMsgSendYn; // 대체문자 발송여부
private String subMsgTxt; // 대체문자 내용 (개별 수신자별)
// 발송 관련
private String reserveYn; // 예약발송 여부 (기본: N)
private String callFrom; // 발신번호
private String reqDate; // 요청일시 (예약발송시)
private String sendKind; // 발송 구분 , A : API / H : 사이트
// 버튼 정보 (모든 수신자 공통)
@Builder.Default
private List<KakaoButtonVO> buttonVOList = new ArrayList<>();
// 수신자 목록 (핵심: 개별 발송시 1명만 포함)
@Builder.Default
private List<MjonFTSendVO> mjonFTSendVOList = new ArrayList<>();
}

View File

@ -55,9 +55,6 @@ public class MsgFtRequestVO implements Serializable {
@ToString.Exclude
private MultipartFile templateImage;
@JsonIgnore
@ToString.Exclude
private MultipartFile subImage;
// ====== AOP/DB 저장용(문자열/숫자만) 메타데이터 필드들 ======
// template
@ -70,9 +67,31 @@ public class MsgFtRequestVO implements Serializable {
private String templateImageSavedPath; // 서버 저장 경로(선택)
private String templateAtchFileId; // 내부 첨부ID(선택)
private String templateMsgType;
private String templateImageUrl;
/**
* @description : 친구톡 이미지 등록된 URL
*/
private String templateImageUrl;
/**
* @description : 치환문자 이미지
* 이미지 문자가 있고 대체문자가 있으면(subMsgSendYn="Y") filepath에 atchFileId 있어야함
*/
private String filePath1;
/**
* @description : 버튼 사용
*/
private List<FtButtonVO> buttons = new ArrayList<>();
// sub
// @JsonIgnore
// @ToString.Exclude
// private MultipartFile subImage;
// private String subImageName;
// private String subImageContentType;
// private Long subImageSize;

View File

@ -5,9 +5,6 @@ import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@ToString
@ -29,15 +26,6 @@ public class VarFtListMapVO {
*/
private String subMsgTxt;
/**
* @description : 치환문자 이미지
*/
private String filePath1;
/**
* @description : 버튼 사용
*/
private List<FtButtonVO> buttons = new ArrayList<>();
//
// /**

View File

@ -41,7 +41,7 @@ public class FtIndexedParameterParserService {
* @param request HTTP 요청 객체
* @return 파싱된 VarListMapVO 리스트
*/
public List<VarFtListMapVO> parseIndexedParameters(MsgFtRequestVO msgAtRequestVO, HttpServletRequest request) {
public List<VarFtListMapVO> parseIndexedParameters(MsgFtRequestVO msgFtRequestVO, HttpServletRequest request) {
List<VarFtListMapVO> varListMap = new ArrayList<>();
// 모든 파라미터 가져오기
@ -51,7 +51,7 @@ public class FtIndexedParameterParserService {
Map<Integer, Map<String, String>> indexedDataMap = new HashMap<>();
String subMsgSendYn = msgAtRequestVO.getSubMsgSendYn();
String subMsgSendYn = msgFtRequestVO.getSubMsgSendYn();
// 모든 파라미터를 순회하며 인덱스된 파라미터 찾기
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
@ -84,18 +84,24 @@ public class FtIndexedParameterParserService {
if("Y".equals(subMsgSendYn)){
vo.setSubMsgTxt(dataMap.get("subMsgTxt"));
}
// 버튼 데이터 처리
String buttonData = dataMap.get("button");
if (buttonData != null && !buttonData.trim().isEmpty()) {
try {
JsonNode buttonJson = objectMapper.readTree(buttonData);
JsonNode buttonArray = buttonJson.get("button");
List<FtButtonVO> buttonList = new ArrayList<>();
if (buttonArray != null && buttonArray.isArray()) {
for (JsonNode button : buttonArray) {
FtButtonVO ftButton = FtButtonVO.builder()
// 필수 필드 하나라도 있으면 리스트에 추가
if (vo.getPhone() != null || vo.getTemplateContent() != null) {
varListMap.add(vo);
}
}
// 버튼 파라미터를 요청 레벨에서 번만 파싱
String buttonData = request.getParameter("button");
if (buttonData != null && !buttonData.trim().isEmpty()) {
try {
JsonNode buttonJson = objectMapper.readTree(buttonData);
JsonNode buttonArray = buttonJson.get("button");
List<FtButtonVO> buttonList = new ArrayList<>();
if (buttonArray != null && buttonArray.isArray()) {
for (JsonNode button : buttonArray) {
FtButtonVO ftButton = FtButtonVO.builder()
.name(getJsonText(button, "name"))
.linkType(getJsonText(button, "linkType"))
.linkTypeName(getJsonText(button, "linkTypeName"))
@ -104,24 +110,17 @@ public class FtIndexedParameterParserService {
.linkIos(getJsonText(button, "linkIos"))
.linkAnd(getJsonText(button, "linkAnd"))
.build();
buttonList.add(ftButton);
}
buttonList.add(ftButton);
}
vo.setButtons(buttonList);
} catch (Exception e) {
log.error("버튼 JSON 파싱 실패 - index: {}, data: {}", index, buttonData, e);
vo.setButtons(new ArrayList<>());
}
// MsgFtRequestVO에 버튼 설정 (모든 수신자 공통)
msgFtRequestVO.setButtons(buttonList);
} catch (Exception e) {
log.error("버튼 JSON 파싱 실패: {}", buttonData, e);
msgFtRequestVO.setButtons(new ArrayList<>());
}
// 필수 필드 하나라도 있으면 리스트에 추가
if (vo.getPhone() != null || vo.getTemplateContent() != null) {
varListMap.add(vo);
}
}
return varListMap;
}

View File

@ -1,23 +1,25 @@
package com.itn.mjonApi.mjon.api.kakao.ft.send.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itn.mjonApi.cmn.domain.biz.template.BizTemplateRequest;
import com.itn.mjonApi.cmn.domain.biz.template.detail.TemplateDetailResponse;
import com.itn.mjonApi.cmn.msg.RestResponse;
import com.itn.mjonApi.cmn.apiServer.ApiService;
import com.itn.mjonApi.cmn.domain.StatusResponse;
import com.itn.mjonApi.mjon.api.kakao.at.inqry.mapper.domain.MjKakaoProfileInfoVO;
import com.itn.mjonApi.mjon.api.kakao.at.inqry.service.InqryService;
import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.MsgFtRequestVO;
import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.VarFtListMapVO;
import com.itn.mjonApi.mjon.api.kakao.utils.FtFileMetaUtil;
import com.itn.mjonApi.util.MunjaUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.Response;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* fileName : AtParameterProcessingService.java
@ -40,6 +42,12 @@ public class FtParameterProcessingService {
@Autowired
private InqryService inqryService;
private ApiService<Response> apiService;
@Autowired
public FtParameterProcessingService(ApiService<Response> apiService) {
this.apiService = apiService;
}
/**
* HttpServletRequest에서 동적으로 인덱스된 파라미터들을 파싱하고 검증하여
* MsgAtRequestVO의 varListMap에 설정
@ -63,11 +71,12 @@ public class FtParameterProcessingService {
// if (STAT_2030 != null) return STAT_2030;
// 파싱 로직을 IndexedParameterParserService에 위임
log.info("msgFtRequestVO.getAdFlag() :: [{}]", msgFtRequestVO.getAdFlag());
List<VarFtListMapVO> parsedList = indexedParameterParserService.parseIndexedParameters(msgFtRequestVO, request);
// 파싱된 VO에 대해 검증 수행
for (VarFtListMapVO vo : parsedList) {
String validationError = MunjaUtil.kakaoFtValidate(vo, msgFtRequestVO.getSubMsgSendYn());
String validationError = MunjaUtil.kakaoFtValidate(vo, msgFtRequestVO);
if (StringUtils.isNotEmpty(validationError)) {
return validationError; // 검증 실패 오류 코드 반환
@ -80,35 +89,6 @@ public class FtParameterProcessingService {
return null; // 모든 검증 통과
}
private String validateTemplateCode(String mberId, String senderKey, String templateCode) {
try {
BizTemplateRequest request = BizTemplateRequest.builder()
.mberId(mberId)
.senderKey(senderKey)
.templateCode(templateCode)
.build();
RestResponse response = inqryService.getTemplateDetail(request);
JsonNode node = new ObjectMapper().valueToTree(response.getData());
// 전체 출력
// log.info("data 전체 :: {}", node.toPrettyString());
// resultCode가 있으면 채널ID 오류 + inqryService.getTemplateDetail 참조
if (node.has("resultCode")) {
return "STAT_"+node.get("resultCode").asText();
}
TemplateDetailResponse detail = (TemplateDetailResponse)
response.getData();
// 템플릿 상세 정보 활용
log.info("template detail :: [{}]", detail);
return "200".equals(detail.getCode()) ? null : "STAT_2030";
} catch (Exception e) {
e.printStackTrace();
return "STAT_2099";
}
}
public String validateSenderKey(String mberId, String senderKey) {
if (StringUtils.isEmpty(senderKey)) {
@ -120,6 +100,41 @@ public class FtParameterProcessingService {
}
public String getImagesInfo(MsgFtRequestVO msgFtRequestVO) throws IOException, NoSuchAlgorithmException {
log.info("msgFtRequestVO.getTemplateImage() : [{}]", msgFtRequestVO.getTemplateImage());
if (FtFileMetaUtil.hasFile(msgFtRequestVO.getTemplateImage())) {
String code = FtFileMetaUtil.fillTemplateMeta(msgFtRequestVO);
if (StringUtils.isNotEmpty(code)) return code;
// 메타데이터 처리 성공 업로드 API 호출
try {
// Option 1: ApiService에 multipart 메소드 추가
StatusResponse uploadResponse = apiService.postMultipartForEntity(
"/web/mjon/kakao/template/sendKakaoFriendsTemplateImageUploadAjax_advc.do",
msgFtRequestVO,
msgFtRequestVO.getTemplateImage()
);
// .is2xxSuccessful() 상태코드가 200번대(200 ~ 299)인지 확인
if (uploadResponse != null && uploadResponse.getStatus().is2xxSuccessful()) {
Map<String, Object> obj = (Map<String, Object>) uploadResponse.getObject();
String imgUrl = (String) obj.get("imgUrl");
String atchFileId = (String) obj.get("atchFileId");
log.info("imgUrl: {}, atchFileId: {}", imgUrl, atchFileId);
msgFtRequestVO.setTemplateImageUrl(imgUrl);
msgFtRequestVO.setFilePath1(atchFileId);
}else{
// return new RestResponse(new FailRestResponse("STAT_2099", ""));
return "STAT_2099";
}
} catch (Exception e) {
log.error("템플릿 이미지 업로드 실패", e);
return "STAT_2099";
}
}
return null;
}
}

View File

@ -1,15 +1,14 @@
package com.itn.mjonApi.mjon.api.kakao.ft.send.service.impl;
import com.itn.mjonApi.cmn.apiServer.ApiService;
import com.itn.mjonApi.cmn.domain.StatusResponse;
import com.itn.mjonApi.cmn.msg.FailRestResponse;
import com.itn.mjonApi.cmn.msg.RestResponse;
import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.MsgFtRequestVO;
import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.*;
import com.itn.mjonApi.mjon.api.kakao.ft.send.service.FtParameterProcessingService;
import com.itn.mjonApi.mjon.api.kakao.ft.send.service.SendFtService;
import com.itn.mjonApi.mjon.api.kakao.utils.FtFileMetaUtil;
import com.itn.mjonApi.mjon.api.msg.inqry.mapper.PriceMapper;
import com.itn.mjonApi.mjon.api.msg.send.mapper.SendMapper;
import com.itn.mjonApi.mjon.api.msg.send.mapper.domain.MjonResponseVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.Response;
import org.apache.commons.lang3.StringUtils;
@ -19,6 +18,8 @@ import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@ -60,48 +61,7 @@ public class SendFtServiceImpl implements SendFtService {
// return this._getTestMsgReturnData(msgRequestVO.getTest_yn());
}
if (FtFileMetaUtil.hasFile(msgFtRequestVO.getTemplateImage())) {
String code = FtFileMetaUtil.fillTemplateMeta(msgFtRequestVO);
if (StringUtils.isNotEmpty(code)) return new RestResponse(new FailRestResponse(code, ""));
}
// if (FtFileMetaUtil.hasFile(msgFtRequestVO.getSubImage())) {
// code = FtFileMetaUtil.fillSubMeta(msgFtRequestVO);
// if (StringUtils.isNotEmpty(code)) return new RestResponse(new FailRestResponse(code, ""));
// }
if (FtFileMetaUtil.hasFile(msgFtRequestVO.getTemplateImage())) {
String code = FtFileMetaUtil.fillTemplateMeta(msgFtRequestVO);
if (StringUtils.isNotEmpty(code)) return new RestResponse(new FailRestResponse(code, ""));
// 메타데이터 처리 성공 업로드 API 호출
try {
// Option 1: ApiService에 multipart 메소드 추가
StatusResponse uploadResponse = apiService.postMultipartForEntity(
"/web/mjon/kakao/template/sendKakaoFriendsTemplateImageUploadAjax_advc.do",
msgFtRequestVO,
msgFtRequestVO.getTemplateImage()
);
log.info("전체 StatusResponse :: [{}]", uploadResponse);
log.info("object 필드만 :: [{}]", uploadResponse.getObject());
// 업로드 결과 처리
// if ("OK".equals(uploadResponse.getResult())) {
// // 성공 처리
// log.info("템플릿 이미지 업로드 성공");
// } else {
// // 실패 처리
// return new RestResponse(new FailRestResponse(uploadResponse.getStatCode(), ""));
// }
} catch (Exception e) {
log.error("템플릿 이미지 업로드 실패", e);
return new RestResponse(new FailRestResponse("UPLOAD_ERROR", ""));
}
}
// 데이터
// Form-data의 인덱스된 파라미터들을 VarListMapVO 리스트로 변환 (동적 처리 _1~_100)
String falseCode = ftParameterProcessingService.processIndexedParameters(msgFtRequestVO, request);
if(StringUtils.isNotEmpty(falseCode)){
@ -109,29 +69,128 @@ public class SendFtServiceImpl implements SendFtService {
return new RestResponse(new FailRestResponse(falseCode,""));
}
// 이미지 정제
String falseImageCode = ftParameterProcessingService.getImagesInfo(msgFtRequestVO);
if(StringUtils.isNotEmpty(falseImageCode)) return new RestResponse(new FailRestResponse(falseImageCode,""));
//
//
//
//
// MjonResponseVO munjaSendResponse = apiService.postForEntity(
// "/web/mjon/kakao/friendstalk/kakaoFriendsTalkMsgSendAjax_advc.do"
// , msgFtRequestVO
// , String.class
// );
// ========== 새로운 개별 발송 코드 ==========
List<MjonResponseVO> responses = new ArrayList<>();
List<String> successList = new ArrayList<>();
List<String> failList = new ArrayList<>();
// convertMjonDataToApiResponse => MjonResponseVO 데이터를 ApiResponse 데이터로 변환하는 메소드
// log.info(" + munjaSendResponse :: [{}]", munjaSendResponse.toString());
// if("OK".equals(munjaSendResponse.getResult())){ // 성공
// return new RestResponse(SendSucRestResponse.convertMjonDataToApiResponse(munjaSendResponse));
// }else{ // 실패
// return new RestResponse(new FailRestResponse(munjaSendResponse.getStatCode(),""));
// }
log.info("msgFtRequestVO.toString() :: [{}]", msgFtRequestVO.toString());
// 수신자별로 개별 발송 처리
// List<KakaoFTSendVO> individualRequestList = new ArrayList<>();
for (VarFtListMapVO recipient : msgFtRequestVO.getVarListMap()) {
try {
// 개별 수신자용 KakaoFTSendVO 생성
KakaoFTSendVO individualRequest = createKakaoFTSendVO(msgFtRequestVO, recipient);
// individualRequestList.add(individualRequest);
return new RestResponse(msgFtRequestVO);
log.info("individualRequest: {}", individualRequest);
// 개별 API 호출
MjonResponseVO response = apiService.postForEntity(
"/web/mjon/kakao/friendstalk/kakaoFriendsTalkMsgSendAjax_advc.do",
individualRequest,
String.class
);
log.info("response: {}", response);
responses.add(response);
successList.add(recipient.getPhone());
log.info("개별 발송 성공 - 수신번호: {}", recipient.getPhone());
} catch (Exception e) {
log.error("개별 발송 실패 - 수신번호: {}, 오류: {}", recipient.getPhone(), e.getMessage());
failList.add(recipient.getPhone());
}
}
// SMS API 형태의 응답 생성
FtSendSuccessResponse ftResponse = FtSendSuccessResponse.createFtResponse(
msgFtRequestVO.getVarListMap().size(),
successList.size(),
failList.size(),
successList
);
return new RestResponse(ftResponse);
// return new RestResponse(individualRequestList);
}
/**
* MsgFtRequestVO + VarFtListMapVO를 KakaoFTSendVO로 변환
* (개별 수신자용 객체 생성)
*/
private KakaoFTSendVO createKakaoFTSendVO(MsgFtRequestVO source, VarFtListMapVO recipient) {
// 버튼 변환 (FtButtonVO KakaoButtonVO)
List<KakaoButtonVO> kakaoButtons = source.getButtons().stream()
.map(this::convertToKakaoButton)
.collect(Collectors.toList());
// 개별 수신자 객체 생성
MjonFTSendVO mjonFTSend = MjonFTSendVO.builder()
.phone(recipient.getPhone()) // 핵심: 수신번호
// .callTo(recipient.getPhone()) // 동일값
// .eventYn("N") // 기본값
.build();
// KakaoFTSendVO 생성
return KakaoFTSendVO.builder()
// 공통 정보 (모든 요청에 동일)
.mberId(source.getMberId())
.sendKind(source.getSendKind())
.reserveYn("N")
.adFlag(source.getAdFlag())
.senderKey(source.getSenderKey())
.callFrom(source.getCallFrom())
.buttonVOList(kakaoButtons)
// 이미지 관련
.templateImageUrl(source.getTemplateImageUrl())
.atchFileId(source.getFilePath1())
.imageType(source.getTemplateImageUrl() != null ? source.getTemplateMsgType() : null)
// .imageFileName(extractFileName(source.getTemplateImageUrl()))
.imageFileName(source.getTemplateImageName())
// 개별 수신자 정보
.templateContent(recipient.getTemplateContent()) // 개별 내용
.subMsgSendYn(source.getSubMsgSendYn()) // 개별 대체문자
.subMsgTxt(recipient.getSubMsgTxt()) // 개별 대체문자
.mjonFTSendVOList(Arrays.asList(mjonFTSend)) // 단일 수신자만
.build();
}
/**
* FtButtonVO를 KakaoButtonVO로 변환
*/
private KakaoButtonVO convertToKakaoButton(FtButtonVO source) {
return KakaoButtonVO.builder()
.name(source.getName())
.linkType(source.getLinkType())
.linkTypeName(source.getLinkTypeName())
.linkMo(source.getLinkMo())
.linkPc(source.getLinkPc())
.linkIos(source.getLinkIos())
.linkAnd(source.getLinkAnd())
.build();
}
/**
* 이미지 URL에서 파일명 추출
*/
private String extractFileName(String imageUrl) {
if (imageUrl == null) return null;
return imageUrl.substring(imageUrl.lastIndexOf('/') + 1);
}
}

View File

@ -1,6 +1,7 @@
package com.itn.mjonApi.util;
import com.itn.mjonApi.mjon.api.kakao.at.send.mapper.domain.VarAtListMapVO;
import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.MsgFtRequestVO;
import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.VarFtListMapVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
@ -141,7 +142,7 @@ public class MunjaUtil {
}
public static String kakaoFtValidate(VarFtListMapVO vo, String subMsgSendYn) {
public static String kakaoFtValidate(VarFtListMapVO vo, MsgFtRequestVO msgFtRequestVO) {
log.info(" vo.toString() [{}]", vo.toString());
@ -158,11 +159,17 @@ public class MunjaUtil {
}
// 대체문자 검증 (대체문자 발송이 활성화된 경우에만)
if ("Y".equals(subMsgSendYn)) {
if ("Y".equals(msgFtRequestVO.getSubMsgSendYn())) {
String subMsgTxt = vo.getSubMsgTxt();
if (StringUtils.isEmpty(subMsgTxt)) {
return "STAT_2042"; // 대체문자 데이터 오류
}
String callFrom = msgFtRequestVO.getCallFrom();
if (StringUtils.isEmpty(callFrom)) {
return "STAT_2043"; // 대체문자 발송 발신번호 필요
}
}
// 모든 검증 통과