-
Notifications
You must be signed in to change notification settings - Fork 1
#28 송금 요청 만료 기능 #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
donggi-lee-bit
wants to merge
30
commits into
main
Choose a base branch
from
feature/28
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
#28 송금 요청 만료 기능 #29
Changes from 10 commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
382ad0a
feat: 송금 요청 만료 기능
donggi-lee-bit 25d0a89
refactor: 송금 요청 검증 메서드 분리
donggi-lee-bit 6e33f1b
refactor: 출금 대기 금액 취소 로직을 도메인 서비스로 캡슐화
donggi-lee-bit f2aa57f
feat: 송금 요청 만료 시 송금자의 출금 대기 금액 롤백 처리
donggi-lee-bit e4336a4
feat: 송금 요청 만료 시간을 계산하여 관리하는 필드 추가
donggi-lee-bit fd80157
refactor: 송금 요청 만료 처리 메서드 내에서 송금자의 송금 대기 금액 롤백 처리
donggi-lee-bit a059aa9
feat: 송금 요청 만료 갱신을 위한 ExpirationQueueManager 추가
donggi-lee-bit 28c7245
feat: 송금 요청 생성 시 만료 갱신을 위해 송금 요청을 큐에 등록
donggi-lee-bit ed56581
feat: 만료된 송금 요청을 큐에서 가져와서 만료 갱신
donggi-lee-bit 3f8a236
feat: 서비스 재시작 시 아직 처리되지 않은 요청들을 조회하여 만료 갱신 큐에 등록
donggi-lee-bit f78de68
refactor: 만료 시각을 송금 요청이 생성되는 시점에 확정되도록 생성 방식 변경
donggi-lee-bit abd9645
remove: DelayQueue 기반 만료 처리 로직 제거
donggi-lee-bit 06ae7fc
feat: 잔액 조회 시 만료된 요청이 존재할 경우 해당 요청을 만료 처리
donggi-lee-bit 4458744
feat: 비동기 처리용 스레드풀 추가
donggi-lee-bit fd51ec6
feat: 기본 스레드풀 정의
donggi-lee-bit cc3843c
feat: 만료된 요청 조회 기능
donggi-lee-bit 5542170
feat: 송금 요청 만료 상태로 일괄 갱신
donggi-lee-bit 93da03b
feat: 송금 요청 상태 변경 이력 일괄 저장
donggi-lee-bit 8a378d1
feat: 네임드락 실행 템플릿
donggi-lee-bit 31a3dba
feat: 네임드락 정책을 관리하는 enum 클래스
donggi-lee-bit 391c921
feat: 송금 요청 만료 등으로 인한 계좌의 송금 대기 금액 롤백
donggi-lee-bit 1aa9a1d
test: Account 도메인의 롤백 검증 로직 추가에 따른 테스트 환경 보완
donggi-lee-bit 06a7a1b
feat: 송금 요청 만료 일괄 처리 기능 추가
donggi-lee-bit 144cbff
refactor: 만료 처리 로직 개선
donggi-lee-bit 5353361
feat: 송금 요청 만료 처리를 위한 스케줄러 로직 추가
donggi-lee-bit 1dc05c6
test: 만료 요청 배치 갱신 로직 테스트 추가
donggi-lee-bit 85929c4
refactor: 상태별 정적 팩토리 메서드 도입하여 RemittanceStatusHistory 생성 로직 개선
donggi-lee-bit 7629744
test: TestContainer로 MySQL DB를 실행시켜 네임드락 경합 테스트 추가
donggi-lee-bit ca3e030
test: SpringBoot 실행이 필요한 테스트에 커스텀 어노테이션 IntegrationTest 을 사용하도록 변경
donggi-lee-bit dc319c9
test: 네임드락 경합 테스트에서 SpyBean 없이 동작하도록 테스트 구조 변경
donggi-lee-bit File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 64 additions & 0 deletions
64
src/main/java/com/donggi/sendzy/remittance/application/RemittanceExpirationService.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package com.donggi.sendzy.remittance.application; | ||
|
|
||
| import com.donggi.sendzy.account.application.AccountLockingService; | ||
| import com.donggi.sendzy.account.domain.AccountService; | ||
| import com.donggi.sendzy.remittance.domain.RemittanceRequest; | ||
| import com.donggi.sendzy.remittance.domain.RemittanceRequestStatus; | ||
| import com.donggi.sendzy.remittance.domain.RemittanceStatusHistory; | ||
| import com.donggi.sendzy.remittance.domain.service.RemittanceRequestService; | ||
| import com.donggi.sendzy.remittance.domain.service.RemittanceStatusHistoryService; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Propagation; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| @RequiredArgsConstructor | ||
| @Service | ||
| public class RemittanceExpirationService { | ||
|
|
||
| private final RemittanceRequestService remittanceRequestService; | ||
| private final RemittanceStatusHistoryService remittanceStatusHistoryService; | ||
| private final AccountLockingService accountLockingService; | ||
| private final AccountService accountService; | ||
|
|
||
| /** | ||
| * 송금 요청 만료 처리 (REQUIRES_NEW 트랜잭션) | ||
| * <p> | ||
| * 송금 요청 수락/거절 흐름 중 만료 상태로 전환해야 하는 경우, | ||
| * 예외 발생 여부와 관계없이 만료 처리와 히스토리 기록이 DB에 반영되어야 하므로 | ||
| * 별도의 트랜잭션으로 분리해 처리합니다. | ||
| * <p> | ||
| * - 기존 트랜잭션이 롤백되더라도, 해당 메서드는 독립적으로 커밋됩니다. | ||
| * - Propagation.REQUIRES_NEW 설정을 통해 트랜잭션을 분리합니다. | ||
| * | ||
| * @param remittanceRequest 만료 처리할 송금 요청 | ||
| */ | ||
| @Transactional(propagation = Propagation.REQUIRES_NEW) | ||
| public void expireRequest(final RemittanceRequest remittanceRequest) { | ||
| // 송금 요청 상태 변경 → EXPIRED | ||
| remittanceRequestService.expire(remittanceRequest); | ||
|
|
||
| // 송금자 계좌 롤백 처리 | ||
| rollbackHoldAmount(remittanceRequest.getSenderId(), remittanceRequest.getAmount()); | ||
|
|
||
| // 히스토리 저장 | ||
| recordStatusHistory(remittanceRequest); | ||
| } | ||
|
|
||
| private void recordStatusHistory(final RemittanceRequest request) { | ||
| remittanceStatusHistoryService.recordStatusHistory( | ||
| new RemittanceStatusHistory( | ||
| request.getId(), | ||
| request.getSenderId(), | ||
| request.getReceiverId(), | ||
| request.getAmount(), | ||
| RemittanceRequestStatus.EXPIRED | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| private void rollbackHoldAmount(final long senderId, final long amount) { | ||
| final var senderAccount = accountLockingService.getByMemberIdForUpdate(senderId); | ||
| accountService.cancelWithdraw(senderAccount, amount); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
src/main/java/com/donggi/sendzy/remittance/exception/ExpiredRemittanceRequestException.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package com.donggi.sendzy.remittance.exception; | ||
|
|
||
| public class ExpiredRemittanceRequestException extends RuntimeException { | ||
|
|
||
| public ExpiredRemittanceRequestException() { | ||
| super("송금 요청이 만료되었습니다."); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
...nggi/sendzy/remittance/infrastructure/expiration/ExpirationQueueInterruptedException.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package com.donggi.sendzy.remittance.infrastructure.expiration; | ||
|
|
||
| public class ExpirationQueueInterruptedException extends RuntimeException { | ||
|
|
||
| public ExpirationQueueInterruptedException() { | ||
| super("만료 큐에서 요청을 가져오는 도중 인터럽트되었습니다."); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expire메서드가 호출될 때expiredAt또한 업데이트 해준다면 데이터가 불완전한 시점을 줄일 수 있을 것으로 보입니다.getExpiredAt은 getter이지만 실제로는 내부expiredAt의 상태를 변경시킬 수 있으므로 메서드 네이밍으로 인한 오해를 불러일으킬 수 있습니다. 메서드 이름을 변경하거나, 내부 로직의 변경이 필요한데요. 메서드 내용을 보니expire메서드가 호출될 때expiredAt을 같이 업데이트해준다면 순수한 getter로서 동작할 수 있겠네요.또한
expiredAt을 계산할 때createdAt.plusDays(EXPIRATION_DAYS);방식으로 계산하는건 꽤나 위험한 방식입니다. 서비스의 만료시간 정책이 중간에 변경된다면 사용자가 인지하는 만료일과 서비스가 실제로 만료처리하는 일자가 달라질 수 있는데요, 우선순위는 항상 사용자가 송금요청시 확인한 만료일이여야 합니다. 만료일시는 송금요청하는 시간에 확정됩니다.만료 시점은 생성일로부터 일정 시간이 지날 때 만료라고 명시되어있으므로
getExpiredAt을 호출할 때 계산하는게 아니라 송금요청 데이터 생성 시 부터 TTL성격의expiredAt데이터를 함께 생성해주는 것이 더 적절하지 않을까요?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
만료 시각을 단순히 만료 여부를 판단하기 위한 보조 도구 정도로만 인식하면서, 생성 시점에 저장해야 한다는 관점을 놓쳤던 것 같습니다. 😂
말씀하신 것처럼 getter 내에서 특정 필드 값을 변경하는 방식은 해당 메서드를 사용하는 쪽에서 의도와는 다른 동작이 있어 혼란이 있어 보입니다.
또한 만료 정책은 변경될 수 있고, 사용자는 송금 요청 시점에 인지한 정책에 따라 만료 상태가 갱신되어야 하므로, getter나 별도의 로직이 아닌 송금 요청 생성 시점의 만료 정책을 적용해 만료 시각을 확정시키는 방법이 더 적절한 설계라고 생각합니다.
해당 내용 반영하여 수정하겠습니다!