-
Notifications
You must be signed in to change notification settings - Fork 8
fix: 외래키로 인해 물리적 탈퇴가 되지 않는 문제 수정 #848
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,10 @@ | ||
| package com.example.solidconnection.scheduler; | ||
|
|
||
| import com.example.solidconnection.application.repository.ApplicationRepository; | ||
| import com.example.solidconnection.chat.repository.ChatMessageRepository; | ||
| import com.example.solidconnection.chat.repository.ChatParticipantRepository; | ||
| import com.example.solidconnection.chat.repository.ChatReadStatusRepository; | ||
| import com.example.solidconnection.chat.repository.ChatRoomRepository; | ||
| import com.example.solidconnection.community.comment.repository.CommentRepository; | ||
| import com.example.solidconnection.community.post.repository.PostLikeRepository; | ||
| import com.example.solidconnection.community.post.repository.PostRepository; | ||
|
|
@@ -19,6 +21,7 @@ | |
| import com.example.solidconnection.score.repository.LanguageTestScoreRepository; | ||
| import com.example.solidconnection.siteuser.domain.SiteUser; | ||
| import com.example.solidconnection.siteuser.repository.SiteUserRepository; | ||
| import com.example.solidconnection.siteuser.repository.UserBanRepository; | ||
| import com.example.solidconnection.siteuser.repository.UserBlockRepository; | ||
| import com.example.solidconnection.university.repository.LikedUnivApplyInfoRepository; | ||
| import java.time.LocalDate; | ||
|
|
@@ -49,10 +52,13 @@ public class UserRemovalScheduler { | |
| private final MentoringRepository mentoringRepository; | ||
| private final NewsRepository newsRepository; | ||
| private final LikedNewsRepository likedNewsRepository; | ||
| private final ChatRoomRepository chatRoomRepository; | ||
| private final ChatParticipantRepository chatParticipantRepository; | ||
| private final ChatMessageRepository chatMessageRepository; | ||
| private final ChatReadStatusRepository chatReadStatusRepository; | ||
| private final ReportRepository reportRepository; | ||
| private final UserBlockRepository userBlockRepository; | ||
| private final UserBanRepository userBanRepository; | ||
| private final MentorApplicationRepository mentorApplicationRepository; | ||
| private final S3Service s3Service; | ||
|
|
||
|
|
@@ -72,21 +78,19 @@ private void deleteUserAndRelatedData(SiteUser user) { | |
| long siteUserId = user.getId(); | ||
|
|
||
| likedNewsRepository.deleteAllBySiteUserId(siteUserId); | ||
| newsRepository.deleteAllBySiteUserId(siteUserId); | ||
| deleteNews(siteUserId); | ||
|
|
||
| postLikeRepository.deleteAllBySiteUserId(siteUserId); | ||
| commentRepository.deleteAllBySiteUserId(siteUserId); | ||
| postRepository.deleteAllBySiteUserId(siteUserId); | ||
| deletePosts(siteUserId); | ||
|
|
||
| mentoringRepository.deleteAllByMenteeId(siteUserId); | ||
| mentorRepository.deleteAllBySiteUserId(siteUserId); | ||
| deleteChatRooms(siteUserId); | ||
| deleteMentorings(siteUserId); | ||
| mentorApplicationRepository.deleteAllBySiteUserId(siteUserId); | ||
|
|
||
| List<Long> chatParticipantIds = chatParticipantRepository.findAllIdsBySiteUserId(siteUserId); | ||
| chatReadStatusRepository.deleteAllByChatParticipantIdIn(chatParticipantIds); | ||
| chatParticipantRepository.deleteAllBySiteUserId(siteUserId); | ||
| reportRepository.deleteAllByReporterId(siteUserId); | ||
| userBlockRepository.deleteAllByBlockerIdOrBlockedId(siteUserId, siteUserId); | ||
| deleteUserBans(siteUserId); | ||
|
|
||
| applicationRepository.deleteAllBySiteUserId(siteUserId); | ||
| gpaScoreRepository.deleteAllBySiteUserId(siteUserId); | ||
|
|
@@ -99,4 +103,62 @@ private void deleteUserAndRelatedData(SiteUser user) { | |
|
|
||
| siteUserRepository.delete(user); | ||
| } | ||
|
|
||
| /* | ||
| * 다른 사용자가 누른 좋아요가 남아 있으면 뉴스를 삭제할 수 없으므로 함께 삭제한다. | ||
| * */ | ||
| private void deleteNews(long siteUserId) { | ||
| List<Long> newsIds = newsRepository.findAllIdsBySiteUserId(siteUserId); | ||
| if (!newsIds.isEmpty()) { | ||
| likedNewsRepository.deleteAllByNewsIdIn(newsIds); | ||
| } | ||
| newsRepository.deleteAllBySiteUserId(siteUserId); | ||
| } | ||
|
|
||
| /* | ||
| * 신고로 가려진 게시글은 조회 필터에 걸려 삭제되지 않으므로, 플래그를 되돌린 뒤 삭제한다. | ||
| * */ | ||
| private void deletePosts(long siteUserId) { | ||
| postRepository.unmarkDeletedBySiteUserId(siteUserId); | ||
| postRepository.deleteAllBySiteUserId(siteUserId); | ||
| } | ||
|
|
||
| /* | ||
| * 1:1 채팅방은 참여자 한 명이 사라지면 유지될 수 없으므로 방 전체를 삭제한다. | ||
| * - 신고로 가려진 메시지는 조회 필터에 걸리므로, 플래그를 되돌린 뒤 삭제한다. | ||
| * - 채팅방이 멘토링을 참조하므로 멘토링보다 먼저 삭제한다. | ||
| * */ | ||
| private void deleteChatRooms(long siteUserId) { | ||
| List<Long> chatRoomIds = chatParticipantRepository.findAllChatRoomIdsBySiteUserId(siteUserId); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재 ChatRoom의 스키마 상 isGroup 컬럼이 존재합니다! 하지만 현재 findAllChatRoomIdsBySiteUserId가 isGroup 여부를 구분하지 않아서, 탈퇴자가 속한 채팅방은 1:1이든 그룹이든 전부 여기서 삭제 대상이 됩니다. PR 설명에는 "1:1 채팅방은 참여자가 사라지면 유지될 수 없으므로 삭제"라고 되어 있는데 코드에는 그 조건이 명시돼 있지 않네요. 지금은 그룹 채팅방을 생성하는 경로가 없어 실제 영향은 없지만, ChatRoom.isGroup이 이미 존재하고 ChatService에서도 분기 처리하는 걸 보면 나중에 그룹 채팅에 대해서도 대비가 되게 구현하는 게 좋아보입니다. 그때 멤버 한 명 탈퇴로 다른 멤버들의 방 전체가 사라지는 회귀를 막기 위해, isGroup = false인 방만 대상으로 삼도록 조건을 걸거나 최소한 의도를 주석/TODO로 남겨두면 좋을 것 같습니다! |
||
| if (chatRoomIds.isEmpty()) { | ||
| return; | ||
| } | ||
| chatMessageRepository.unmarkDeletedByChatRoomIdIn(chatRoomIds); | ||
| chatMessageRepository.deleteAllByChatRoomIdIn(chatRoomIds); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a deleted room contains files uploaded through the chat upload endpoint, this call cascades removal of the Useful? React with 👍 / 👎. |
||
| chatReadStatusRepository.deleteAllByChatRoomIdIn(chatRoomIds); | ||
| chatParticipantRepository.deleteAllByChatRoomIdIn(chatRoomIds); | ||
| chatRoomRepository.deleteAllById(chatRoomIds); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1:1 채팅방을 통째로 삭제하면, 탈퇴하지 않은 상대방 입장에서는 아무 통보 없이 대화 기록이 사라지게 됩니다! 참여자 한 명이 빠지면 방을 유지하기 어렵다는 제약 자체는 이해되지만, 이게 기획 쪽과 합의된 정책인지 확인이 필요해 보입니다. 필요하면 상대방 메시지는 보존하고 탈퇴한 참여자 정보만 "알 수 없음" 등으로 치환하는 방식도 고려해볼 수 있을 것 같습니다. |
||
| } | ||
|
|
||
| /* | ||
| * 멘티로 참여한 멘토링과 멘토로 참여한 멘토링을 모두 삭제한 뒤 멘토를 삭제한다. | ||
| * */ | ||
| private void deleteMentorings(long siteUserId) { | ||
| mentoringRepository.deleteAllByMenteeId(siteUserId); | ||
| List<Long> mentorIds = mentorRepository.findAllIdsBySiteUserId(siteUserId); | ||
| if (!mentorIds.isEmpty()) { | ||
| mentoringRepository.deleteAllByMentorIdIn(mentorIds); | ||
| } | ||
| mentorRepository.deleteAllBySiteUserId(siteUserId); | ||
| } | ||
|
|
||
| /* | ||
| * 탈퇴자를 대상으로 한 정지 기록은 삭제한다. | ||
| * 탈퇴자가 집행한 정지 기록은 다른 사용자의 이력이므로, 집행자 참조만 해제하고 보존한다. | ||
| * */ | ||
| private void deleteUserBans(long siteUserId) { | ||
| userBanRepository.deleteAllByBannedUserId(siteUserId); | ||
| userBanRepository.clearBannedBy(siteUserId); | ||
| userBanRepository.clearUnbannedBy(siteUserId); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,7 +27,7 @@ public class UserBan extends BaseEntity { | |
| @Column(name = "banned_user_id", nullable = false) | ||
| private Long bannedUserId; | ||
|
|
||
| @Column(name = "banned_by", nullable = false) | ||
| @Column(name = "banned_by") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Because this change intentionally makes AGENTS.md reference: AGENTS.md:L301-L305 Useful? React with 👍 / 👎. |
||
| private Long bannedBy; | ||
|
|
||
| @Column(name = "duration", nullable = false) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| ALTER TABLE user_ban | ||
| MODIFY COLUMN banned_by BIGINT NULL; |
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.
deleteAllByReporterId는 이 사용자가 "신고한" 기록만 지우는데, 이 사용자가 "신고당한" 기록(report.reported_id = siteUserId)이나 이 사용자의 게시글/채팅메시지를 대상(target_id)으로 한 신고 기록은 그대로 남아 고아 데이터가 발생할 수 있을 것 같습니다!
target_id/reported_id에는 FK가 없어서(ReportRepository, V25__create_report_table.sql 확인) 삭제 자체가 실패하지는 않지만, 대상이 사라진 report 행이 고아 데이터로 계속 쌓이게 됩니다. 이번 PR 범위 밖일 수 있지만 후속으로 reportedId/targetId 기준 정리도 필요해 보입니다.