전송내역 리스트, 상세 완료

This commit is contained in:
hehihoho3@gmail.com 2025-08-26 14:53:52 +09:00
parent 2db3d41020
commit bc09c23a0d
10 changed files with 471 additions and 49 deletions

View File

@ -152,6 +152,9 @@ public class InqryServiceImpl implements InqryService {
private @Nullable RestResponse isSenderKeyChk(BizTemplateRequest bizTemplateRequest) {
List<MjKakaoProfileInfoVO> chnlIdList = this.getChnlId(bizTemplateRequest.getMberId());
log.info("bizTemplateRequest.getSenderKey() :: [{}]", bizTemplateRequest.getSenderKey());
log.info("chnlIdList :: [{}]", chnlIdList.toString());
boolean skErr = chnlIdList.stream()
.anyMatch(p -> bizTemplateRequest.getSenderKey().equals(p.getSenderKey()));
if(!skErr){

View File

@ -14,7 +14,7 @@ public class VarListMapVO {
/**
* @description : 수신자번호
*/
private String callTo;
private String callToList;
/**
* @description : 카카오 내용

View File

@ -0,0 +1,125 @@
package com.itn.mjonApi.mjon.api.kakao.at.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.at.send.mapper.domain.MsgAtRequestVO;
import com.itn.mjonApi.mjon.api.kakao.at.send.mapper.domain.VarListMapVO;
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 AtParameterProcessingService {
@Autowired
private IndexedParameterParserService indexedParameterParserService;
@Autowired
private InqryService inqryService;
/**
* HttpServletRequest에서 동적으로 인덱스된 파라미터들을 파싱하고 검증하여
* MsgAtRequestVO의 varListMap에 설정
*
* @param msgAtRequestVO 요청 VO 객체
* @param request HTTP 요청 객체
* @return 검증 실패 오류 코드, 성공 null
*/
public String processIndexedParameters(MsgAtRequestVO msgAtRequestVO, HttpServletRequest request) {
// 기존 varListMap 초기화
msgAtRequestVO.setVarListMap(new ArrayList<>());
// 채널ID 확인
String STAT_2010 = this.validateSenderKey(msgAtRequestVO.getMberId(), msgAtRequestVO.getSenderKey());
if (STAT_2010 != null) return STAT_2010;
// 템플릿 코드 확인
String STAT_2030 = this.validateTemplateCode(msgAtRequestVO.getMberId(), msgAtRequestVO.getSenderKey(), msgAtRequestVO.getTemplateCode());
if (STAT_2030 != null) return STAT_2030;
// 파싱 로직을 IndexedParameterParserService에 위임
List<VarListMapVO> parsedList = indexedParameterParserService.parseIndexedParameters(msgAtRequestVO, request);
// 파싱된 VO에 대해 검증 수행
for (VarListMapVO vo : parsedList) {
String validationError = MunjaUtil.kakaoCmnValidate(vo, msgAtRequestVO.getSubMsgSendYn());
if (StringUtils.isNotEmpty(validationError)) {
return validationError; // 검증 실패 오류 코드 반환
}
// 검증 통과한 VO를 리스트에 추가
msgAtRequestVO.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";
}
}

View File

@ -9,6 +9,8 @@ import com.itn.mjonApi.mjon.api.kakao.at.send.service.AtParameterProcessingServi
import com.itn.mjonApi.mjon.api.kakao.at.send.service.SendAtService;
import com.itn.mjonApi.mjon.api.msg.inqry.mapper.PriceMapper;
import com.itn.mjonApi.mjon.api.msg.send.mapper.SendMapper;
import com.itn.mjonApi.mjon.api.msg.send.mapper.domain.MjonResponseVO;
import com.itn.mjonApi.mjon.api.msg.send.mapper.domain.SendSucRestResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.Response;
import org.apache.commons.lang3.StringUtils;
@ -70,24 +72,22 @@ public class SendAtServiceImpl implements SendAtService {
//
//
//
// MjonResponseVO munjaSendResponse = apiService.postForEntity(
// "/web/mjon/kakao/alimtalk/kakaoAlimTalkMsgSendAjax_advc.do"
// , msgAtRequestVO
// , String.class
// );
MjonResponseVO munjaSendResponse = apiService.postForEntity(
"/web/mjon/kakao/alimtalk/kakaoAlimTalkMsgSendAjax_advc.do"
, msgAtRequestVO
, String.class
);
// convertMjonDataToApiResponse => MjonResponseVO 데이터를 ApiResponse 데이터로 변환하는 메소드
// log.info(" + munjaSendResponse :: [{}]", munjaSendResponse.toString());
/*
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(msgAtRequestVO);
// return new RestResponse(msgAtRequestVO);
}
}

View File

@ -108,7 +108,7 @@ public class MunjaUtil {
public static String kakaoCmnValidate(VarListMapVO vo, String subMsgSendYn) {
// 수신번호 검증
String callTo = vo.getCallTo();
String callTo = vo.getCallToList();
if (MunjaUtil.getCallToChk(callTo)) {
return "STAT_1020"; // 수신자 전화번호 오류
}

View File

@ -2,7 +2,7 @@
# DB INFO
spring.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.url=jdbc:log4jdbc:mysql://192.168.0.125:3306/mjon?serverTimezone=Asia/Seoul
spring.datasource.url=jdbc:log4jdbc:mysql://192.168.0.125:3306/mjon_advc?serverTimezone=Asia/Seoul
#spring.datasource.url=jdbc:log4jdbc:mysql://139.150.72.157:3306/mjon?serverTimezone=Asia/Seoul
spring.datasource.username=mjonUr
@ -10,7 +10,14 @@ spring.datasource.password=mjon!@#$
server.port=8088
#logging.level.root=info
# Logging Configuration
logging.level.root=info
logging.level.jdbc.sqltiming=info
logging.level.jdbc.resultset=info
logging.level.jdbc.resultsettable=info
logging.level.jdbc.sqlonly=debug
logging.level.jdbc.audit=warn
logging.level.jdbc.connection=warn
#??? ?? ??
api.root.url=http://localhost:8080/

View File

@ -1,2 +1,20 @@
# log4jdbc 설정
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
log4jdbc.dump.sql.maxlinelength=0
# SQL 출력 설정
log4jdbc.dump.sql.maxlinelength=0
log4jdbc.trim.sql=true
log4jdbc.trim.sql.extrablanklines=false
# ResultSet 출력 활성화 (가장 중요한 설정)
log4jdbc.dump.sql.select=true
log4jdbc.dump.sql.insert=true
log4jdbc.dump.sql.update=true
log4jdbc.dump.sql.delete=true
# ResultSet 테이블 형태 출력을 위한 추가 설정
log4jdbc.dump.fulldebugstacktrace=false
log4jdbc.suppress.generated.keys.exception=false
# 에러 억제 설정
log4jdbc.auto.load.popular.drivers=false

View File

@ -81,6 +81,23 @@
</appender>
<!-- Loggers -->
<!-- log4jdbc 로거 설정 - SQL 쿼리와 결과값 출력 -->
<logger name="jdbc.sqltiming" level="INFO" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<logger name="jdbc.sqlonly" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<logger name="jdbc.audit" level="WARN" />
<logger name="jdbc.resultset" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<logger name="jdbc.resultsettable" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<logger name="jdbc.connection" level="WARN" />
<!-- <logger name="org.apache.catalina" level="ERROR">
</logger>

View File

@ -102,23 +102,21 @@
A.RSLT_CODE ,
A.RSLT_CODE2 ,
CASE
WHEN B.MSG_TYPE = '6'
AND B.FILE_CNT > 0
THEN '그림(MMS)'
WHEN B.MSG_TYPE = '6'
AND B.FILE_CNT = 0
THEN '장문(LMS)'
ELSE '단문(SMS)'
END msgTypeName ,
WHEN B.MSG_TYPE = '6' AND B.FILE_CNT > 0 THEN '그림(MMS)'
WHEN B.MSG_TYPE = '6' AND B.FILE_CNT = 0 THEN '장문(LMS)'
WHEN B.MSG_TYPE = '4' THEN '단문(SMS)'
WHEN B.MSG_TYPE = '8' THEN '알림톡(AT)'
WHEN B.MSG_TYPE = '9' THEN '친구톡(FT)'
ELSE '기타'
END msgTypeName ,
CASE
WHEN B.MSG_TYPE = '6'
AND B.FILE_CNT > 0
THEN '3'
WHEN B.MSG_TYPE = '6'
AND B.FILE_CNT = 0
THEN '2'
ELSE '1'
END orderByCode , (
WHEN B.MSG_TYPE = '6' AND B.FILE_CNT > 0 THEN '3'
WHEN B.MSG_TYPE = '6' AND B.FILE_CNT = 0 THEN '2'
WHEN B.MSG_TYPE = '4' THEN '1'
WHEN B.MSG_TYPE = '8' THEN '4'
WHEN B.MSG_TYPE = '9' THEN '5'
ELSE '9'
END orderByCode ,(
CASE
WHEN A.AGENT_CODE = '01'
AND
@ -214,7 +212,10 @@
'2',
'3')
AND MSG_TYPE IN ('4',
'6')
'6',
'8',
'9'
)
GROUP BY MSG_GROUP_ID
/*
ORDER BY 1=1,
@ -301,7 +302,10 @@
WHERE 1 =1
AND MD.DEL_FLAG = 'N'
AND MD.MSG_TYPE IN ('4',
'6')
'6',
'8',
'9'
)
)
A
GROUP BY A.MSG_GROUP_ID,
@ -366,24 +370,23 @@
DEL_FLAG AS delFlag ,
*/
MSG_TYPE AS msgType ,
CASE
WHEN MSG_TYPE = '6' AND FILE_CNT > 0 THEN '그림(MMS)'
WHEN MSG_TYPE = '6' AND FILE_CNT = 0 THEN '장문(LMS)'
WHEN MSG_TYPE = '4' THEN '단문(SMS)'
WHEN MSG_TYPE = '8' THEN '알림톡(AT)'
WHEN MSG_TYPE = '9' THEN '친구톡(FT)'
ELSE '기타'
END msgTypeName ,
CASE
WHEN MSG_TYPE = '6'
AND FILE_CNT > 0
THEN '그림(MMS)'
WHEN MSG_TYPE = '6'
AND FILE_CNT = 0
THEN '장문(LMS)'
ELSE '단문(SMS)'
END msgTypeName ,
CASE
WHEN MSG_TYPE = '6'
AND FILE_CNT > 0
THEN '3'
WHEN MSG_TYPE = '6'
AND FILE_CNT = 0
THEN '2'
ELSE '1'
WHEN MSG_TYPE = '6' AND FILE_CNT > 0 THEN '3'
WHEN MSG_TYPE = '6' AND FILE_CNT = 0 THEN '2'
WHEN MSG_TYPE = '4' THEN '1'
WHEN MSG_TYPE = '8' THEN '4'
WHEN MSG_TYPE = '9' THEN '5'
ELSE '9'
END orderByCode ,
/*

View File

@ -0,0 +1,249 @@
package com.itn.mjonApi.common;
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 java.util.ArrayList;
import java.util.List;
/**
* 테스트 유틸리티 클래스
* 공통적으로 사용되는 테스트 데이터 생성 유틸리티 메서드 제공
*
* @author hylee
* @date 2025-08-18
*/
public class TestUtils {
private static final ObjectMapper objectMapper = new ObjectMapper();
/**
* 테스트용 MsgAtRequestVO 객체 생성
*
* @param mberId 멤버 ID
* @param senderKey 발신 프로필
* @param templateCode 템플릿 코드
* @return 테스트용 MsgAtRequestVO 객체
*/
public static MsgAtRequestVO createMsgAtRequestVO(String mberId, String senderKey, String templateCode) {
MsgAtRequestVO vo = new MsgAtRequestVO();
vo.setMberId(mberId);
vo.setAccessKey("testAccessKey");
vo.setSenderKey(senderKey);
vo.setTemplateCode(templateCode);
vo.setSubMsgSendYn("N");
vo.setTest_yn("Y"); // 테스트 모드
// VarListMap 생성 (동적 파라미터)
List<VarListMapVO> varListMap = new ArrayList<>();
varListMap.add(createVarListMapVO("01012345678", "테스트사용자1", "테스트사용자1"));
varListMap.add(createVarListMapVO("01087654321", "테스트사용자2", "테스트사용자2"));
vo.setVarListMap(varListMap);
return vo;
}
/**
* 테스트용 VarListMapVO 객체 생성
*
* @param phone 전화번호
* @param name 이름
* @param replaceValue 치환값
* @return 테스트용 VarListMapVO 객체
*/
public static VarListMapVO createVarListMapVO(String phone, String name, String replaceValue) {
VarListMapVO vo = new VarListMapVO();
vo.setCallToList(phone);
vo.setTemplateTitle(name);
vo.setTemplateContent("테스트 메시지 " + name + "님 안녕하세요");
vo.setSubMsgTxt(replaceValue);
return vo;
}
/**
* 테스트용 MjKakaoProfileInfoVO 객체 생성
*
* @param senderKey 발신 프로필
* @param channelName 채널명
* @param status 상태
* @return 테스트용 MjKakaoProfileInfoVO 객체
*/
public static MjKakaoProfileInfoVO createKakaoProfileVO(String senderKey, String channelName, String status) {
MjKakaoProfileInfoVO vo = new MjKakaoProfileInfoVO();
vo.setSenderKey(senderKey);
vo.setYellowId(channelName); // 카카오톡 채널 @ID
vo.setUserId("testMember"); // 회원 아이디
vo.setCategoryName(status); // 상태 정보를 카테고리명으로 사용
return vo;
}
/**
* 테스트용 BizTemplateRequest 객체 생성
*
* @param senderKey 발신 프로필
* @param templateCode 템플릿 코드
* @return 테스트용 BizTemplateRequest 객체
*/
public static BizTemplateRequest createBizTemplateRequest(String senderKey, String templateCode) {
BizTemplateRequest request = new BizTemplateRequest();
request.setSenderKey(senderKey);
request.setTemplateCode(templateCode);
return request;
}
/**
* 승인된 상태의 카카오 프로필 목록 생성
*
* @param count 생성할 프로필 개수
* @return 승인된 프로필 목록
*/
public static List<MjKakaoProfileInfoVO> createApprovedProfiles(int count) {
List<MjKakaoProfileInfoVO> profiles = new ArrayList<>();
for (int i = 1; i <= count; i++) {
profiles.add(createKakaoProfileVO(
"senderKey" + i,
"테스트채널" + i,
"APPROVED"
));
}
return profiles;
}
/**
* 다양한 상태의 카카오 프로필 목록 생성
*
* @return 다양한 상태의 프로필 목록
*/
public static List<MjKakaoProfileInfoVO> createMixedStatusProfiles() {
List<MjKakaoProfileInfoVO> profiles = new ArrayList<>();
profiles.add(createKakaoProfileVO("approved_key", "승인된채널", "APPROVED"));
profiles.add(createKakaoProfileVO("pending_key", "대기중채널", "PENDING"));
profiles.add(createKakaoProfileVO("rejected_key", "거절된채널", "REJECTED"));
profiles.add(createKakaoProfileVO("blocked_key", "차단된채널", "BLOCKED"));
return profiles;
}
/**
* 대체문자 발송 옵션이 포함된 MsgAtRequestVO 생성
*
* @param mberId 멤버 ID
* @param senderKey 발신 프로필
* @param templateCode 템플릿 코드
* @return 대체문자 옵션 포함 MsgAtRequestVO
*/
public static MsgAtRequestVO createMsgAtRequestVOWithSubMsg(String mberId, String senderKey, String templateCode) {
MsgAtRequestVO vo = createMsgAtRequestVO(mberId, senderKey, templateCode);
vo.setSubMsgSendYn("Y");
vo.setCallFrom("0212345678"); // 발신번호 설정
return vo;
}
/**
* JSON 문자열을 객체로 변환
*
* @param json JSON 문자열
* @param clazz 변환할 클래스
* @param <T> 반환 타입
* @return 변환된 객체
*/
public static <T> T fromJson(String json, Class<T> clazz) {
try {
return objectMapper.readValue(json, clazz);
} catch (Exception e) {
throw new RuntimeException("JSON 변환 실패: " + e.getMessage(), e);
}
}
/**
* 객체를 JSON 문자열로 변환
*
* @param object 변환할 객체
* @return JSON 문자열
*/
public static String toJson(Object object) {
try {
return objectMapper.writeValueAsString(object);
} catch (Exception e) {
throw new RuntimeException("JSON 변환 실패: " + e.getMessage(), e);
}
}
/**
* 테스트용 AccessKey 생성
*
* @return 테스트용 AccessKey
*/
public static String getTestAccessKey() {
return "TEST_ACCESS_KEY_" + System.currentTimeMillis();
}
/**
* 테스트용 전화번호 생성
*
* @param index 인덱스
* @return 테스트용 전화번호
*/
public static String getTestPhoneNumber(int index) {
return String.format("010%08d", index);
}
/**
* 테스트용 이름 생성
*
* @param index 인덱스
* @return 테스트용 이름
*/
public static String getTestName(int index) {
return "테스트사용자" + index;
}
/**
* 동적 파라미터용 VarListMapVO 목록 생성
*
* @param count 생성할 개수
* @return VarListMapVO 목록
*/
public static List<VarListMapVO> createVarListMap(int count) {
List<VarListMapVO> varListMap = new ArrayList<>();
for (int i = 1; i <= count; i++) {
varListMap.add(createVarListMapVO(
getTestPhoneNumber(i),
getTestName(i),
getTestName(i)
));
}
return varListMap;
}
/**
* 에러 코드 검증용 상수
*/
public static class ErrorCodes {
public static final String INVALID_SENDER_KEY = "STAT_2010";
public static final String EMPTY_SENDER_KEY = "STAT_2011";
public static final String TEMPLATE_NOT_FOUND = "TEMPLATE_NOT_FOUND";
public static final String UNAUTHORIZED = "UNAUTHORIZED";
public static final String INTERNAL_SERVER_ERROR = "INTERNAL_SERVER_ERROR";
}
/**
* HTTP 상태 코드 상수
*/
public static class HttpStatus {
public static final int OK = 200;
public static final int BAD_REQUEST = 400;
public static final int UNAUTHORIZED = 401;
public static final int FORBIDDEN = 403;
public static final int NOT_FOUND = 404;
public static final int INTERNAL_SERVER_ERROR = 500;
}
}