From 04e5fd5e134d0091deb5ab7f3752cad769f79274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9B=90=EC=A7=80=EC=9C=A4?= Date: Sun, 17 May 2026 21:21:52 +0900 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20=EC=A0=95=EC=82=B0=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=EC=97=90=20=EC=9E=85=EA=B8=88=20=ED=99=95=EC=9D=B8=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20ID=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/PaymentRequestReader.java | 10 ++++++ .../impl/PaymentRequestValidator.java | 10 +++--- .../query/QuerySettlementService.java | 6 +++- .../PaymentRequestRepository.java | 11 ++++++ .../response/SettlementDetailResponse.java | 15 ++++++-- .../response/SettlementMemberResponse.java | 34 +++++++++++++++++++ .../PaymentRequestReaderTest.java | 27 +++++++++++++++ .../PaymentRequestValidatorTest.java | 31 ++++++++++++----- .../controller/SettlementControllerTest.java | 7 ++-- .../service/QuerySettlementServiceTest.java | 8 +++++ 10 files changed, 139 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/dnd/moddo/event/presentation/response/SettlementMemberResponse.java diff --git a/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestReader.java b/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestReader.java index 9deb063e..53cd86df 100644 --- a/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestReader.java +++ b/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestReader.java @@ -61,4 +61,14 @@ public PaymentRequestsResponse findByTargetUserId(Long targetUserId) { return PaymentRequestsResponse.of(responses); } + public Map findPendingRequestIdByMemberId(Long settlementId) { + return paymentRequestRepository.findBySettlementIdAndStatus(settlementId, PaymentRequestStatus.PENDING) + .stream() + .collect(Collectors.toMap( + PaymentRequest::getRequestMemberId, + PaymentRequest::getId, + (first, ignored) -> first + )); + } + } diff --git a/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestValidator.java b/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestValidator.java index 5bb2f704..1799082d 100644 --- a/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestValidator.java +++ b/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestValidator.java @@ -2,6 +2,7 @@ import org.springframework.stereotype.Component; +import com.dnd.moddo.auth.model.exception.UserPermissionException; import com.dnd.moddo.event.domain.member.Member; import com.dnd.moddo.event.domain.paymentRequest.PaymentRequest; import com.dnd.moddo.event.domain.paymentRequest.PaymentRequestStatus; @@ -9,7 +10,6 @@ import com.dnd.moddo.event.domain.paymentRequest.exception.ManagerPaymentRequestNotAllowedException; import com.dnd.moddo.event.domain.paymentRequest.exception.PaymentRequestAlreadyApprovedException; import com.dnd.moddo.event.domain.paymentRequest.exception.PaymentRequestNotPendingException; -import com.dnd.moddo.event.domain.paymentRequest.exception.PaymentRequestUnauthorizedException; import com.dnd.moddo.event.infrastructure.PaymentRequestRepository; import lombok.RequiredArgsConstructor; @@ -27,7 +27,7 @@ public void validateCreateRequest(Long settlementId, Member requestMember) { public void validateProcessRequest(PaymentRequest paymentRequest, Long userId) { validatePendingStatus(paymentRequest); - validateTargetUser(paymentRequest, userId); + validateManagerPermission(paymentRequest, userId); } private void validateDuplicateRequest(Long settlementId, Long requestMemberId) { @@ -66,9 +66,9 @@ private void validatePendingStatus(PaymentRequest paymentRequest) { } } - private void validateTargetUser(PaymentRequest paymentRequest, Long userId) { - if (!paymentRequest.getTargetUser().getId().equals(userId)) { - throw new PaymentRequestUnauthorizedException(paymentRequest.getId(), userId); + private void validateManagerPermission(PaymentRequest paymentRequest, Long userId) { + if (!paymentRequest.getSettlement().isWriter(userId)) { + throw new UserPermissionException(); } } } diff --git a/src/main/java/com/dnd/moddo/event/application/query/QuerySettlementService.java b/src/main/java/com/dnd/moddo/event/application/query/QuerySettlementService.java index 686b4413..71b472e4 100644 --- a/src/main/java/com/dnd/moddo/event/application/query/QuerySettlementService.java +++ b/src/main/java/com/dnd/moddo/event/application/query/QuerySettlementService.java @@ -2,11 +2,13 @@ import java.time.Duration; import java.util.List; +import java.util.Map; import org.springframework.stereotype.Service; import com.dnd.moddo.common.cache.CacheExecutor; import com.dnd.moddo.common.cache.CacheKeys; +import com.dnd.moddo.event.application.impl.PaymentRequestReader; import com.dnd.moddo.event.application.impl.SettlementReader; import com.dnd.moddo.event.application.impl.SettlementValidator; import com.dnd.moddo.event.domain.member.Member; @@ -29,6 +31,7 @@ public class QuerySettlementService { private static final Duration SETTLEMENT_LIST_CACHE_TTL = Duration.ofMinutes(5); private final SettlementReader settlementReader; + private final PaymentRequestReader paymentRequestReader; private final SettlementValidator settlementValidator; private final CacheExecutor cacheExecutor; @@ -36,7 +39,8 @@ public SettlementDetailResponse findOne(Long settlementId, Long userId) { Settlement settlement = settlementReader.read(settlementId); settlementValidator.checkSettlementAuthor(settlement, userId); List members = settlementReader.findBySettlement(settlementId); - return SettlementDetailResponse.of(settlement, members); + Map paymentRequestIdByMemberId = paymentRequestReader.findPendingRequestIdByMemberId(settlementId); + return SettlementDetailResponse.of(settlement, members, paymentRequestIdByMemberId); } public SettlementHeaderResponse findBySettlementHeader(Long settlementId) { diff --git a/src/main/java/com/dnd/moddo/event/infrastructure/PaymentRequestRepository.java b/src/main/java/com/dnd/moddo/event/infrastructure/PaymentRequestRepository.java index 4b830dec..609fa7f6 100644 --- a/src/main/java/com/dnd/moddo/event/infrastructure/PaymentRequestRepository.java +++ b/src/main/java/com/dnd/moddo/event/infrastructure/PaymentRequestRepository.java @@ -32,4 +32,15 @@ default PaymentRequest getById(Long paymentRequestId) { @Query("select pr from PaymentRequest pr where pr.targetUser.id = :targetUserId") List findByTargetUserId(@Param("targetUserId") Long targetUserId); + + @Query(""" + select pr + from PaymentRequest pr + where pr.settlement.id = :settlementId + and pr.status = :status + """) + List findBySettlementIdAndStatus( + @Param("settlementId") Long settlementId, + @Param("status") PaymentRequestStatus status + ); } diff --git a/src/main/java/com/dnd/moddo/event/presentation/response/SettlementDetailResponse.java b/src/main/java/com/dnd/moddo/event/presentation/response/SettlementDetailResponse.java index b9ebdff3..21477e91 100644 --- a/src/main/java/com/dnd/moddo/event/presentation/response/SettlementDetailResponse.java +++ b/src/main/java/com/dnd/moddo/event/presentation/response/SettlementDetailResponse.java @@ -1,6 +1,7 @@ package com.dnd.moddo.event.presentation.response; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import com.dnd.moddo.event.domain.member.Member; @@ -9,11 +10,19 @@ public record SettlementDetailResponse( Long id, String groupName, - List members + List members ) { public static SettlementDetailResponse of(Settlement settlement, List members) { - List memberResponses = members.stream() - .map(MemberResponse::of) + return of(settlement, members, Map.of()); + } + + public static SettlementDetailResponse of( + Settlement settlement, + List members, + Map paymentRequestIdByMemberId + ) { + List memberResponses = members.stream() + .map(member -> SettlementMemberResponse.of(member, paymentRequestIdByMemberId.get(member.getId()))) .collect(Collectors.toList()); return new SettlementDetailResponse( settlement.getId(), diff --git a/src/main/java/com/dnd/moddo/event/presentation/response/SettlementMemberResponse.java b/src/main/java/com/dnd/moddo/event/presentation/response/SettlementMemberResponse.java new file mode 100644 index 00000000..63460ef4 --- /dev/null +++ b/src/main/java/com/dnd/moddo/event/presentation/response/SettlementMemberResponse.java @@ -0,0 +1,34 @@ +package com.dnd.moddo.event.presentation.response; + +import java.time.LocalDateTime; + +import com.dnd.moddo.event.domain.member.ExpenseRole; +import com.dnd.moddo.event.domain.member.Member; + +import lombok.Builder; + +@Builder +public record SettlementMemberResponse( + Long id, + ExpenseRole role, + String name, + String profile, + Long userId, + Boolean isPaid, + LocalDateTime paidAt, + Long paymentRequestId +) { + + public static SettlementMemberResponse of(Member member, Long paymentRequestId) { + return SettlementMemberResponse.builder() + .id(member.getId()) + .name(member.getName()) + .role(member.getRole()) + .userId(member.getUserId()) + .isPaid(member.isPaid()) + .paidAt(member.getPaidAt()) + .paymentRequestId(paymentRequestId) + .profile(member.getProfileUrl()) + .build(); + } +} diff --git a/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestReaderTest.java b/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestReaderTest.java index bdaec97e..3f5d8211 100644 --- a/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestReaderTest.java +++ b/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestReaderTest.java @@ -100,6 +100,33 @@ void findByTargetUserId() { assertThat(second.totalAmount()).isEqualTo(5000L); } + @Test + @DisplayName("정산의 대기 중인 입금 확인 요청 ID를 멤버 ID 기준으로 조회할 수 있다.") + void findPendingRequestIdByMemberId() { + Settlement settlement = mock(Settlement.class); + Member member = Member.builder() + .name("김반숙") + .profileId(1) + .settlement(settlement) + .role(ExpenseRole.PARTICIPANT) + .build(); + PaymentRequest paymentRequest = PaymentRequest.builder() + .settlement(settlement) + .requestMember(member) + .targetUser(mock(com.dnd.moddo.user.domain.User.class)) + .build(); + + setField(member, "id", 11L); + setField(paymentRequest, "id", 100L); + + when(paymentRequestRepository.findBySettlementIdAndStatus(1L, PaymentRequestStatus.PENDING)) + .thenReturn(List.of(paymentRequest)); + + java.util.Map result = paymentRequestReader.findPendingRequestIdByMemberId(1L); + + assertThat(result).containsEntry(11L, 100L); + } + private void setField(Object target, String fieldName, Object value) { try { java.lang.reflect.Field field = target.getClass().getDeclaredField(fieldName); diff --git a/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestValidatorTest.java b/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestValidatorTest.java index 47efb1c7..e6c05c9e 100644 --- a/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestValidatorTest.java +++ b/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestValidatorTest.java @@ -10,6 +10,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import com.dnd.moddo.auth.model.exception.UserPermissionException; import com.dnd.moddo.event.application.impl.PaymentRequestValidator; import com.dnd.moddo.event.domain.member.Member; import com.dnd.moddo.event.domain.paymentRequest.PaymentRequest; @@ -18,9 +19,8 @@ import com.dnd.moddo.event.domain.paymentRequest.exception.ManagerPaymentRequestNotAllowedException; import com.dnd.moddo.event.domain.paymentRequest.exception.PaymentRequestAlreadyApprovedException; import com.dnd.moddo.event.domain.paymentRequest.exception.PaymentRequestNotPendingException; -import com.dnd.moddo.event.domain.paymentRequest.exception.PaymentRequestUnauthorizedException; +import com.dnd.moddo.event.domain.settlement.Settlement; import com.dnd.moddo.event.infrastructure.PaymentRequestRepository; -import com.dnd.moddo.user.domain.User; @ExtendWith(MockitoExtension.class) class PaymentRequestValidatorTest { @@ -71,18 +71,31 @@ void validateCreateRequestFailWhenAlreadyApproved() { } @Test - @DisplayName("처리 대상 유저가 아니면 승인 또는 거절할 수 없다.") - void validateProcessRequestFailWhenUnauthorized() { + @DisplayName("총무가 아니면 승인 또는 거절할 수 없다.") + void validateProcessRequestFailWhenNotManager() { PaymentRequest paymentRequest = mock(PaymentRequest.class); - User targetUser = mock(User.class); + Settlement settlement = mock(Settlement.class); when(paymentRequest.getStatus()).thenReturn(PaymentRequestStatus.PENDING); - when(paymentRequest.getTargetUser()).thenReturn(targetUser); - when(paymentRequest.getId()).thenReturn(1L); - when(targetUser.getId()).thenReturn(100L); + when(paymentRequest.getSettlement()).thenReturn(settlement); + when(settlement.isWriter(200L)).thenReturn(false); assertThatThrownBy(() -> paymentRequestValidator.validateProcessRequest(paymentRequest, 200L)) - .isInstanceOf(PaymentRequestUnauthorizedException.class); + .isInstanceOf(UserPermissionException.class); + } + + @Test + @DisplayName("총무이면 승인 또는 거절할 수 있다.") + void validateProcessRequestSuccessWhenManager() { + PaymentRequest paymentRequest = mock(PaymentRequest.class); + Settlement settlement = mock(Settlement.class); + + when(paymentRequest.getStatus()).thenReturn(PaymentRequestStatus.PENDING); + when(paymentRequest.getSettlement()).thenReturn(settlement); + when(settlement.isWriter(100L)).thenReturn(true); + + assertThatCode(() -> paymentRequestValidator.validateProcessRequest(paymentRequest, 100L)) + .doesNotThrowAnyException(); } @Test diff --git a/src/test/java/com/dnd/moddo/domain/settlement/controller/SettlementControllerTest.java b/src/test/java/com/dnd/moddo/domain/settlement/controller/SettlementControllerTest.java index 57cd04c2..889f308c 100644 --- a/src/test/java/com/dnd/moddo/domain/settlement/controller/SettlementControllerTest.java +++ b/src/test/java/com/dnd/moddo/domain/settlement/controller/SettlementControllerTest.java @@ -24,6 +24,7 @@ import com.dnd.moddo.event.presentation.response.SettlementDetailResponse; import com.dnd.moddo.event.presentation.response.SettlementHeaderResponse; import com.dnd.moddo.event.presentation.response.SettlementListResponse; +import com.dnd.moddo.event.presentation.response.SettlementMemberResponse; import com.dnd.moddo.event.presentation.response.SettlementResponse; import com.dnd.moddo.event.presentation.response.SettlementSaveResponse; import com.dnd.moddo.event.presentation.response.SettlementShareResponse; @@ -90,10 +91,11 @@ void updateAccount() throws Exception { void getSettlement() throws Exception { // given SettlementDetailResponse response = new SettlementDetailResponse(1L, "모또 모임", List.of( - new MemberResponse(1L, MANAGER, "김모또", "https://moddo-s3.s3.amazonaws.com/profile/MODDO.png", + new SettlementMemberResponse(1L, MANAGER, "김모또", "https://moddo-s3.s3.amazonaws.com/profile/MODDO.png", 1L, true, - LocalDateTime.now()) + LocalDateTime.now(), + null) )); given(loginUserArgumentResolver.supportsParameter(any())) @@ -108,6 +110,7 @@ void getSettlement() throws Exception { // when & then mockMvc.perform(get("/api/v1/groups/{code}", "code")) .andExpect(status().isOk()) + .andExpect(jsonPath("$.members[0].paymentRequestId").value(Matchers.nullValue())) .andDo(restDocs.document( pathParameters( parameterWithName("code").description("정산 코드") diff --git a/src/test/java/com/dnd/moddo/domain/settlement/service/QuerySettlementServiceTest.java b/src/test/java/com/dnd/moddo/domain/settlement/service/QuerySettlementServiceTest.java index b07f7442..21477e32 100644 --- a/src/test/java/com/dnd/moddo/domain/settlement/service/QuerySettlementServiceTest.java +++ b/src/test/java/com/dnd/moddo/domain/settlement/service/QuerySettlementServiceTest.java @@ -17,6 +17,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import com.dnd.moddo.common.cache.CacheExecutor; +import com.dnd.moddo.event.application.impl.PaymentRequestReader; import com.dnd.moddo.event.application.impl.SettlementReader; import com.dnd.moddo.event.application.impl.SettlementValidator; import com.dnd.moddo.event.application.query.QuerySettlementService; @@ -42,6 +43,9 @@ class QuerySettlementServiceTest { @Mock private SettlementReader settlementReader; + @Mock + private PaymentRequestReader paymentRequestReader; + @Mock private SettlementValidator settlementValidator; @@ -63,6 +67,7 @@ void setUp() { .build(); setField(settlement, "id", 1L); + setField(member, "id", 10L); } @Test @@ -71,6 +76,7 @@ void FindOne_Success() { // Given when(settlementReader.read(anyLong())).thenReturn(settlement); when(settlementReader.findBySettlement(settlement.getId())).thenReturn(List.of(member)); + when(paymentRequestReader.findPendingRequestIdByMemberId(settlement.getId())).thenReturn(java.util.Map.of(10L, 100L)); doNothing().when(settlementValidator).checkSettlementAuthor(settlement, 1L); // When @@ -82,9 +88,11 @@ void FindOne_Success() { assertThat(response.groupName()).isEqualTo(settlement.getName()); assertThat(response.members()).hasSize(1); assertThat(response.members().get(0).name()).isEqualTo(member.getName()); + assertThat(response.members().get(0).paymentRequestId()).isEqualTo(100L); verify(settlementReader, times(1)).read(1L); verify(settlementReader, times(1)).findBySettlement(settlement.getId()); + verify(paymentRequestReader, times(1)).findPendingRequestIdByMemberId(settlement.getId()); verify(settlementValidator, times(1)).checkSettlementAuthor(settlement, 1L); } From 251ec164eebddff4b366509381942206532afe95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9B=90=EC=A7=80=EC=9C=A4?= Date: Sun, 17 May 2026 21:27:33 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20=EB=AF=B8=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20=ED=83=9C=EC=8A=A4=ED=81=AC=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=A4=91=EB=8B=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../outbox/application/impl/OutboxEventPublishExecutor.java | 2 +- .../implementation/OutboxEventPublishExecutorTest.java | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/dnd/moddo/outbox/application/impl/OutboxEventPublishExecutor.java b/src/main/java/com/dnd/moddo/outbox/application/impl/OutboxEventPublishExecutor.java index 2a0aebcf..6e2cb3d0 100644 --- a/src/main/java/com/dnd/moddo/outbox/application/impl/OutboxEventPublishExecutor.java +++ b/src/main/java/com/dnd/moddo/outbox/application/impl/OutboxEventPublishExecutor.java @@ -62,7 +62,7 @@ private void appendSettlementCompletedTasks(OutboxEvent outboxEvent) { for (Member member : memberReader.findAssignedMembersBySettlementId(outboxEvent.getAggregateId())) { Long targetUserId = member.getUserId(); eventTasks.add(EventTask.pending(outboxEvent, EventTaskType.REWARD_GRANT, targetUserId)); - eventTasks.add(EventTask.pending(outboxEvent, EventTaskType.NOTIFICATION_SEND, targetUserId)); + // eventTasks.add(EventTask.pending(outboxEvent, EventTaskType.NOTIFICATION_SEND, targetUserId)); } if (!eventTasks.isEmpty()) { diff --git a/src/test/java/com/dnd/moddo/domain/outbox/service/implementation/OutboxEventPublishExecutorTest.java b/src/test/java/com/dnd/moddo/domain/outbox/service/implementation/OutboxEventPublishExecutorTest.java index ad44e6f0..00b524e7 100644 --- a/src/test/java/com/dnd/moddo/domain/outbox/service/implementation/OutboxEventPublishExecutorTest.java +++ b/src/test/java/com/dnd/moddo/domain/outbox/service/implementation/OutboxEventPublishExecutorTest.java @@ -74,14 +74,12 @@ void appendTasksForSettlementCompleted() { ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); verify(eventTaskCreator).createAll(captor.capture()); - assertThat(captor.getValue()).hasSize(4); + assertThat(captor.getValue()).hasSize(2); assertThat(captor.getValue()) .extracting(EventTask::getTaskType, EventTask::getTargetUserId) .containsExactly( tuple(EventTaskType.REWARD_GRANT, 20L), - tuple(EventTaskType.NOTIFICATION_SEND, 20L), - tuple(EventTaskType.REWARD_GRANT, 30L), - tuple(EventTaskType.NOTIFICATION_SEND, 30L) + tuple(EventTaskType.REWARD_GRANT, 30L) ); } From 00dd2cf3fd01663e9a3dd242493a6fc60203868a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9B=90=EC=A7=80=EC=9C=A4?= Date: Sun, 17 May 2026 21:38:07 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20=EC=9E=85=EA=B8=88=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=20=EC=9A=94=EC=B2=AD=20=EA=B6=8C=ED=95=9C=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/application/impl/PaymentRequestValidator.java | 2 +- .../service/implementation/PaymentRequestValidatorTest.java | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestValidator.java b/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestValidator.java index 1799082d..6ad5aeee 100644 --- a/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestValidator.java +++ b/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestValidator.java @@ -26,8 +26,8 @@ public void validateCreateRequest(Long settlementId, Member requestMember) { } public void validateProcessRequest(PaymentRequest paymentRequest, Long userId) { - validatePendingStatus(paymentRequest); validateManagerPermission(paymentRequest, userId); + validatePendingStatus(paymentRequest); } private void validateDuplicateRequest(Long settlementId, Long requestMemberId) { diff --git a/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestValidatorTest.java b/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestValidatorTest.java index e6c05c9e..e4f4f287 100644 --- a/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestValidatorTest.java +++ b/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestValidatorTest.java @@ -76,12 +76,12 @@ void validateProcessRequestFailWhenNotManager() { PaymentRequest paymentRequest = mock(PaymentRequest.class); Settlement settlement = mock(Settlement.class); - when(paymentRequest.getStatus()).thenReturn(PaymentRequestStatus.PENDING); when(paymentRequest.getSettlement()).thenReturn(settlement); when(settlement.isWriter(200L)).thenReturn(false); assertThatThrownBy(() -> paymentRequestValidator.validateProcessRequest(paymentRequest, 200L)) .isInstanceOf(UserPermissionException.class); + verify(paymentRequest, never()).getStatus(); } @Test @@ -102,7 +102,10 @@ void validateProcessRequestSuccessWhenManager() { @DisplayName("대기 상태가 아니면 승인 또는 거절할 수 없다.") void validateProcessRequestFailWhenNotPending() { PaymentRequest paymentRequest = mock(PaymentRequest.class); + Settlement settlement = mock(Settlement.class); + when(paymentRequest.getSettlement()).thenReturn(settlement); + when(settlement.isWriter(100L)).thenReturn(true); when(paymentRequest.getStatus()).thenReturn(PaymentRequestStatus.APPROVED); when(paymentRequest.getId()).thenReturn(1L); From bc750605a137b942df6fa248888d44d04c2c1224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9B=90=EC=A7=80=EC=9C=A4?= Date: Sun, 17 May 2026 21:41:14 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20=EC=A4=91=EB=B3=B5=20=EC=9E=85?= =?UTF-8?q?=EA=B8=88=20=ED=99=95=EC=9D=B8=20=EC=9A=94=EC=B2=AD=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/PaymentRequestReader.java | 4 ++- .../PaymentRequestReaderTest.java | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestReader.java b/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestReader.java index 53cd86df..3a6ad564 100644 --- a/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestReader.java +++ b/src/main/java/com/dnd/moddo/event/application/impl/PaymentRequestReader.java @@ -67,7 +67,9 @@ public Map findPendingRequestIdByMemberId(Long settlementId) { .collect(Collectors.toMap( PaymentRequest::getRequestMemberId, PaymentRequest::getId, - (first, ignored) -> first + (first, duplicate) -> { + throw new IllegalStateException("중복된 PENDING 입금 확인 요청이 존재합니다."); + } )); } diff --git a/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestReaderTest.java b/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestReaderTest.java index 3f5d8211..53ebeab7 100644 --- a/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestReaderTest.java +++ b/src/test/java/com/dnd/moddo/domain/paymentRequest/service/implementation/PaymentRequestReaderTest.java @@ -127,6 +127,39 @@ void findPendingRequestIdByMemberId() { assertThat(result).containsEntry(11L, 100L); } + @Test + @DisplayName("같은 멤버의 대기 중인 입금 확인 요청이 중복되면 예외가 발생한다.") + void findPendingRequestIdByMemberIdFailWhenDuplicatePendingRequest() { + Settlement settlement = mock(Settlement.class); + Member member = Member.builder() + .name("김반숙") + .profileId(1) + .settlement(settlement) + .role(ExpenseRole.PARTICIPANT) + .build(); + PaymentRequest first = PaymentRequest.builder() + .settlement(settlement) + .requestMember(member) + .targetUser(mock(com.dnd.moddo.user.domain.User.class)) + .build(); + PaymentRequest second = PaymentRequest.builder() + .settlement(settlement) + .requestMember(member) + .targetUser(mock(com.dnd.moddo.user.domain.User.class)) + .build(); + + setField(member, "id", 11L); + setField(first, "id", 100L); + setField(second, "id", 101L); + + when(paymentRequestRepository.findBySettlementIdAndStatus(1L, PaymentRequestStatus.PENDING)) + .thenReturn(List.of(first, second)); + + assertThatThrownBy(() -> paymentRequestReader.findPendingRequestIdByMemberId(1L)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("중복된 PENDING 입금 확인 요청이 존재합니다."); + } + private void setField(Object target, String fieldName, Object value) { try { java.lang.reflect.Field field = target.getClass().getDeclaredField(fieldName);