diff --git a/src/main/java/com/itn/mjonApi/cmn/aop/LogAspect.java b/src/main/java/com/itn/mjonApi/cmn/aop/LogAspect.java index 718eab1..36e5316 100644 --- a/src/main/java/com/itn/mjonApi/cmn/aop/LogAspect.java +++ b/src/main/java/com/itn/mjonApi/cmn/aop/LogAspect.java @@ -351,9 +351,12 @@ public class LogAspect { case "MjKakaoProfileInfoVO": case "BizTemplateRequest": case "MsgAtRequestVO": + case "MsgFtRequestVO": + case "String": + case "ArrayList": return ApiObjectUtil.toJson(returnValue); default: - log.info("데이터를 추가해 주세요"); + log.info("데이터를 추가해 주세요 [{}]", classNm); return "데이터를 추가해 주세요"; } } diff --git a/src/main/java/com/itn/mjonApi/cmn/apiServer/ApiService.java b/src/main/java/com/itn/mjonApi/cmn/apiServer/ApiService.java index 04d512f..bdbe643 100644 --- a/src/main/java/com/itn/mjonApi/cmn/apiServer/ApiService.java +++ b/src/main/java/com/itn/mjonApi/cmn/apiServer/ApiService.java @@ -145,6 +145,9 @@ public class ApiService { if (msgFtRequestVO.getSendKind() != null) { body.add("sendKind", msgFtRequestVO.getSendKind()); } + if ( msgFtRequestVO.getTemplateMsgType() != null) { + body.add("imageType", msgFtRequestVO.getTemplateMsgType()); + } if (msgFtRequestVO.getTemplateCode() != null) { body.add("templateCode", msgFtRequestVO.getTemplateCode()); } @@ -159,7 +162,7 @@ public class ApiService { StatusResponse.class ); - log.info("Upload response :: [{}]", response.getBody()); +// log.info("Upload response :: [{}]", response.getBody()); return response.getBody(); } diff --git a/src/main/java/com/itn/mjonApi/cmn/msg/StatMsg.java b/src/main/java/com/itn/mjonApi/cmn/msg/StatMsg.java index def54b9..f80160b 100644 --- a/src/main/java/com/itn/mjonApi/cmn/msg/StatMsg.java +++ b/src/main/java/com/itn/mjonApi/cmn/msg/StatMsg.java @@ -50,6 +50,7 @@ public enum StatMsg { , STAT_2040("2040","본문 데이터 오류") , STAT_2041("2041","타이틀 데이터 오류") , STAT_2042("2042","대체문자 데이터 오류") + , STAT_2043("2043","대체문자 발송 시 발신번호가 필요합니다.") , STAT_2050("2050","지원하지 않는 이미지 형식입니다.") , STAT_2051("2051","이미지 용량은 5MB 이내여야 합니다.") @@ -60,6 +61,8 @@ public enum StatMsg { , STAT_2056("2056","비율 2:1 이상 또는 3:4 이하만 허용됩니다.") , STAT_2057("2057","대체문자(MMS) 이미지는 10MB를 초과할 수 없습니다.") + , STAT_2080("2080","친구톡은 20시 50분부터 익일 08시까지 발송이 제한됩니다.") + , STAT_2099("2099","기타 시스템 오류") diff --git a/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/mapper/domain/KakaoFTSendVO.java b/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/mapper/domain/KakaoFTSendVO.java new file mode 100644 index 0000000..04cddb6 --- /dev/null +++ b/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/mapper/domain/KakaoFTSendVO.java @@ -0,0 +1,44 @@ +package com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain; + +import lombok.*; +import java.util.ArrayList; +import java.util.List; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@ToString +@Getter +@Setter +public class KakaoFTSendVO { + + // 카카오 발신 관련 + private String mberId; // mberId + private String senderKey; // 카카오 발신 키 (필수) + private String adFlag; // 카카오 발신 키 (필수) + + // 이미지 관련 + private String imageFileName; // 이미지 파일명 + private String imageType; // 이미지 타입 (I: 이미지, null: 텍스트만) + private String atchFileId; // 첨부파일 ID + private String templateImageUrl; // 친구톡 이미지 URL + + // 메시지 내용 + private String templateContent; // 친구톡 발송 내용 (개별 수신자별) + private String subMsgSendYn; // 대체문자 발송여부 + private String subMsgTxt; // 대체문자 내용 (개별 수신자별) + + // 발송 관련 + private String reserveYn; // 예약발송 여부 (기본: N) + private String callFrom; // 발신번호 + private String reqDate; // 요청일시 (예약발송시) + private String sendKind; // 발송 구분 , A : API / H : 사이트 + + // 버튼 정보 (모든 수신자 공통) + @Builder.Default + private List buttonVOList = new ArrayList<>(); + + // 수신자 목록 (핵심: 개별 발송시 1명만 포함) + @Builder.Default + private List mjonFTSendVOList = new ArrayList<>(); +} diff --git a/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/mapper/domain/MsgFtRequestVO.java b/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/mapper/domain/MsgFtRequestVO.java index b93b919..ca8d51d 100644 --- a/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/mapper/domain/MsgFtRequestVO.java +++ b/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/mapper/domain/MsgFtRequestVO.java @@ -55,9 +55,6 @@ public class MsgFtRequestVO implements Serializable { @ToString.Exclude private MultipartFile templateImage; - @JsonIgnore - @ToString.Exclude - private MultipartFile subImage; // ====== ⬇️ AOP/DB 저장용(문자열/숫자만) 메타데이터 필드들 ====== // template @@ -70,9 +67,31 @@ public class MsgFtRequestVO implements Serializable { private String templateImageSavedPath; // 서버 저장 경로(선택) private String templateAtchFileId; // 내부 첨부ID(선택) private String templateMsgType; - private String templateImageUrl; + + /** + * @description : 친구톡 이미지 등록된 URL + */ + private String templateImageUrl; + + /** + * @description : 치환문자 이미지 + * 이미지 문자가 있고 대체문자가 있으면(subMsgSendYn="Y") filepath에 atchFileId 있어야함 + */ + private String filePath1; + + + /** + * @description : 버튼 사용 + */ + private List buttons = new ArrayList<>(); + // sub + +// @JsonIgnore +// @ToString.Exclude +// private MultipartFile subImage; + // private String subImageName; // private String subImageContentType; // private Long subImageSize; diff --git a/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/mapper/domain/VarFtListMapVO.java b/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/mapper/domain/VarFtListMapVO.java index f9682fe..318ff74 100644 --- a/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/mapper/domain/VarFtListMapVO.java +++ b/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/mapper/domain/VarFtListMapVO.java @@ -5,9 +5,6 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; -import java.util.ArrayList; -import java.util.List; - @Getter @Setter @ToString @@ -29,15 +26,6 @@ public class VarFtListMapVO { */ private String subMsgTxt; - /** - * @description : 치환문자 이미지 - */ - private String filePath1; - - /** - * @description : 버튼 사용 - */ - private List buttons = new ArrayList<>(); // // /** diff --git a/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/service/FtIndexedParameterParserService.java b/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/service/FtIndexedParameterParserService.java index 95d751b..e075d48 100644 --- a/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/service/FtIndexedParameterParserService.java +++ b/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/service/FtIndexedParameterParserService.java @@ -41,7 +41,7 @@ public class FtIndexedParameterParserService { * @param request HTTP 요청 객체 * @return 파싱된 VarListMapVO 리스트 */ - public List parseIndexedParameters(MsgFtRequestVO msgAtRequestVO, HttpServletRequest request) { + public List parseIndexedParameters(MsgFtRequestVO msgFtRequestVO, HttpServletRequest request) { List varListMap = new ArrayList<>(); // 모든 파라미터 맵 가져오기 @@ -51,7 +51,7 @@ public class FtIndexedParameterParserService { Map> indexedDataMap = new HashMap<>(); - String subMsgSendYn = msgAtRequestVO.getSubMsgSendYn(); + String subMsgSendYn = msgFtRequestVO.getSubMsgSendYn(); // 모든 파라미터를 순회하며 인덱스된 파라미터 찾기 for (Map.Entry entry : parameterMap.entrySet()) { @@ -84,18 +84,24 @@ public class FtIndexedParameterParserService { if("Y".equals(subMsgSendYn)){ vo.setSubMsgTxt(dataMap.get("subMsgTxt")); } - - // 버튼 데이터 처리 - String buttonData = dataMap.get("button"); - if (buttonData != null && !buttonData.trim().isEmpty()) { - try { - JsonNode buttonJson = objectMapper.readTree(buttonData); - JsonNode buttonArray = buttonJson.get("button"); - - List buttonList = new ArrayList<>(); - if (buttonArray != null && buttonArray.isArray()) { - for (JsonNode button : buttonArray) { - FtButtonVO ftButton = FtButtonVO.builder() + // 필수 필드 중 하나라도 있으면 리스트에 추가 + if (vo.getPhone() != null || vo.getTemplateContent() != null) { + varListMap.add(vo); + } + } + + // 버튼 파라미터를 요청 레벨에서 한 번만 파싱 + String buttonData = request.getParameter("button"); + + if (buttonData != null && !buttonData.trim().isEmpty()) { + try { + JsonNode buttonJson = objectMapper.readTree(buttonData); + JsonNode buttonArray = buttonJson.get("button"); + + List buttonList = new ArrayList<>(); + if (buttonArray != null && buttonArray.isArray()) { + for (JsonNode button : buttonArray) { + FtButtonVO ftButton = FtButtonVO.builder() .name(getJsonText(button, "name")) .linkType(getJsonText(button, "linkType")) .linkTypeName(getJsonText(button, "linkTypeName")) @@ -104,24 +110,17 @@ public class FtIndexedParameterParserService { .linkIos(getJsonText(button, "linkIos")) .linkAnd(getJsonText(button, "linkAnd")) .build(); - buttonList.add(ftButton); - } + buttonList.add(ftButton); } - vo.setButtons(buttonList); - } catch (Exception e) { - log.error("버튼 JSON 파싱 실패 - index: {}, data: {}", index, buttonData, e); - vo.setButtons(new ArrayList<>()); } + // ✅ MsgFtRequestVO에 버튼 설정 (모든 수신자 공통) + msgFtRequestVO.setButtons(buttonList); + } catch (Exception e) { + log.error("버튼 JSON 파싱 실패: {}", buttonData, e); + msgFtRequestVO.setButtons(new ArrayList<>()); } - - // 필수 필드 중 하나라도 있으면 리스트에 추가 - if (vo.getPhone() != null || vo.getTemplateContent() != null) { - varListMap.add(vo); - } - - - } + return varListMap; } diff --git a/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/service/FtParameterProcessingService.java b/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/service/FtParameterProcessingService.java index 5bd9384..36313e7 100644 --- a/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/service/FtParameterProcessingService.java +++ b/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/service/FtParameterProcessingService.java @@ -1,23 +1,25 @@ package com.itn.mjonApi.mjon.api.kakao.ft.send.service; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.itn.mjonApi.cmn.domain.biz.template.BizTemplateRequest; -import com.itn.mjonApi.cmn.domain.biz.template.detail.TemplateDetailResponse; -import com.itn.mjonApi.cmn.msg.RestResponse; +import com.itn.mjonApi.cmn.apiServer.ApiService; +import com.itn.mjonApi.cmn.domain.StatusResponse; import com.itn.mjonApi.mjon.api.kakao.at.inqry.mapper.domain.MjKakaoProfileInfoVO; import com.itn.mjonApi.mjon.api.kakao.at.inqry.service.InqryService; import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.MsgFtRequestVO; import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.VarFtListMapVO; +import com.itn.mjonApi.mjon.api.kakao.utils.FtFileMetaUtil; import com.itn.mjonApi.util.MunjaUtil; import lombok.extern.slf4j.Slf4j; +import org.apache.catalina.connector.Response; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * fileName : AtParameterProcessingService.java @@ -40,6 +42,12 @@ public class FtParameterProcessingService { @Autowired private InqryService inqryService; + private ApiService apiService; + + @Autowired + public FtParameterProcessingService(ApiService apiService) { + this.apiService = apiService; + } /** * HttpServletRequest에서 동적으로 인덱스된 파라미터들을 파싱하고 검증하여 * MsgAtRequestVO의 varListMap에 설정 @@ -63,11 +71,12 @@ public class FtParameterProcessingService { // if (STAT_2030 != null) return STAT_2030; // 파싱 로직을 IndexedParameterParserService에 위임 + log.info("msgFtRequestVO.getAdFlag() :: [{}]", msgFtRequestVO.getAdFlag()); List parsedList = indexedParameterParserService.parseIndexedParameters(msgFtRequestVO, request); // 파싱된 각 VO에 대해 검증 수행 for (VarFtListMapVO vo : parsedList) { - String validationError = MunjaUtil.kakaoFtValidate(vo, msgFtRequestVO.getSubMsgSendYn()); + String validationError = MunjaUtil.kakaoFtValidate(vo, msgFtRequestVO); if (StringUtils.isNotEmpty(validationError)) { return validationError; // 검증 실패 시 오류 코드 반환 @@ -80,35 +89,6 @@ public class FtParameterProcessingService { return null; // 모든 검증 통과 } - private String validateTemplateCode(String mberId, String senderKey, String templateCode) { - try { - BizTemplateRequest request = BizTemplateRequest.builder() - .mberId(mberId) - .senderKey(senderKey) - .templateCode(templateCode) - .build(); - - RestResponse response = inqryService.getTemplateDetail(request); - JsonNode node = new ObjectMapper().valueToTree(response.getData()); - // 전체 출력 -// log.info("data 전체 :: {}", node.toPrettyString()); - // resultCode가 있으면 채널ID 오류 + inqryService.getTemplateDetail 참조 - if (node.has("resultCode")) { - return "STAT_"+node.get("resultCode").asText(); - } - - TemplateDetailResponse detail = (TemplateDetailResponse) - response.getData(); - - // 템플릿 상세 정보 활용 - log.info("template detail :: [{}]", detail); - return "200".equals(detail.getCode()) ? null : "STAT_2030"; - - } catch (Exception e) { - e.printStackTrace(); - return "STAT_2099"; - } - } public String validateSenderKey(String mberId, String senderKey) { if (StringUtils.isEmpty(senderKey)) { @@ -120,6 +100,41 @@ public class FtParameterProcessingService { } + public String getImagesInfo(MsgFtRequestVO msgFtRequestVO) throws IOException, NoSuchAlgorithmException { + log.info("msgFtRequestVO.getTemplateImage() : [{}]", msgFtRequestVO.getTemplateImage()); + if (FtFileMetaUtil.hasFile(msgFtRequestVO.getTemplateImage())) { + String code = FtFileMetaUtil.fillTemplateMeta(msgFtRequestVO); + if (StringUtils.isNotEmpty(code)) return code; + // 메타데이터 처리 성공 후 업로드 API 호출 + try { + // Option 1: ApiService에 multipart 메소드 추가 + StatusResponse uploadResponse = apiService.postMultipartForEntity( + "/web/mjon/kakao/template/sendKakaoFriendsTemplateImageUploadAjax_advc.do", + msgFtRequestVO, + msgFtRequestVO.getTemplateImage() + ); + // .is2xxSuccessful() 상태코드가 200번대(200 ~ 299)인지 확인 + if (uploadResponse != null && uploadResponse.getStatus().is2xxSuccessful()) { + Map obj = (Map) uploadResponse.getObject(); + String imgUrl = (String) obj.get("imgUrl"); + String atchFileId = (String) obj.get("atchFileId"); + + log.info("imgUrl: {}, atchFileId: {}", imgUrl, atchFileId); + msgFtRequestVO.setTemplateImageUrl(imgUrl); + msgFtRequestVO.setFilePath1(atchFileId); + }else{ +// return new RestResponse(new FailRestResponse("STAT_2099", "")); + return "STAT_2099"; + } + + } catch (Exception e) { + log.error("템플릿 이미지 업로드 실패", e); + return "STAT_2099"; + } + } + return null; + + } } \ No newline at end of file diff --git a/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/service/impl/SendFtServiceImpl.java b/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/service/impl/SendFtServiceImpl.java index 5ef64e8..15a991b 100644 --- a/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/service/impl/SendFtServiceImpl.java +++ b/src/main/java/com/itn/mjonApi/mjon/api/kakao/ft/send/service/impl/SendFtServiceImpl.java @@ -1,15 +1,14 @@ package com.itn.mjonApi.mjon.api.kakao.ft.send.service.impl; import com.itn.mjonApi.cmn.apiServer.ApiService; -import com.itn.mjonApi.cmn.domain.StatusResponse; import com.itn.mjonApi.cmn.msg.FailRestResponse; import com.itn.mjonApi.cmn.msg.RestResponse; -import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.MsgFtRequestVO; +import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.*; import com.itn.mjonApi.mjon.api.kakao.ft.send.service.FtParameterProcessingService; import com.itn.mjonApi.mjon.api.kakao.ft.send.service.SendFtService; -import com.itn.mjonApi.mjon.api.kakao.utils.FtFileMetaUtil; import com.itn.mjonApi.mjon.api.msg.inqry.mapper.PriceMapper; import com.itn.mjonApi.mjon.api.msg.send.mapper.SendMapper; +import com.itn.mjonApi.mjon.api.msg.send.mapper.domain.MjonResponseVO; import lombok.extern.slf4j.Slf4j; import org.apache.catalina.connector.Response; import org.apache.commons.lang3.StringUtils; @@ -19,6 +18,8 @@ import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.security.NoSuchAlgorithmException; +import java.util.*; +import java.util.stream.Collectors; @Slf4j @@ -60,48 +61,7 @@ public class SendFtServiceImpl implements SendFtService { // return this._getTestMsgReturnData(msgRequestVO.getTest_yn()); } - - - if (FtFileMetaUtil.hasFile(msgFtRequestVO.getTemplateImage())) { - String code = FtFileMetaUtil.fillTemplateMeta(msgFtRequestVO); - if (StringUtils.isNotEmpty(code)) return new RestResponse(new FailRestResponse(code, "")); - } -// if (FtFileMetaUtil.hasFile(msgFtRequestVO.getSubImage())) { -// code = FtFileMetaUtil.fillSubMeta(msgFtRequestVO); -// if (StringUtils.isNotEmpty(code)) return new RestResponse(new FailRestResponse(code, "")); -// } - if (FtFileMetaUtil.hasFile(msgFtRequestVO.getTemplateImage())) { - String code = FtFileMetaUtil.fillTemplateMeta(msgFtRequestVO); - if (StringUtils.isNotEmpty(code)) return new RestResponse(new FailRestResponse(code, "")); - - // 메타데이터 처리 성공 후 업로드 API 호출 - try { - // Option 1: ApiService에 multipart 메소드 추가 - StatusResponse uploadResponse = apiService.postMultipartForEntity( - "/web/mjon/kakao/template/sendKakaoFriendsTemplateImageUploadAjax_advc.do", - msgFtRequestVO, - msgFtRequestVO.getTemplateImage() - ); - - log.info("전체 StatusResponse :: [{}]", uploadResponse); - log.info("object 필드만 :: [{}]", uploadResponse.getObject()); - - // 업로드 결과 처리 -// if ("OK".equals(uploadResponse.getResult())) { -// // 성공 처리 -// log.info("템플릿 이미지 업로드 성공"); -// } else { -// // 실패 처리 -// return new RestResponse(new FailRestResponse(uploadResponse.getStatCode(), "")); -// } - - } catch (Exception e) { - log.error("템플릿 이미지 업로드 실패", e); - return new RestResponse(new FailRestResponse("UPLOAD_ERROR", "")); - } - } - - + // 데이터 // Form-data의 인덱스된 파라미터들을 VarListMapVO 리스트로 변환 (동적 처리 _1~_100) String falseCode = ftParameterProcessingService.processIndexedParameters(msgFtRequestVO, request); if(StringUtils.isNotEmpty(falseCode)){ @@ -109,29 +69,128 @@ public class SendFtServiceImpl implements SendFtService { return new RestResponse(new FailRestResponse(falseCode,"")); } + // 이미지 정제 + String falseImageCode = ftParameterProcessingService.getImagesInfo(msgFtRequestVO); + if(StringUtils.isNotEmpty(falseImageCode)) return new RestResponse(new FailRestResponse(falseImageCode,"")); -// -// -// -// -// MjonResponseVO munjaSendResponse = apiService.postForEntity( -// "/web/mjon/kakao/friendstalk/kakaoFriendsTalkMsgSendAjax_advc.do" -// , msgFtRequestVO -// , String.class -// ); + // ========== 새로운 개별 발송 코드 ========== + List responses = new ArrayList<>(); + List successList = new ArrayList<>(); + List failList = new ArrayList<>(); - // convertMjonDataToApiResponse => MjonResponseVO 데이터를 ApiResponse 데이터로 변환하는 메소드 -// log.info(" + munjaSendResponse :: [{}]", munjaSendResponse.toString()); -// if("OK".equals(munjaSendResponse.getResult())){ // 성공 -// return new RestResponse(SendSucRestResponse.convertMjonDataToApiResponse(munjaSendResponse)); -// }else{ // 실패 -// return new RestResponse(new FailRestResponse(munjaSendResponse.getStatCode(),"")); -// } + log.info("msgFtRequestVO.toString() :: [{}]", msgFtRequestVO.toString()); + // 각 수신자별로 개별 발송 처리 +// List individualRequestList = new ArrayList<>(); + for (VarFtListMapVO recipient : msgFtRequestVO.getVarListMap()) { + try { + // 개별 수신자용 KakaoFTSendVO 생성 + KakaoFTSendVO individualRequest = createKakaoFTSendVO(msgFtRequestVO, recipient); +// individualRequestList.add(individualRequest); - return new RestResponse(msgFtRequestVO); + log.info("individualRequest: {}", individualRequest); + + // 개별 API 호출 + MjonResponseVO response = apiService.postForEntity( + "/web/mjon/kakao/friendstalk/kakaoFriendsTalkMsgSendAjax_advc.do", + individualRequest, + String.class + ); + log.info("response: {}", response); + + responses.add(response); + successList.add(recipient.getPhone()); + + log.info("개별 발송 성공 - 수신번호: {}", recipient.getPhone()); + + } catch (Exception e) { + log.error("개별 발송 실패 - 수신번호: {}, 오류: {}", recipient.getPhone(), e.getMessage()); + failList.add(recipient.getPhone()); + } + } + + // SMS API 형태의 응답 생성 + FtSendSuccessResponse ftResponse = FtSendSuccessResponse.createFtResponse( + msgFtRequestVO.getVarListMap().size(), + successList.size(), + failList.size(), + successList + ); + + return new RestResponse(ftResponse); +// return new RestResponse(individualRequestList); + } + + /** + * MsgFtRequestVO + VarFtListMapVO를 KakaoFTSendVO로 변환 + * (개별 수신자용 객체 생성) + */ + private KakaoFTSendVO createKakaoFTSendVO(MsgFtRequestVO source, VarFtListMapVO recipient) { + + // 버튼 변환 (FtButtonVO → KakaoButtonVO) + List kakaoButtons = source.getButtons().stream() + .map(this::convertToKakaoButton) + .collect(Collectors.toList()); + + // 개별 수신자 객체 생성 + MjonFTSendVO mjonFTSend = MjonFTSendVO.builder() + .phone(recipient.getPhone()) // 핵심: 수신번호 +// .callTo(recipient.getPhone()) // 동일값 +// .eventYn("N") // 기본값 + .build(); + + // KakaoFTSendVO 생성 + return KakaoFTSendVO.builder() + // 공통 정보 (모든 요청에 동일) + + .mberId(source.getMberId()) + .sendKind(source.getSendKind()) + .reserveYn("N") + .adFlag(source.getAdFlag()) + + .senderKey(source.getSenderKey()) + .callFrom(source.getCallFrom()) + .buttonVOList(kakaoButtons) + + // 이미지 관련 + .templateImageUrl(source.getTemplateImageUrl()) + .atchFileId(source.getFilePath1()) + .imageType(source.getTemplateImageUrl() != null ? source.getTemplateMsgType() : null) +// .imageFileName(extractFileName(source.getTemplateImageUrl())) + .imageFileName(source.getTemplateImageName()) + + // 개별 수신자 정보 + .templateContent(recipient.getTemplateContent()) // 개별 내용 + .subMsgSendYn(source.getSubMsgSendYn()) // 개별 대체문자 + .subMsgTxt(recipient.getSubMsgTxt()) // 개별 대체문자 + .mjonFTSendVOList(Arrays.asList(mjonFTSend)) // 단일 수신자만 + + .build(); + } + + /** + * FtButtonVO를 KakaoButtonVO로 변환 + */ + private KakaoButtonVO convertToKakaoButton(FtButtonVO source) { + return KakaoButtonVO.builder() + .name(source.getName()) + .linkType(source.getLinkType()) + .linkTypeName(source.getLinkTypeName()) + .linkMo(source.getLinkMo()) + .linkPc(source.getLinkPc()) + .linkIos(source.getLinkIos()) + .linkAnd(source.getLinkAnd()) + .build(); + } + + /** + * 이미지 URL에서 파일명 추출 + */ + private String extractFileName(String imageUrl) { + if (imageUrl == null) return null; + return imageUrl.substring(imageUrl.lastIndexOf('/') + 1); } } diff --git a/src/main/java/com/itn/mjonApi/util/MunjaUtil.java b/src/main/java/com/itn/mjonApi/util/MunjaUtil.java index 80a66fa..5b187aa 100644 --- a/src/main/java/com/itn/mjonApi/util/MunjaUtil.java +++ b/src/main/java/com/itn/mjonApi/util/MunjaUtil.java @@ -1,6 +1,7 @@ package com.itn.mjonApi.util; import com.itn.mjonApi.mjon.api.kakao.at.send.mapper.domain.VarAtListMapVO; +import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.MsgFtRequestVO; import com.itn.mjonApi.mjon.api.kakao.ft.send.mapper.domain.VarFtListMapVO; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; @@ -141,7 +142,7 @@ public class MunjaUtil { } - public static String kakaoFtValidate(VarFtListMapVO vo, String subMsgSendYn) { + public static String kakaoFtValidate(VarFtListMapVO vo, MsgFtRequestVO msgFtRequestVO) { log.info(" vo.toString() [{}]", vo.toString()); @@ -158,11 +159,17 @@ public class MunjaUtil { } // 대체문자 검증 (대체문자 발송이 활성화된 경우에만) - if ("Y".equals(subMsgSendYn)) { + if ("Y".equals(msgFtRequestVO.getSubMsgSendYn())) { String subMsgTxt = vo.getSubMsgTxt(); if (StringUtils.isEmpty(subMsgTxt)) { return "STAT_2042"; // 대체문자 데이터 오류 } + + String callFrom = msgFtRequestVO.getCallFrom(); + if (StringUtils.isEmpty(callFrom)) { + return "STAT_2043"; // 대체문자 발송 시 발신번호 필요 + } + } // 모든 검증 통과