mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-02-21 22:41:05 +08:00
Add post and tag review workflow
This commit is contained in:
@@ -45,6 +45,7 @@ public class AdminPostController {
|
||||
dto.setAuthor(post.getAuthor().getUsername());
|
||||
dto.setCategory(toCategoryDto(post.getCategory()));
|
||||
dto.setViews(post.getViews());
|
||||
dto.setStatus(post.getStatus());
|
||||
return dto;
|
||||
}
|
||||
|
||||
@@ -67,6 +68,7 @@ public class AdminPostController {
|
||||
private String author;
|
||||
private CategoryDto category;
|
||||
private long views;
|
||||
private com.openisle.model.PostStatus status;
|
||||
}
|
||||
|
||||
@Data
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.openisle.controller;
|
||||
|
||||
import com.openisle.model.Tag;
|
||||
import com.openisle.service.TagService;
|
||||
import com.openisle.service.PostService;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/tags")
|
||||
@RequiredArgsConstructor
|
||||
public class AdminTagController {
|
||||
private final TagService tagService;
|
||||
private final PostService postService;
|
||||
|
||||
@GetMapping("/pending")
|
||||
public List<TagDto> pendingTags() {
|
||||
return tagService.listPendingTags().stream()
|
||||
.map(t -> toDto(t, postService.countPostsByTag(t.getId())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/approve")
|
||||
public TagDto approve(@PathVariable Long id) {
|
||||
Tag tag = tagService.approveTag(id);
|
||||
long count = postService.countPostsByTag(tag.getId());
|
||||
return toDto(tag, count);
|
||||
}
|
||||
|
||||
private TagDto toDto(Tag tag, long count) {
|
||||
TagDto dto = new TagDto();
|
||||
dto.setId(tag.getId());
|
||||
dto.setName(tag.getName());
|
||||
dto.setDescription(tag.getDescription());
|
||||
dto.setIcon(tag.getIcon());
|
||||
dto.setSmallIcon(tag.getSmallIcon());
|
||||
dto.setCount(count);
|
||||
return dto;
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class TagDto {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String description;
|
||||
private String icon;
|
||||
private String smallIcon;
|
||||
private Long count;
|
||||
}
|
||||
}
|
||||
@@ -117,6 +117,7 @@ public class PostController {
|
||||
dto.setCategory(toCategoryDto(post.getCategory()));
|
||||
dto.setTags(post.getTags().stream().map(this::toTagDto).collect(Collectors.toList()));
|
||||
dto.setViews(post.getViews());
|
||||
dto.setStatus(post.getStatus());
|
||||
|
||||
List<ReactionDto> reactions = reactionService.getReactionsForPost(post.getId())
|
||||
.stream()
|
||||
@@ -232,6 +233,7 @@ public class PostController {
|
||||
private CategoryDto category;
|
||||
private java.util.List<TagDto> tags;
|
||||
private long views;
|
||||
private com.openisle.model.PostStatus status;
|
||||
private List<CommentDto> comments;
|
||||
private List<ReactionDto> reactions;
|
||||
private java.util.List<AuthorDto> participants;
|
||||
|
||||
@@ -3,6 +3,9 @@ package com.openisle.controller;
|
||||
import com.openisle.model.Tag;
|
||||
import com.openisle.service.TagService;
|
||||
import com.openisle.service.PostService;
|
||||
import com.openisle.repository.UserRepository;
|
||||
import com.openisle.model.PublishMode;
|
||||
import com.openisle.model.Role;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -16,10 +19,18 @@ import java.util.stream.Collectors;
|
||||
public class TagController {
|
||||
private final TagService tagService;
|
||||
private final PostService postService;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
@PostMapping
|
||||
public TagDto create(@RequestBody TagRequest req) {
|
||||
Tag tag = tagService.createTag(req.getName(), req.getDescription(), req.getIcon(), req.getSmallIcon());
|
||||
public TagDto create(@RequestBody TagRequest req, org.springframework.security.core.Authentication auth) {
|
||||
boolean approved = true;
|
||||
if (postService.getPublishMode() == PublishMode.REVIEW && auth != null) {
|
||||
com.openisle.model.User user = userRepository.findByUsername(auth.getName()).orElseThrow();
|
||||
if (user.getRole() != Role.ADMIN) {
|
||||
approved = false;
|
||||
}
|
||||
}
|
||||
Tag tag = tagService.createTag(req.getName(), req.getDescription(), req.getIcon(), req.getSmallIcon(), approved);
|
||||
long count = postService.countPostsByTag(tag.getId());
|
||||
return toDto(tag, count);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ public enum NotificationType {
|
||||
COMMENT_REPLY,
|
||||
/** Someone reacted to your post or comment */
|
||||
REACTION,
|
||||
/** A new post is waiting for review */
|
||||
POST_REVIEW_REQUEST,
|
||||
/** Your post under review was approved or rejected */
|
||||
POST_REVIEWED,
|
||||
/** A subscribed post received a new comment */
|
||||
|
||||
@@ -26,4 +26,7 @@ public class Tag {
|
||||
|
||||
@Column(name = "description", nullable = false)
|
||||
private String description;
|
||||
|
||||
@Column(nullable = false)
|
||||
private boolean approved = true;
|
||||
}
|
||||
|
||||
@@ -7,4 +7,7 @@ import java.util.List;
|
||||
|
||||
public interface TagRepository extends JpaRepository<Tag, Long> {
|
||||
List<Tag> findByNameContainingIgnoreCase(String keyword);
|
||||
List<Tag> findByApproved(boolean approved);
|
||||
List<Tag> findByApprovedTrue();
|
||||
List<Tag> findByNameContainingIgnoreCaseAndApprovedTrue(String keyword);
|
||||
}
|
||||
|
||||
@@ -8,4 +8,5 @@ public interface UserRepository extends JpaRepository<User, Long> {
|
||||
Optional<User> findByUsername(String username);
|
||||
Optional<User> findByEmail(String email);
|
||||
java.util.List<User> findByUsernameContainingIgnoreCase(String keyword);
|
||||
java.util.List<User> findByRole(com.openisle.model.Role role);
|
||||
}
|
||||
|
||||
@@ -80,6 +80,13 @@ public class PostService {
|
||||
post.setTags(new java.util.HashSet<>(tags));
|
||||
post.setStatus(publishMode == PublishMode.REVIEW ? PostStatus.PENDING : PostStatus.PUBLISHED);
|
||||
post = postRepository.save(post);
|
||||
if (post.getStatus() == PostStatus.PENDING) {
|
||||
java.util.List<User> admins = userRepository.findByRole(com.openisle.model.Role.ADMIN);
|
||||
for (User admin : admins) {
|
||||
notificationService.createNotification(admin,
|
||||
NotificationType.POST_REVIEW_REQUEST, post, null, null, author, null);
|
||||
}
|
||||
}
|
||||
// notify followers of author
|
||||
for (User u : subscriptionService.getSubscribers(author.getUsername())) {
|
||||
if (!u.getId().equals(author.getId())) {
|
||||
@@ -100,7 +107,14 @@ public class PostService {
|
||||
Post post = postRepository.findById(id)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Post not found"));
|
||||
if (post.getStatus() != PostStatus.PUBLISHED) {
|
||||
throw new IllegalArgumentException("Post not found");
|
||||
if (viewer == null) {
|
||||
throw new IllegalArgumentException("Post not found");
|
||||
}
|
||||
User viewerUser = userRepository.findByUsername(viewer)
|
||||
.orElseThrow(() -> new IllegalArgumentException("User not found"));
|
||||
if (!viewerUser.getRole().equals(com.openisle.model.Role.ADMIN) && !viewerUser.getId().equals(post.getAuthor().getId())) {
|
||||
throw new IllegalArgumentException("Post not found");
|
||||
}
|
||||
}
|
||||
post.setViews(post.getViews() + 1);
|
||||
post = postRepository.save(post);
|
||||
|
||||
@@ -13,16 +13,21 @@ public class TagService {
|
||||
private final TagRepository tagRepository;
|
||||
private final TagValidator tagValidator;
|
||||
|
||||
public Tag createTag(String name, String description, String icon, String smallIcon) {
|
||||
public Tag createTag(String name, String description, String icon, String smallIcon, boolean approved) {
|
||||
tagValidator.validate(name);
|
||||
Tag tag = new Tag();
|
||||
tag.setName(name);
|
||||
tag.setDescription(description);
|
||||
tag.setIcon(icon);
|
||||
tag.setSmallIcon(smallIcon);
|
||||
tag.setApproved(approved);
|
||||
return tagRepository.save(tag);
|
||||
}
|
||||
|
||||
public Tag createTag(String name, String description, String icon, String smallIcon) {
|
||||
return createTag(name, description, icon, smallIcon, true);
|
||||
}
|
||||
|
||||
public Tag updateTag(Long id, String name, String description, String icon, String smallIcon) {
|
||||
Tag tag = tagRepository.findById(id)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Tag not found"));
|
||||
@@ -46,19 +51,30 @@ public class TagService {
|
||||
tagRepository.deleteById(id);
|
||||
}
|
||||
|
||||
public Tag approveTag(Long id) {
|
||||
Tag tag = tagRepository.findById(id)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Tag not found"));
|
||||
tag.setApproved(true);
|
||||
return tagRepository.save(tag);
|
||||
}
|
||||
|
||||
public List<Tag> listPendingTags() {
|
||||
return tagRepository.findByApproved(false);
|
||||
}
|
||||
|
||||
public Tag getTag(Long id) {
|
||||
return tagRepository.findById(id)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Tag not found"));
|
||||
}
|
||||
|
||||
public List<Tag> listTags() {
|
||||
return tagRepository.findAll();
|
||||
return tagRepository.findByApprovedTrue();
|
||||
}
|
||||
|
||||
public List<Tag> searchTags(String keyword) {
|
||||
if (keyword == null || keyword.isBlank()) {
|
||||
return tagRepository.findAll();
|
||||
return tagRepository.findByApprovedTrue();
|
||||
}
|
||||
return tagRepository.findByNameContainingIgnoreCase(keyword);
|
||||
return tagRepository.findByNameContainingIgnoreCaseAndApprovedTrue(keyword);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user