diff --git a/backend/src/main/java/com/openisle/model/Comment.java b/backend/src/main/java/com/openisle/model/Comment.java index d613f13da..377ff4537 100644 --- a/backend/src/main/java/com/openisle/model/Comment.java +++ b/backend/src/main/java/com/openisle/model/Comment.java @@ -5,6 +5,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @@ -13,6 +15,8 @@ import java.time.LocalDateTime; @Setter @NoArgsConstructor @Table(name = "comments") +@SQLDelete(sql = "UPDATE comments SET deleted_at = CURRENT_TIMESTAMP(6) WHERE id = ?") +@Where(clause = "deleted_at IS NULL") public class Comment { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -41,4 +45,7 @@ public class Comment { @Column private LocalDateTime pinnedAt; + @Column(name = "deleted_at") + private LocalDateTime deletedAt; + } diff --git a/backend/src/main/java/com/openisle/model/PointHistory.java b/backend/src/main/java/com/openisle/model/PointHistory.java index 347d4c75a..133c23f1f 100644 --- a/backend/src/main/java/com/openisle/model/PointHistory.java +++ b/backend/src/main/java/com/openisle/model/PointHistory.java @@ -4,6 +4,8 @@ import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @@ -13,6 +15,8 @@ import java.time.LocalDateTime; @Setter @NoArgsConstructor @Table(name = "point_histories") +@SQLDelete(sql = "UPDATE point_histories SET deleted_at = CURRENT_TIMESTAMP(6) WHERE id = ?") +@Where(clause = "deleted_at IS NULL") public class PointHistory { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -46,4 +50,7 @@ public class PointHistory { @Column(name = "created_at", nullable = false) private LocalDateTime createdAt; + + @Column(name = "deleted_at") + private LocalDateTime deletedAt; } diff --git a/backend/src/main/java/com/openisle/repository/PointHistoryRepository.java b/backend/src/main/java/com/openisle/repository/PointHistoryRepository.java index ac62b7df3..dabc1f805 100644 --- a/backend/src/main/java/com/openisle/repository/PointHistoryRepository.java +++ b/backend/src/main/java/com/openisle/repository/PointHistoryRepository.java @@ -2,6 +2,7 @@ package com.openisle.repository; import com.openisle.model.PointHistory; import com.openisle.model.User; +import com.openisle.model.Comment; import org.springframework.data.jpa.repository.JpaRepository; import java.time.LocalDateTime; @@ -12,4 +13,6 @@ public interface PointHistoryRepository extends JpaRepository findByUserAndCreatedAtAfterOrderByCreatedAtDesc(User user, LocalDateTime createdAt); + + List findByComment(Comment comment); } diff --git a/backend/src/main/java/com/openisle/service/CommentService.java b/backend/src/main/java/com/openisle/service/CommentService.java index 8121b80d9..e37ded111 100644 --- a/backend/src/main/java/com/openisle/service/CommentService.java +++ b/backend/src/main/java/com/openisle/service/CommentService.java @@ -11,6 +11,7 @@ import com.openisle.repository.UserRepository; import com.openisle.repository.ReactionRepository; import com.openisle.repository.CommentSubscriptionRepository; import com.openisle.repository.NotificationRepository; +import com.openisle.repository.PointHistoryRepository; import com.openisle.service.NotificationService; import com.openisle.service.SubscriptionService; import com.openisle.model.Role; @@ -37,6 +38,7 @@ public class CommentService { private final ReactionRepository reactionRepository; private final CommentSubscriptionRepository commentSubscriptionRepository; private final NotificationRepository notificationRepository; + private final PointHistoryRepository pointHistoryRepository; private final ImageUploader imageUploader; @Transactional @@ -235,10 +237,14 @@ public class CommentService { for (Comment c : replies) { deleteCommentCascade(c); } + // 逻辑删除相关的积分历史记录 + pointHistoryRepository.findByComment(comment).forEach(pointHistoryRepository::delete); + // 删除其他相关数据 reactionRepository.findByComment(comment).forEach(reactionRepository::delete); commentSubscriptionRepository.findByComment(comment).forEach(commentSubscriptionRepository::delete); notificationRepository.deleteAll(notificationRepository.findByComment(comment)); imageUploader.removeReferences(imageUploader.extractUrls(comment.getContent())); + // 逻辑删除评论 commentRepository.delete(comment); log.debug("deleteCommentCascade removed comment {}", comment.getId()); } diff --git a/backend/src/main/resources/db/migration/V4__add_logical_delete_support.sql b/backend/src/main/resources/db/migration/V4__add_logical_delete_support.sql new file mode 100644 index 000000000..1e0067974 --- /dev/null +++ b/backend/src/main/resources/db/migration/V4__add_logical_delete_support.sql @@ -0,0 +1,11 @@ +-- Add logical delete support for comments and point_histories tables + +-- Add deleted_at column to comments table +ALTER TABLE comments ADD COLUMN deleted_at DATETIME(6) NULL; + +-- Add deleted_at column to point_histories table +ALTER TABLE point_histories ADD COLUMN deleted_at DATETIME(6) NULL; + +-- Add index for better performance on logical delete queries +CREATE INDEX idx_comments_deleted_at ON comments(deleted_at); +CREATE INDEX idx_point_histories_deleted_at ON point_histories(deleted_at); diff --git a/backend/src/test/java/com/openisle/service/CommentServiceTest.java b/backend/src/test/java/com/openisle/service/CommentServiceTest.java index 95f55875d..9d22c32aa 100644 --- a/backend/src/test/java/com/openisle/service/CommentServiceTest.java +++ b/backend/src/test/java/com/openisle/service/CommentServiceTest.java @@ -6,6 +6,7 @@ import com.openisle.repository.UserRepository; import com.openisle.repository.ReactionRepository; import com.openisle.repository.CommentSubscriptionRepository; import com.openisle.repository.NotificationRepository; +import com.openisle.repository.PointHistoryRepository; import com.openisle.exception.RateLimitException; import org.junit.jupiter.api.Test; @@ -24,10 +25,11 @@ class CommentServiceTest { ReactionRepository reactionRepo = mock(ReactionRepository.class); CommentSubscriptionRepository subRepo = mock(CommentSubscriptionRepository.class); NotificationRepository nRepo = mock(NotificationRepository.class); + PointHistoryRepository pointHistoryRepo = mock(PointHistoryRepository.class); ImageUploader imageUploader = mock(ImageUploader.class); CommentService service = new CommentService(commentRepo, postRepo, userRepo, - notifService, subService, reactionRepo, subRepo, nRepo, imageUploader); + notifService, subService, reactionRepo, subRepo, nRepo, pointHistoryRepo, imageUploader); when(commentRepo.countByAuthorAfter(eq("alice"), any())).thenReturn(3L);