File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
package itn.com.cmm.util;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import itn.com.cmm.MjonMsgSendVO;
import itn.com.cmm.OptimalMsgResultDTO;
import itn.let.mail.service.StatusResponse;
import itn.let.mjo.event.service.MjonEventVO;
import itn.let.mjo.mjocommon.MjonCommon;
import itn.let.mjo.msg.service.MjonMsgVO;
import itn.let.mjo.msgagent.service.MjonMsgAgentStsVO;
import itn.let.mjo.spammsg.web.ComGetSpamStringParser;
import itn.let.sym.site.service.JoinSettingVO;
import itn.let.uss.umt.service.MberManageVO;
import itn.let.uss.umt.service.UserManageVO;
import lombok.extern.slf4j.Slf4j;
/**
*
* @author : 이호영
* @fileName : MsgSendUtils.java
* @date : 2024.09.23
* @description : 메세지발송 데이터 다루는 유틸
* ===========================================================
* DATE AUTHOR NOTE
* ----------------------------------------------------------- *
* 2024.09.23 이호영 최초 생성
*
*
*
*/
@Slf4j
public final class MsgSendUtils {
// 단문 메세지 타입
public static final String SHORT_MSG_TYPE = "4";
// 장문 메세지 타입
public static final String LONG_MSG_TYPE = "6";
// 이벤트 최저 잔액
public static final double MIN_EVENT_REMAIN_CASH = 7.5; // 이벤트 최소 잔액
/**
* @methodName : getMsgType
* @author : 이호영
* @date : 2024.09.23
* @description : msgType 재정의
* @param mjonMsgVO
* @param smsTxtByte
* @return
* @throws UnsupportedEncodingException
*/
public static String getMsgTypeWithByteValidation(MjonMsgSendVO 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;
}
public static float getValidPrice(Float personalPrice, Float defaultPrice) {
return (personalPrice != null && personalPrice > 0) ? personalPrice : defaultPrice;
}
/**
* @methodName : determinePriceByMsgType
* @author : 이호영
* @date : 2024.09.24
* @description : 문자 메시지 타입에 따라 단가를 결정하는 메서드
* @param mjonMsgVO
* @param shortPrice
* @param longPrice
* @param picturePrice
* @param picture2Price
* @param picture3Price
* @return
*/
public static float determinePriceByMsgType(MjonMsgVO mjonMsgVO, float shortPrice, float longPrice,
float picturePrice, float picture2Price, float picture3Price) {
float price = 0;
String msgType = mjonMsgVO.getMsgType();
if (SHORT_MSG_TYPE.equals(msgType)) {
price = shortPrice;
} else if (LONG_MSG_TYPE.equals(msgType)) {
// 파일 첨부 여부에 따라 장문 단가 설정
if (mjonMsgVO.getFileName3() != null) {
price = picture3Price;
} else if (mjonMsgVO.getFileName2() != null) {
price = picture2Price;
} else if (mjonMsgVO.getFileName1() != null) {
price = picturePrice;
} else {
price = longPrice;
}
}
return price;
}
/**
* @methodName : isReplacementRequired
* @author : 이호영
* @date : 2024.11.12
* @description : 치환데이터가 있는지 확인
* @param mjonMsgVO
* @return
*/
public static boolean isRepleasYN(MjonMsgVO mjonMsgVO) {
// 치환 구문 패턴 리스트
String[] placeholders = {"\\[\\*이름\\*\\]", "\\[\\*1\\*\\]", "\\[\\*2\\*\\]", "\\[\\*4\\*\\]", "\\[\\*3\\*\\]"};
for (String placeholder : placeholders) {
Pattern pattern = Pattern.compile(placeholder);
Matcher matcher = pattern.matcher(mjonMsgVO.getSmsTxt());
// 해당 패턴이 존재하면 true 반환
if (matcher.find()) {
return true;
}
}
return false;
}
/**
* @methodName : populateReplacementLists
* @author : 이호영
* @date : 2024.09.26
* @description : 배열에 데이터를 채우는 메서드
* 1. 치환 문자열 데이터 확인 및 문자 치환
* 2. 스팸 문자 체크
* 3. 메세지 타입 구하기
* 4. 제목 셋팅 : 타입에 따라 분기
* 5. 이미지 갯수 셋팅
* 6. 예약 및 분할에 따른 시간 설정
* 7. 전송사 코드 셋팅
* @param mjonMsgVO
* @param lists
* @param statusResponse
* @param agentSendCounts
* @param sendRateList
* @param isHolidayNotified
* @return call by reference
* @throws Exception
*/
public static Boolean populateSendLists(MjonMsgVO mjonMsgVO, List<MjonMsgSendVO> mjonMsgSendListVO
, StatusResponse statusResponse, List<String> resultSpamTxt
, Map<String, Integer> agentSendCounts, List<MjonMsgVO> sendRateList, boolean isHolidayNotified, UserManageVO userManageVO) throws Exception{
log.info(" :: populateSendLists :: ");
String smishingYn = userManageVO.getSmishingYn();
String exceptSpamYn = userManageVO.getExceptSpamYn();
// 예약 시간 기본값 설정
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
// ReqDate가 비어 있으면 현재 시간으로 설정, 그렇지 않으면 ReqDate로 설정
// 화면에서 예약문자면 예약시간을 regDate로 설정한다.
Date baseDate;
if (StringUtils.isEmpty(mjonMsgVO.getReqDate())) {
mjonMsgVO.setReqDate(sdf.format(now)); // ReqDate에 현재 시간 설정
baseDate = now;
} else {
baseDate = sdf.parse(mjonMsgVO.getReqDate()); // ReqDate를 baseDate로 설정
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(baseDate); // calendar에 baseDate 설정
int counter = 0; // 분할 건수 카운터
// 데이터 모두 스팸 체크를 안하고 건별로 갯수를 정해서 스팸체크를 한다.
int spamChkSize = getSpamChkSize(mjonMsgSendListVO.size());
int sampleCounter = 0;
// 치환 구문과 필드 getter 매핑
Map<String, Function<MjonMsgSendVO, String>> placeholders = new HashMap<>();
placeholders.put("[*이름*]", MjonMsgSendVO::getName);
placeholders.put("[*1*]", MjonMsgSendVO::getRep1);
placeholders.put("[*2*]", MjonMsgSendVO::getRep2);
placeholders.put("[*3*]", MjonMsgSendVO::getRep3);
placeholders.put("[*4*]", MjonMsgSendVO::getRep4);
boolean hasPerformedSpamCheck = false; // 치환 문자가 없는 경우, 스팸 체크가 한 번만 수행되도록 제어
boolean hasPerformedMsgType = false; // 치환 문자가 없는 경우, 메세지 타입 체크 한번
boolean hasPerformedDelayYn = false; // 치환 문자가 없는 경우,
String msgKind = mjonMsgVO.getMsgKind();
String smsTxtTemp = mjonMsgVO.getSmsTxt();
String mmsSubject = mjonMsgVO.getMmsSubject();
Boolean replaceYN = getReplaceYN(smsTxtTemp);
String msgTypeResult = null;
for (MjonMsgSendVO sendVO : mjonMsgSendListVO) {
sendVO.setCallFrom(mjonMsgVO.getCallFrom());
sendVO.setCallTo(sendVO.getPhone());
sendVO.setUserId(mjonMsgVO.getUserId());
String smsTxt = smsTxtTemp;
// 치환 문자면
if(replaceYN) {
// 각 치환 구문을 확인하고 치환할 값이 없으면 오류 반환
for (Map.Entry<String, Function<MjonMsgSendVO, String>> entry : placeholders.entrySet()) {
String placeholder = entry.getKey();
String value = entry.getValue().apply(sendVO);
if (smsTxt.contains(placeholder)) {
if (StringUtils.isEmpty(value)) {
// statusResponseSet(statusResponse, HttpStatus.BAD_REQUEST, "치환 문구중 " + placeholder + " 데이터가 없습니다.");
StatusResponse.statusResponseSet(statusResponse, HttpStatus.BAD_REQUEST, "치환 문구중 " + placeholder + " 데이터가 없습니다.", "STAT_1040");
return false;
}
smsTxt = smsTxt.replace(placeholder, value);
// log.info(" + smsTxt [{}]", smsTxt);
}
}
}
String smsSpamChkTxt = smsTxt;
if(StringUtils.isNotEmpty(smsTxt)) {
smsSpamChkTxt = smsTxt.replaceAll(String.valueOf((char) 13), "");
}
// 이미지 셋팅
setImagePathsForMsgSendVO(mjonMsgVO, sendVO);
// == 치환 여부에 따라 처리 로직 분기 ==
// 치환 문자가 아닌 경우
if (!replaceYN) {
// 스팸 체크와 메시지 타입 체크 각각 한 번만 수행
if (!hasPerformedSpamCheck && "N".equals(exceptSpamYn)) {
checkSpamAndSetStatus(mjonMsgVO, smsSpamChkTxt, resultSpamTxt, isHolidayNotified);
hasPerformedSpamCheck = true;
}
if (!hasPerformedMsgType) {
msgTypeResult = getMsgTypeWithByteValidation(sendVO, smsTxt);
if ("INVALID".equals(msgTypeResult)) {
// statusResponseSet(statusResponse, HttpStatus.BAD_REQUEST, "문자 치환 후 전송 문자 길이를 초과하였습니다.");
StatusResponse.statusResponseSet(statusResponse, HttpStatus.BAD_REQUEST, "문자 치환 후 전송 문자 길이를 초과하였습니다.", "STAT_1050");
return false;
}
hasPerformedMsgType = true;
}
}
else
{// 치환 문자인 경우
// 스팸 체크는 `spamChkSize`만큼 반복 수행
if (sampleCounter < spamChkSize && !"Y".equals(mjonMsgVO.getSpamStatus()) && "N".equals(exceptSpamYn)) {
checkSpamAndSetStatus(mjonMsgVO, smsSpamChkTxt, resultSpamTxt, isHolidayNotified);
sampleCounter++;
}
// 메시지 타입 체크는 매번 수행
msgTypeResult = getMsgTypeWithByteValidation(sendVO, smsTxt);
if ("INVALID".equals(msgTypeResult)) {
// statusResponseSet(statusResponse, HttpStatus.BAD_REQUEST, "문자 치환 후 전송 문자 길이를 초과하였습니다.");
StatusResponse.statusResponseSet(statusResponse, HttpStatus.BAD_REQUEST, "문자 치환 후 전송 문자 길이를 초과하였습니다.", "STAT_1050");
return false;
}
}
sendVO.setSmsTxt(smsTxt);
sendVO.setMsgType(msgTypeResult);
// 제목 셋팅
if (LONG_MSG_TYPE.equals(msgTypeResult)){
String mmsTitleTemp = "";
// 제목이 비어 있고 전송할 SMS 텍스트가 존재하는 경우에만 처리
if (StringUtils.isEmpty(mmsSubject) && StringUtils.isNotEmpty(smsTxt)) {
// SMS 텍스트를 줄 단위로 나누기
mmsTitleTemp = getMmsgSubject(smsTxt, msgKind);
// 설정된 제목을 현재 메시지 객체에 적용
sendVO.setSubject(mmsTitleTemp);
}else {
// 설정된 제목을 현재 메시지 객체에 적용
sendVO.setSubject(mmsSubject);
}
}
/* @isHolidayNotified
* - 관리자 알림 설정으로 인해
* - 해당 시간이면 지연 미처리
* @smishingYn
* - 회원 별 '스미싱 온' 상태값
* - Y면 알림, 지연 처리해야 함
* */
if("Y".equalsIgnoreCase(smishingYn) && isHolidayNotified) {
mjonMsgVO.setSpamStatus("Y");
mjonMsgVO.setSmishingYn("Y");
mjonMsgVO.setDelayYn("Y");
}
// 지연 여부 처리
if (( "Y".equalsIgnoreCase(smishingYn) || "Y".equalsIgnoreCase(mjonMsgVO.getDelayYn()))
&& !hasPerformedDelayYn && isHolidayNotified) {
calendar.add(Calendar.MINUTE, 30); // 모든 시간을 30분 뒤로 미룸
// TEST
// calendar.add(Calendar.MINUTE, 5); // 모든 시간을 30분 뒤로 미룸
hasPerformedDelayYn = true;
}
// 예약 여부 확인
if ("Y".equalsIgnoreCase(mjonMsgVO.getReserveYn())) {
// 분할 발송일 경우
if ("on".equalsIgnoreCase(mjonMsgVO.getDivideChk())) {
if (counter == Integer.parseInt(mjonMsgVO.getDivideCnt())) { // 지정된 건수마다 간격 추가
counter = 0;
calendar.add(Calendar.MINUTE, Integer.parseInt(mjonMsgVO.getDivideTime()));
}
counter++;
}
// 예약 시간 설정
sendVO.setReqDate(sdf.format(calendar.getTime())); // 분할된 시간 설정 또는 기본 예약 시간 사용
} else {
// 예약 여부가 N일 경우에도 기본 예약 시간 설정
sendVO.setReqDate(sdf.format(calendar.getTime()));
}
// 전송사 코드 셋팅
String agentCode = "00".equals(mjonMsgVO.getAgentCode())
? MsgSendUtils.assignAgentBasedOnCount(agentSendCounts, sendRateList)
: mjonMsgVO.getAgentCode();
sendVO.setAgentCode(agentCode);
}
// Group TB에 넣어줄 제목
// 치환안된 sms 데이터로 넣어야함
log.info(" mjonMsgSendListVO size :: [{}]", mjonMsgSendListVO.size());
if (LONG_MSG_TYPE.equals(mjonMsgSendListVO.get(0).getMsgType())){
mjonMsgVO.setMmsSubject(getMmsgSubject(smsTxtTemp, msgKind));
}
return true;
}
/**
* @methodName : setImagePathsForMsgSendVO
* @author : 이호영
* @date : 2024.11.26
* @description : vo에 이미지 셋팅
* @param mjonMsgVO
* @param sendVO
*/
private static void setImagePathsForMsgSendVO(MjonMsgVO mjonMsgVO, MjonMsgSendVO sendVO) {
int fileCount = Integer.parseInt(mjonMsgVO.getFileCnt());
switch (fileCount) {
case 3:
sendVO.setFilePath3(mjonMsgVO.getFileName3());
case 2:
sendVO.setFilePath2(mjonMsgVO.getFileName2());
case 1:
sendVO.setFilePath1(mjonMsgVO.getFileName1());
break;
default:
// fileCount가 0이거나 설정할 파일이 없는 경우
break;
}
sendVO.setFileCnt(mjonMsgVO.getFileCnt());
}
private static void checkSpamAndSetStatus(MjonMsgVO mjonMsgVO, String personalizedSmsTxt, List<String> resultSpamTxt, boolean isHolidayNotified) throws Exception {
mjonMsgVO.setSpamStatus("N");
mjonMsgVO.setDelayYn("N");
if(StringUtils.isNotEmpty(personalizedSmsTxt)) {
String resultParser = ComGetSpamStringParser.getSpamTextParse(personalizedSmsTxt).trim();
int spmCnt = 0;
String spmFilterTxt = "";
for (String spmTxt : resultSpamTxt) {
String parserStr = ComGetSpamStringParser.getSpamTextParse(spmTxt).trim();
if (resultParser.contains(parserStr)) {
spmCnt++;
spmFilterTxt += spmTxt + ",";
}
}
if (spmCnt > 0) { // 스팸 문자가 포함된 경우
if (StringUtil.getWordRight(spmFilterTxt.trim(), 1).equals(",")) {
// 처음부터 idx 만큼 잘라낸 나머지 글자
spmFilterTxt = StringUtil.getWordLeft(spmFilterTxt.trim(), 1);
}
/* @isHolidayNotified
* - 관리자 알림 설정으로 인해
* - 해당 시간이면 지연 미처리
* */
mjonMsgVO.setSpamStatus("Y");
if(isHolidayNotified) {
mjonMsgVO.setDelayYn("Y");
}
}
}
}
public static Boolean getReplaceYN(String smsTxtTemplate) {// 여러 치환 구문이 포함된 정규식 패턴
if (smsTxtTemplate == null) {
return false; // null일 경우 false 반환
}
Boolean replaceYN = false;
Pattern pattern = Pattern.compile("\\[\\*이름\\*\\]|\\[\\*1\\*\\]|\\[\\*2\\*\\]|\\[\\*3\\*\\]|\\[\\*4\\*\\]");
Matcher matcher = pattern.matcher(smsTxtTemplate);
// 정규식 패턴에 해당하는 치환 구문이 존재하는지 확인
if (matcher.find()) {
replaceYN = true;
}
return replaceYN;
}
/**
* @methodName : getSpamChkSize
* @author : 이호영
* @date : 2024.11.13
* @description : 수신자 건수별로 스팸체크하는 갯수 설정
* @param size
* @return
*/
private static int getSpamChkSize(int size) {
int chkSize = 1; // 기본 샘플 크기
// 수신자 수에 따른 샘플 크기 결정
if (size > 100 && size <= 1000) {
chkSize = 10;
} else if (size > 1000) {
chkSize = 50;
}
return chkSize;
}
/**
* 특정 플레이스홀더를 리스트에서 가져온 값으로 치환합니다.
*
* @param smsTxt 문자 내용
* @param placeholder 치환할 플레이스홀더 (예: [*이름*])
* @param list 치환할 데이터 리스트
* @param index 현재 인덱스
* @return 치환된 문자 내용
*/
private static String replacePlaceholder(String smsTxt, String placeholder, String[] list, int index) {
if (smsTxt.contains(placeholder)) {
if (list.length > index && StringUtil.isNotEmpty(list[index])) {
smsTxt = smsTxt.replaceAll(placeholder, StringUtil.getString(list[index].replaceAll("§", ",")));
} else {
smsTxt = smsTxt.replaceAll(placeholder, "");
}
}
return smsTxt;
}
// 배열 인덱스를 안전하게 접근하는 메서드
private static String getSafeValue(String[] array, int index) {
return (array != null && array.length > index) ? array[index] : " ";
}
// 치환 처리에 특수문자 제거 로직이 포함된 메서드
private static String getReplacementValue(String[] array, int index, String placeholder) {
if (array != null && array.length > index && StringUtil.isNotEmpty(array[index])) {
return StringUtil.getString(array[index].replaceAll("§", ","));
} else {
return " ";
}
}
public static void statusResponseSet (StatusResponse statusResponse, HttpStatus httpStatus, String msg ) {
statusResponse.setStatus(httpStatus);
statusResponse.setMessage(msg);
}
public static StatusResponse validateFilesForMessageSending(int fileCount, MjonMsgVO mjonMsgVO) {
if (fileCount > 0) {
boolean allFilesAreNull = Stream
.of(mjonMsgVO.getFileName1(), mjonMsgVO.getFileName2(), mjonMsgVO.getFileName3())
.allMatch(Objects::isNull);
if (allFilesAreNull) {
return new StatusResponse(HttpStatus.NO_CONTENT, "문자 메세지 이미지 추가에 오류가 발생하여 문자 발송이 취소 되었습니다.");
}
}
return null; // 유효성 검사를 통과한 경우
}
/**
* @methodName : validateReplacementData
* @author : 이호영
* @date : 2024.09.25
* @description : 치환문자가 사용될 때 데이터가 올바른지 확인하는 메서드
* @param mjonMsgVO
* @param list
* @return boolean
*/
/*
* public static boolean validateReplacementData(MjonMsgVO mjonMsgVO,
* List<MjonMsgSendVO> list) { // 치환 구문과 필드 getter 매핑 Map<String,
* Function<MjonMsgSendVO, String>> placeholders = new HashMap<>();
* placeholders.put("\\[\\*이름\\*\\]", MjonMsgSendVO::getName);
* placeholders.put("\\[\\*1\\*\\]", MjonMsgSendVO::getRep1);
* placeholders.put("\\[\\*2\\*\\]", MjonMsgSendVO::getRep2);
* placeholders.put("\\[\\*3\\*\\]", MjonMsgSendVO::getRep3);
* placeholders.put("\\[\\*4\\*\\]", MjonMsgSendVO::getRep4);
*
* // smsTxt 에서 필요한 치환 구문이 포함되어 있는지 확인 String smsTxt = mjonMsgVO.getSmsTxt();
* for (Map.Entry<String, Function<MjonMsgSendVO, String>> entry :
* placeholders.entrySet()) { if
* (Pattern.compile(entry.getKey()).matcher(smsTxt).find()) { // 해당 치환 구문이 존재할
* 경우 모든 수신자에서 필드 값 확인 for (MjonMsgSendVO recipient : list) { if
* (StringUtils.isEmpty(entry.getValue().apply(recipient))) { return false; //
* 데이터가 없는 경우 } } } } // 모든 치환 구문이 유효한 데이터를 가지고 있으면 true 반환 return true; }
*/
// 배열이 비어있는지 확인하는 메서드
public static boolean isEmpty(String[] array) {
return array == null || array.length == 0;
}
public static void setPriceforVO(MjonMsgVO mjonMsgVO, List<MjonMsgSendVO> mjonMsgSendVOList, JoinSettingVO sysJoinSetVO,
MberManageVO mberManageVO) {
// 사용자 단가 정보 설정 (협의 단가가 없을 경우 시스템 단가를 적용)
float shortPrice = MsgSendUtils.getValidPrice(mberManageVO.getShortPrice(), sysJoinSetVO.getShortPrice());
float longPrice = MsgSendUtils.getValidPrice(mberManageVO.getLongPrice(), sysJoinSetVO.getLongPrice());
float picturePrice = MsgSendUtils.getValidPrice(mberManageVO.getPicturePrice(), sysJoinSetVO.getPicturePrice());
float picture2Price = MsgSendUtils.getValidPrice(mberManageVO.getPicture2Price(), sysJoinSetVO.getPicture2Price());
float picture3Price = MsgSendUtils.getValidPrice(mberManageVO.getPicture3Price(), sysJoinSetVO.getPicture3Price());
// 각 메시지 타입에 따라 사용자 단가 설정 및 총 단가 계산
float totalPrice = 0.0f;
for (MjonMsgSendVO sendVO : mjonMsgSendVOList) {
String msgType = sendVO.getMsgType();
String eachPrice;
switch (msgType) {
case SHORT_MSG_TYPE: // 단문 메시지 타입
eachPrice = Float.toString(shortPrice);
break;
case LONG_MSG_TYPE: // 장문 또는 이미지 메시지 타입
eachPrice = getPicturePrice(sendVO, longPrice, picturePrice, picture2Price, picture3Price);
break;
default:
// 기본값이 필요하다면 추가 가능
throw new IllegalArgumentException("setPriceforVO :: type Exception ");
}
sendVO.setEachPrice(eachPrice);
// log.info(" eachPrice :: [{}]", eachPrice);
// 각 가격을 합산
totalPrice += Float.parseFloat(eachPrice);
}
mjonMsgVO.setTotalPrice(totalPrice);
}
/**
* @methodName : setPriceforVO
* @author : 이호영
* @date : 2024.12.02
* @description : total금액 구하기
* @param mjonMsgSendVOList
* @return
*/
public static float setPriceforVO(List<MjonMsgSendVO> mjonMsgSendVOList) {
float totalPrice = 0.0f;
for (MjonMsgSendVO sendVO : mjonMsgSendVOList) {
String priceStr = sendVO.getEachPrice();
if(StringUtils.isNotEmpty(priceStr)) {
totalPrice += Float.parseFloat(priceStr);
}
}
return totalPrice;
}
/**
* 이미지 파일 경로를 기준으로 적절한 가격을 반환하는 헬퍼 메소드.
*/
private static String getPicturePrice(MjonMsgSendVO sendVO, float longPrice, float picturePrice, float picture2Price, float picture3Price) {
switch (sendVO.getFilePath3() != null ? "FILE3" :
sendVO.getFilePath2() != null ? "FILE2" :
sendVO.getFilePath1() != null ? "FILE1" : "NONE") {
case "FILE3":
return Float.toString(picture3Price);
case "FILE2":
return Float.toString(picture2Price);
case "FILE1":
return Float.toString(picturePrice);
default:
return Float.toString(longPrice);
}
}
/**
* @methodName : getOptimalMsgList
* @author : 이호영
* @date : 2024.11.26
* @description : 주어진 이벤트 정보와 메시지 리스트를 기반으로, 잔액 내에서 최적의 메시지 리스트를 생성하고 결과를 반환합니다.
* 만약 잔액이 부족하거나 이벤트가 종료된 상태라면, 이벤트 종료 정보를 포함하여 반환합니다.
*
* @param eventMberInfo 이벤트 정보 객체 (MjonEventVO). 이벤트 상태 및 잔액 정보를 포함.
* @param mjonMsgSendVOList 최적화를 수행할 메시지 리스트 (List<MjonMsgSendVO>).
* @return OptimalMsgResultDTO 최적 메시지 리스트와 이벤트 상태 정보를 포함한 DTO 객체.
* @throws Exception 메시지 단가 계산 오류 또는 기타 처리 중 발생한 예외를 상위로 전달.
*/
public static OptimalMsgResultDTO getOptimalMsgList(
MjonEventVO eventMberInfo,
List<MjonMsgSendVO> mjonMsgSendVOList) throws Exception {
// 최적의 메시지 리스트를 담을 리스트
List<MjonMsgSendVO> optimalList = new ArrayList<>();
// 이벤트 정보가 없는 경우
if (eventMberInfo == null) {
return OptimalMsgResultDTO.builder()
.optimalMsgList(optimalList)
.eventInfo(null)
.build();
}
// 이벤트 잔액과 종료 조건 확인
double remainCash = eventMberInfo.getEventRemainCash();
String eventEndDate = eventMberInfo.getEventEndDate();
if (isEventExpired(remainCash, eventEndDate)) {
MjonEventVO returnEventMberInfo = terminateEvent(eventMberInfo, remainCash);
return OptimalMsgResultDTO.builder()
.optimalMsgList(optimalList)
.eventInfo(returnEventMberInfo)
.build();
}
// 단가 설정
float eventShortPrice = Float.parseFloat(eventMberInfo.getEventShortPrice());
float eventLongPrice = Float.parseFloat(eventMberInfo.getEventLongPrice());
float eventPicturePrice = Float.parseFloat(eventMberInfo.getEventPicturePrice());
float eventPicture2Price = Float.parseFloat(eventMberInfo.getEventPicture2Price());
float eventPicture3Price = Float.parseFloat(eventMberInfo.getEventPicture3Price());
// 최적의 메시지 리스트 생성
double sum = 0.0;
Iterator<MjonMsgSendVO> iterator = mjonMsgSendVOList.iterator();
while (iterator.hasNext()) {
MjonMsgSendVO msg = iterator.next();
// 단가 계산 및 예외 처리
String eachPrice = getEachPriceOrThrow(msg, eventShortPrice, eventLongPrice, eventPicturePrice, eventPicture2Price, eventPicture3Price);
float floatEachPrice = Float.parseFloat(eachPrice);
// 최적의 메시지 리스트에 추가
if (sum + floatEachPrice <= remainCash) {
sum += floatEachPrice;
msg.setEachPrice(eachPrice);
msg.setEventYn("Y");
optimalList.add(msg);
iterator.remove();
} else {
break; // 예산 초과로 추가 불가능
}
}
// 이벤트 잔액 처리하는 매소드 terminateEvent
// double remainAmt = targetCash - sum;
// MjonEventVO returnEventMberInfo = terminateEvent(eventMberInfo, remainAmt);
return OptimalMsgResultDTO.builder()
.optimalMsgList(optimalList)
// .eventInfo(returnEventMberInfo)
.build();
// 결과 반환
// return OptimalMsgResultDTO.builder()
// .optimalMsgList(optimalList)
// .eventInfo(null)
// .build();
}
/**
* @methodName : getEachPriceOrThrow
* @author : 이호영
* @date : 2024.11.26
* @description :
* @param msg
* @param shortPrice
* @param longPrice
* @param picturePrice
* @param picture2Price
* @param picture3Price
* @return
*/
private static String getEachPriceOrThrow(
MjonMsgSendVO msg,
float shortPrice,
float longPrice,
float picturePrice,
float picture2Price,
float picture3Price) {
switch (msg.getMsgType()) {
case SHORT_MSG_TYPE:
return Float.toString(shortPrice);
case LONG_MSG_TYPE:
return getPicturePrice(msg, longPrice, picturePrice, picture2Price, picture3Price);
default:
throw new IllegalArgumentException("Unsupported message type: " + msg.getMsgType());
}
}
private static boolean isEventExpired(double targetCash, String eventEndDate) throws Exception {
return !MJUtil.getCompareDate(eventEndDate) || targetCash < MIN_EVENT_REMAIN_CASH;
}
/**
* @methodName : terminateEvent
* @author : 이호영
* @date : 2024.11.26
* @description : 이벤트 종료 VO 생성
* @param eventMberInfo
* @param remainCash
* @return
*/
public static MjonEventVO terminateEvent(MjonEventVO eventMberInfo, double remainCash) {
// TODO Auto-generated method stub
// 이벤트 상태를 종료로 변경
MjonEventVO returnEventMberInfo = new MjonEventVO();
returnEventMberInfo.setEventInfoId(eventMberInfo.getEventInfoId());
returnEventMberInfo.setMberId(eventMberInfo.getMberId());
returnEventMberInfo.setEventRemainCash(remainCash);
returnEventMberInfo.setEventStatus("Y");
if (remainCash < MIN_EVENT_REMAIN_CASH) {
returnEventMberInfo.setEventStatus("E");
returnEventMberInfo.setEventMemo("발송 최소 금액("+ MIN_EVENT_REMAIN_CASH +") 부족 혹은 이벤트 종료일 초과되어 이벤트 종료 시킴");
}
return returnEventMberInfo;
}
/**
* @methodName : handleHotlineAgentRate
* @author : 이호영
* @date : 2024.11.26
* @description : 전용 전송사 비율이 0이면 대표 전송사로 수정
* @param userInfo
* @param tmp
* @param hotlineAgentCode
* @return
*/
public static String handleHotlineAgentRate(MberManageVO userInfo, MjonMsgAgentStsVO tmp, String hotlineAgentCode) {
String sendRate = tmp.getSendRate();
String repAgent = tmp.getRepAgent();
String useYn = tmp.getUseYn();
// 블라인드 코드가 'N'인 경우 전송 비율 및 사용 가능 여부 확인
if ("N".equals(userInfo.getBlineCode())) {
// 전송 비율이 0이거나 사용 불가인 경우 대표 전송사로 변경
if ("0".equals(sendRate) || "N".equals(useYn)) {
hotlineAgentCode = repAgent;
}
}
return hotlineAgentCode; // 변경된 hotlineAgentCode 반환
}
/**
* @methodName : initializeAgentSendCounts
* @author : 이호영
* @date : 2024.11.26
* @description : 전송사별 코드로 map 초기화 하기
* @return
*/
public static Map<String, Integer> initializeAgentSendCounts() {
Map<String, Integer> agentSendCounts = new HashMap<>();
agentSendCounts.put("01", 0); // 아이하트
agentSendCounts.put("02", 0); // 현대 퓨쳐넷
agentSendCounts.put("03", 0); // 아이엠오
agentSendCounts.put("04", 0); // 비즈뿌리오
agentSendCounts.put("05", 0); // 제이제이
agentSendCounts.put("07", 0); // 인비토
return agentSendCounts;
}
/**
* @methodName : calculateSendCounts
* @author : 이호영
* @date : 2024.11.26
* @description : agentSendCounts에 있는 에이젼트 map에 전송사 비율에 따라 카운트 put 하기
* @param agentSendCounts
* @param sendRate
* @param finalSize
*/
public static void calculateSendCounts(Map<String, Integer> agentSendCounts, MjonMsgVO sendRate, int finalSize) {
String agentCode = sendRate.getAgentCode();
float sendRatePercentage = Float.parseFloat(sendRate.getSendRate());
int sendCount = Math.round(finalSize * sendRatePercentage);
if (agentSendCounts.containsKey(agentCode)) {
agentSendCounts.put(agentCode, sendCount);
}
}
/**
* @methodName : assignAgentBasedOnCount
* @author : 이호영
* @date : 2024.11.26
* @description : initializeAgentSendCounts()에서 만든 map으로 발송 agent 데이터 Set
* @param agentSendCounts
* @param sendRateList
* @return
*/
public static String assignAgentBasedOnCount(Map<String, Integer> agentSendCounts, List<MjonMsgVO> sendRateList) {
// 전송사 코드 순서대로 확인 :: initializeAgentSendCounts 메소드와 맞춰야함
List<String> agentCodes = Arrays.asList("01", "02", "03", "04", "05", "07");
// 전송 가능한 코드 찾기
for (String agentCode : agentCodes) {
if (agentSendCounts.get(agentCode) > 0) {
agentSendCounts.put(agentCode, agentSendCounts.get(agentCode) - 1);
return agentCode;
}
}
// 모든 코드의 전송 가능 횟수가 0이면 대표 전송사로 할당
return sendRateList.get(0).getRepAgent();
}
/**
* @methodName : getMmsgSubject
* @author : 이호영
* @date : 2024.12.02
* @description : 타이틀 생성
* @param smsTxt
* @param msgKind
* @return
*/
public static String getMmsgSubject(String smsTxt, String msgKind) {
String mmsTitleTemp = "";
if(StringUtils.isEmpty(smsTxt)) {
return mmsTitleTemp;
}
// SMS 텍스트를 줄 단위로 나누기
String[] split = smsTxt.split("\n");
if (split.length > 0) {
// "C" 메시지 종류인 경우 두 번째 줄, 그렇지 않으면 첫 번째 줄을 제목으로 설정
mmsTitleTemp = "C".equals(msgKind) && split.length > 1 ? split[1].trim() : split[0].trim();
// 제목 글자 수를 20자로 제한 (초과 시 잘라냄)
mmsTitleTemp = mmsTitleTemp.length() > 20 ? mmsTitleTemp.substring(0, 20) : mmsTitleTemp;
}
return mmsTitleTemp;
}
/**
* @methodName : eventYN
* @author : 이호영
* @date : 2025.02.10
* @description : 이벤트 확인
* @param eventMberInfo
* @return
*/
public static Boolean isNotEvent(MjonEventVO eventMberInfo) {
if (eventMberInfo == null
|| "E".equals(eventMberInfo.getEventStatus())
|| org.apache.commons.lang3.StringUtils.isEmpty(eventMberInfo.getEventEndDate())
) {
return true;
}
return false;
}
}