Skip to content

Conversation

@hellozo0
Copy link
Contributor

구현사항

  1. 송금사용자의 설정에 따라 즉시 송금/ 대기후 수락 송금 방법으로 진행
    • 사용자의 설정값을 받아, Transaction DB에 저장후 바로 송금큐에 넣거나. Pending으로 상태값 변경
  2. 송금 대기 걸기
  3. 송금 72시간 limit 이 지나면 취소하기, 취소시 원래 금액 되돌리기
  4. 송금 limit 24시간 남았을 때, 수령 리마인드 발송

프로그래밍 요구사항

  1. 송금사용자의 설정에 따라 즉시 송금/ 대기후 수락 송금 방법으로 진행
    • Case를 나눠서 즉시 송금일 경우, 바로 받는 함수로 연결
    • Peding을 할 경우에는 내 계좌에서 돈을 빼고 Pending상태로 변환
  2. 송금 대기
    • transaction PENDING으로 상태값 변경
    • 송금을 받으라는 메일 전송 로직 연결
  3. 송금 72시간 limit 이 지나면 취소하기
    • 취소의 경우에는 시간에 조금 민감하다는 생각이 들어 1분마다 스케줄러를 돌면서 수행
    • 커서 페이징을 통해서 1000개씩, 쿼리 조회
    • 쿼리 조회 조건은 transaction이 PENDING이며, PENDINGDate가 72시간이 지났으면 조회
    • 취소 후에는 transaction 상태값 변경후 환불 송금 진행(송금 큐에 넣기, 기존 함수 사용)
  4. 송금 limit 24시간 남았을 때, 수령 리마인드 발송
    • 알림은 시간에 조금 덜 예민하다는 생각이들어 부하를 줄이고자... 5분마다 스케줄러를 돌면서 수행
    • 위와 동일하게 커서페이징 1000개씩, 쿼리 조회
    • 쿼리 조회 조건은 transaction이 PENDING이며, PENDINGDate가 24시간 이내로 남았으며, 24시간 알림 메일이 안보내 진 것을 조회한다.
    • 메일을 보내고 나서는 mailSent = true로 변경

미완성 부분

  1. 송금 대기 요청 수락(돈받기)
  2. PENDING으로 정산할 경우, 내 계좌에서 돈을 미리 빼기 (기존의 돈이 부족한지 파악하고, 부족하다면 충전하는 로직 그대로 사용)
  3. 실제로 제대로 동작하는지 테스트 전. (위의 함수 구현 후에 테스트 예정)

개선사항

  1. 송금/이체 레디스큐를 Stream으로 변환 작업 ( 이부분에서 시간이 계속..오래 걸리네요.. 어렵다..)
  2. 송금 로직을 하나로 합쳐서 똑같은 로직을 분산해서 사용하지 않도록 수정
  3. SettlementService 로직 분리
  4. Transaction 부분 견고하게 롤백 설계, 전송 실패 시에 대한 보완할 부분 추가
  5. 코드 스멜 제거 (현재 깔끔하게 정리하지 못했습니다! Stream으로 변환하면서 정리하겠습니다)

- [Settlement]  대기하는 로직이 추가하면서 기존의 엔티티들에서 필요 없는 remainAmount 삭제
- [SettlementMember] 트랜잭션 테이블이 추가되면서 필요 없는 status 삭제
- Transaction 엔티티 추가
- 정산을 위한 송금 요청 (PENDING)
- 정산 요청 수락 (코드 미완)
- 72시간 초과시 자동 취소 (커서 페이징으로 구현)
- 24시간마다 남은 송금 리마인드 알림 (5분마다)
@hellozo0 hellozo0 self-assigned this Feb 18, 2025
Copy link

@opixxx opixxx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다. 간단하게 의견 남겼습니다. 추가 완성되면 다시 한 번 봐볼게요

@Query("SELECT sm FROM SettlementMember sm WHERE sm.settlement.id = :settlementId and sm.accountId = :memberAccountId")
Optional<SettlementMember> findByAccountIdWithXLock(@Param("settlementId") Long settlementId, @Param("memberAccountId") Long memberAccountId);

Optional<SettlementMember> findBySettlement(long settlementId);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findBySettlementId()로 해야하지 않나요?

ORDER BY t.pendingDate, t.id
LIMIT :size
""")
List<Transaction> findExpiredTransactionsWithCursor(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인덱스 설정을 어떻게 하시나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스크린샷 2025-02-21 오후 3 28 02 현재 pending,expired 인덱스는 이렇게 설계 되어 있습니다!

* [Step4] (72시간 초과 시 자동 취소) > (환불 + 상태 변경)
*/
@Transactional
public void processExpiredTransactions(List<Transaction> transactions) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

동시에 취소, 수령 요청이 오면 어떻게 되나요?

Copy link
Contributor Author

@hellozo0 hellozo0 Feb 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redis큐에 송금 & 수령 & 취소 와 관련된 트랜잭션들을 관리하고 있는데 생각을 해보니
지금 상태값을 먼저 바꾸고 레디스에 넣고 있는 형태라 지금의 코드에서는 상태값을 먼저 확인하고 먼저 실행되는 로직으로 redis 큐에 넣는 방식으로 해야할것 같습니다!

지금 다시 보니 레디스랑 transaction 둘다 쓰니 문제가 많은것 같네요

* [Step4] 돈을 보냈다는 알림 보내는 함수
* "송금 요청이 도착했습니다. 72시간 내에 수령해주세요!"
* */
private void sendNotification(long requestAccountId, String title) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메일을 보내는 함수는 따른 클래스로 빼도 좋을 것 같아요.

OR (t.pendingDate = :lastPendingDate AND t.id > :lastTransactionId))
ORDER BY t.pendingDate, t.id
LIMIT :size
""")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 마감 시간으로 부터 24시간이 남은 데이터를 가져오는 쿼리인데 굳이 커서 페이징을 멀티 value로 할 필요가 있을까요?

Copy link
Contributor Author

@hellozo0 hellozo0 Feb 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일단은 현재 mailSent를 통해서, 24시간 알림이 안간 데이터가 있다면 다음 시도때 재시도될 수 있게 구현하기 위해서 컬럼을 추가 햇고, 데이터가 많을거라 생각해서 컬럼들이 많아졌습니다!

AND (t.pendingDate > :lastPendingDate OR (t.pendingDate = :lastPendingDate AND t.id > :lastTransactionId))
이 부분은 중복데이터 때문에 추가했는데, <OR (t.pendingDate = :lastPendingDate> 이부분이 필요한 상황이 안 생길거 같아서 삭제해도 될거 같네요..!

Copy link
Member

@nyh365 nyh365 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 머지 대상 브랜치가 main으로 되어있어요. 고생하셨습니다~

sendNotification(transaction.getReceiverAccountId(), StringEnum.ALERT_24_MAIL_TITLE.toString());
});

updateMailSentStatus(transactions);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

알림이 중복으로 갈 것 같아 고민했는데 따로 상태를 추가해서 처리하셨네요👍

@hellozo0 hellozo0 changed the base branch from main to base/hellozo0 March 13, 2025 02:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants