mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-06-08 11:07:34 +08:00
Add post and tag review workflow
This commit is contained in:
@@ -4,16 +4,19 @@ import { reactive } from 'vue'
|
|||||||
const TOKEN_KEY = 'token'
|
const TOKEN_KEY = 'token'
|
||||||
const USER_ID_KEY = 'userId'
|
const USER_ID_KEY = 'userId'
|
||||||
const USERNAME_KEY = 'username'
|
const USERNAME_KEY = 'username'
|
||||||
|
const ROLE_KEY = 'role'
|
||||||
|
|
||||||
export const authState = reactive({
|
export const authState = reactive({
|
||||||
loggedIn: false,
|
loggedIn: false,
|
||||||
userId: null,
|
userId: null,
|
||||||
username: null
|
username: null,
|
||||||
|
role: null
|
||||||
})
|
})
|
||||||
|
|
||||||
authState.loggedIn = localStorage.getItem(TOKEN_KEY) !== null && localStorage.getItem(TOKEN_KEY) !== ''
|
authState.loggedIn = localStorage.getItem(TOKEN_KEY) !== null && localStorage.getItem(TOKEN_KEY) !== ''
|
||||||
authState.userId = localStorage.getItem(USER_ID_KEY)
|
authState.userId = localStorage.getItem(USER_ID_KEY)
|
||||||
authState.username = localStorage.getItem(USERNAME_KEY)
|
authState.username = localStorage.getItem(USERNAME_KEY)
|
||||||
|
authState.role = localStorage.getItem(ROLE_KEY)
|
||||||
|
|
||||||
export function getToken() {
|
export function getToken() {
|
||||||
return localStorage.getItem(TOKEN_KEY)
|
return localStorage.getItem(TOKEN_KEY)
|
||||||
@@ -33,6 +36,10 @@ export function clearToken() {
|
|||||||
export function setUserInfo({ id, username }) {
|
export function setUserInfo({ id, username }) {
|
||||||
authState.userId = id
|
authState.userId = id
|
||||||
authState.username = username
|
authState.username = username
|
||||||
|
if (arguments[0] && arguments[0].role) {
|
||||||
|
authState.role = arguments[0].role
|
||||||
|
localStorage.setItem(ROLE_KEY, arguments[0].role)
|
||||||
|
}
|
||||||
if (id !== undefined && id !== null) localStorage.setItem(USER_ID_KEY, id)
|
if (id !== undefined && id !== null) localStorage.setItem(USER_ID_KEY, id)
|
||||||
if (username) localStorage.setItem(USERNAME_KEY, username)
|
if (username) localStorage.setItem(USERNAME_KEY, username)
|
||||||
}
|
}
|
||||||
@@ -40,8 +47,10 @@ export function setUserInfo({ id, username }) {
|
|||||||
export function clearUserInfo() {
|
export function clearUserInfo() {
|
||||||
localStorage.removeItem(USER_ID_KEY)
|
localStorage.removeItem(USER_ID_KEY)
|
||||||
localStorage.removeItem(USERNAME_KEY)
|
localStorage.removeItem(USERNAME_KEY)
|
||||||
|
localStorage.removeItem(ROLE_KEY)
|
||||||
authState.userId = null
|
authState.userId = null
|
||||||
authState.username = null
|
authState.username = null
|
||||||
|
authState.role = null
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchCurrentUser() {
|
export async function fetchCurrentUser() {
|
||||||
@@ -61,7 +70,7 @@ export async function fetchCurrentUser() {
|
|||||||
export async function loadCurrentUser() {
|
export async function loadCurrentUser() {
|
||||||
const user = await fetchCurrentUser()
|
const user = await fetchCurrentUser()
|
||||||
if (user) {
|
if (user) {
|
||||||
setUserInfo({ id: user.id, username: user.username })
|
setUserInfo({ id: user.id, username: user.username, role: user.role })
|
||||||
}
|
}
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ export default {
|
|||||||
POST_VIEWED: 'fas fa-eye',
|
POST_VIEWED: 'fas fa-eye',
|
||||||
COMMENT_REPLY: 'fas fa-reply',
|
COMMENT_REPLY: 'fas fa-reply',
|
||||||
POST_REVIEWED: 'fas fa-check',
|
POST_REVIEWED: 'fas fa-check',
|
||||||
|
POST_REVIEW_REQUEST: 'fas fa-gavel',
|
||||||
POST_UPDATED: 'fas fa-comment-dots',
|
POST_UPDATED: 'fas fa-comment-dots',
|
||||||
USER_ACTIVITY: 'fas fa-user',
|
USER_ACTIVITY: 'fas fa-user',
|
||||||
FOLLOWED_POST: 'fas fa-feather-alt',
|
FOLLOWED_POST: 'fas fa-feather-alt',
|
||||||
@@ -310,6 +311,18 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else if (n.type === 'POST_REVIEW_REQUEST') {
|
||||||
|
notifications.value.push({
|
||||||
|
...n,
|
||||||
|
src: n.fromUser ? n.fromUser.avatar : null,
|
||||||
|
icon: n.fromUser ? undefined : iconMap[n.type],
|
||||||
|
iconClick: () => {
|
||||||
|
if (n.post) {
|
||||||
|
markRead(n.id)
|
||||||
|
router.push(`/posts/${n.post.id}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
notifications.value.push({
|
notifications.value.push({
|
||||||
...n,
|
...n,
|
||||||
@@ -330,6 +343,8 @@ export default {
|
|||||||
return '有人回复了你'
|
return '有人回复了你'
|
||||||
case 'REACTION':
|
case 'REACTION':
|
||||||
return '有人点赞'
|
return '有人点赞'
|
||||||
|
case 'POST_REVIEW_REQUEST':
|
||||||
|
return '帖子待审核'
|
||||||
case 'POST_REVIEWED':
|
case 'POST_REVIEWED':
|
||||||
return '帖子审核结果'
|
return '帖子审核结果'
|
||||||
case 'POST_UPDATED':
|
case 'POST_UPDATED':
|
||||||
|
|||||||
@@ -13,11 +13,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="article-title-container-right">
|
<div class="article-title-container-right">
|
||||||
<div class="article-pending-button">
|
<div v-if="status === 'PENDING'" class="article-pending-button">
|
||||||
PENDING
|
审核中
|
||||||
</div>
|
</div>
|
||||||
<div class="article-block-button">
|
<div v-if="status === 'REJECTED'" class="article-block-button">
|
||||||
REJECT
|
已拒绝
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="loggedIn && !isAuthor && !subscribed"
|
v-if="loggedIn && !isAuthor && !subscribed"
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<i class="fas fa-user-minus"></i>
|
<i class="fas fa-user-minus"></i>
|
||||||
取消订阅
|
取消订阅
|
||||||
</div>
|
</div>
|
||||||
<DropdownMenu :items="reviewMenuItems">
|
<DropdownMenu v-if="isAdmin && status === 'PENDING'" :items="reviewMenuItems">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<i class="fas fa-ellipsis-vertical action-menu-icon"></i>
|
<i class="fas fa-ellipsis-vertical action-menu-icon"></i>
|
||||||
</template>
|
</template>
|
||||||
@@ -132,9 +132,10 @@ export default {
|
|||||||
const author = ref('')
|
const author = ref('')
|
||||||
const postContent = ref('')
|
const postContent = ref('')
|
||||||
const category = ref('')
|
const category = ref('')
|
||||||
const tags = ref([])
|
const tags = ref([])
|
||||||
const postReactions = ref([])
|
const postReactions = ref([])
|
||||||
const comments = ref([])
|
const comments = ref([])
|
||||||
|
const status = ref('PUBLISHED')
|
||||||
const isWaitingFetchingPost = ref(false);
|
const isWaitingFetchingPost = ref(false);
|
||||||
const isWaitingPostingComment = ref(false);
|
const isWaitingPostingComment = ref(false);
|
||||||
const postTime = ref('')
|
const postTime = ref('')
|
||||||
@@ -142,11 +143,12 @@ export default {
|
|||||||
const mainContainer = ref(null)
|
const mainContainer = ref(null)
|
||||||
const currentIndex = ref(1)
|
const currentIndex = ref(1)
|
||||||
const subscribed = ref(false)
|
const subscribed = ref(false)
|
||||||
const loggedIn = computed(() => authState.loggedIn)
|
const loggedIn = computed(() => authState.loggedIn)
|
||||||
|
const isAdmin = computed(() => authState.role === 'ADMIN')
|
||||||
const isAuthor = computed(() => authState.username === author.value.username)
|
const isAuthor = computed(() => authState.username === author.value.username)
|
||||||
const reviewMenuItems = [
|
const reviewMenuItems = [
|
||||||
{ text: '通过审核', onClick: () => {} },
|
{ text: '通过审核', onClick: () => approvePost() },
|
||||||
{ text: '驳回', color: 'red', onClick: () => {} }
|
{ text: '驳回', color: 'red', onClick: () => rejectPost() }
|
||||||
]
|
]
|
||||||
|
|
||||||
const gatherPostItems = () => {
|
const gatherPostItems = () => {
|
||||||
@@ -226,6 +228,7 @@ export default {
|
|||||||
postReactions.value = data.reactions || []
|
postReactions.value = data.reactions || []
|
||||||
comments.value = (data.comments || []).map(mapComment)
|
comments.value = (data.comments || []).map(mapComment)
|
||||||
subscribed.value = !!data.subscribed
|
subscribed.value = !!data.subscribed
|
||||||
|
status.value = data.status
|
||||||
postTime.value = TimeManager.format(data.createdAt)
|
postTime.value = TimeManager.format(data.createdAt)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
gatherPostItems()
|
gatherPostItems()
|
||||||
@@ -329,12 +332,42 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsubscribePost = async () => {
|
const unsubscribePost = async () => {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
if (!token) {
|
if (!token) {
|
||||||
toast.error('请先登录')
|
toast.error('请先登录')
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const approvePost = async () => {
|
||||||
|
const token = getToken()
|
||||||
|
if (!token) return
|
||||||
|
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/approve`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { Authorization: `Bearer ${token}` }
|
||||||
|
})
|
||||||
|
if (res.ok) {
|
||||||
|
status.value = 'PUBLISHED'
|
||||||
|
toast.success('已通过审核')
|
||||||
|
} else {
|
||||||
|
toast.error('操作失败')
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rejectPost = async () => {
|
||||||
|
const token = getToken()
|
||||||
|
if (!token) return
|
||||||
|
const res = await fetch(`${API_BASE_URL}/api/admin/posts/${postId}/reject`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { Authorization: `Bearer ${token}` }
|
||||||
|
})
|
||||||
|
if (res.ok) {
|
||||||
|
status.value = 'REJECTED'
|
||||||
|
toast.success('已驳回')
|
||||||
|
} else {
|
||||||
|
toast.error('操作失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
const res = await fetch(`${API_BASE_URL}/api/subscriptions/posts/${postId}`, {
|
const res = await fetch(`${API_BASE_URL}/api/subscriptions/posts/${postId}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: { Authorization: `Bearer ${token}` }
|
headers: { Authorization: `Bearer ${token}` }
|
||||||
@@ -403,7 +436,11 @@ export default {
|
|||||||
gotoProfile,
|
gotoProfile,
|
||||||
subscribed,
|
subscribed,
|
||||||
loggedIn,
|
loggedIn,
|
||||||
isAuthor
|
isAuthor,
|
||||||
|
status,
|
||||||
|
isAdmin,
|
||||||
|
approvePost,
|
||||||
|
rejectPost
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ public class AdminPostController {
|
|||||||
dto.setAuthor(post.getAuthor().getUsername());
|
dto.setAuthor(post.getAuthor().getUsername());
|
||||||
dto.setCategory(toCategoryDto(post.getCategory()));
|
dto.setCategory(toCategoryDto(post.getCategory()));
|
||||||
dto.setViews(post.getViews());
|
dto.setViews(post.getViews());
|
||||||
|
dto.setStatus(post.getStatus());
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +68,7 @@ public class AdminPostController {
|
|||||||
private String author;
|
private String author;
|
||||||
private CategoryDto category;
|
private CategoryDto category;
|
||||||
private long views;
|
private long views;
|
||||||
|
private com.openisle.model.PostStatus status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@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.setCategory(toCategoryDto(post.getCategory()));
|
||||||
dto.setTags(post.getTags().stream().map(this::toTagDto).collect(Collectors.toList()));
|
dto.setTags(post.getTags().stream().map(this::toTagDto).collect(Collectors.toList()));
|
||||||
dto.setViews(post.getViews());
|
dto.setViews(post.getViews());
|
||||||
|
dto.setStatus(post.getStatus());
|
||||||
|
|
||||||
List<ReactionDto> reactions = reactionService.getReactionsForPost(post.getId())
|
List<ReactionDto> reactions = reactionService.getReactionsForPost(post.getId())
|
||||||
.stream()
|
.stream()
|
||||||
@@ -232,6 +233,7 @@ public class PostController {
|
|||||||
private CategoryDto category;
|
private CategoryDto category;
|
||||||
private java.util.List<TagDto> tags;
|
private java.util.List<TagDto> tags;
|
||||||
private long views;
|
private long views;
|
||||||
|
private com.openisle.model.PostStatus status;
|
||||||
private List<CommentDto> comments;
|
private List<CommentDto> comments;
|
||||||
private List<ReactionDto> reactions;
|
private List<ReactionDto> reactions;
|
||||||
private java.util.List<AuthorDto> participants;
|
private java.util.List<AuthorDto> participants;
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ package com.openisle.controller;
|
|||||||
import com.openisle.model.Tag;
|
import com.openisle.model.Tag;
|
||||||
import com.openisle.service.TagService;
|
import com.openisle.service.TagService;
|
||||||
import com.openisle.service.PostService;
|
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.Data;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@@ -16,10 +19,18 @@ import java.util.stream.Collectors;
|
|||||||
public class TagController {
|
public class TagController {
|
||||||
private final TagService tagService;
|
private final TagService tagService;
|
||||||
private final PostService postService;
|
private final PostService postService;
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public TagDto create(@RequestBody TagRequest req) {
|
public TagDto create(@RequestBody TagRequest req, org.springframework.security.core.Authentication auth) {
|
||||||
Tag tag = tagService.createTag(req.getName(), req.getDescription(), req.getIcon(), req.getSmallIcon());
|
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());
|
long count = postService.countPostsByTag(tag.getId());
|
||||||
return toDto(tag, count);
|
return toDto(tag, count);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ public enum NotificationType {
|
|||||||
COMMENT_REPLY,
|
COMMENT_REPLY,
|
||||||
/** Someone reacted to your post or comment */
|
/** Someone reacted to your post or comment */
|
||||||
REACTION,
|
REACTION,
|
||||||
|
/** A new post is waiting for review */
|
||||||
|
POST_REVIEW_REQUEST,
|
||||||
/** Your post under review was approved or rejected */
|
/** Your post under review was approved or rejected */
|
||||||
POST_REVIEWED,
|
POST_REVIEWED,
|
||||||
/** A subscribed post received a new comment */
|
/** A subscribed post received a new comment */
|
||||||
|
|||||||
@@ -26,4 +26,7 @@ public class Tag {
|
|||||||
|
|
||||||
@Column(name = "description", nullable = false)
|
@Column(name = "description", nullable = false)
|
||||||
private String description;
|
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> {
|
public interface TagRepository extends JpaRepository<Tag, Long> {
|
||||||
List<Tag> findByNameContainingIgnoreCase(String keyword);
|
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> findByUsername(String username);
|
||||||
Optional<User> findByEmail(String email);
|
Optional<User> findByEmail(String email);
|
||||||
java.util.List<User> findByUsernameContainingIgnoreCase(String keyword);
|
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.setTags(new java.util.HashSet<>(tags));
|
||||||
post.setStatus(publishMode == PublishMode.REVIEW ? PostStatus.PENDING : PostStatus.PUBLISHED);
|
post.setStatus(publishMode == PublishMode.REVIEW ? PostStatus.PENDING : PostStatus.PUBLISHED);
|
||||||
post = postRepository.save(post);
|
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
|
// notify followers of author
|
||||||
for (User u : subscriptionService.getSubscribers(author.getUsername())) {
|
for (User u : subscriptionService.getSubscribers(author.getUsername())) {
|
||||||
if (!u.getId().equals(author.getId())) {
|
if (!u.getId().equals(author.getId())) {
|
||||||
@@ -100,7 +107,14 @@ public class PostService {
|
|||||||
Post post = postRepository.findById(id)
|
Post post = postRepository.findById(id)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("Post not found"));
|
.orElseThrow(() -> new IllegalArgumentException("Post not found"));
|
||||||
if (post.getStatus() != PostStatus.PUBLISHED) {
|
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.setViews(post.getViews() + 1);
|
||||||
post = postRepository.save(post);
|
post = postRepository.save(post);
|
||||||
|
|||||||
@@ -13,16 +13,21 @@ public class TagService {
|
|||||||
private final TagRepository tagRepository;
|
private final TagRepository tagRepository;
|
||||||
private final TagValidator tagValidator;
|
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);
|
tagValidator.validate(name);
|
||||||
Tag tag = new Tag();
|
Tag tag = new Tag();
|
||||||
tag.setName(name);
|
tag.setName(name);
|
||||||
tag.setDescription(description);
|
tag.setDescription(description);
|
||||||
tag.setIcon(icon);
|
tag.setIcon(icon);
|
||||||
tag.setSmallIcon(smallIcon);
|
tag.setSmallIcon(smallIcon);
|
||||||
|
tag.setApproved(approved);
|
||||||
return tagRepository.save(tag);
|
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) {
|
public Tag updateTag(Long id, String name, String description, String icon, String smallIcon) {
|
||||||
Tag tag = tagRepository.findById(id)
|
Tag tag = tagRepository.findById(id)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("Tag not found"));
|
.orElseThrow(() -> new IllegalArgumentException("Tag not found"));
|
||||||
@@ -46,19 +51,30 @@ public class TagService {
|
|||||||
tagRepository.deleteById(id);
|
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) {
|
public Tag getTag(Long id) {
|
||||||
return tagRepository.findById(id)
|
return tagRepository.findById(id)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("Tag not found"));
|
.orElseThrow(() -> new IllegalArgumentException("Tag not found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Tag> listTags() {
|
public List<Tag> listTags() {
|
||||||
return tagRepository.findAll();
|
return tagRepository.findByApprovedTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Tag> searchTags(String keyword) {
|
public List<Tag> searchTags(String keyword) {
|
||||||
if (keyword == null || keyword.isBlank()) {
|
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