친구톡 발송 진행중
This commit is contained in:
parent
bc09c23a0d
commit
f5029faba2
@ -3,14 +3,19 @@ package com.itn.mjonApi.cmn.apiServer;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.itn.mjonApi.cmn.domain.StatusResponse;
|
||||
import com.itn.mjonApi.cmn.domain.biz.template.BizTemplateRequest;
|
||||
import com.itn.mjonApi.cmn.domain.biz.template.detail.TemplateDetailResponse;
|
||||
import com.itn.mjonApi.cmn.domain.biz.template.list.TemplateListResponse;
|
||||
import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.MsgFtRequestVO;
|
||||
import com.itn.mjonApi.mjon.api.msg.send.mapper.domain.MjonResponseVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
|
||||
/**
|
||||
@ -126,4 +131,36 @@ public class ApiService <T> {
|
||||
return returnEntity;
|
||||
}
|
||||
|
||||
public StatusResponse postMultipartForEntity(String url, MsgFtRequestVO msgFtRequestVO, MultipartFile templateImage) {
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||
|
||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
||||
|
||||
// 파일 추가
|
||||
body.add("templateImage", templateImage.getResource());
|
||||
|
||||
// MsgFtRequestVO 필드들을 kakaoVO에 맞게 매핑
|
||||
if (msgFtRequestVO.getSendKind() != null) {
|
||||
body.add("sendKind", msgFtRequestVO.getSendKind());
|
||||
}
|
||||
if (msgFtRequestVO.getTemplateCode() != null) {
|
||||
body.add("templateCode", msgFtRequestVO.getTemplateCode());
|
||||
}
|
||||
|
||||
|
||||
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
|
||||
|
||||
ResponseEntity<StatusResponse> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
StatusResponse.class
|
||||
);
|
||||
|
||||
log.info("Upload response :: [{}]", response.getBody());
|
||||
return response.getBody();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
41
src/main/java/com/itn/mjonApi/cmn/domain/StatusResponse.java
Normal file
41
src/main/java/com/itn/mjonApi/cmn/domain/StatusResponse.java
Normal file
@ -0,0 +1,41 @@
|
||||
package com.itn.mjonApi.cmn.domain;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
/*
|
||||
* • 1XX : 조건부 응답
|
||||
* • 2XX : 성공
|
||||
* • 3XX : 리다이렉션 완료
|
||||
* • 4XX : 요청 오류
|
||||
* • 500 : 서버 오류
|
||||
*
|
||||
* 참고 : https://km0830.tistory.com/33
|
||||
*
|
||||
* ====== 자주 사용하는 코드 =====
|
||||
* 200 : Ok : 서버가 클라이언트의 요청을 성공적으로 처리, 웹 페이지에서는 페이지 요청이 정상적으로 완료 (Ok)
|
||||
* 400 : Bad Request : 잘못 요청 (Bad Request)
|
||||
* 401 : Unauthorized : 권한 없음, 예를 들면, 로그인 페이지가 필요한 페이지를 로그인 없이 접속하려는 경우 반환되는 코드 (인증 실패) (Unauthorized)
|
||||
*
|
||||
* */
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
public class StatusResponse {
|
||||
|
||||
private HttpStatus status;
|
||||
|
||||
private String message;
|
||||
|
||||
private Object object;
|
||||
|
||||
private Object apiReturn;
|
||||
|
||||
private String messageTemp;
|
||||
|
||||
// private String timestamp;
|
||||
}
|
||||
@ -2,6 +2,7 @@ package com.itn.mjonApi.cmn.model;
|
||||
|
||||
import com.itn.mjonApi.mjon.api.msg.inqry.mapper.PriceMapper;
|
||||
import com.itn.mjonApi.mjon.api.msg.inqry.mapper.domain.PriceVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@ -19,6 +20,7 @@ import java.util.Map;
|
||||
* 2023-07-03 hylee 최초 생성
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class Price {
|
||||
|
||||
@ -29,48 +31,88 @@ public class Price {
|
||||
double mberMoney = priceMapper.selectMberMoney(mberId);
|
||||
|
||||
//시스템 단가 변수
|
||||
double sys_shortPrice = 0.0f;
|
||||
double sys_longPrice = 0.0f;
|
||||
double sys_picturePrice = 0.0f;
|
||||
double sys_shortPrice = 0.0f;
|
||||
double sys_longPrice = 0.0f;
|
||||
double sys_picturePrice = 0.0f;
|
||||
double sys_kakaoAtPrice = 0.0f;
|
||||
double sys_kakaoFtPrice = 0.0f;
|
||||
double sys_kakaoFtImgPrice = 0.0f;
|
||||
double sys_kakaoFtWideImgPrice = 0.0f;
|
||||
|
||||
//최종 단가 변수
|
||||
double shortPrice = 0.0f;
|
||||
double longPrice = 0.0f;
|
||||
double picturePrice = 0.0f;
|
||||
double shortPrice = 0.0f;
|
||||
double longPrice = 0.0f;
|
||||
double picturePrice = 0.0f;
|
||||
double kakaoAtPrice = 0.0f;
|
||||
double kakaoFtPrice = 0.0f;
|
||||
double kakaoFtImgPrice = 0.0f;
|
||||
double kakaoFtWideImgPrice = 0.0f;
|
||||
|
||||
//1.시스템 기본 단가, 사용자 개인단가 정보 불러오기
|
||||
Map<String, String> priceMap = priceMapper.selectMberPriceInfo(mberId);
|
||||
|
||||
//1-2.단가 계산을 위한 set
|
||||
sys_shortPrice = Double.parseDouble(String.valueOf(priceMap.get("sysShortPrice")));
|
||||
sys_longPrice = Double.parseDouble(String.valueOf(priceMap.get("sysLongPrice")));
|
||||
sys_picturePrice = Double.parseDouble(String.valueOf(priceMap.get("sysPicturePrice")));
|
||||
sys_shortPrice = Double.parseDouble(String.valueOf(priceMap.get("sysShortPrice")));
|
||||
sys_longPrice = Double.parseDouble(String.valueOf(priceMap.get("sysLongPrice")));
|
||||
sys_picturePrice = Double.parseDouble(String.valueOf(priceMap.get("sysPicturePrice")));
|
||||
|
||||
sys_kakaoAtPrice = Double.parseDouble(String.valueOf(priceMap.get("sysKakaoAtPrice")));
|
||||
sys_kakaoFtPrice = Double.parseDouble(String.valueOf(priceMap.get("sysKakaoFtPrice")));
|
||||
sys_kakaoFtImgPrice = Double.parseDouble(String.valueOf(priceMap.get("sysKakaoFtImgPrice")));
|
||||
sys_kakaoFtWideImgPrice = Double.parseDouble(String.valueOf(priceMap.get("sysKakaoFtWideImgPrice")));
|
||||
|
||||
|
||||
|
||||
shortPrice = Double.parseDouble(String.valueOf(priceMap.get("shortPrice")));
|
||||
longPrice = Double.parseDouble(String.valueOf(priceMap.get("longPrice")));
|
||||
picturePrice = Double.parseDouble(String.valueOf(priceMap.get("picturePrice")));
|
||||
|
||||
kakaoAtPrice = Double.parseDouble(String.valueOf(priceMap.get("kakaoAtPrice")));
|
||||
kakaoFtPrice = Double.parseDouble(String.valueOf(priceMap.get("kakaoFtPrice")));
|
||||
kakaoFtImgPrice = Double.parseDouble(String.valueOf(priceMap.get("kakaoFtImgPrice")));
|
||||
kakaoFtWideImgPrice = Double.parseDouble(String.valueOf(priceMap.get("kakaoFtWideImgPrice")));
|
||||
|
||||
//1-3. 최종 단가 계산
|
||||
shortPrice = shortPrice == 0.0f ? sys_shortPrice : shortPrice;
|
||||
longPrice = longPrice == 0.0f ? sys_longPrice : longPrice;
|
||||
picturePrice = picturePrice == 0.0f ? sys_picturePrice : picturePrice;
|
||||
|
||||
kakaoAtPrice = kakaoAtPrice == 0.0f ? sys_kakaoAtPrice : kakaoAtPrice;
|
||||
kakaoFtPrice = kakaoFtPrice == 0.0f ? sys_kakaoFtPrice : kakaoFtPrice;
|
||||
kakaoFtImgPrice = kakaoFtImgPrice == 0.0f ? sys_kakaoFtImgPrice : kakaoFtImgPrice;
|
||||
kakaoFtWideImgPrice = kakaoFtWideImgPrice == 0.0f ? sys_kakaoFtWideImgPrice : kakaoFtWideImgPrice;
|
||||
|
||||
//2. 단가별 발송 가능건수 계산을위한 변수 set
|
||||
int shortSendPsbltEa = 0;
|
||||
int longSendPsbltEa = 0;
|
||||
int pictureSendPsbltEa = 0;
|
||||
|
||||
int kakaoAtSendPsbltEa = 0;
|
||||
int kakaoFtSendPsbltEa = 0;
|
||||
int kakaoFtImgSendPsbltEa = 0;
|
||||
int kakaoFtWideImgSendPsbltEa = 0;
|
||||
|
||||
//2-1. 소수점 연산을 위한 BigDecimal Casting
|
||||
BigDecimal mberMoney_big = new BigDecimal(String.valueOf(mberMoney));
|
||||
BigDecimal shortPrice_big = new BigDecimal(String.valueOf(priceMap.get("sysShortPrice")));
|
||||
BigDecimal longPrice_big = new BigDecimal(String.valueOf(priceMap.get("sysLongPrice")));
|
||||
BigDecimal picturePrice_big = new BigDecimal(String.valueOf(priceMap.get("sysPicturePrice")));
|
||||
BigDecimal shortPrice_big = new BigDecimal(String.valueOf(shortPrice));
|
||||
BigDecimal longPrice_big = new BigDecimal(String.valueOf(longPrice));
|
||||
BigDecimal picturePrice_big = new BigDecimal(String.valueOf(picturePrice));
|
||||
|
||||
BigDecimal kakaoAtPrice_big = new BigDecimal(String.valueOf(kakaoAtPrice));
|
||||
BigDecimal kakaoFtPrice_big = new BigDecimal(String.valueOf(kakaoFtPrice));
|
||||
BigDecimal kakaoFtImgPrice_big = new BigDecimal(String.valueOf(kakaoFtImgPrice));
|
||||
BigDecimal kakaoFtWideImgPrice_big = new BigDecimal(String.valueOf(kakaoFtWideImgPrice));
|
||||
|
||||
//2-2. mberMoney가 0일경우 제외
|
||||
if(mberMoney_big.compareTo(BigDecimal.ZERO) != 0) {
|
||||
shortSendPsbltEa = mberMoney_big.divide(shortPrice_big, BigDecimal.ROUND_DOWN).intValue();
|
||||
longSendPsbltEa = mberMoney_big.divide(longPrice_big, BigDecimal.ROUND_DOWN).intValue();
|
||||
pictureSendPsbltEa = mberMoney_big.divide(picturePrice_big, BigDecimal.ROUND_DOWN).intValue();
|
||||
shortSendPsbltEa = mberMoney_big.divide(shortPrice_big, BigDecimal.ROUND_DOWN).intValue();
|
||||
longSendPsbltEa = mberMoney_big.divide(longPrice_big, BigDecimal.ROUND_DOWN).intValue();
|
||||
pictureSendPsbltEa = mberMoney_big.divide(picturePrice_big, BigDecimal.ROUND_DOWN).intValue();
|
||||
|
||||
kakaoAtSendPsbltEa = mberMoney_big.divide(kakaoAtPrice_big, BigDecimal.ROUND_DOWN).intValue();
|
||||
kakaoFtSendPsbltEa = mberMoney_big.divide(kakaoFtPrice_big, BigDecimal.ROUND_DOWN).intValue();
|
||||
kakaoFtImgSendPsbltEa = mberMoney_big.divide(kakaoFtImgPrice_big, BigDecimal.ROUND_DOWN).intValue();
|
||||
kakaoFtWideImgSendPsbltEa = mberMoney_big.divide(kakaoFtWideImgPrice_big, BigDecimal.ROUND_DOWN).intValue();
|
||||
}
|
||||
|
||||
//result set
|
||||
@ -78,11 +120,28 @@ public class Price {
|
||||
.shortPrice(shortPrice)
|
||||
.longPrice(longPrice)
|
||||
.picturePrice(picturePrice)
|
||||
|
||||
.kakaoAtPrice(kakaoAtPrice)
|
||||
.kakaoFtPrice(kakaoFtPrice)
|
||||
.kakaoFtImgPrice(kakaoFtImgPrice)
|
||||
.kakaoFtWideImgPrice(kakaoFtWideImgPrice)
|
||||
|
||||
|
||||
.shortSendPsbltEa(shortSendPsbltEa)
|
||||
.longSendPsbltEa(longSendPsbltEa)
|
||||
.pictureSendPsbltEa(pictureSendPsbltEa)
|
||||
|
||||
.kakaoAtSendPsbltEa(kakaoAtSendPsbltEa)
|
||||
.kakaoFtSendPsbltEa(kakaoFtSendPsbltEa)
|
||||
.kakaoFtImgSendPsbltEa(kakaoFtImgSendPsbltEa)
|
||||
.kakaoFtWideImgSendPsbltEa(kakaoFtWideImgSendPsbltEa)
|
||||
|
||||
.mberMoney(mberMoney)
|
||||
.build();
|
||||
|
||||
log.info(" + priceVO :: [{}]", priceVO);
|
||||
|
||||
|
||||
return priceVO;
|
||||
}
|
||||
|
||||
|
||||
@ -51,6 +51,15 @@ public enum StatMsg {
|
||||
, STAT_2041("2041","타이틀 데이터 오류")
|
||||
, STAT_2042("2042","대체문자 데이터 오류")
|
||||
|
||||
, STAT_2050("2050","지원하지 않는 이미지 형식입니다.")
|
||||
, STAT_2051("2051","이미지 용량은 5MB 이내여야 합니다.")
|
||||
, STAT_2052("2052","이미지를 읽을 수 없습니다.")
|
||||
, STAT_2053("2053","지원하지 않는 이미지 형식입니다.")
|
||||
, STAT_2054("2054","이미지 가로폭인 500px 미만입니다.")
|
||||
, STAT_2055("2055","유효한 이미지 파일이 없습니다.")
|
||||
, STAT_2056("2056","비율 2:1 이상 또는 3:4 이하만 허용됩니다.")
|
||||
, STAT_2057("2057","대체문자(MMS) 이미지는 10MB를 초과할 수 없습니다.")
|
||||
|
||||
, STAT_2099("2099","기타 시스템 오류")
|
||||
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ import com.itn.mjonApi.util.email.EmailVO;
|
||||
import com.itn.mjonApi.util.email.SendMail;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -16,7 +15,6 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* packageName : com.itn.mjonApi.etc.ganpandaum.service.impl
|
||||
@ -70,33 +68,16 @@ public class GdServiceImpl implements GdService {
|
||||
SendMail sMail = new SendMail();
|
||||
|
||||
try {
|
||||
emailContent = Jsoup.connect(GANPANDAUP_ESTIMATE_TEMPLATE_URL)
|
||||
.data("query", "Java")
|
||||
.userAgent("Mozilla")
|
||||
.cookie("auth", "token")
|
||||
.timeout(3000)
|
||||
.post()
|
||||
.toString();
|
||||
// ./src/main/resources/templates/estimate.html
|
||||
emailContent = emailContent
|
||||
.replace("[[_Company_]]", gdVO.getGdCompany())
|
||||
.replace("[[_Name_]]", gdVO.getGdName())
|
||||
.replace("[[_Phone_]]", gdVO.getGdPhone())
|
||||
.replace("[[_Email_]]", gdVO.getGdEmail())
|
||||
.replace("[[_Addr_]]", gdVO.getGdAddr())
|
||||
.replace("[[_Content_]]", gdVO.getGdContent())
|
||||
;
|
||||
|
||||
|
||||
|
||||
// 메일 첨부파일을 위한 절대경로
|
||||
// 메일 제목
|
||||
String mailTitle = "[간판다움 견적의뢰] "+gdVO.getGdName()+"["+gdVO.getGdCompany()+"]님의 견적 의뢰입니다._"+LocalDate.now();
|
||||
|
||||
sMail.itnSendMail(
|
||||
EmailVO.builder()
|
||||
.title(mailTitle)
|
||||
.contents(emailContent)
|
||||
.title("현재달 월급명세서 전달드립니다.")
|
||||
.contents("안녕하세요 귀하에 노고에 감사드립니다. 비밀번호는 생년워일입니다.")
|
||||
.fileInfo(p_file)
|
||||
.atch_file_name(fileNm)
|
||||
.send_to(GANPANDAUP_RECEIVER_EMAIL)
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package com.itn.mjonApi.mjon.api.kakao.at.send.mapper.domain;
|
||||
|
||||
import com.itn.mjonApi.cmn.domain.SendRequestCmnVO;
|
||||
import lombok.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
@ -44,7 +43,7 @@ public class MsgAtRequestVO implements Serializable {
|
||||
|
||||
private String test_yn;
|
||||
|
||||
private List<VarListMapVO> varListMap = new ArrayList<>();
|
||||
private List<VarAtListMapVO> varListMap = new ArrayList<>();
|
||||
|
||||
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import lombok.ToString;
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public class VarListMapVO {
|
||||
public class VarAtListMapVO {
|
||||
|
||||
|
||||
/**
|
||||
@ -0,0 +1,90 @@
|
||||
package com.itn.mjonApi.mjon.api.kakao.at.send.service;
|
||||
|
||||
import com.itn.mjonApi.mjon.api.kakao.at.send.mapper.domain.MsgAtRequestVO;
|
||||
import com.itn.mjonApi.mjon.api.kakao.at.send.mapper.domain.VarAtListMapVO;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* fileName : IndexedParameterParserService.java
|
||||
* author : hylee
|
||||
* date : 2025-08-18
|
||||
* description : 알림톡 인덱스된 파라미터 파싱 서비스
|
||||
* parseIndexedParametersFromRequest 메서드의 아키텍처 분리를 위해 생성
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-08-18 hylee 최초 생성
|
||||
*/
|
||||
@Service
|
||||
public class AtIndexedParameterParserService {
|
||||
|
||||
// 정규식 패턴을 static final로 캐싱하여 성능 최적화
|
||||
private static final Pattern INDEX_PATTERN =
|
||||
Pattern.compile("^(callTo|templateContent|templateTitle|subMsgTxt)_(\\d+)$");
|
||||
|
||||
/**
|
||||
* HttpServletRequest에서 동적으로 인덱스된 파라미터들을 파싱하여 VarListMapVO 리스트로 변환
|
||||
* callTo_1~100, templateContent_1~100, templateTitle_1~100, subMsgTxt_1~100 등을 동적 처리
|
||||
*
|
||||
* @param request HTTP 요청 객체
|
||||
* @return 파싱된 VarListMapVO 리스트
|
||||
*/
|
||||
public List<VarAtListMapVO> parseIndexedParameters(MsgAtRequestVO msgAtRequestVO, HttpServletRequest request) {
|
||||
List<VarAtListMapVO> varListMap = new ArrayList<>();
|
||||
|
||||
// 모든 파라미터 맵 가져오기
|
||||
Map<String, String[]> parameterMap = request.getParameterMap();
|
||||
|
||||
// 인덱스별 데이터를 저장할 맵
|
||||
Map<Integer, Map<String, String>> indexedDataMap = new HashMap<>();
|
||||
|
||||
|
||||
String subMsgSendYn = msgAtRequestVO.getSubMsgSendYn();
|
||||
|
||||
// 모든 파라미터를 순회하며 인덱스된 파라미터 찾기
|
||||
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
|
||||
String paramName = entry.getKey();
|
||||
String[] paramValues = entry.getValue();
|
||||
|
||||
Matcher matcher = INDEX_PATTERN.matcher(paramName);
|
||||
if (matcher.matches() && paramValues.length > 0) {
|
||||
String fieldName = matcher.group(1); // callTo, templateContent 등
|
||||
int index = Integer.parseInt(matcher.group(2)); // 1, 2, 3 등
|
||||
String value = paramValues[0]; // 파라미터 값
|
||||
|
||||
// 인덱스별 데이터 맵에 저장
|
||||
indexedDataMap.computeIfAbsent(index, k -> new HashMap<>()).put(fieldName, value);
|
||||
}
|
||||
}
|
||||
|
||||
// 인덱스 순서대로 VarListMapVO 생성
|
||||
List<Integer> sortedIndexes = new ArrayList<>(indexedDataMap.keySet());
|
||||
Collections.sort(sortedIndexes);
|
||||
|
||||
for (Integer index : sortedIndexes) {
|
||||
Map<String, String> dataMap = indexedDataMap.get(index);
|
||||
|
||||
VarAtListMapVO vo = new VarAtListMapVO();
|
||||
vo.setCallToList(dataMap.get("callTo"));
|
||||
vo.setTemplateContent(dataMap.get("templateContent"));
|
||||
vo.setTemplateTitle(dataMap.get("templateTitle"));
|
||||
|
||||
// 치환 데이터는 subMsgSendYn 이 Y일때
|
||||
if("Y".equals(subMsgSendYn)){
|
||||
vo.setSubMsgTxt(dataMap.get("subMsgTxt"));
|
||||
}
|
||||
|
||||
// 필수 필드 중 하나라도 있으면 리스트에 추가
|
||||
if (vo.getCallToList() != null || vo.getTemplateContent() != null || vo.getTemplateTitle() != null) {
|
||||
varListMap.add(vo);
|
||||
}
|
||||
}
|
||||
|
||||
return varListMap;
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@ import com.itn.mjonApi.cmn.msg.RestResponse;
|
||||
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.at.send.mapper.domain.MsgAtRequestVO;
|
||||
import com.itn.mjonApi.mjon.api.kakao.at.send.mapper.domain.VarListMapVO;
|
||||
import com.itn.mjonApi.mjon.api.kakao.at.send.mapper.domain.VarAtListMapVO;
|
||||
import com.itn.mjonApi.util.MunjaUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -35,7 +35,7 @@ import java.util.List;
|
||||
public class AtParameterProcessingService {
|
||||
|
||||
@Autowired
|
||||
private IndexedParameterParserService indexedParameterParserService;
|
||||
private AtIndexedParameterParserService indexedParameterParserService;
|
||||
|
||||
@Autowired
|
||||
private InqryService inqryService;
|
||||
@ -63,11 +63,11 @@ public class AtParameterProcessingService {
|
||||
if (STAT_2030 != null) return STAT_2030;
|
||||
|
||||
// 파싱 로직을 IndexedParameterParserService에 위임
|
||||
List<VarListMapVO> parsedList = indexedParameterParserService.parseIndexedParameters(msgAtRequestVO, request);
|
||||
List<VarAtListMapVO> parsedList = indexedParameterParserService.parseIndexedParameters(msgAtRequestVO, request);
|
||||
|
||||
// 파싱된 각 VO에 대해 검증 수행
|
||||
for (VarListMapVO vo : parsedList) {
|
||||
String validationError = MunjaUtil.kakaoCmnValidate(vo, msgAtRequestVO.getSubMsgSendYn());
|
||||
for (VarAtListMapVO vo : parsedList) {
|
||||
String validationError = MunjaUtil.kakaoAtValidate(vo, msgAtRequestVO.getSubMsgSendYn());
|
||||
|
||||
if (StringUtils.isNotEmpty(validationError)) {
|
||||
return validationError; // 검증 실패 시 오류 코드 반환
|
||||
|
||||
@ -48,25 +48,8 @@ public class SendAtRestController {
|
||||
|
||||
// https://smartsms.aligo.in/alimapi.html
|
||||
|
||||
|
||||
return ResponseEntity.ok().body(sendAtService.sendAtData(msgAtRequestVO, request));
|
||||
// return ResponseEntity.ok().body(new RestResponse(msgAtRequestVO));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param msgsRequestVO
|
||||
* @description [문자 발송] 다른 내용으로 여려명에게 보냄
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
/* @PostMapping("/api/send/sendMsgs")
|
||||
public ResponseEntity<RestResponse> sendMsgs(MsgsRequestVO msgsRequestVO) throws Exception {
|
||||
return ResponseEntity.ok().body(sendAtService.sendMsgsData_advc(msgsRequestVO));
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
package com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@ToString
|
||||
public class FtButtonVO {
|
||||
private String name;
|
||||
private String linkType; // AC, DS, WL, AL, BK, MD
|
||||
private String linkTypeName; // 채널 추가, 배송조회, 웹링크, 앱링크, 봇키워드, 메시지전달
|
||||
private String linkMo; // 모바일 링크
|
||||
private String linkPc; // PC 링크
|
||||
private String linkIos; // iOS Scheme
|
||||
private String linkAnd; // Android Scheme
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* packageName : com.itn.mjonApi.cmn.msg
|
||||
* fileName : mjonResponse
|
||||
* author : hylee
|
||||
* date : 2023-05-12
|
||||
* description : 문자온 프로젝트에서 받은 리턴값
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2023-05-12 hylee 최초 생성
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonIgnoreProperties(ignoreUnknown = true) // JSON에 있지만 VO에 없는 필드를 무시하고 무사히 역직렬화해 줌
|
||||
@ToString
|
||||
public class MjonFtResponseVO {
|
||||
|
||||
private String result;
|
||||
private String message;
|
||||
private String resultSts; // 전송결과 갯수
|
||||
private String resultBlockSts; // 수신거부 갯수
|
||||
private String msgGroupId;
|
||||
private String afterCash;
|
||||
private String msgType;
|
||||
private String statCode;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param apiReturnNode
|
||||
* @return ResponseEntity vo convert
|
||||
* @throws JsonProcessingException
|
||||
*/
|
||||
// public static MjonAtResponseVO getMjonResponse(JsonNode apiReturnNode) throws JsonProcessingException {
|
||||
// ObjectMapper objectMapper = new ObjectMapper();
|
||||
// return objectMapper.treeToValue(apiReturnNode, MjonAtResponseVO.class);
|
||||
// }
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
package com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* fileName : MsgAtRequestVO.java
|
||||
* author : hylee
|
||||
* date : 2025-07-29
|
||||
* description : 알림톡
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-07-29 hylee 최초 생성
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@ToString
|
||||
@Getter
|
||||
@Setter
|
||||
public class MsgFtRequestVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String sendKind = "A";
|
||||
|
||||
private String mberId; // value = "사용자 ID", example = "goodgkdus"
|
||||
|
||||
private String accessKey; // value = "Api Key", example = "0367a25ec370d1141898a0b9767103"
|
||||
|
||||
private String senderKey; // 카카오 알림톡 채널ID
|
||||
|
||||
private String templateCode; // 카카오 알림톡 템플릿 코드
|
||||
|
||||
private String callFrom; // value = "발신번호 :: 정책이 필요함", example = "01011112222"
|
||||
|
||||
// 대체문자 여부
|
||||
private String subMsgSendYn;
|
||||
|
||||
// 광고 여부
|
||||
private String adFlag;
|
||||
|
||||
private String test_yn;
|
||||
|
||||
|
||||
// 실제 업로드 파일(로그/직렬화 제외)
|
||||
@JsonIgnore
|
||||
@ToString.Exclude
|
||||
private MultipartFile templateImage;
|
||||
|
||||
@JsonIgnore
|
||||
@ToString.Exclude
|
||||
private MultipartFile subImage;
|
||||
|
||||
// ====== ⬇️ AOP/DB 저장용(문자열/숫자만) 메타데이터 필드들 ======
|
||||
// template
|
||||
private String templateImageName;
|
||||
private String templateImageContentType;
|
||||
private Long templateImageSize; // bytes
|
||||
private Integer templateImageWidth; // px
|
||||
private Integer templateImageHeight; // px
|
||||
private String templateImageSha256; // hexdigest
|
||||
private String templateImageSavedPath; // 서버 저장 경로(선택)
|
||||
private String templateAtchFileId; // 내부 첨부ID(선택)
|
||||
private String templateMsgType;
|
||||
private String templateImageUrl;
|
||||
|
||||
// sub
|
||||
// private String subImageName;
|
||||
// private String subImageContentType;
|
||||
// private Long subImageSize;
|
||||
// private Integer subImageWidth;
|
||||
// private Integer subImageHeight;
|
||||
// private String subImageSha256;
|
||||
// private String subImageSavedPath;
|
||||
// private String subAtchFileId;
|
||||
// private String subMsgType;
|
||||
|
||||
private List<VarFtListMapVO> varListMap = new ArrayList<>();
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain;
|
||||
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public class VarFtListMapVO {
|
||||
|
||||
|
||||
/**
|
||||
* @description : 수신자번호
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* @description : 친구톡 내용
|
||||
*/
|
||||
private String templateContent;
|
||||
|
||||
/**
|
||||
* @description : 치환문자
|
||||
*/
|
||||
private String subMsgTxt;
|
||||
|
||||
/**
|
||||
* @description : 치환문자 이미지
|
||||
*/
|
||||
private String filePath1;
|
||||
|
||||
/**
|
||||
* @description : 버튼 사용
|
||||
*/
|
||||
private List<FtButtonVO> buttons = new ArrayList<>();
|
||||
|
||||
//
|
||||
// /**
|
||||
// * @description : [*3*] - 치환문자
|
||||
// */
|
||||
// private String rep3;
|
||||
//
|
||||
// /**
|
||||
// * @description : [*4*] - 치환문자
|
||||
// */
|
||||
// private String rep4;
|
||||
//
|
||||
// /**
|
||||
// * @description : 제목
|
||||
// */
|
||||
// private String subject;
|
||||
//
|
||||
// /**
|
||||
// * @description : 내용
|
||||
// */
|
||||
// private String message;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,146 @@
|
||||
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.mjon.api.kakao.ft.send.mapper.domain.FtButtonVO;
|
||||
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.springframework.stereotype.Service;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* fileName : IndexedParameterParserService.java
|
||||
* author : hylee
|
||||
* date : 2025-08-18
|
||||
* description : 알림톡 인덱스된 파라미터 파싱 서비스
|
||||
* parseIndexedParametersFromRequest 메서드의 아키텍처 분리를 위해 생성
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-08-18 hylee 최초 생성
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class FtIndexedParameterParserService {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
// 정규식 패턴을 static final로 캐싱하여 성능 최적화
|
||||
private static final Pattern INDEX_PATTERN =
|
||||
Pattern.compile("^(callTo|templateContent|subMsgTxt|button)_(\\d+)$");
|
||||
|
||||
/**
|
||||
* HttpServletRequest에서 동적으로 인덱스된 파라미터들을 파싱하여 VarListMapVO 리스트로 변환
|
||||
* callTo_1~100, templateContent_1~100, templateTitle_1~100, subMsgTxt_1~100 등을 동적 처리
|
||||
*
|
||||
* @param request HTTP 요청 객체
|
||||
* @return 파싱된 VarListMapVO 리스트
|
||||
*/
|
||||
public List<VarFtListMapVO> parseIndexedParameters(MsgFtRequestVO msgAtRequestVO, HttpServletRequest request) {
|
||||
List<VarFtListMapVO> varListMap = new ArrayList<>();
|
||||
|
||||
// 모든 파라미터 맵 가져오기
|
||||
Map<String, String[]> parameterMap = request.getParameterMap();
|
||||
|
||||
// 인덱스별 데이터를 저장할 맵
|
||||
Map<Integer, Map<String, String>> indexedDataMap = new HashMap<>();
|
||||
|
||||
|
||||
String subMsgSendYn = msgAtRequestVO.getSubMsgSendYn();
|
||||
|
||||
// 모든 파라미터를 순회하며 인덱스된 파라미터 찾기
|
||||
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
|
||||
String paramName = entry.getKey();
|
||||
String[] paramValues = entry.getValue();
|
||||
|
||||
Matcher matcher = INDEX_PATTERN.matcher(paramName);
|
||||
if (matcher.matches() && paramValues.length > 0) {
|
||||
String fieldName = matcher.group(1); // callTo, templateContent 등
|
||||
int index = Integer.parseInt(matcher.group(2)); // 1, 2, 3 등
|
||||
String value = paramValues[0]; // 파라미터 값
|
||||
|
||||
// 인덱스별 데이터 맵에 저장
|
||||
indexedDataMap.computeIfAbsent(index, k -> new HashMap<>()).put(fieldName, value);
|
||||
}
|
||||
}
|
||||
|
||||
// 인덱스 순서대로 VarListMapVO 생성
|
||||
List<Integer> sortedIndexes = new ArrayList<>(indexedDataMap.keySet());
|
||||
Collections.sort(sortedIndexes);
|
||||
|
||||
for (Integer index : sortedIndexes) {
|
||||
Map<String, String> dataMap = indexedDataMap.get(index);
|
||||
|
||||
VarFtListMapVO vo = new VarFtListMapVO();
|
||||
vo.setPhone(dataMap.get("callTo"));
|
||||
vo.setTemplateContent(dataMap.get("templateContent"));
|
||||
|
||||
// 치환 데이터는 subMsgSendYn 이 Y일때
|
||||
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()
|
||||
.name(getJsonText(button, "name"))
|
||||
.linkType(getJsonText(button, "linkType"))
|
||||
.linkTypeName(getJsonText(button, "linkTypeName"))
|
||||
.linkMo(getJsonText(button, "linkMo"))
|
||||
.linkPc(getJsonText(button, "linkPc"))
|
||||
.linkIos(getJsonText(button, "linkIos"))
|
||||
.linkAnd(getJsonText(button, "linkAnd"))
|
||||
.build();
|
||||
buttonList.add(ftButton);
|
||||
}
|
||||
}
|
||||
vo.setButtons(buttonList);
|
||||
} catch (Exception e) {
|
||||
log.error("버튼 JSON 파싱 실패 - index: {}, data: {}", index, buttonData, e);
|
||||
vo.setButtons(new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
||||
// 필수 필드 중 하나라도 있으면 리스트에 추가
|
||||
if (vo.getPhone() != null || vo.getTemplateContent() != null) {
|
||||
varListMap.add(vo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
return varListMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* JsonNode에서 안전하게 텍스트 값을 추출하는 헬퍼 메서드
|
||||
*
|
||||
* @param node JSON 노드
|
||||
* @param fieldName 필드명
|
||||
* @return 텍스트 값 (null 또는 빈 값일 경우 null 반환)
|
||||
*/
|
||||
private String getJsonText(JsonNode node, String fieldName) {
|
||||
if (node != null && node.has(fieldName)) {
|
||||
JsonNode fieldNode = node.get(fieldName);
|
||||
if (!fieldNode.isNull()) {
|
||||
String text = fieldNode.asText();
|
||||
return text.isEmpty() ? null : text;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
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.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.util.MunjaUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* fileName : AtParameterProcessingService.java
|
||||
* author : hylee
|
||||
* date : 2025-08-18
|
||||
* description : 알림톡 파라미터 처리 서비스
|
||||
* MsgAtRequestVO의 비즈니스 로직을 담당
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-08-18 hylee 최초 생성
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class FtParameterProcessingService {
|
||||
|
||||
@Autowired
|
||||
private FtIndexedParameterParserService indexedParameterParserService;
|
||||
|
||||
@Autowired
|
||||
private InqryService inqryService;
|
||||
|
||||
/**
|
||||
* HttpServletRequest에서 동적으로 인덱스된 파라미터들을 파싱하고 검증하여
|
||||
* MsgAtRequestVO의 varListMap에 설정
|
||||
*
|
||||
* @param msgFtRequestVO 요청 VO 객체
|
||||
* @param request HTTP 요청 객체
|
||||
* @return 검증 실패 시 오류 코드, 성공 시 null
|
||||
*/
|
||||
public String processIndexedParameters(MsgFtRequestVO msgFtRequestVO, HttpServletRequest request) {
|
||||
|
||||
// 기존 varListMap 초기화
|
||||
msgFtRequestVO.setVarListMap(new ArrayList<>());
|
||||
|
||||
|
||||
// 채널ID 확인
|
||||
String STAT_2010 = this.validateSenderKey(msgFtRequestVO.getMberId(), msgFtRequestVO.getSenderKey());
|
||||
if (STAT_2010 != null) return STAT_2010;
|
||||
|
||||
// 템플릿 코드 확인
|
||||
// String STAT_2030 = this.validateTemplateCode(msgFtRequestVO.getMberId(), msgFtRequestVO.getSenderKey(), msgFtRequestVO.getTemplateCode());
|
||||
// if (STAT_2030 != null) return STAT_2030;
|
||||
|
||||
// 파싱 로직을 IndexedParameterParserService에 위임
|
||||
List<VarFtListMapVO> parsedList = indexedParameterParserService.parseIndexedParameters(msgFtRequestVO, request);
|
||||
|
||||
// 파싱된 각 VO에 대해 검증 수행
|
||||
for (VarFtListMapVO vo : parsedList) {
|
||||
String validationError = MunjaUtil.kakaoFtValidate(vo, msgFtRequestVO.getSubMsgSendYn());
|
||||
|
||||
if (StringUtils.isNotEmpty(validationError)) {
|
||||
return validationError; // 검증 실패 시 오류 코드 반환
|
||||
}
|
||||
|
||||
// 검증 통과한 VO를 리스트에 추가
|
||||
msgFtRequestVO.getVarListMap().add(vo);
|
||||
}
|
||||
|
||||
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)) {
|
||||
return "STAT_2010";
|
||||
}
|
||||
List<MjKakaoProfileInfoVO> resultList = inqryService.getChnlId(mberId);
|
||||
boolean ok = resultList.stream().anyMatch(p -> senderKey.equals(p.getSenderKey()));
|
||||
return ok ? null : "STAT_2010";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.itn.mjonApi.mjon.api.kakao.ft.send.service;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.itn.mjonApi.cmn.msg.RestResponse;
|
||||
import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.MsgFtRequestVO;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public interface SendFtService {
|
||||
|
||||
|
||||
// RestResponse sendMsgData(MsgRequestVO msgRequestVO) throws Exception;
|
||||
//
|
||||
// RestResponse sendMsgData_advc(MsgRequestVO msgRequestVO) throws Exception;
|
||||
//
|
||||
// RestResponse sendMsgsData(MsgsRequestVO msgsRequestVO) throws Exception;
|
||||
//
|
||||
// RestResponse sendMsgsData_advc(MsgsRequestVO msgsRequestVO) throws Exception;
|
||||
|
||||
RestResponse sendFtData(MsgFtRequestVO msgFtRequestVO, HttpServletRequest request) throws IOException, NoSuchAlgorithmException;
|
||||
}
|
||||
@ -0,0 +1,137 @@
|
||||
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.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 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;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SendFtServiceImpl implements SendFtService {
|
||||
|
||||
@Autowired
|
||||
private FtParameterProcessingService ftParameterProcessingService;
|
||||
|
||||
private ApiService<Response> apiService;
|
||||
|
||||
@Autowired
|
||||
SendMapper sendMapper;
|
||||
|
||||
@Autowired
|
||||
PriceMapper priceMapper;
|
||||
|
||||
@Autowired
|
||||
public SendFtServiceImpl(ApiService<Response> apiService) {
|
||||
this.apiService = apiService;
|
||||
}
|
||||
|
||||
private static final String replaseStrList = "[*이름*],[*1*],[*2*],[*3*],[*4*]";
|
||||
|
||||
|
||||
/**
|
||||
* @param
|
||||
* @return
|
||||
* @throws Exception 처리 중 예외 발생 가능
|
||||
* @date 2025-07-29
|
||||
* @Discription 치환없는 알림톡 데이터
|
||||
* @author hylee
|
||||
*/
|
||||
@Override
|
||||
public RestResponse sendFtData(MsgFtRequestVO msgFtRequestVO, HttpServletRequest request) throws IOException, NoSuchAlgorithmException {
|
||||
|
||||
if(StringUtils.isNotEmpty(msgFtRequestVO.getTest_yn())){
|
||||
// YF => 실패 테스트 데이터
|
||||
// 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)){
|
||||
log.info("falseCode :: [{}]", falseCode);
|
||||
return new RestResponse(new FailRestResponse(falseCode,""));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// MjonResponseVO munjaSendResponse = apiService.postForEntity(
|
||||
// "/web/mjon/kakao/friendstalk/kakaoFriendsTalkMsgSendAjax_advc.do"
|
||||
// , msgFtRequestVO
|
||||
// , String.class
|
||||
// );
|
||||
|
||||
|
||||
// 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(),""));
|
||||
// }
|
||||
|
||||
return new RestResponse(msgFtRequestVO);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package com.itn.mjonApi.mjon.api.kakao.ft.send.web;
|
||||
|
||||
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.service.SendFtService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* packageName : com.itn.mjonApi.mjon.send.web
|
||||
* fileName : SendRestController
|
||||
* author : hylee
|
||||
* date : 2023-02-15
|
||||
* description :
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2023-02-15 hylee 최초 생성
|
||||
*/
|
||||
|
||||
// 치환문자가 있으면 , => §로 치환
|
||||
|
||||
@CrossOrigin("*") // 모든 요청에 접근 허용
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class SendFtRestController {
|
||||
|
||||
|
||||
@Autowired
|
||||
private SendFtService sendFtService;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param msgFtRequestVO
|
||||
* @param request
|
||||
* @Discription [문자 발송] 같은 내용으로 여려명에게 보냄
|
||||
* @return
|
||||
*/
|
||||
@PostMapping(value = "/api/kakao/ft/sendMsg",
|
||||
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public ResponseEntity<RestResponse> sendMsg(
|
||||
@ModelAttribute MsgFtRequestVO msgFtRequestVO // 폼 텍스트들 바인딩
|
||||
, HttpServletRequest request) throws Exception {
|
||||
|
||||
// https://smartsms.aligo.in/friendapi.html
|
||||
|
||||
return ResponseEntity.ok().body(sendFtService.sendFtData(msgFtRequestVO, request));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,260 @@
|
||||
package com.itn.mjonApi.mjon.api.kakao.utils;
|
||||
|
||||
import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.MsgFtRequestVO;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Kakao FT 이미지 메타데이터 유틸리티.
|
||||
* - 템플릿/서브 이미지의 파일명, 용량, 폭/높이, SHA-256 해시 세팅
|
||||
* - 템플릿: I/W 타입 판정(2:1, 4:3, 가로≥500) 및 검증
|
||||
* - 서브(MMS): jpg/jpeg/png/gif, ≤10MB만 검증(권장 640×960은 안내 수준)
|
||||
* - 실패 시 코드 문자열 반환(성공 시 null)
|
||||
*/
|
||||
public class FtFileMetaUtil {
|
||||
|
||||
// ===== 실패코드 상수 정의 =====
|
||||
private static final String OK = null; // 성공 시 null 반환
|
||||
private static final String FT_E_EMPTY = "STAT_2055"; // 파일이 비어있음
|
||||
private static final String FT_E_IMG_READ_FAIL = "STAT_2052"; // 이미지 읽기 실패
|
||||
private static final String FT_E_WIDTH_LT_500 = "STAT_2054"; // 가로폭 500px 미만
|
||||
private static final String FT_E_RATIO_OUT_OF_RANGE = "STAT_2056"; // 비율이 2:1~4:3 범위 밖
|
||||
private static final String FT_E_SIZE_GT_5MB = "STAT_2051"; // 파일 크기 초과(5MB 이상)
|
||||
private static final String FT_E_CONTENT_TYPE = "STAT_2050"; // 허용되지 않는 콘텐츠 타입// 추가
|
||||
private static final String FT_E_SIZE_GT_10MB = "STAT_2057"; // 대체문자(MMS) 이미지는 10MB를 초과할 수 없습니다.
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 템플릿 이미지의 메타데이터(파일명, 용량, 폭/높이, 해시, 타입)를 MsgFtRequestVO에 채움
|
||||
* @param vo 메타데이터를 세팅할 VO
|
||||
* @throws IOException
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
public static String fillTemplateMeta(MsgFtRequestVO vo) throws IOException, NoSuchAlgorithmException {
|
||||
return fillMeta(vo.getTemplateImage(),
|
||||
(name) -> vo.setTemplateImageName(name),
|
||||
(ct) -> vo.setTemplateImageContentType(ct),
|
||||
(size) -> vo.setTemplateImageSize(size),
|
||||
(w) -> vo.setTemplateImageWidth(w),
|
||||
(h) -> vo.setTemplateImageHeight(h),
|
||||
(hash) -> vo.setTemplateImageSha256(hash),
|
||||
(type) -> vo.setTemplateMsgType(type) // I/W 세팅
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 서브(와이드) 이미지의 메타데이터를 MsgFtRequestVO에 채움
|
||||
* - MMS 규격 검증(타입/10MB)만 수행, I/W 판정 없음
|
||||
* @param vo 메타데이터를 세팅할 VO
|
||||
* @throws IOException
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
// public static String fillSubMeta(MsgFtRequestVO vo) throws IOException, NoSuchAlgorithmException {
|
||||
// return fillMetaForSubImage(vo.getSubImage(),
|
||||
// (name) -> vo.setSubImageName(name),
|
||||
// (ct) -> vo.setSubImageContentType(ct),
|
||||
// (size) -> vo.setSubImageSize(size),
|
||||
// (w) -> vo.setSubImageWidth(w),
|
||||
// (h) -> vo.setSubImageHeight(h),
|
||||
// (hash) -> vo.setSubImageSha256(hash)
|
||||
// );
|
||||
// }
|
||||
|
||||
/**
|
||||
* subImage(MMS) 전용 메타데이터 수집/검증 메서드
|
||||
* - 검증: 파일 존재 여부, 용량(≤10MB), Content-Type(jpg/jpeg/png/gif)
|
||||
* - 세팅: 파일명, Content-Type, 파일 크기, width/height, SHA-256
|
||||
* - 비율/가로 500px 검증은 하지 않음 (권장 640×960은 안내 수준)
|
||||
*
|
||||
* @param f 업로드된 서브 이미지 파일
|
||||
* @param nameC 파일명 setter
|
||||
* @param ctC Content-Type setter
|
||||
* @param sizeC 파일 크기 setter
|
||||
* @param wC width setter
|
||||
* @param hC height setter
|
||||
* @param hashC SHA-256 setter
|
||||
* @return 실패코드(String), 성공 시 null(OK)
|
||||
* @throws IOException
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
private static String fillMetaForSubImage(
|
||||
MultipartFile f,
|
||||
Consumer<String> nameC,
|
||||
Consumer<String> ctC,
|
||||
Consumer<Long> sizeC,
|
||||
Consumer<Integer> wC,
|
||||
Consumer<Integer> hC,
|
||||
Consumer<String> hashC
|
||||
) throws IOException, NoSuchAlgorithmException {
|
||||
// 1) 파일 존재/빈 파일 체크
|
||||
if (f == null || f.isEmpty()) return FT_E_EMPTY;
|
||||
|
||||
// 2) 용량(10MB) 제한
|
||||
if (f.getSize() > 10L * 1024 * 1024) return FT_E_SIZE_GT_10MB;
|
||||
|
||||
// 3) 허용 Content-Type 확인 (jpg/jpeg/png/gif)
|
||||
String ct = f.getContentType();
|
||||
if (!isAllowedSubCt(ct)) return FT_E_CONTENT_TYPE;
|
||||
|
||||
// 4) 기본 메타 세팅
|
||||
nameC.accept(f.getOriginalFilename());
|
||||
ctC.accept(ct);
|
||||
sizeC.accept(f.getSize());
|
||||
|
||||
// 5) 이미지 읽기 → width/height 세팅 (비율 제한 없음)
|
||||
int w = 0, h = 0;
|
||||
try (InputStream is = f.getInputStream()) {
|
||||
BufferedImage img = ImageIO.read(is);
|
||||
if (img == null) return FT_E_IMG_READ_FAIL;
|
||||
w = img.getWidth();
|
||||
h = img.getHeight();
|
||||
wC.accept(w);
|
||||
hC.accept(h);
|
||||
} catch (Exception e) {
|
||||
return FT_E_IMG_READ_FAIL;
|
||||
}
|
||||
|
||||
// sha-256
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
md.update(f.getBytes());
|
||||
hashC.accept(bytesToHex(md.digest()));
|
||||
|
||||
// 권장 사이즈(640×960)는 안내사항이므로 실패코드 미반환
|
||||
return OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* 공통 메타데이터 채우기 유틸
|
||||
* - 파일명, Content-Type, 파일 크기
|
||||
* - 이미지 폭/높이
|
||||
* - 파일 SHA-256 해시
|
||||
* - 가로/세로 비율 기반의 메시지 타입(I/W) 판정
|
||||
*
|
||||
* @param f MultipartFile (이미지 파일)
|
||||
* @param nameC 파일명 setter
|
||||
* @param ctC ContentType setter
|
||||
* @param sizeC 파일 크기 setter
|
||||
* @param wC width setter
|
||||
* @param hC height setter
|
||||
* @param hashC SHA-256 setter
|
||||
* @param typeC 이미지 타입(I/W) setter
|
||||
* @return 실패코드(String), 성공 시 null 반환
|
||||
*/
|
||||
private static String fillMeta(MultipartFile f,
|
||||
Consumer<String> nameC,
|
||||
Consumer<String> ctC,
|
||||
Consumer<Long> sizeC,
|
||||
Consumer<Integer> wC,
|
||||
Consumer<Integer> hC,
|
||||
Consumer<String> hashC,
|
||||
Consumer<String> typeC
|
||||
) throws IOException, NoSuchAlgorithmException {
|
||||
if (f == null || f.isEmpty()) return FT_E_EMPTY;
|
||||
|
||||
// 용량/콘텐츠타입 1차 검증
|
||||
if (f.getSize() > 5L * 1024 * 1024) return FT_E_SIZE_GT_5MB;
|
||||
String ct = f.getContentType();
|
||||
if (ct == null || !(ct.equalsIgnoreCase("image/jpeg") || ct.equalsIgnoreCase("image/png"))) {
|
||||
return FT_E_CONTENT_TYPE;
|
||||
}
|
||||
|
||||
nameC.accept(f.getOriginalFilename());
|
||||
ctC.accept(ct);
|
||||
sizeC.accept(f.getSize());
|
||||
|
||||
int w = 0, h = 0;
|
||||
try (InputStream is = f.getInputStream()) {
|
||||
BufferedImage img = ImageIO.read(is);
|
||||
if (img == null) return FT_E_IMG_READ_FAIL;
|
||||
w = img.getWidth();
|
||||
h = img.getHeight();
|
||||
wC.accept(w);
|
||||
hC.accept(h);
|
||||
} catch (Exception e) {
|
||||
return FT_E_IMG_READ_FAIL;
|
||||
}
|
||||
|
||||
// sha-256
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
md.update(f.getBytes());
|
||||
hashC.accept(bytesToHex(md.digest()));
|
||||
|
||||
// 규칙 검증 + 타입(I/W) 판정
|
||||
String typeOrCode = calcFtMsgTypeOrCode(w, h);
|
||||
if (typeOrCode.length() == 1) { // "I" or "W"
|
||||
typeC.accept(typeOrCode);
|
||||
return OK;
|
||||
}
|
||||
return typeOrCode; // 실패코드 리턴
|
||||
}
|
||||
|
||||
/**
|
||||
* byte[] → 16진수 문자열 변환
|
||||
* @param bytes SHA-256 결과 바이트 배열
|
||||
* @return Hex 문자열
|
||||
*/
|
||||
private static String bytesToHex(byte[] bytes) {
|
||||
StringBuilder sb = new StringBuilder(bytes.length * 2);
|
||||
for (byte b : bytes) sb.append(String.format("%02x", b));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 가로/세로 크기를 기반으로 이미지 타입 판정
|
||||
* - 폭 < 500px → 오류코드 반환
|
||||
* - 비율이 2:1(약 2.0) ±10% → "I"
|
||||
* - 비율이 4:3(약 1.333) ±10% → "W"
|
||||
* - 그 외 → 오류코드 반환
|
||||
*
|
||||
* @param width 이미지 가로 폭
|
||||
* @param height 이미지 세로 높이
|
||||
* @return "I" 또는 "W" / 실패코드
|
||||
*/
|
||||
public static String calcFtMsgTypeOrCode(int width, int height) {
|
||||
if (width < 500 || height <= 0) return FT_E_WIDTH_LT_500;
|
||||
|
||||
double r = (double) width / (double) height;
|
||||
// 2:1 ~= 2.0, 4:3 ~= 1.333... (±10%)
|
||||
boolean isI = (r >= 1.8 && r <= 2.2);
|
||||
boolean isW = (r >= 1.28 && r <= 1.36);
|
||||
|
||||
if (isI) return "I";
|
||||
if (isW) return "W";
|
||||
return FT_E_RATIO_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* MultipartFile 존재 여부 헬퍼
|
||||
* @param f 업로드 파일
|
||||
* @return 파일이 null이 아니고 비어있지 않으면 true
|
||||
*/
|
||||
public static boolean hasFile(MultipartFile f){ return f != null && !f.isEmpty(); }
|
||||
|
||||
|
||||
/**
|
||||
* 서브(MMS) 허용 Content-Type 검사
|
||||
* 허용: image/jpeg, image/jpg, image/png, image/gif
|
||||
* @param ct 요청의 Content-Type
|
||||
* @return 허용 타입이면 true
|
||||
*/
|
||||
private static boolean isAllowedSubCt(String ct) {
|
||||
return ct != null && (
|
||||
ct.equalsIgnoreCase("image/jpeg") ||
|
||||
ct.equalsIgnoreCase("image/jpg") || // 일부 클라이언트 대비
|
||||
ct.equalsIgnoreCase("image/png") ||
|
||||
ct.equalsIgnoreCase("image/gif")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,14 +1,9 @@
|
||||
package com.itn.mjonApi.mjon.api.msg.inqry.mapper.domain;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import lombok.*;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@ -27,13 +22,23 @@ public class PriceResponse {
|
||||
private double shortPrice; // 단문 이용단가
|
||||
private double longPrice; // 장문 이용단가
|
||||
private double picturePrice; // 그림 이용단가
|
||||
|
||||
|
||||
private double kakaoAtPrice; // 그림 이용단가
|
||||
private double kakaoFtPrice; // 그림 이용단가
|
||||
private double kakaoFtImgPrice; // 그림 이용단가
|
||||
private double kakaoFtWideImgPrice; // 그림 이용단가
|
||||
|
||||
private double mberMoney; // 잔액
|
||||
|
||||
private int shortSendPsbltEa; // 단문 발송 가능건 수
|
||||
private int longSendPsbltEa; // 장문 발송 가능건 수
|
||||
private int pictureSendPsbltEa; // 그림 발송 가능건 수
|
||||
|
||||
private int kakaoAtSendPsbltEa; // 그림 발송 가능건 수
|
||||
private int kakaoFtSendPsbltEa; // 그림 발송 가능건 수
|
||||
private int kakaoFtImgSendPsbltEa; // 그림 발송 가능건 수
|
||||
private int kakaoFtWideImgSendPsbltEa; // 그림 발송 가능건 수
|
||||
|
||||
/*
|
||||
* 200-OK : 정상접속
|
||||
* 401-Unauthorized : 인증실패
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
package com.itn.mjonApi.mjon.api.msg.inqry.mapper.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import lombok.*;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PriceVO implements Serializable{
|
||||
@ -22,11 +19,21 @@ public class PriceVO implements Serializable{
|
||||
private double shortPrice; // 단문 이용단가
|
||||
private double longPrice; // 장문 이용단가
|
||||
private double picturePrice; // 그림 이용단가
|
||||
|
||||
|
||||
private double kakaoAtPrice; // 알림톡 이용단가
|
||||
private double kakaoFtPrice; // 친구톡 이용단가
|
||||
private double kakaoFtImgPrice; // 친구톡 그림 이용단가
|
||||
private double kakaoFtWideImgPrice; // 친구톡 와이드 그림 이용단가
|
||||
|
||||
private double mberMoney; // 잔액
|
||||
|
||||
private int shortSendPsbltEa; // 단문 발송 가능건 수
|
||||
private int longSendPsbltEa; // 장문 발송 가능건 수
|
||||
private int pictureSendPsbltEa; // 그림 발송 가능건 수
|
||||
|
||||
|
||||
private int kakaoAtSendPsbltEa; // 알림톡 발송 가능건 수
|
||||
private int kakaoFtSendPsbltEa; // 친구톡 발송 가능건 수
|
||||
private int kakaoFtImgSendPsbltEa; // 친구톡 그림 발송 가능건 수
|
||||
private int kakaoFtWideImgSendPsbltEa; // 친구톡 와이드 그림 발송 가능건 수
|
||||
|
||||
}
|
||||
|
||||
@ -59,10 +59,20 @@ public class PriceServiceImpl implements PriceService {
|
||||
.shortPrice(priceVO.getShortPrice())
|
||||
.longPrice(priceVO.getLongPrice())
|
||||
.picturePrice(priceVO.getPicturePrice())
|
||||
|
||||
.kakaoAtPrice(priceVO.getKakaoAtPrice())
|
||||
.kakaoFtPrice(priceVO.getKakaoFtPrice())
|
||||
.kakaoFtImgPrice(priceVO.getKakaoFtImgPrice())
|
||||
.kakaoFtWideImgPrice(priceVO.getKakaoFtWideImgPrice())
|
||||
//3. 발송가능건수
|
||||
.shortSendPsbltEa(priceVO.getShortSendPsbltEa())
|
||||
.longSendPsbltEa(priceVO.getLongSendPsbltEa())
|
||||
.pictureSendPsbltEa(priceVO.getPictureSendPsbltEa())
|
||||
|
||||
.kakaoAtSendPsbltEa(priceVO.getKakaoAtSendPsbltEa())
|
||||
.kakaoFtSendPsbltEa(priceVO.getKakaoFtSendPsbltEa())
|
||||
.kakaoFtImgSendPsbltEa(priceVO.getKakaoFtImgSendPsbltEa())
|
||||
.kakaoFtWideImgSendPsbltEa(priceVO.getKakaoFtWideImgSendPsbltEa())
|
||||
.build();
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package com.itn.mjonApi.util;
|
||||
|
||||
import com.itn.mjonApi.mjon.api.kakao.at.send.mapper.domain.VarListMapVO;
|
||||
import com.itn.mjonApi.mjon.api.kakao.at.send.mapper.domain.VarAtListMapVO;
|
||||
import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.VarFtListMapVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@ -15,6 +17,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
* -----------------------------------------------------------
|
||||
* 2023-05-17 hylee 최초 생성
|
||||
*/
|
||||
@Slf4j
|
||||
public class MunjaUtil {
|
||||
|
||||
|
||||
@ -105,7 +108,7 @@ public class MunjaUtil {
|
||||
* @param subMsgSendYn 대체문자 발송 여부
|
||||
* @return 검증 실패 시 오류 코드, 성공 시 null
|
||||
*/
|
||||
public static String kakaoCmnValidate(VarListMapVO vo, String subMsgSendYn) {
|
||||
public static String kakaoAtValidate(VarAtListMapVO vo, String subMsgSendYn) {
|
||||
|
||||
// 수신번호 검증
|
||||
String callTo = vo.getCallToList();
|
||||
@ -138,5 +141,33 @@ public class MunjaUtil {
|
||||
}
|
||||
|
||||
|
||||
public static String kakaoFtValidate(VarFtListMapVO vo, String subMsgSendYn) {
|
||||
|
||||
log.info(" vo.toString() [{}]", vo.toString());
|
||||
|
||||
// 수신번호 검증
|
||||
String callTo = vo.getPhone();
|
||||
if (MunjaUtil.getCallToChk(callTo)) {
|
||||
return "STAT_1020"; // 수신자 전화번호 오류
|
||||
}
|
||||
|
||||
// 본문 데이터 검증
|
||||
String smsTxt = vo.getTemplateContent();
|
||||
if (StringUtils.isEmpty(smsTxt)) {
|
||||
return "STAT_2040"; // 본문 데이터 오류
|
||||
}
|
||||
|
||||
// 대체문자 검증 (대체문자 발송이 활성화된 경우에만)
|
||||
if ("Y".equals(subMsgSendYn)) {
|
||||
String subMsgTxt = vo.getSubMsgTxt();
|
||||
if (StringUtils.isEmpty(subMsgTxt)) {
|
||||
return "STAT_2042"; // 대체문자 데이터 오류
|
||||
}
|
||||
}
|
||||
|
||||
// 모든 검증 통과
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -19,16 +19,24 @@
|
||||
resultType="hashmap"
|
||||
>
|
||||
|
||||
SELECT a.SHORT_PRICE AS sysShortPrice,
|
||||
a.LONG_PRICE AS sysLongPrice,
|
||||
a.PICTURE_PRICE AS sysPicturePrice,
|
||||
a.PICTURE2_PRICE AS sysPicturePrice2,
|
||||
a.PICTURE3_PRICE AS sysPicturePrice3,
|
||||
b.SHORT_PRICE AS shortPrice,
|
||||
b.LONG_PRICE AS longPrice,
|
||||
b.PICTURE_PRICE AS picturePrice,
|
||||
b.PICTURE2_PRICE AS picturePrice2,
|
||||
b.PICTURE3_PRICE AS picturePrice3
|
||||
SELECT a.SHORT_PRICE AS sysShortPrice
|
||||
, a.LONG_PRICE AS sysLongPrice
|
||||
, a.PICTURE_PRICE AS sysPicturePrice
|
||||
, a.PICTURE2_PRICE AS sysPicturePrice2
|
||||
, a.PICTURE3_PRICE AS sysPicturePrice3
|
||||
, a.KAKAO_AT_PRICE AS sysKakaoAtPrice
|
||||
, a.KAKAO_FT_PRICE AS sysKakaoFtPrice
|
||||
, a.KAKAO_FT_IMG_PRICE AS sysKakaoFtImgPrice
|
||||
, a.KAKAO_FT_WIDE_IMG_PRICE AS sysKakaoFtWideImgPrice
|
||||
, b.SHORT_PRICE AS shortPrice
|
||||
, b.LONG_PRICE AS longPrice
|
||||
, b.PICTURE_PRICE AS picturePrice
|
||||
, b.PICTURE2_PRICE AS picturePrice2
|
||||
, b.PICTURE3_PRICE AS picturePrice3
|
||||
, b.KAKAO_AT_PRICE AS kakaoAtPrice
|
||||
, b.KAKAO_FT_PRICE AS kakaoFtPrice
|
||||
, b.KAKAO_FT_IMG_PRICE AS kakaoFtImgPrice
|
||||
, b.KAKAO_FT_WIDE_IMG_PRICE AS kakaoFtWideImgPrice
|
||||
FROM mj_mber_setting a ,
|
||||
lettngnrlmber b
|
||||
WHERE b.mber_id = #{mberId}
|
||||
|
||||
@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.itn.mjonApi.cmn.domain.biz.template.BizTemplateRequest;
|
||||
import com.itn.mjonApi.mjon.api.kakao.at.inqry.mapper.domain.MjKakaoProfileInfoVO;
|
||||
import com.itn.mjonApi.mjon.api.kakao.at.send.mapper.domain.MsgAtRequestVO;
|
||||
import com.itn.mjonApi.mjon.api.kakao.at.send.mapper.domain.VarListMapVO;
|
||||
import com.itn.mjonApi.mjon.api.kakao.at.send.mapper.domain.VarAtListMapVO;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -38,7 +38,7 @@ public class TestUtils {
|
||||
vo.setTest_yn("Y"); // 테스트 모드
|
||||
|
||||
// VarListMap 생성 (동적 파라미터)
|
||||
List<VarListMapVO> varListMap = new ArrayList<>();
|
||||
List<VarAtListMapVO> varListMap = new ArrayList<>();
|
||||
varListMap.add(createVarListMapVO("01012345678", "테스트사용자1", "테스트사용자1"));
|
||||
varListMap.add(createVarListMapVO("01087654321", "테스트사용자2", "테스트사용자2"));
|
||||
vo.setVarListMap(varListMap);
|
||||
@ -54,8 +54,8 @@ public class TestUtils {
|
||||
* @param replaceValue 치환값
|
||||
* @return 테스트용 VarListMapVO 객체
|
||||
*/
|
||||
public static VarListMapVO createVarListMapVO(String phone, String name, String replaceValue) {
|
||||
VarListMapVO vo = new VarListMapVO();
|
||||
public static VarAtListMapVO createVarListMapVO(String phone, String name, String replaceValue) {
|
||||
VarAtListMapVO vo = new VarAtListMapVO();
|
||||
vo.setCallToList(phone);
|
||||
vo.setTemplateTitle(name);
|
||||
vo.setTemplateContent("테스트 메시지 " + name + "님 안녕하세요");
|
||||
@ -210,8 +210,8 @@ public class TestUtils {
|
||||
* @param count 생성할 개수
|
||||
* @return VarListMapVO 목록
|
||||
*/
|
||||
public static List<VarListMapVO> createVarListMap(int count) {
|
||||
List<VarListMapVO> varListMap = new ArrayList<>();
|
||||
public static List<VarAtListMapVO> createVarListMap(int count) {
|
||||
List<VarAtListMapVO> varListMap = new ArrayList<>();
|
||||
|
||||
for (int i = 1; i <= count; i++) {
|
||||
varListMap.add(createVarListMapVO(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user