From be7552bbf13ffbb258032c32b5d547bd33651c38 Mon Sep 17 00:00:00 2001 From: Hogeun Lee Date: Sat, 24 Oct 2026 14:43:41 +0900 Subject: [PATCH] =?UTF-8?q?[FIX/#511]=20Partner=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=EC=8B=9C=20=EC=97=B0=EA=B4=80=20=EC=A0=9C=ED=9C=B4=20=EB=B9=84?= =?UTF-8?q?=ED=99=9C=EC=84=B1=ED=99=94=20=EB=B0=8F=20=EB=B3=B5=EA=B5=AC=20?= =?UTF-8?q?=EC=8B=9C=20=EB=B3=B5=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Partner 탈퇴(직접/백오피스 강제) 시 partner_id 기준 ACTIVE/SUSPEND Paper의 원래 상태를 기록하고 INACTIVE로 전환 - WithdrawalService.restoreMember 추가, 백오피스 복구/재가입/로그인 복구 경로에서 기록된 상태로 Paper 복원 - Paper에 status_before_partner_withdrawal 컬럼 추가 - PartnerRepository 미제휴 추천/지도/검색 쿼리에서 탈퇴 회원 제외 - 탈퇴 업체 대상 제안서 초안/채팅방 신규 생성 차단 - 탈퇴/복구 단위 테스트 및 H2 통합 테스트 추가 Co-Authored-By: Claude Opus 5.5 --- .../security/adapter/CommonAuthAdapter.java | 7 +- .../auth/service/SignUpServiceImpl.java | 3 +- .../auth/service/WithdrawalService.java | 1 + .../auth/service/WithdrawalServiceImpl.java | 24 ++++++ .../service/BackofficeMemberServiceImpl.java | 2 +- .../domain/chat/service/ChatServiceImpl.java | 2 +- .../partner/repository/PartnerRepository.java | 19 +++-- .../domain/partnership/entity/Paper.java | 4 + .../repository/PaperRepository.java | 22 ++++++ .../service/PartnershipServiceImpl.java | 2 +- .../BackofficeMemberServiceTest.java | 58 ++++++++++++++ .../auth/service/SignUpServiceImplTest.java | 7 +- .../service/WithdrawalServiceImplTest.java | 75 +++++++++++++++++++ .../chat/service/ChatServiceImplTest.java | 6 +- 14 files changed, 213 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/assu/server/domain/auth/security/adapter/CommonAuthAdapter.java b/src/main/java/com/assu/server/domain/auth/security/adapter/CommonAuthAdapter.java index 1a24dc8e..6174b92a 100644 --- a/src/main/java/com/assu/server/domain/auth/security/adapter/CommonAuthAdapter.java +++ b/src/main/java/com/assu/server/domain/auth/security/adapter/CommonAuthAdapter.java @@ -5,9 +5,9 @@ import com.assu.server.domain.auth.exception.CustomAuthException; import com.assu.server.domain.auth.repository.CommonAuthRepository; import com.assu.server.domain.auth.security.userdetails.CommonUserDetails; +import com.assu.server.domain.auth.service.WithdrawalService; import com.assu.server.domain.common.enums.ActivationStatus; import com.assu.server.domain.member.entity.Member; -import com.assu.server.domain.member.repository.MemberRepository; import com.assu.server.global.apiPayload.code.status.ErrorStatus; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -25,7 +25,7 @@ public class CommonAuthAdapter implements RealmAuthAdapter { private final CommonAuthRepository commonAuthRepository; - private final MemberRepository memberRepository; + private final WithdrawalService withdrawalService; private final PasswordEncoder passwordEncoder; @Override @@ -70,8 +70,7 @@ public Member loadMember(String email) { commonAuthRepository.save(commonAuth); if (member.isWithdrawn()) { - member.restore(); - memberRepository.save(member); + withdrawalService.restoreMember(member); } return member; diff --git a/src/main/java/com/assu/server/domain/auth/service/SignUpServiceImpl.java b/src/main/java/com/assu/server/domain/auth/service/SignUpServiceImpl.java index 65af4c26..a633a0f0 100644 --- a/src/main/java/com/assu/server/domain/auth/service/SignUpServiceImpl.java +++ b/src/main/java/com/assu/server/domain/auth/service/SignUpServiceImpl.java @@ -70,6 +70,7 @@ public class SignUpServiceImpl implements SignUpService { private final PhoneAuthService phoneAuthService; private final CommonAuthRepository commonAuthRepository; private final DiscordNotifier discordNotifier; + private final WithdrawalService withdrawalService; private RealmAuthAdapter pickAdapter(AuthRealm realm) { return realmAuthAdapters.stream() @@ -276,7 +277,7 @@ private SignUpResponseDTO restoreWithdrawnPartner( } // isActivated는 유지한다 — 승인받았던 업체는 재승인 없이 바로 로그인 가능 - member.restore(); + withdrawalService.restoreMember(member); member.updateTermAgreements(req.locationAgree(), req.marketingAgree()); memberRepository.save(member); diff --git a/src/main/java/com/assu/server/domain/auth/service/WithdrawalService.java b/src/main/java/com/assu/server/domain/auth/service/WithdrawalService.java index 61ec43fc..30f14158 100644 --- a/src/main/java/com/assu/server/domain/auth/service/WithdrawalService.java +++ b/src/main/java/com/assu/server/domain/auth/service/WithdrawalService.java @@ -6,4 +6,5 @@ public interface WithdrawalService { void withdrawCurrentUser(String authorization); void withdrawMember(Long memberId); void withdrawMember(Member member); + void restoreMember(Member member); } diff --git a/src/main/java/com/assu/server/domain/auth/service/WithdrawalServiceImpl.java b/src/main/java/com/assu/server/domain/auth/service/WithdrawalServiceImpl.java index b9e59507..ea669b1b 100644 --- a/src/main/java/com/assu/server/domain/auth/service/WithdrawalServiceImpl.java +++ b/src/main/java/com/assu/server/domain/auth/service/WithdrawalServiceImpl.java @@ -1,10 +1,13 @@ package com.assu.server.domain.auth.service; import com.assu.server.domain.auth.security.jwt.JwtUtil; +import com.assu.server.domain.common.enums.ActivationStatus; +import com.assu.server.domain.common.enums.UserRole; import com.assu.server.domain.deviceToken.repository.DeviceTokenRepository; import com.assu.server.domain.member.entity.Member; import com.assu.server.domain.member.repository.MemberRepository; import com.assu.server.domain.auth.exception.CustomAuthException; +import com.assu.server.domain.partnership.repository.PaperRepository; import com.assu.server.global.apiPayload.code.status.ErrorStatus; import io.jsonwebtoken.Claims; import org.springframework.transaction.annotation.Transactional; @@ -12,6 +15,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.List; + @Slf4j @Service @RequiredArgsConstructor @@ -20,6 +25,7 @@ public class WithdrawalServiceImpl implements WithdrawalService { private final MemberRepository memberRepository; private final DeviceTokenRepository deviceTokenRepository; + private final PaperRepository paperRepository; private final JwtUtil jwtUtil; @Override @@ -50,8 +56,26 @@ public void withdrawMember(Member member) { member.withdraw(); memberRepository.save(member); + if (member.getRole() == UserRole.PARTNER) { + paperRepository.deactivateByPartnerWithdrawal( + member.getId(), + List.of(ActivationStatus.ACTIVE, ActivationStatus.SUSPEND), + ActivationStatus.INACTIVE + ); + } + deviceTokenRepository.deleteAllByMemberId(member.getId()); jwtUtil.removeAllRefreshTokens(member.getId()); jwtUtil.revokeAllAccessTokensSince(member.getId()); } + + @Override + public void restoreMember(Member member) { + member.restore(); + memberRepository.save(member); + + if (member.getRole() == UserRole.PARTNER) { + paperRepository.restoreByPartnerRestoration(member.getId()); + } + } } diff --git a/src/main/java/com/assu/server/domain/backoffice/service/BackofficeMemberServiceImpl.java b/src/main/java/com/assu/server/domain/backoffice/service/BackofficeMemberServiceImpl.java index 191d6ba1..2e847b84 100644 --- a/src/main/java/com/assu/server/domain/backoffice/service/BackofficeMemberServiceImpl.java +++ b/src/main/java/com/assu/server/domain/backoffice/service/BackofficeMemberServiceImpl.java @@ -137,7 +137,7 @@ public BackofficeMemberSummaryDTO restoreMember(Long memberId) { throw new CustomAuthException(ErrorStatus.MEMBER_NOT_DELETED); } - member.restore(); + withdrawalService.restoreMember(member); return BackofficeMemberSummaryDTO.from(loadProfileForSummary(member)); } diff --git a/src/main/java/com/assu/server/domain/chat/service/ChatServiceImpl.java b/src/main/java/com/assu/server/domain/chat/service/ChatServiceImpl.java index e9df8f52..06214d24 100644 --- a/src/main/java/com/assu/server/domain/chat/service/ChatServiceImpl.java +++ b/src/main/java/com/assu/server/domain/chat/service/ChatServiceImpl.java @@ -67,7 +67,7 @@ public ChatResponseDTO.CreateChatRoomResponseDTO createChatRoom(ChatRequestDTO.C Admin admin = adminRepository.findById(adminId) .orElseThrow(() -> new DatabaseException(ErrorStatus.NO_SUCH_ADMIN)); - Partner partner = partnerRepository.findById(partnerId) + Partner partner = partnerRepository.findByIdAndMember_DeletedAtIsNull(partnerId) .orElseThrow(() -> new DatabaseException(ErrorStatus.NO_SUCH_PARTNER)); Store store = storeRepository.findByPartnerId(partnerId) .orElseThrow(() -> new DatabaseException(ErrorStatus.NO_SUCH_STORE)); diff --git a/src/main/java/com/assu/server/domain/partner/repository/PartnerRepository.java b/src/main/java/com/assu/server/domain/partner/repository/PartnerRepository.java index e99a7e89..126dfb04 100644 --- a/src/main/java/com/assu/server/domain/partner/repository/PartnerRepository.java +++ b/src/main/java/com/assu/server/domain/partner/repository/PartnerRepository.java @@ -7,6 +7,7 @@ import org.springframework.data.repository.query.Param; import java.util.List; +import java.util.Optional; public interface PartnerRepository extends JpaRepository { @@ -14,10 +15,13 @@ public interface PartnerRepository extends JpaRepository { boolean existsByPhoneNumAndMember_DeletedAtIsNull(String phoneNum); + Optional findByIdAndMember_DeletedAtIsNull(Long id); + @Query(""" SELECT COUNT(p) FROM Partner p - WHERE NOT EXISTS ( + WHERE p.member.deletedAt IS NULL + AND NOT EXISTS ( SELECT 1 FROM Paper pa WHERE pa.partner = p AND pa.admin.id = :adminId @@ -29,7 +33,8 @@ WHERE NOT EXISTS ( @Query(""" SELECT p FROM Partner p - WHERE NOT EXISTS ( + WHERE p.member.deletedAt IS NULL + AND NOT EXISTS ( SELECT 1 FROM Paper pa WHERE pa.partner = p AND pa.admin.id = :adminId @@ -42,8 +47,9 @@ WHERE NOT EXISTS ( @Query(""" SELECT DISTINCT p FROM Partner p - LEFT JOIN FETCH p.member - WHERE p.point IS NOT NULL + LEFT JOIN FETCH p.member m + WHERE m.deletedAt IS NULL + AND p.point IS NOT NULL AND ST_Contains(ST_GeomFromText(:wkt, 4326), p.point) = true """) List findAllWithinViewportWithMember(@Param("wkt") String wkt); @@ -52,8 +58,9 @@ AND ST_Contains(ST_GeomFromText(:wkt, 4326), p.point) = true @Query(""" SELECT DISTINCT p FROM Partner p - LEFT JOIN FETCH p.member - WHERE LOWER(p.name) LIKE LOWER(CONCAT('%', :keyword, '%')) + LEFT JOIN FETCH p.member m + WHERE m.deletedAt IS NULL + AND LOWER(p.name) LIKE LOWER(CONCAT('%', :keyword, '%')) """) List searchPartnerByKeywordWithMember( @Param("keyword") String keyword diff --git a/src/main/java/com/assu/server/domain/partnership/entity/Paper.java b/src/main/java/com/assu/server/domain/partnership/entity/Paper.java index 771baaf5..d36484cb 100644 --- a/src/main/java/com/assu/server/domain/partnership/entity/Paper.java +++ b/src/main/java/com/assu/server/domain/partnership/entity/Paper.java @@ -45,5 +45,9 @@ public class Paper extends BaseEntity { @Column(name = "contract_image_key", length = 512) private String contractImageKey; + @Enumerated(EnumType.STRING) + @Column(name = "status_before_partner_withdrawal") + private ActivationStatus statusBeforePartnerWithdrawal; + public void updateContractImageKey(String key) { this.contractImageKey = key; } } diff --git a/src/main/java/com/assu/server/domain/partnership/repository/PaperRepository.java b/src/main/java/com/assu/server/domain/partnership/repository/PaperRepository.java index 8b45a216..e78ccdfd 100644 --- a/src/main/java/com/assu/server/domain/partnership/repository/PaperRepository.java +++ b/src/main/java/com/assu/server/domain/partnership/repository/PaperRepository.java @@ -109,4 +109,26 @@ void updatePaperStatus(@Param("today") LocalDate today, @Param("inactiveStatus") ActivationStatus inactiveStatus, @Param("activeStatus") ActivationStatus activeStatus); + @Modifying(flushAutomatically = true) + @Query(""" + UPDATE Paper p + SET p.statusBeforePartnerWithdrawal = p.isActivated, + p.isActivated = :inactiveStatus + WHERE p.partner.id = :partnerId + AND p.isActivated IN :targetStatuses + """) + void deactivateByPartnerWithdrawal(@Param("partnerId") Long partnerId, + @Param("targetStatuses") List targetStatuses, + @Param("inactiveStatus") ActivationStatus inactiveStatus); + + @Modifying(flushAutomatically = true) + @Query(""" + UPDATE Paper p + SET p.isActivated = p.statusBeforePartnerWithdrawal, + p.statusBeforePartnerWithdrawal = null + WHERE p.partner.id = :partnerId + AND p.statusBeforePartnerWithdrawal IS NOT NULL + """) + void restoreByPartnerRestoration(@Param("partnerId") Long partnerId); + } diff --git a/src/main/java/com/assu/server/domain/partnership/service/PartnershipServiceImpl.java b/src/main/java/com/assu/server/domain/partnership/service/PartnershipServiceImpl.java index c29a4f0c..3a9da564 100644 --- a/src/main/java/com/assu/server/domain/partnership/service/PartnershipServiceImpl.java +++ b/src/main/java/com/assu/server/domain/partnership/service/PartnershipServiceImpl.java @@ -360,7 +360,7 @@ public ManualPartnershipResponseDTO createManualPartnership( public PartnershipDraftResponseDTO createDraftPartnership(PartnershipDraftRequestDTO request, Long adminId) { Admin admin = adminRepository.findById(adminId) .orElseThrow(() -> new DatabaseException(ErrorStatus.NO_SUCH_ADMIN)); - Partner partner = partnerRepository.findById(request.partnerId()) + Partner partner = partnerRepository.findByIdAndMember_DeletedAtIsNull(request.partnerId()) .orElseThrow(() -> new DatabaseException(ErrorStatus.NO_SUCH_PARTNER)); Store store = storeRepository.findByPartner(partner) .orElseThrow(() -> new DatabaseException(ErrorStatus.NO_SUCH_STORE)); diff --git a/src/test/java/com/assu/server/backoffice/BackofficeMemberServiceTest.java b/src/test/java/com/assu/server/backoffice/BackofficeMemberServiceTest.java index 3813d013..de5526e1 100644 --- a/src/test/java/com/assu/server/backoffice/BackofficeMemberServiceTest.java +++ b/src/test/java/com/assu/server/backoffice/BackofficeMemberServiceTest.java @@ -10,10 +10,13 @@ import com.assu.server.domain.member.repository.MemberRepository; import com.assu.server.domain.partner.entity.Partner; import com.assu.server.domain.partner.repository.PartnerRepository; +import com.assu.server.domain.partnership.entity.Paper; +import com.assu.server.domain.partnership.repository.PaperRepository; import com.assu.server.domain.store.entity.Store; import com.assu.server.domain.store.repository.StoreRepository; import com.assu.server.global.apiPayload.code.status.ErrorStatus; import com.assu.server.support.CommonMockConfig; +import jakarta.persistence.EntityManager; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -22,6 +25,8 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDate; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -46,6 +51,12 @@ class BackofficeMemberServiceTest { @Autowired private StoreRepository storeRepository; + @Autowired + private PaperRepository paperRepository; + + @Autowired + private EntityManager entityManager; + @Test @DisplayName("SUSPEND Partner 회원을 승인하면 ACTIVE 및 license verified 처리") void approvePartnerMember() { @@ -118,6 +129,53 @@ void cannotApproveActiveMember() { .isEqualTo(ErrorStatus.MEMBER_NOT_PENDING_APPROVAL); } + @Test + @DisplayName("제휴업체를 강제 탈퇴시키면 ACTIVE/SUSPEND 제휴만 INACTIVE가 되고, 복구하면 원래 상태로 돌아온다") + void forceWithdrawAndRestorePartner_TogglesPartnerPapers() { + Member member = createPartnerMember(ActivationStatus.ACTIVE); + Partner partner = member.getPartnerProfile(); + Store store = storeRepository.findByPartner(partner).orElseThrow(); + + Long activeId = createPaper(partner, store, ActivationStatus.ACTIVE).getId(); + Long suspendId = createPaper(partner, store, ActivationStatus.SUSPEND).getId(); + Long inactiveId = createPaper(partner, store, ActivationStatus.INACTIVE).getId(); + Long manualId = createPaper(null, store, ActivationStatus.ACTIVE).getId(); + + backofficeMemberService.forceWithdrawMember(member.getId()); + entityManager.flush(); + entityManager.clear(); + + assertThat(findPaper(activeId).getIsActivated()).isEqualTo(ActivationStatus.INACTIVE); + assertThat(findPaper(suspendId).getIsActivated()).isEqualTo(ActivationStatus.INACTIVE); + assertThat(findPaper(inactiveId).getIsActivated()).isEqualTo(ActivationStatus.INACTIVE); + assertThat(findPaper(inactiveId).getStatusBeforePartnerWithdrawal()).isNull(); + assertThat(findPaper(manualId).getIsActivated()).isEqualTo(ActivationStatus.ACTIVE); + + backofficeMemberService.restoreMember(member.getId()); + entityManager.flush(); + entityManager.clear(); + + assertThat(findPaper(activeId).getIsActivated()).isEqualTo(ActivationStatus.ACTIVE); + assertThat(findPaper(suspendId).getIsActivated()).isEqualTo(ActivationStatus.SUSPEND); + assertThat(findPaper(inactiveId).getIsActivated()).isEqualTo(ActivationStatus.INACTIVE); + assertThat(findPaper(activeId).getStatusBeforePartnerWithdrawal()).isNull(); + assertThat(findPaper(manualId).getIsActivated()).isEqualTo(ActivationStatus.ACTIVE); + } + + private Paper createPaper(Partner partner, Store store, ActivationStatus status) { + return paperRepository.save(Paper.builder() + .partner(partner) + .store(store) + .isActivated(status) + .partnershipPeriodStart(LocalDate.now().minusDays(1)) + .partnershipPeriodEnd(LocalDate.now().plusDays(30)) + .build()); + } + + private Paper findPaper(Long paperId) { + return paperRepository.findById(paperId).orElseThrow(); + } + private Member createPartnerMember(ActivationStatus status) { Member member = memberRepository.save(Member.builder() .role(UserRole.PARTNER) diff --git a/src/test/java/com/assu/server/domain/auth/service/SignUpServiceImplTest.java b/src/test/java/com/assu/server/domain/auth/service/SignUpServiceImplTest.java index 6038e1ad..2c6ed00c 100644 --- a/src/test/java/com/assu/server/domain/auth/service/SignUpServiceImplTest.java +++ b/src/test/java/com/assu/server/domain/auth/service/SignUpServiceImplTest.java @@ -114,6 +114,9 @@ class SignUpServiceImplTest { @Mock private PasswordEncoder passwordEncoder; + @Mock + private WithdrawalService withdrawalService; + private static final String PHONE = "01012345678"; private static final String MASTER_PHONE = "01000000000"; private static final String EMAIL = "partner@assu.site"; @@ -124,7 +127,7 @@ void setUp() { memberRepository, studentRepository, partnerRepository, adminRepository, List.of(realmAuthAdapter), amazonS3Manager, jwtUtil, new GeometryFactory(), storeRepository, ssuAuthService, ssuAuthRepository, - studentService, phoneAuthService, commonAuthRepository, discordNotifier); + studentService, phoneAuthService, commonAuthRepository, discordNotifier, withdrawalService); } @AfterEach @@ -478,7 +481,7 @@ void signupPartner_WithdrawnPartner_RestoresAccountKeepingActivationStatus() { SignUpResponseDTO response = signUpService.signupPartner(request, licenseImage); // 3. Then - verify(member, times(1)).restore(); + verify(withdrawalService, times(1)).restoreMember(member); verify(member, never()).setIsActivated(any()); verify(commonAuth, times(1)).updatePassword("hashed"); verify(partner, times(1)).updateBusinessInfo( diff --git a/src/test/java/com/assu/server/domain/auth/service/WithdrawalServiceImplTest.java b/src/test/java/com/assu/server/domain/auth/service/WithdrawalServiceImplTest.java index 60632edc..196a6dc7 100644 --- a/src/test/java/com/assu/server/domain/auth/service/WithdrawalServiceImplTest.java +++ b/src/test/java/com/assu/server/domain/auth/service/WithdrawalServiceImplTest.java @@ -6,6 +6,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; +import java.util.List; import java.util.Optional; import org.junit.jupiter.api.DisplayName; @@ -17,9 +18,12 @@ import com.assu.server.domain.auth.exception.CustomAuthException; import com.assu.server.domain.auth.security.jwt.JwtUtil; +import com.assu.server.domain.common.enums.ActivationStatus; +import com.assu.server.domain.common.enums.UserRole; import com.assu.server.domain.deviceToken.repository.DeviceTokenRepository; import com.assu.server.domain.member.entity.Member; import com.assu.server.domain.member.repository.MemberRepository; +import com.assu.server.domain.partnership.repository.PaperRepository; import com.assu.server.global.apiPayload.code.status.ErrorStatus; import io.jsonwebtoken.Claims; @@ -36,6 +40,9 @@ class WithdrawalServiceImplTest { @Mock private DeviceTokenRepository deviceTokenRepository; + @Mock + private PaperRepository paperRepository; + @Mock private JwtUtil jwtUtil; @@ -85,6 +92,7 @@ void withdrawCurrentUser_AlreadyWithdrawn_ThrowsException() { verify(memberRepository, never()).save(any()); verify(deviceTokenRepository, never()).deleteAllByMemberId(anyLong()); verify(jwtUtil, never()).removeAllRefreshTokens(anyLong()); + verify(paperRepository, never()).deactivateByPartnerWithdrawal(anyLong(), any(), any()); } @Test @@ -141,4 +149,71 @@ void withdrawCurrentUser_Success_ClearsDeviceTokens() { // 3. Then verify(deviceTokenRepository, times(1)).deleteAllByMemberId(MEMBER_ID); } + + @Test + @DisplayName("제휴업체가 탈퇴하면 ACTIVE/SUSPEND 제휴를 INACTIVE로 전환한다") + void withdrawMember_Partner_DeactivatesPapers() { + // 1. Given + Member member = mock(Member.class); + when(member.getId()).thenReturn(MEMBER_ID); + when(member.isWithdrawn()).thenReturn(false); + when(member.getRole()).thenReturn(UserRole.PARTNER); + + // 2. When + withdrawalService.withdrawMember(member); + + // 3. Then + verify(paperRepository, times(1)).deactivateByPartnerWithdrawal( + MEMBER_ID, + List.of(ActivationStatus.ACTIVE, ActivationStatus.SUSPEND), + ActivationStatus.INACTIVE); + } + + @Test + @DisplayName("제휴업체가 아닌 회원이 탈퇴하면 제휴 상태를 변경하지 않는다") + void withdrawMember_NonPartner_DoesNotTouchPapers() { + // 1. Given + Member member = mock(Member.class); + when(member.getId()).thenReturn(MEMBER_ID); + when(member.isWithdrawn()).thenReturn(false); + when(member.getRole()).thenReturn(UserRole.ADMIN); + + // 2. When + withdrawalService.withdrawMember(member); + + // 3. Then + verify(paperRepository, never()).deactivateByPartnerWithdrawal(anyLong(), any(), any()); + } + + @Test + @DisplayName("제휴업체를 복구하면 탈퇴 시 비활성화된 제휴를 원래 상태로 되돌린다") + void restoreMember_Partner_RestoresPapers() { + // 1. Given + Member member = mock(Member.class); + when(member.getId()).thenReturn(MEMBER_ID); + when(member.getRole()).thenReturn(UserRole.PARTNER); + + // 2. When + withdrawalService.restoreMember(member); + + // 3. Then + verify(member, times(1)).restore(); + verify(memberRepository, times(1)).save(member); + verify(paperRepository, times(1)).restoreByPartnerRestoration(MEMBER_ID); + } + + @Test + @DisplayName("제휴업체가 아닌 회원을 복구하면 제휴 상태를 변경하지 않는다") + void restoreMember_NonPartner_DoesNotTouchPapers() { + // 1. Given + Member member = mock(Member.class); + when(member.getRole()).thenReturn(UserRole.STUDENT); + + // 2. When + withdrawalService.restoreMember(member); + + // 3. Then + verify(member, times(1)).restore(); + verify(paperRepository, never()).restoreByPartnerRestoration(anyLong()); + } } diff --git a/src/test/java/com/assu/server/domain/chat/service/ChatServiceImplTest.java b/src/test/java/com/assu/server/domain/chat/service/ChatServiceImplTest.java index f0c4acdc..2e515e17 100644 --- a/src/test/java/com/assu/server/domain/chat/service/ChatServiceImplTest.java +++ b/src/test/java/com/assu/server/domain/chat/service/ChatServiceImplTest.java @@ -135,7 +135,7 @@ void createChatRoom_StorePartnerMismatch_ThrowsException() { Store store = Store.builder().id(500L).partner(otherPartner).build(); when(adminRepository.findById(ADMIN_ID)).thenReturn(Optional.of(admin)); - when(partnerRepository.findById(PARTNER_ID)).thenReturn(Optional.of(partner)); + when(partnerRepository.findByIdAndMember_DeletedAtIsNull(PARTNER_ID)).thenReturn(Optional.of(partner)); when(storeRepository.findByPartnerId(PARTNER_ID)).thenReturn(Optional.of(store)); // 2. When @@ -161,7 +161,7 @@ void createChatRoom_ExistingRoom_ReturnsExisting() { .id(ROOM_ID).admin(admin).partner(partner).build(); when(adminRepository.findById(ADMIN_ID)).thenReturn(Optional.of(admin)); - when(partnerRepository.findById(PARTNER_ID)).thenReturn(Optional.of(partner)); + when(partnerRepository.findByIdAndMember_DeletedAtIsNull(PARTNER_ID)).thenReturn(Optional.of(partner)); when(storeRepository.findByPartnerId(PARTNER_ID)).thenReturn(Optional.of(store)); when(chatRepository.findChattingRoomByAdminIdAndPartnerId(ADMIN_ID, PARTNER_ID)).thenReturn(existingRoom); @@ -186,7 +186,7 @@ void createChatRoom_NewRoom_SavesAndReturns() { Store store = Store.builder().id(500L).partner(partner).build(); when(adminRepository.findById(ADMIN_ID)).thenReturn(Optional.of(admin)); - when(partnerRepository.findById(PARTNER_ID)).thenReturn(Optional.of(partner)); + when(partnerRepository.findByIdAndMember_DeletedAtIsNull(PARTNER_ID)).thenReturn(Optional.of(partner)); when(storeRepository.findByPartnerId(PARTNER_ID)).thenReturn(Optional.of(store)); when(chatRepository.findChattingRoomByAdminIdAndPartnerId(ADMIN_ID, PARTNER_ID)).thenReturn(null);