mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-05-09 04:07:31 +08:00
Add post and comment deletion
This commit is contained in:
@@ -70,6 +70,7 @@ import ReactionsGroup from './ReactionsGroup.vue'
|
|||||||
import DropdownMenu from './DropdownMenu.vue'
|
import DropdownMenu from './DropdownMenu.vue'
|
||||||
const CommentItem = {
|
const CommentItem = {
|
||||||
name: 'CommentItem',
|
name: 'CommentItem',
|
||||||
|
emits: ['deleted'],
|
||||||
props: {
|
props: {
|
||||||
comment: {
|
comment: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -84,7 +85,7 @@ const CommentItem = {
|
|||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props, { emit }) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const showReplies = ref(props.defaultShowReplies)
|
const showReplies = ref(props.defaultShowReplies)
|
||||||
watch(
|
watch(
|
||||||
@@ -105,7 +106,22 @@ const CommentItem = {
|
|||||||
const commentMenuItems = computed(() =>
|
const commentMenuItems = computed(() =>
|
||||||
isAuthor.value ? [{ text: '删除评论', color: 'red', onClick: () => deleteComment() }] : []
|
isAuthor.value ? [{ text: '删除评论', color: 'red', onClick: () => deleteComment() }] : []
|
||||||
)
|
)
|
||||||
const deleteComment = () => {
|
const deleteComment = async () => {
|
||||||
|
const token = getToken()
|
||||||
|
if (!token) {
|
||||||
|
toast.error('请先登录')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const res = await fetch(`${API_BASE_URL}/api/comments/${props.comment.id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: { Authorization: `Bearer ${token}` }
|
||||||
|
})
|
||||||
|
if (res.ok) {
|
||||||
|
toast.success('已删除')
|
||||||
|
emit('deleted', props.comment.id)
|
||||||
|
} else {
|
||||||
|
toast.error('操作失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const submitReply = async (text) => {
|
const submitReply = async (text) => {
|
||||||
if (!text.trim()) return
|
if (!text.trim()) return
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
<div class="comments-container">
|
<div class="comments-container">
|
||||||
<BaseTimeline :items="comments">
|
<BaseTimeline :items="comments">
|
||||||
<template #item="{ item }">
|
<template #item="{ item }">
|
||||||
<CommentItem :key="item.id" :comment="item" :level="level + 1" :default-show-replies="item.openReplies" />
|
<CommentItem :key="item.id" :comment="item" :level="level + 1" :default-show-replies="item.openReplies" @deleted="onCommentDeleted" />
|
||||||
</template>
|
</template>
|
||||||
</BaseTimeline>
|
</BaseTimeline>
|
||||||
<!-- <CommentItem
|
<!-- <CommentItem
|
||||||
@@ -209,6 +209,24 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const removeCommentFromList = (id, list) => {
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
const item = list[i]
|
||||||
|
if (item.id === id) {
|
||||||
|
list.splice(i, 1)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (item.reply && item.reply.length) {
|
||||||
|
if (removeCommentFromList(id, item.reply)) return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const onCommentDeleted = (id) => {
|
||||||
|
removeCommentFromList(Number(id), comments.value)
|
||||||
|
}
|
||||||
|
|
||||||
const fetchPost = async () => {
|
const fetchPost = async () => {
|
||||||
try {
|
try {
|
||||||
isWaitingFetchingPost.value = true;
|
isWaitingFetchingPost.value = true;
|
||||||
@@ -347,6 +365,21 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const deletePost = async () => {
|
const deletePost = async () => {
|
||||||
|
const token = getToken()
|
||||||
|
if (!token) {
|
||||||
|
toast.error('请先登录')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const res = await fetch(`${API_BASE_URL}/api/posts/${postId}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: { Authorization: `Bearer ${token}` }
|
||||||
|
})
|
||||||
|
if (res.ok) {
|
||||||
|
toast.success('已删除')
|
||||||
|
router.push('/')
|
||||||
|
} else {
|
||||||
|
toast.error('操作失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const rejectPost = async () => {
|
const rejectPost = async () => {
|
||||||
@@ -443,6 +476,8 @@ export default {
|
|||||||
status,
|
status,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
approvePost,
|
approvePost,
|
||||||
|
onCommentDeleted,
|
||||||
|
deletePost,
|
||||||
rejectPost
|
rejectPost
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,11 @@ public class CommentController {
|
|||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/comments/{id}")
|
||||||
|
public void deleteComment(@PathVariable Long id, Authentication auth) {
|
||||||
|
commentService.deleteComment(auth.getName(), id);
|
||||||
|
}
|
||||||
|
|
||||||
private CommentDto toDto(Comment comment) {
|
private CommentDto toDto(Comment comment) {
|
||||||
CommentDto dto = new CommentDto();
|
CommentDto dto = new CommentDto();
|
||||||
dto.setId(comment.getId());
|
dto.setId(comment.getId());
|
||||||
|
|||||||
@@ -48,6 +48,11 @@ public class PostController {
|
|||||||
return ResponseEntity.ok(toDto(post));
|
return ResponseEntity.ok(toDto(post));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public void deletePost(@PathVariable Long id, Authentication auth) {
|
||||||
|
postService.deletePost(id, auth.getName());
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public ResponseEntity<PostDto> getPost(@PathVariable Long id, Authentication auth) {
|
public ResponseEntity<PostDto> getPost(@PathVariable Long id, Authentication auth) {
|
||||||
String viewer = auth != null ? auth.getName() : null;
|
String viewer = auth != null ? auth.getName() : null;
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.openisle.repository;
|
|||||||
|
|
||||||
import com.openisle.model.Notification;
|
import com.openisle.model.Notification;
|
||||||
import com.openisle.model.User;
|
import com.openisle.model.User;
|
||||||
|
import com.openisle.model.Post;
|
||||||
|
import com.openisle.model.Comment;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -11,4 +13,6 @@ public interface NotificationRepository extends JpaRepository<Notification, Long
|
|||||||
List<Notification> findByUserOrderByCreatedAtDesc(User user);
|
List<Notification> findByUserOrderByCreatedAtDesc(User user);
|
||||||
List<Notification> findByUserAndReadOrderByCreatedAtDesc(User user, boolean read);
|
List<Notification> findByUserAndReadOrderByCreatedAtDesc(User user, boolean read);
|
||||||
long countByUserAndRead(User user, boolean read);
|
long countByUserAndRead(User user, boolean read);
|
||||||
|
List<Notification> findByPost(Post post);
|
||||||
|
List<Notification> findByComment(Comment comment);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,12 @@ import com.openisle.model.NotificationType;
|
|||||||
import com.openisle.repository.CommentRepository;
|
import com.openisle.repository.CommentRepository;
|
||||||
import com.openisle.repository.PostRepository;
|
import com.openisle.repository.PostRepository;
|
||||||
import com.openisle.repository.UserRepository;
|
import com.openisle.repository.UserRepository;
|
||||||
|
import com.openisle.repository.ReactionRepository;
|
||||||
|
import com.openisle.repository.CommentSubscriptionRepository;
|
||||||
|
import com.openisle.repository.NotificationRepository;
|
||||||
import com.openisle.service.NotificationService;
|
import com.openisle.service.NotificationService;
|
||||||
import com.openisle.service.SubscriptionService;
|
import com.openisle.service.SubscriptionService;
|
||||||
|
import com.openisle.model.Role;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -24,6 +28,9 @@ public class CommentService {
|
|||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final NotificationService notificationService;
|
private final NotificationService notificationService;
|
||||||
private final SubscriptionService subscriptionService;
|
private final SubscriptionService subscriptionService;
|
||||||
|
private final ReactionRepository reactionRepository;
|
||||||
|
private final CommentSubscriptionRepository commentSubscriptionRepository;
|
||||||
|
private final NotificationRepository notificationRepository;
|
||||||
|
|
||||||
public Comment addComment(String username, Long postId, String content) {
|
public Comment addComment(String username, Long postId, String content) {
|
||||||
User author = userRepository.findByUsername(username)
|
User author = userRepository.findByUsername(username)
|
||||||
@@ -115,4 +122,28 @@ public class CommentService {
|
|||||||
public java.util.List<Comment> getCommentsByIds(java.util.List<Long> ids) {
|
public java.util.List<Comment> getCommentsByIds(java.util.List<Long> ids) {
|
||||||
return commentRepository.findAllById(ids);
|
return commentRepository.findAllById(ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@org.springframework.transaction.annotation.Transactional
|
||||||
|
public void deleteComment(String username, Long id) {
|
||||||
|
User user = userRepository.findByUsername(username)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("User not found"));
|
||||||
|
Comment comment = commentRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Comment not found"));
|
||||||
|
if (!user.getId().equals(comment.getAuthor().getId()) && user.getRole() != Role.ADMIN) {
|
||||||
|
throw new IllegalArgumentException("Unauthorized");
|
||||||
|
}
|
||||||
|
deleteCommentCascade(comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@org.springframework.transaction.annotation.Transactional
|
||||||
|
public void deleteCommentCascade(Comment comment) {
|
||||||
|
List<Comment> replies = commentRepository.findByParentOrderByCreatedAtAsc(comment);
|
||||||
|
for (Comment c : replies) {
|
||||||
|
deleteCommentCascade(c);
|
||||||
|
}
|
||||||
|
reactionRepository.findByComment(comment).forEach(reactionRepository::delete);
|
||||||
|
commentSubscriptionRepository.findByComment(comment).forEach(commentSubscriptionRepository::delete);
|
||||||
|
notificationRepository.findByComment(comment).forEach(n -> { n.setComment(null); notificationRepository.save(n); });
|
||||||
|
commentRepository.delete(comment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,19 @@ import com.openisle.model.PostStatus;
|
|||||||
import com.openisle.model.PublishMode;
|
import com.openisle.model.PublishMode;
|
||||||
import com.openisle.model.User;
|
import com.openisle.model.User;
|
||||||
import com.openisle.model.Category;
|
import com.openisle.model.Category;
|
||||||
|
import com.openisle.model.Comment;
|
||||||
import com.openisle.model.NotificationType;
|
import com.openisle.model.NotificationType;
|
||||||
import com.openisle.repository.PostRepository;
|
import com.openisle.repository.PostRepository;
|
||||||
import com.openisle.repository.UserRepository;
|
import com.openisle.repository.UserRepository;
|
||||||
import com.openisle.repository.CategoryRepository;
|
import com.openisle.repository.CategoryRepository;
|
||||||
import com.openisle.repository.TagRepository;
|
import com.openisle.repository.TagRepository;
|
||||||
import com.openisle.service.SubscriptionService;
|
import com.openisle.service.SubscriptionService;
|
||||||
|
import com.openisle.service.CommentService;
|
||||||
|
import com.openisle.repository.CommentRepository;
|
||||||
|
import com.openisle.repository.ReactionRepository;
|
||||||
|
import com.openisle.repository.PostSubscriptionRepository;
|
||||||
|
import com.openisle.repository.NotificationRepository;
|
||||||
|
import com.openisle.model.Role;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -27,6 +34,11 @@ public class PostService {
|
|||||||
private PublishMode publishMode;
|
private PublishMode publishMode;
|
||||||
private final NotificationService notificationService;
|
private final NotificationService notificationService;
|
||||||
private final SubscriptionService subscriptionService;
|
private final SubscriptionService subscriptionService;
|
||||||
|
private final CommentService commentService;
|
||||||
|
private final CommentRepository commentRepository;
|
||||||
|
private final ReactionRepository reactionRepository;
|
||||||
|
private final PostSubscriptionRepository postSubscriptionRepository;
|
||||||
|
private final NotificationRepository notificationRepository;
|
||||||
|
|
||||||
@org.springframework.beans.factory.annotation.Autowired
|
@org.springframework.beans.factory.annotation.Autowired
|
||||||
public PostService(PostRepository postRepository,
|
public PostService(PostRepository postRepository,
|
||||||
@@ -35,6 +47,11 @@ public class PostService {
|
|||||||
TagRepository tagRepository,
|
TagRepository tagRepository,
|
||||||
NotificationService notificationService,
|
NotificationService notificationService,
|
||||||
SubscriptionService subscriptionService,
|
SubscriptionService subscriptionService,
|
||||||
|
CommentService commentService,
|
||||||
|
CommentRepository commentRepository,
|
||||||
|
ReactionRepository reactionRepository,
|
||||||
|
PostSubscriptionRepository postSubscriptionRepository,
|
||||||
|
NotificationRepository notificationRepository,
|
||||||
@Value("${app.post.publish-mode:DIRECT}") PublishMode publishMode) {
|
@Value("${app.post.publish-mode:DIRECT}") PublishMode publishMode) {
|
||||||
this.postRepository = postRepository;
|
this.postRepository = postRepository;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
@@ -42,6 +59,11 @@ public class PostService {
|
|||||||
this.tagRepository = tagRepository;
|
this.tagRepository = tagRepository;
|
||||||
this.notificationService = notificationService;
|
this.notificationService = notificationService;
|
||||||
this.subscriptionService = subscriptionService;
|
this.subscriptionService = subscriptionService;
|
||||||
|
this.commentService = commentService;
|
||||||
|
this.commentRepository = commentRepository;
|
||||||
|
this.reactionRepository = reactionRepository;
|
||||||
|
this.postSubscriptionRepository = postSubscriptionRepository;
|
||||||
|
this.notificationRepository = notificationRepository;
|
||||||
this.publishMode = publishMode;
|
this.publishMode = publishMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,6 +339,24 @@ public class PostService {
|
|||||||
return post;
|
return post;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@org.springframework.transaction.annotation.Transactional
|
||||||
|
public void deletePost(Long id, String username) {
|
||||||
|
Post post = postRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Post not found"));
|
||||||
|
User user = userRepository.findByUsername(username)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("User not found"));
|
||||||
|
if (!user.getId().equals(post.getAuthor().getId()) && user.getRole() != Role.ADMIN) {
|
||||||
|
throw new IllegalArgumentException("Unauthorized");
|
||||||
|
}
|
||||||
|
for (Comment c : commentRepository.findByPostAndParentIsNullOrderByCreatedAtAsc(post)) {
|
||||||
|
commentService.deleteCommentCascade(c);
|
||||||
|
}
|
||||||
|
reactionRepository.findByPost(post).forEach(reactionRepository::delete);
|
||||||
|
postSubscriptionRepository.findByPost(post).forEach(postSubscriptionRepository::delete);
|
||||||
|
notificationRepository.findByPost(post).forEach(n -> { n.setPost(null); notificationRepository.save(n); });
|
||||||
|
postRepository.delete(post);
|
||||||
|
}
|
||||||
|
|
||||||
public java.util.List<Post> getPostsByIds(java.util.List<Long> ids) {
|
public java.util.List<Post> getPostsByIds(java.util.List<Long> ids) {
|
||||||
return postRepository.findAllById(ids);
|
return postRepository.findAllById(ids);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user