JIWOO 2025-08-14
이지우 - 카카오 환불 수정. 트랜잭션 분리를 위하여 스케줄 Util에서 반복문으로 처리
@dbc1f2335ad5bd9f6b3ee57fc7d3740b449b7674
src/main/java/itn/let/kakao/user/kakaoAt/service/KakaoAlimTalkService.java
--- src/main/java/itn/let/kakao/user/kakaoAt/service/KakaoAlimTalkService.java
+++ src/main/java/itn/let/kakao/user/kakaoAt/service/KakaoAlimTalkService.java
@@ -34,4 +34,8 @@
 	//카카오(알림톡, 친구톡 통합) 전송 실패 환불리스트 조회
 	public void selectKakaoSentRefundList() throws Exception;
 	
+	//카카오(알림톡, 친구톡 통합) 전송 실패 환불리스트 조회
+	public List<KakaoVO> selectKakaoSentRefundListForSingle() throws Exception;
+	
+	public void kakaoSingleRefund(KakaoVO kakaoVO) throws Exception;
 }
src/main/java/itn/let/kakao/user/kakaoAt/service/impl/KakaoAlimTalkServiceImpl.java
--- src/main/java/itn/let/kakao/user/kakaoAt/service/impl/KakaoAlimTalkServiceImpl.java
+++ src/main/java/itn/let/kakao/user/kakaoAt/service/impl/KakaoAlimTalkServiceImpl.java
@@ -1,7 +1,6 @@
 package itn.let.kakao.user.kakaoAt.service.impl;
 
 import java.math.BigDecimal;
-import java.math.RoundingMode;
 import java.text.SimpleDateFormat;
 import java.time.Duration;
 import java.time.Instant;
@@ -11,8 +10,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
 import java.util.stream.Collectors;
 
 import javax.annotation.Resource;
@@ -22,12 +19,13 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 import egovframework.rte.fdl.cmmn.EgovAbstractServiceImpl;
 import egovframework.rte.fdl.idgnr.EgovIdGnrService;
 import egovframework.rte.fdl.security.userdetails.util.EgovUserDetailsHelper;
 import itn.com.cmm.LoginVO;
-import itn.com.cmm.MjonMsgSendVO;
 import itn.com.utl.fcc.service.EgovStringUtil;
 import itn.let.kakao.kakaoComm.BizKakaoPriceVO;
 import itn.let.kakao.kakaoComm.KakaoSendAdvcVO;
@@ -51,7 +49,6 @@
 import itn.let.sym.site.service.JoinSettingVO;
 import itn.let.sym.site.service.impl.SiteManagerDAO;
 import itn.let.uss.umt.service.EgovUserManageService;
-import itn.let.uss.umt.service.UserManageVO;
 import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
@@ -946,7 +943,7 @@
 	private void handleRefund(KakaoVO vo, String msg) throws Exception {
 		// mj_cash 테이블에 환불 내역 추가 및 회원 금액 업데이트
 		// eachPrice는 환불될 금액이므로 양수여야 합니다.
-		priceAndPoint.insertCashAndPoint(
+		priceAndPoint.insertCashAndPointNoUpdate(
 			vo.getUserId(),
 			Float.parseFloat(vo.getEachPrice()), // 환불 금액은 양수
 			msg,
@@ -1126,8 +1123,33 @@
 		return statusResponse;
 	}
 	
-	
+	@Override
+	public List<KakaoVO> selectKakaoSentRefundListForSingle() throws Exception{
+		return kakaoAlimTalkDAO.selectKakaoSentRefundList();
+	}	
 
+	@Override
+	public void kakaoSingleRefund(KakaoVO kakaoVO) throws Exception {
+		
+		System.out.println(kakaoVO.getMsgGroupId() +"________결과 : " +kakaoVO.getRsltCode() +"     대체문자 전송 : "+kakaoVO.getSubMsgSendYn());
+		kakaoVO.setMsgTypeTxt(AT_MSG_TYPE.equals(kakaoVO.getMsgType()) ? "알림톡" : "친구톡");
+		
+		if(KAKAO_SUCCESS_CODE.equals(kakaoVO.getRsltCode())) {
+			//1.카카오톡 발송 성공
+			processKakaoSendCharge(kakaoVO); 
+			
+		}else if(RESEND_YN_YES.equals(kakaoVO.getSubMsgSendYn())) {//카카오톡 발송 실패, 대체문자 발송 신청 O
+			//2.카카오톡 발송 실패 + 대체문자 신청 O
+			handleAlternativeMessageScenario(kakaoVO);
+			
+		}else {
+			//3.카카오톡 발송 실패 + 대체문자 신청 X : 전액 환불
+			handleRefund(kakaoVO, "카카오 " + kakaoVO.getMsgTypeTxt() + " 전송 실패로 인한 결제 금액 환불");
+		}
+		
+		//모든 유형 환불 완료 처리
+		mjonMsgDAO.updateRefundY(kakaoVO);
+	}
 
 //	// 보유 금액이 충분한지 확인하는 메서드
 //	private boolean isCashSufficient(String userId, List<KakaoSendAdvcVO> kakaoSendAdvcListVO) throws Exception {
@@ -1149,35 +1171,6 @@
 //		// 비교 수행
 //		return befCash.compareTo(totalEachPrice) >= 0;
 //	}
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
 	
 	
 }
src/main/java/itn/let/kakao/user/kakaoAt/web/KakaoAlimTalkSendController.java
--- src/main/java/itn/let/kakao/user/kakaoAt/web/KakaoAlimTalkSendController.java
+++ src/main/java/itn/let/kakao/user/kakaoAt/web/KakaoAlimTalkSendController.java
@@ -9,9 +9,11 @@
 import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 import javax.annotation.Resource;
@@ -61,7 +63,6 @@
 import itn.com.cmm.util.StringUtil;
 import itn.com.utl.fcc.service.EgovStringUtil;
 import itn.let.kakao.kakaoComm.KakaoReturnVO;
-import itn.let.kakao.kakaoComm.KakaoSendAdvcVO;
 import itn.let.kakao.kakaoComm.KakaoSendUtil;
 import itn.let.kakao.kakaoComm.KakaoVO;
 import itn.let.kakao.kakaoComm.kakaoApi.KakaoApiJsonSave;
@@ -79,6 +80,9 @@
 import itn.let.mjo.msgholiday.service.MsgAlarmSetVO;
 import itn.let.mjo.msgholiday.service.MsgHolidayService;
 import itn.let.mjo.msgholiday.service.MsgHolidayVO;
+import itn.let.mjo.pay.service.MjonPayService;
+import itn.let.mjo.pay.service.MjonPayVO;
+import itn.let.org.web.OrgChartManageController;
 import itn.let.sym.site.service.EgovSiteManagerService;
 import itn.let.sym.site.service.JoinSettingVO;
 import itn.let.uss.umt.service.EgovUserManageService;
@@ -101,6 +105,8 @@
  */
 @Controller
 public class KakaoAlimTalkSendController {
+
+    private final OrgChartManageController orgChartManageController;
 
 	@Resource(name = "egovMjonMsgGroupIdGnrService")
 	private EgovIdGnrService idgenMjonMsgGroupId;
@@ -149,6 +155,13 @@
 	
 	@Autowired
 	private MjonCommon mjonCommon;
+	
+	@Resource(name = "mjonPayService")
+	private MjonPayService mjonPayService;
+
+    KakaoAlimTalkSendController(OrgChartManageController orgChartManageController) {
+        this.orgChartManageController = orgChartManageController;
+    }
 
 	@RequestMapping(value= {"/web/mjon/alimtalk/kakaoAlimtalkMsgDataView.do"})
 	public String KakaoAlimtalkMsgDataView(ModelMap model
@@ -1541,6 +1554,25 @@
 		return "web/kakao/msgdata/at/KakaoAlimtalkMsgDataView_tmp";
 	}
 	
+	public void kakaoRefundSingleTransaction() throws Exception{
+		System.out.println("=============카카오 환불 싱글 트랜잭션 수행 =============");
+		
+		List<KakaoVO> kakaoRefundList = kakaoAlimTalkService.selectKakaoSentRefundListForSingle();
+		Set<String> targetIdSet = new HashSet<>();
+		
+		for(KakaoVO kakaoVO : kakaoRefundList) {
+			kakaoAlimTalkService.kakaoSingleRefund(kakaoVO);
+			targetIdSet.add(kakaoVO.getUserId());
+		}
+		
+		MjonPayVO mjonPayVO = new MjonPayVO();
+		for(String userId : targetIdSet) {
+			mjonPayVO.setUserId(userId);
+			mjonPayService.updateMemberCash(mjonPayVO); //회원정보 업데이트
+		}
+		
+	}
+	
 	/**
 	* @Method Name : kakaoMsgSendRefundTestAjax
 	* @작성일 : 2025. 8. 6.
@@ -1555,7 +1587,7 @@
 		ModelAndView modelAndView = new ModelAndView(); 
 		modelAndView.setViewName("jsonView");
 		
-		kakaoAlimTalkService.selectKakaoSentRefundList();
+		this.kakaoRefundSingleTransaction();
 		
 		modelAndView.addObject("result", "success");
 		return modelAndView;
src/main/java/itn/let/module/base/PriceAndPoint.java
--- src/main/java/itn/let/module/base/PriceAndPoint.java
+++ src/main/java/itn/let/module/base/PriceAndPoint.java
@@ -213,6 +213,36 @@
 		kakaoAlimTalkDAO.insertKakaoSendPrice(kakaoVO);
 		
 	}
- 
+
+	/**
+	 * @methodName	: insertCashAndPoint 
+	 * @author		: 이지우
+	 * @date		: 2025. 8. 14.
+	 * @description	: insertCashAndPoint 에서 updateMemberCash 제외
+	 * @return : void
+	 * @param userId
+	 * @param totPrice
+	 * @param memo
+	 * @param msgGroupId
+	 * @param userData
+	 * @throws Exception
+	 * 
+	 */
+	public void insertCashAndPointNoUpdate(
+			String userId
+			, float totPrice
+			, String memo
+			, String msgGroupId
+			, String userData
+			) throws Exception {
+		
+		MjonPayVO mjonPayVO = buildPayVO(userId, totPrice, memo, msgGroupId);
+
+//		 환불로 인해 userData가 추후 사용될 경우 여기에 처리
+		 if (StringUtils.isNotEmpty(userData)) { mjonPayVO.setOrderId(userData); }
+		
+		mjonPayDAO.insertCash(mjonPayVO); //캐시 
+		
+	}
  
 }
(No newline at end of file)
src/main/java/itn/let/schdlr/service/SchedulerUtil.java
--- src/main/java/itn/let/schdlr/service/SchedulerUtil.java
+++ src/main/java/itn/let/schdlr/service/SchedulerUtil.java
@@ -1,12 +1,16 @@
 package itn.let.schdlr.service;
 
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import javax.annotation.Resource;
 import javax.sql.DataSource;
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Profile;
@@ -24,12 +28,15 @@
 import itn.let.fax.admin.service.FaxStatVO;
 import itn.let.kakao.admin.kakaoAt.service.MjonKakaoAtStatVO;
 import itn.let.kakao.admin.statistics.service.KakaoStatisticsService;
+import itn.let.kakao.kakaoComm.KakaoVO;
+import itn.let.kakao.user.kakaoAt.service.KakaoAlimTalkService;
 import itn.let.lett.service.LetterService;
 import itn.let.mail.service.MailTemplateService;
 import itn.let.mjo.msg.service.MjonMsgService;
 import itn.let.mjo.msg.service.MjonMsgStatVO;
-import itn.let.mjo.msg.service.MjonMsgVO;
 import itn.let.mjo.msgdata.service.impl.MjonMsgDataDAO;
+import itn.let.mjo.pay.service.MjonPayVO;
+import itn.let.mjo.pay.service.impl.MjonPayDAO;
 import itn.let.sts.com.StatsVO;
 import itn.let.sts.cst.service.EgovConectStatsService;
 import itn.let.uss.umt.service.EgovUserManageService;
@@ -81,6 +88,12 @@
 	
 	@Resource(name="MjonMsgDataDAO")
 	private MjonMsgDataDAO mjonMsgDataDAO;
+	
+	@Resource(name="KakaoAlimTalkService")
+	private KakaoAlimTalkService kakaoAlimTalkService;
+	
+	@Autowired
+	private MjonPayDAO mjonPayDAO;
 	
     /** 설정값 가져오기 */
 	@Value("#{globalSettings['Globals.Env']}")
@@ -466,20 +479,26 @@
 		return new JdbcTemplateLockProvider(dataSource);
 	}
 	
-	/**
 	@Scheduled(cron = "0 0/3 * * * ?") 	// 3분마다 실행
 	@SchedulerLock(name = "runKakaoOneTime", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
 	public void runKakaoOneTime() throws Exception {
 		
-		// do something...
-		try {
 			System.out.println("=============SchedulerUtil=====runKakaoOneTime =============>");
-			schdlrManageService.kakaoFailPayBack();
-		}catch(Exception ex) {
-			ex.printStackTrace();
-		}	
+			List<KakaoVO> kakaoRefundList = kakaoAlimTalkService.selectKakaoSentRefundListForSingle();
+			Set<String> targetIdSet = new HashSet<>();
+			
+			for(KakaoVO kakaoVO : kakaoRefundList) {
+				kakaoAlimTalkService.kakaoSingleRefund(kakaoVO);
+				targetIdSet.add(kakaoVO.getUserId());
+			}
+			
+			MjonPayVO mjonPayVO = new MjonPayVO();
+			for(String userId : targetIdSet) {
+				mjonPayVO.setUserId(userId);
+				mjonPayDAO.updateMemberCash(mjonPayVO); //회원정보 업데이트
+			}
+			
 	}
-	*/
 	//환불 실행
 	private void PayBack(String p_type) throws Exception {
 		
src/main/resources/egovframework/sqlmap/let/mjo/kakao/Kakao_AT_SQL_Mysql.xml
--- src/main/resources/egovframework/sqlmap/let/mjo/kakao/Kakao_AT_SQL_Mysql.xml
+++ src/main/resources/egovframework/sqlmap/let/mjo/kakao/Kakao_AT_SQL_Mysql.xml
@@ -235,6 +235,7 @@
 			AND	MMD.REFUND_YN          = 'N'
 			AND	MMD.RESERVE_C_YN       = 'N'
 			AND MMD.MSG_TYPE IN(8, 9)
+		ORDER BY MMD.USER_ID ASC
 	</select>
 	
 	<select id="kakaoAlimTalkDAO.selectKakaoAtSentRefundList" resultClass="kakaoVO">
Add a comment
List