From 650cdef617f8272a615ac405a7eb6ce95d64495a Mon Sep 17 00:00:00 2001 From: jjunh33 Date: Fri, 6 Nov 2026 10:33:50 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=ED=9A=8C=EC=9B=90=20=ED=83=88?= =?UTF-8?q?=ED=87=B4=20=EC=8B=9C=20=EB=82=A9=EB=B6=80=EC=9E=90=20=EB=AA=85?= =?UTF-8?q?=EB=B6=80=20=EC=97=B0=EA=B2=B0=20=ED=95=B4=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/member/usecase/MemberWithdrawUseCase.java | 6 +++++- .../api/app/event/event/usecase/FakePayerService.java | 5 +++++ .../stream/welfare/domain/fee/domain/Payer.java | 6 ++++++ .../welfare/domain/fee/service/PayerService.java | 3 +++ .../domain/fee/service/impl/PayerServiceImpl.java | 10 ++++++++++ 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/usecase/MemberWithdrawUseCase.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/usecase/MemberWithdrawUseCase.java index add3a3739..6876dc464 100644 --- a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/usecase/MemberWithdrawUseCase.java +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/usecase/MemberWithdrawUseCase.java @@ -2,6 +2,7 @@ import kr.ac.kookmin.stream.auth.domain.oauth.service.OAuthService; import kr.ac.kookmin.stream.member.domain.member.service.MemberService; +import kr.ac.kookmin.stream.welfare.domain.fee.service.PayerService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -12,12 +13,15 @@ public class MemberWithdrawUseCase { private final MemberService memberService; private final OAuthService oauthService; + private final PayerService payerService; // 로그인은 provider 계정에 연결된 회원 ID로 회원을 찾으므로, 연결이 남으면 탈퇴한 회원을 찾다가 다시 로그인할 수 없다. - // 회원 탈퇴와 연결 해제를 한 트랜잭션으로 묶는다 + // 납부자 명부 행도 탈퇴한 회원에 묶여 있으면 다시 가입한 회원과 연결되지 않는다. + // 회원 탈퇴와 두 연결 해제를 한 트랜잭션으로 묶는다 @Transactional public void withdraw(Long memberId) { memberService.withdraw(memberId); oauthService.unlinkAll(memberId); + payerService.unlinkMember(memberId); } } diff --git a/api/app-api/src/test/java/kr/ac/kookmin/stream/api/app/event/event/usecase/FakePayerService.java b/api/app-api/src/test/java/kr/ac/kookmin/stream/api/app/event/event/usecase/FakePayerService.java index d7b65dcfb..2f1cd15d2 100644 --- a/api/app-api/src/test/java/kr/ac/kookmin/stream/api/app/event/event/usecase/FakePayerService.java +++ b/api/app-api/src/test/java/kr/ac/kookmin/stream/api/app/event/event/usecase/FakePayerService.java @@ -43,4 +43,9 @@ public PageResult search(String keyword, PageOffset pageOffset) { public void linkMember(Long memberId, String studentId) { throw new UnsupportedOperationException(); } + + @Override + public void unlinkMember(Long memberId) { + throw new UnsupportedOperationException(); + } } diff --git a/core/domain/welfare/src/main/java/kr/ac/kookmin/stream/welfare/domain/fee/domain/Payer.java b/core/domain/welfare/src/main/java/kr/ac/kookmin/stream/welfare/domain/fee/domain/Payer.java index 82398f400..ee48f9f29 100644 --- a/core/domain/welfare/src/main/java/kr/ac/kookmin/stream/welfare/domain/fee/domain/Payer.java +++ b/core/domain/welfare/src/main/java/kr/ac/kookmin/stream/welfare/domain/fee/domain/Payer.java @@ -46,4 +46,10 @@ public void linkMember(Long memberId) { this.memberId = memberId; this.registered = true; } + + // 연결된 회원이 탈퇴하면 연결을 끊어 가입 전 명부 행으로 되돌린다. 같은 학번으로 다시 가입하면 linkMember로 새 회원과 연결된다 + public void unlinkMember() { + this.memberId = null; + this.registered = false; + } } diff --git a/core/domain/welfare/src/main/java/kr/ac/kookmin/stream/welfare/domain/fee/service/PayerService.java b/core/domain/welfare/src/main/java/kr/ac/kookmin/stream/welfare/domain/fee/service/PayerService.java index 7067d88cd..29236b5cf 100644 --- a/core/domain/welfare/src/main/java/kr/ac/kookmin/stream/welfare/domain/fee/service/PayerService.java +++ b/core/domain/welfare/src/main/java/kr/ac/kookmin/stream/welfare/domain/fee/service/PayerService.java @@ -22,4 +22,7 @@ public interface PayerService { /** 학번이 같은 미연결 명부 행(member_id가 null)이 있으면 이 회원과 연결한다. 없으면 아무 일도 하지 않는다. */ void linkMember(Long memberId, String studentId); + + /** 이 회원과 연결된 명부 행이 있으면 연결을 끊는다(member_id를 비운다). 회원 탈퇴 때 쓴다. 없으면 아무 일도 하지 않는다. */ + void unlinkMember(Long memberId); } diff --git a/core/domain/welfare/src/main/java/kr/ac/kookmin/stream/welfare/domain/fee/service/impl/PayerServiceImpl.java b/core/domain/welfare/src/main/java/kr/ac/kookmin/stream/welfare/domain/fee/service/impl/PayerServiceImpl.java index 56be1f8fe..9cf23a46b 100644 --- a/core/domain/welfare/src/main/java/kr/ac/kookmin/stream/welfare/domain/fee/service/impl/PayerServiceImpl.java +++ b/core/domain/welfare/src/main/java/kr/ac/kookmin/stream/welfare/domain/fee/service/impl/PayerServiceImpl.java @@ -78,4 +78,14 @@ public void linkMember(Long memberId, String studentId) { studentTransferStatusService.createPaidForLinkedPayer(memberId); }); } + + // 끊지 않으면 명부 행이 탈퇴한 회원에 묶여 남아, 같은 학번으로 다시 가입해도 linkMember가 찾지 못해 납부자로 인정되지 않는다 + @Override + @Transactional + public void unlinkMember(Long memberId) { + payerRepository.findByMemberId(memberId).ifPresent(payer -> { + payer.unlinkMember(); + payerRepository.save(payer); + }); + } } From ef0e7e2cbdcaa27cfc95fcdfcb1295e21aed69e7 Mon Sep 17 00:00:00 2001 From: jjunh33 Date: Fri, 6 Nov 2026 10:33:50 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=EC=9D=B4=EB=AF=B8=20=ED=83=88?= =?UTF-8?q?=ED=87=B4=ED=95=9C=20=ED=9A=8C=EC=9B=90=EC=9D=98=20=EB=82=A9?= =?UTF-8?q?=EB=B6=80=EC=9E=90=20=EB=AA=85=EB=B6=80=20=EC=97=B0=EA=B2=B0=20?= =?UTF-8?q?=ED=95=B4=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../V65__unlink_payers_from_withdrawn_members.sql | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 infrastructure/db/src/main/resources/db/migration/V65__unlink_payers_from_withdrawn_members.sql diff --git a/infrastructure/db/src/main/resources/db/migration/V65__unlink_payers_from_withdrawn_members.sql b/infrastructure/db/src/main/resources/db/migration/V65__unlink_payers_from_withdrawn_members.sql new file mode 100644 index 000000000..2e6d35801 --- /dev/null +++ b/infrastructure/db/src/main/resources/db/migration/V65__unlink_payers_from_withdrawn_members.sql @@ -0,0 +1,9 @@ +-- 이미 탈퇴(소프트 삭제)한 회원에 연결된 납부자 명부 행의 연결을 끊는다. +-- 탈퇴할 때 명부 연결을 끊지 않아, 같은 학번으로 다시 가입해도 로그인 때 연결할 미연결 행(member_id IS NULL)을 찾지 못해 +-- 납부자로 인정되지 않았다. 앞으로는 탈퇴할 때 끊으므로, 그전에 탈퇴한 회원의 행만 가입 전 상태로 되돌린다. +-- 다시 적용돼도 결과가 같다. +UPDATE payers p +JOIN members m ON m.member_id = p.member_id +SET p.member_id = NULL, + p.registered = 0 +WHERE m.is_deleted = 1; From 483c908b269233c57889418b985c8b16555b1771 Mon Sep 17 00:00:00 2001 From: jjunh33 Date: Fri, 6 Nov 2026 14:07:22 +0900 Subject: [PATCH 3/3] =?UTF-8?q?chore:=20Flyway=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98=20=EB=B2=84=EC=A0=84=20?= =?UTF-8?q?=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - V65 → V67 unlink_payers_from_withdrawn_members --- ..._members.sql => V67__unlink_payers_from_withdrawn_members.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename infrastructure/db/src/main/resources/db/migration/{V65__unlink_payers_from_withdrawn_members.sql => V67__unlink_payers_from_withdrawn_members.sql} (100%) diff --git a/infrastructure/db/src/main/resources/db/migration/V65__unlink_payers_from_withdrawn_members.sql b/infrastructure/db/src/main/resources/db/migration/V67__unlink_payers_from_withdrawn_members.sql similarity index 100% rename from infrastructure/db/src/main/resources/db/migration/V65__unlink_payers_from_withdrawn_members.sql rename to infrastructure/db/src/main/resources/db/migration/V67__unlink_payers_from_withdrawn_members.sql