이호영 이호영 2025-04-21
친구톡 진행중
@81a027740b663cfc7402ca0c5ebfdce3be9f3852
src/main/java/itn/com/cmm/util/MsgSendUtils.java
--- src/main/java/itn/com/cmm/util/MsgSendUtils.java
+++ src/main/java/itn/com/cmm/util/MsgSendUtils.java
@@ -463,7 +463,7 @@
 		}
 	}
 
-	private static Boolean getReplaceYN(String smsTxtTemplate) {// 여러 치환 구문이 포함된 정규식 패턴
+	public static Boolean getReplaceYN(String smsTxtTemplate) {// 여러 치환 구문이 포함된 정규식 패턴
 
 	    if (smsTxtTemplate == null) {
 	        return false; // null일 경우 false 반환
src/main/java/itn/let/kakao/kakaoComm/KakaoSendUtil.java
--- src/main/java/itn/let/kakao/kakaoComm/KakaoSendUtil.java
+++ src/main/java/itn/let/kakao/kakaoComm/KakaoSendUtil.java
@@ -1,12 +1,15 @@
 package itn.let.kakao.kakaoComm;
 
+import java.io.UnsupportedEncodingException;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -19,6 +22,9 @@
 import org.springframework.stereotype.Component;
 
 import egovframework.rte.fdl.idgnr.EgovIdGnrService;
+import itn.com.cmm.MjonFTSendVO;
+import itn.com.cmm.MjonMsgSendVO;
+import itn.com.cmm.util.MsgSendUtils;
 import itn.com.cmm.util.StringUtil;
 import itn.let.kakao.kakaoComm.kakaoApi.KakaoApiJsonSave;
 import itn.let.kakao.kakaoComm.kakaoApi.KakaoApiTemplate;
@@ -294,25 +300,23 @@
 		
 		
 		
-		KakaoReturnVO templateDetail = kakaoApiTemplate.selectKakaoApiTemplateDetail(kakaoVO);
-		String templateContent = templateDetail.getTemplateContent(); // 알림톡 템플릿
-		kakaoVO.setTemplateContent(templateContent);
-		String templateTitle = templateDetail.getTemplateTitle();
+		String templateContent = kakaoVO.getTemplateContent(); // 친구톡 내용
+//		kakaoVO.setTemplateContent(templateContent);
+//		String templateTitle = templateDetail.getTemplateTitle();
 		
 		
 //		log.info(" + templateDetail :: [{}]", templateDetail);
 //		templateDetail.getButtonList().forEach(t->log.info(" + ButtonList :: [{}]", t.toString()));
 		
 		Boolean hasContentReplacement = this.replBooleanStrChecker(templateContent);
-		Boolean hasTitleReplacement = this.replBooleanStrChecker(templateTitle);
-		Boolean hasButtonReplacement = this.needsButtonReplacement(templateDetail.getButtonList());
+//		Boolean hasTitleReplacement = this.replBooleanStrChecker(templateTitle);
+//		Boolean hasButtonReplacement = this.needsButtonReplacement(templateDetail.getButtonList());
 		
 		/** @jsonStr 필요유무 */
-		boolean hasTitleOrButtons = StringUtils.isNotEmpty(templateTitle)  
-				|| CollectionUtils.isNotEmpty(templateDetail.getButtonList());
+		boolean hasTitleOrButtons = CollectionUtils.isNotEmpty(kakaoVO.getButtonVOList());
 		
 		/** @jsonStr 반복유무 */
-		boolean needsJsonReplacement = hasTitleReplacement || hasButtonReplacement;
+//		boolean needsJsonReplacement = hasTitleReplacement || hasButtonReplacement;
 		String sharedJsonStr = null;
 		
 		String subMsgTxt = kakaoVO.getSubMsgTxt(); // 실패 대체 문자
@@ -322,125 +326,105 @@
 		// 사용자 개인 단가 정보 불러오기
 		MberManageVO mberManageVO = mjonMsgDataService.selectMberManageInfo(kakaoVO.getUserId());
 		
-		
+
+		String smsTxtTemp = templateContent;
+		Boolean replaceYN = MsgSendUtils.getReplaceYN(templateContent);
+
+		boolean hasPerformedMsgType = false; // 치환 문자가 없는 경우, 스팸 체크가 한 번만 수행되도록 제어
 		
 		
 		/** @MSGID KEY값 */
 		List<String> idList = mjonCommon.getNextCustomMsgCId(kakaoVO.getVarListMap().size());
-//		for (int i = 0; i < kakaoSendAdvcListVO.size(); i++) {
-//			kakaoSendAdvcListVO.get(i).setMsgId(idList.get(i));
-//			kakaoSendAdvcListVO.get(i).setBizJsonName(idList.get(i));
-//		}
+
+
+		Map<String, Function<MjonFTSendVO, String>> placeholders = new HashMap<>();
+		placeholders.put("[*이름*]", MjonFTSendVO::getName);
+		placeholders.put("[*1*]", MjonFTSendVO::getRep1);
+		placeholders.put("[*2*]", MjonFTSendVO::getRep2);
+		placeholders.put("[*3*]", MjonFTSendVO::getRep3);
+		placeholders.put("[*4*]", MjonFTSendVO::getRep4);
 		
-		
-		
+
+
+		String msgTypeResult = null;
+		List<MjonFTSendVO> mjonFTSendVOList = kakaoVO.getMjonFTSendVOList();
+
 		// 분할 건수 카운터
 		int counter = 0;  
-		/** @Map에 총 갯수가 수신자 갯수와 동일함 */
-		List<Map<String, String>> varList = kakaoVO.getVarListMap();
-		for (int i = 0; i < varList.size(); i++) {
-			//		for(Map<String, String> variables : kakaoVO.getVarListMap()) {
-			// 치환 데이터
-			Map<String, String> variables = varList.get(i);
-			log.info("");
+		for (MjonFTSendVO sendVO : mjonFTSendVOList) {
+			KakaoSendAdvcVO kakaoSendAdvcVO = new KakaoSendAdvcVO();
 			
-			/** @공통 기본값 */		
-			KakaoSendAdvcVO sendVO = createSendVO(kakaoVO);
-			String msgId = idList.get(i);
-			sendVO.setMsgId(msgId);
-			
-			// step1
-			// Step 1-1: 값 치환 및 수신번호 셋팅
-			// Step 1-2: 수신자 정보 설정 (callToList는 항상 설정).
-			if (variables.containsKey("callToList")) {
-				sendVO.setCallTo(variables.get("callToList"));
-				variables.remove("callToList"); // 사용 후 제거.
-			}
-			
-			/** @Step1-3: 템플릿 치환데이터 설정 */		
-			String templateContentTemp = templateContent;
-			String templateTitleTemp = templateTitle;
-			if (hasContentReplacement) {
-				templateContentTemp = mjonCommon.ATReplaceTemplateVariables(templateContent, variables);
-				if(hasTitleReplacement) {
-					templateTitleTemp = mjonCommon.ATReplaceTemplateVariables(templateTitle, variables);
+			kakaoSendAdvcVO.setCallFrom(kakaoVO.getCallFrom());
+			kakaoSendAdvcVO.setCallTo(sendVO.getPhone());
+			kakaoSendAdvcVO.setUserId(kakaoVO.getUserId());
+
+			String smsTxt = smsTxtTemp;
+			// 치환 문자면
+			if(replaceYN) {
+				
+				// 각 치환 구문을 확인하고 치환할 값이 없으면 오류 반환
+				for (Map.Entry<String, Function<MjonFTSendVO, String>> entry : placeholders.entrySet()) {
+					String placeholder = entry.getKey();
+					String value = entry.getValue().apply(sendVO);
+//					log.info(" + smsTxtTemp [{}]", smsTxtTemp);
+//					log.info(" + placeholder [{}]", placeholder);
+//					log.info(" + value [{}]", value);
+//					log.info(" + smsTxtTemp.contains(placeholder) [{}]", smsTxtTemp.contains(placeholder));
+					if (smsTxt.contains(placeholder)) {
+						if (StringUtils.isEmpty(value)) {
+							statusResponseSet(statusResponse, HttpStatus.BAD_REQUEST, "치환 문구중 " + placeholder + " 데이터가 없습니다.");
+							return false;
+						}
+						smsTxt = smsTxt.replace(placeholder, value);
+//						log.info(" + smsTxt [{}]", smsTxt);
+						
+					}
 				}
 			}
-			/** @버튼 치환 */			// 버튼 리스트가 있으면 치환 수행, 항상 sendVO에 설정
-			List<KakaoButtonVO> buttonList = templateDetail.getButtonList();
-			if(hasButtonReplacement) {
-				buttonList = replaceButtonLinks(buttonList, variables);
+
+			String smsSpamChkTxt = smsTxt;
+			if(StringUtils.isNotEmpty(smsTxt)) {
+				smsSpamChkTxt = smsTxt.replaceAll(String.valueOf((char) 13), "");
 			}
-			sendVO.setButtonList(buttonList);
-			
-			sendVO.setTemplateTitle(templateTitleTemp);
-			sendVO.setTemplateContent(templateContentTemp);
+
 			
 			
-			// Step 1-4: 실패 대체 문자 치환데이터 설정
-			if("Y".equals(kakaoVO.getSubMsgSendYn())) { // 대체문자가 있나?
-				if ("Y".equals(kakaoVO.getSubMsgTxtReplYn())) { // 치환데이터가 있나?
-					subMsgTxt = mjonCommon.ATReplaceTemplateVariables(subMsgTxt, variables);
+			// == 치환 여부에 따라 처리 로직 분기 == 
+			// 치환 문자가 아닌 경우
+			if (!replaceYN) {
+				if (!hasPerformedMsgType) {
+ 					msgTypeResult = getMsgTypeWithByteValidation(sendVO, smsTxt);
+					if ("INVALID".equals(msgTypeResult)) {
+						statusResponseSet(statusResponse, HttpStatus.BAD_REQUEST, "문자 치환 후 전송 문자 길이를 초과하였습니다.");
+						return null;
+					}
+					hasPerformedMsgType = true;
 				}
-				sendVO.setSubMsgTxt(subMsgTxt);// 실패 
-			}
-			sendVO.setSubMsgSendYn(kakaoVO.getSubMsgSendYn()); 
-			
-			
-			/*
-						log.info("kakaoSendAdvcVO Details: [callTo={}\n, templateContent=\n{}\n, subMsgTxt=\n{}]\n\n\n\n",
-								kakaoSendAdvcVO.getCallTo(),
-								kakaoSendAdvcVO.getTemplateContent(),
-								kakaoSendAdvcVO.getSubMsgTxt()
-								);
-			 */
-			
-			// Step1 END
-			
-			
-// step3
-// 바이트 수 체크 및 금액설정
-			
-			
-			Float kakaoAtPrice = mberManageVO.getKakaoAtPrice();
-			// 유효한 단가 계산
-			float shortPrice = getValidPrice(mberManageVO.getShortPrice(), sysJoinSetVO.getShortPrice());
-			float longPrice = getValidPrice(mberManageVO.getLongPrice(), sysJoinSetVO.getLongPrice());
-			
-			
-			String shortPStr = Float.toString(shortPrice);
-			String mmsPStr = Float.toString(longPrice);
-			
-			// 공통 가격 설정
-			sendVO.setSmsPrice(shortPStr);
-			sendVO.setMmsPrice(mmsPStr);
-			
-			
-			if("Y".equals(kakaoVO.getSubMsgSendYn())) {
-				int smsTxtByte = mjonCommon.getSmsTxtBytes(sendVO.getSubMsgTxt());
-				String sendType = getMsgType(smsTxtByte);
-				sendVO.setSubMsgType(sendType);
-				
-				if ("INVALID".equals(sendType)) {
-					statusResponseSet(statusResponse, HttpStatus.BAD_REQUEST, "전송 문자 길이를 초과하였습니다.");return kakaoSendAdvcListVO;
+			} 
+			else  
+			{// 치환 문자인 경우
+
+				// 메시지 타입 체크는 매번 수행
+				msgTypeResult = getMsgTypeWithByteValidation(sendVO, smsTxt);
+				if ("INVALID".equals(msgTypeResult)) {
+					statusResponseSet(statusResponse, HttpStatus.BAD_REQUEST, "문자 치환 후 전송 문자 길이를 초과하였습니다.");
+					return null;
 				}
-				
-				boolean isMms = "MMS".equals(sendType);
-				sendVO.setEachPrice(isMms ? mmsPStr : shortPStr);
-				
-				
-			} else {
-				kakaoAtPrice = getValidPrice(mberManageVO.getKakaoAtPrice(), sysJoinSetVO.getKakaoAtPrice());
-				sendVO.setEachPrice( Float.toString(kakaoAtPrice) );
 			}
 			
 			
 			
-			// step4
-			// 예약 시간 설정 및 분할 데이터 설정
+			
+			kakaoSendAdvcVO.setTemplateContent(smsTxt);
+			kakaoSendAdvcVO.setMsgType(msgTypeResult);
+			
+			
+
+			
+			// 예약 여부 확인
 			if ("Y".equalsIgnoreCase(kakaoVO.getReserveYn())  
-					&& "Y".equalsIgnoreCase(kakaoVO.getDivideChk())  
-					&& counter == Integer.parseInt(kakaoVO.getDivideCnt())) 
+				&& "Y".equalsIgnoreCase(kakaoVO.getDivideChk())  
+				&& counter == Integer.parseInt(kakaoVO.getDivideCnt())) 
 			{
 				counter = 0;
 				calendar.add(Calendar.MINUTE, Integer.parseInt(kakaoVO.getDivideTime()));
@@ -448,36 +432,46 @@
 			counter++;
 			// 즉시 발송인경우 현재 시간 
 			// 예약인 경우 위에 설정한 시간 입력
-			sendVO.setReqDate(DATE_FORMATTER.format(calendar.getTime())); 
+			kakaoSendAdvcVO.setReqDate(DATE_FORMATTER.format(calendar.getTime())); 
+
+			kakaoSendAdvcListVO.add(kakaoSendAdvcVO);
 			
-			
-			
-			/** @step5 전송 메세지 설정 json파일 만들기*/
-			// 타이틀과 버튼이 있고
-			if(hasTitleOrButtons) {
-				// 버튼과 타이틀에 치환데이터가 있으면 json String을 계속 생성
-				if(needsJsonReplacement) {
-					sharedJsonStr = kakaoApiJsonSave.kakaoApiJsonSave_advc(sendVO, templateDetail);
-					sendVO.setBizJsonName(msgId);
-					sendVO.setJsonStr(sharedJsonStr);
-				} else if (StringUtils.isEmpty(sharedJsonStr)) {
-					// 치환 데이터가 없고 아직 생성되지 않았으면 한 번만 생성
-					sharedJsonStr = kakaoApiJsonSave.kakaoApiJsonSave_advc(sendVO, templateDetail);
-					sendVO.setBizJsonName(idList.get(0));
-					sendVO.setJsonStr(sharedJsonStr);
-				}else {
-					sendVO.setBizJsonName(idList.get(0));
-				}
-				
-			}
-			log.info(" sendVO :: [{}]", sendVO);
-			kakaoSendAdvcListVO.add(sendVO);
 		}
 		
 		
 		return kakaoSendAdvcListVO;
 	}
 	
+	public static String getMsgTypeWithByteValidation(MjonFTSendVO  sendVO, String p_smsTxt) throws UnsupportedEncodingException {
+		
+
+		//	// 내문자저장함에 저장 후 문자를 발송하는 경우 문자 타입이 숫자가 아닌 문자로 넘어와서 변경 처리함
+		//	if ("P".equals(msgType) || "L".equals(msgType)) {
+		//	    msgType = "6";
+		//	} else if ("S".equals(msgType)) {
+		//	    msgType = "4";
+		//	}
+			
+		int smsTxtByte = MjonCommon.getSmsTxtBytes(p_smsTxt);
+		String msgType = SHORT_MSG_TYPE;
+	
+		// 1. 2000 Byte 초과는 에러 처리
+		if (smsTxtByte > 2000) {
+			return "INVALID";
+		}
+
+		// 2. 첨부파일 여부 확인 (첨부파일이 있으면 장문으로 설정)
+		if (StringUtils.isNotEmpty(sendVO.getFilePath1())) {
+			msgType = LONG_MSG_TYPE;
+		}
+		// 3. 문자 길이에 따라 메시지 타입 설정 (90 Byte 초과는 장문)
+		else if (smsTxtByte > 90) {
+			msgType = LONG_MSG_TYPE;
+		}
+		return msgType;
+	}
+	
+
 	private Calendar setupBaseDate(KakaoVO kakaoVO, boolean isNotified) throws ParseException {
 		// 예약 시간 기본값 설정
 		Date now = new Date();
Add a comment
List