feat: add post pinning

This commit is contained in:
Tim
2025-07-22 18:11:42 +08:00
parent 929b9baa0c
commit ee118c0bf4
6 changed files with 133 additions and 62 deletions

View File

@@ -36,6 +36,16 @@ public class AdminPostController {
return toDto(postService.rejectPost(id));
}
@PostMapping("/{id}/pin")
public PostDto pin(@PathVariable Long id) {
return toDto(postService.pinPost(id));
}
@PostMapping("/{id}/unpin")
public PostDto unpin(@PathVariable Long id) {
return toDto(postService.unpinPost(id));
}
private PostDto toDto(Post post) {
PostDto dto = new PostDto();
dto.setId(post.getId());
@@ -46,6 +56,7 @@ public class AdminPostController {
dto.setCategory(toCategoryDto(post.getCategory()));
dto.setViews(post.getViews());
dto.setStatus(post.getStatus());
dto.setPinnedAt(post.getPinnedAt());
return dto;
}
@@ -69,6 +80,7 @@ public class AdminPostController {
private CategoryDto category;
private long views;
private com.openisle.model.PostStatus status;
private LocalDateTime pinnedAt;
}
@Data

View File

@@ -135,6 +135,7 @@ public class PostController {
dto.setTags(post.getTags().stream().map(this::toTagDto).collect(Collectors.toList()));
dto.setViews(post.getViews());
dto.setStatus(post.getStatus());
dto.setPinnedAt(post.getPinnedAt());
List<ReactionDto> reactions = reactionService.getReactionsForPost(post.getId())
.stream()
@@ -251,6 +252,7 @@ public class PostController {
private java.util.List<TagDto> tags;
private long views;
private com.openisle.model.PostStatus status;
private LocalDateTime pinnedAt;
private List<CommentDto> comments;
private List<ReactionDto> reactions;
private java.util.List<AuthorDto> participants;

View File

@@ -59,4 +59,7 @@ public class Post {
@Column(nullable = false)
private PostStatus status = PostStatus.PUBLISHED;
@Column
private LocalDateTime pinnedAt;
}

View File

@@ -173,22 +173,14 @@ public class PostService {
java.util.List<Long> tagIds,
Integer page,
Integer pageSize) {
Pageable pageable = null;
if (page != null && pageSize != null) {
pageable = PageRequest.of(page, pageSize, Sort.Direction.DESC, "createdAt");
}
boolean hasCategories = categoryIds != null && !categoryIds.isEmpty();
boolean hasTags = tagIds != null && !tagIds.isEmpty();
if (!hasCategories && !hasTags) {
if (pageable != null) {
return postRepository.findByStatusOrderByViewsDesc(PostStatus.PUBLISHED, pageable);
}
return postRepository.findByStatusOrderByViewsDesc(PostStatus.PUBLISHED);
}
java.util.List<Post> posts;
if (hasCategories) {
if (!hasCategories && !hasTags) {
posts = postRepository.findByStatusOrderByViewsDesc(PostStatus.PUBLISHED);
} else if (hasCategories) {
java.util.List<Category> categories = categoryRepository.findAllById(categoryIds);
if (categories.isEmpty()) {
return java.util.List.of();
@@ -198,49 +190,33 @@ public class PostService {
if (tags.isEmpty()) {
return java.util.List.of();
}
if (pageable != null) {
return postRepository.findByCategoriesAndAllTagsOrderByViewsDesc(
categories, tags, PostStatus.PUBLISHED, tags.size(), pageable);
}
return postRepository.findByCategoriesAndAllTagsOrderByViewsDesc(
posts = postRepository.findByCategoriesAndAllTagsOrderByViewsDesc(
categories, tags, PostStatus.PUBLISHED, tags.size());
} else {
posts = postRepository.findByCategoryInAndStatusOrderByViewsDesc(categories, PostStatus.PUBLISHED);
}
if (pageable != null) {
return postRepository.findByCategoryInAndStatusOrderByViewsDesc(categories, PostStatus.PUBLISHED, pageable);
} else {
java.util.List<com.openisle.model.Tag> tags = tagRepository.findAllById(tagIds);
if (tags.isEmpty()) {
return java.util.List.of();
}
return postRepository.findByCategoryInAndStatusOrderByViewsDesc(categories, PostStatus.PUBLISHED);
posts = postRepository.findByAllTagsOrderByViewsDesc(tags, PostStatus.PUBLISHED, tags.size());
}
java.util.List<com.openisle.model.Tag> tags = tagRepository.findAllById(tagIds);
if (tags.isEmpty()) {
return java.util.List.of();
}
if (pageable != null) {
return postRepository.findByAllTagsOrderByViewsDesc(tags, PostStatus.PUBLISHED, tags.size(), pageable);
}
return postRepository.findByAllTagsOrderByViewsDesc(tags, PostStatus.PUBLISHED, tags.size());
return paginate(sortByPinnedAndViews(posts), page, pageSize);
}
public List<Post> listPostsByCategories(java.util.List<Long> categoryIds,
Integer page,
Integer pageSize) {
Pageable pageable = null;
if (page != null && pageSize != null) {
pageable = PageRequest.of(page, pageSize, Sort.Direction.DESC, "createdAt");
}
if (categoryIds == null || categoryIds.isEmpty()) {
if (pageable != null) {
return postRepository.findByStatusOrderByCreatedAtDesc(PostStatus.PUBLISHED, pageable);
}
return postRepository.findByStatusOrderByCreatedAtDesc(PostStatus.PUBLISHED);
java.util.List<Post> posts = postRepository.findByStatusOrderByCreatedAtDesc(PostStatus.PUBLISHED);
return paginate(sortByPinnedAndCreated(posts), page, pageSize);
}
java.util.List<Category> categories = categoryRepository.findAllById(categoryIds);
if (pageable != null) {
return postRepository.findByCategoryInAndStatusOrderByCreatedAtDesc(categories, PostStatus.PUBLISHED, pageable);
}
return postRepository.findByCategoryInAndStatusOrderByCreatedAtDesc(categories, PostStatus.PUBLISHED);
java.util.List<Post> posts = postRepository.findByCategoryInAndStatusOrderByCreatedAtDesc(categories, PostStatus.PUBLISHED);
return paginate(sortByPinnedAndCreated(posts), page, pageSize);
}
public List<Post> getRecentPostsByUser(String username, int limit) {
@@ -266,20 +242,13 @@ public class PostService {
return java.util.List.of();
}
Pageable pageable = null;
if (page != null && pageSize != null) {
pageable = PageRequest.of(page, pageSize, Sort.Direction.DESC, "createdAt");
}
java.util.List<com.openisle.model.Tag> tags = tagRepository.findAllById(tagIds);
if (tags.isEmpty()) {
return java.util.List.of();
}
if (pageable != null) {
return postRepository.findByAllTagsOrderByCreatedAtDesc(tags, PostStatus.PUBLISHED, tags.size(), pageable);
}
return postRepository.findByAllTagsOrderByCreatedAtDesc(tags, PostStatus.PUBLISHED, tags.size());
java.util.List<Post> posts = postRepository.findByAllTagsOrderByCreatedAtDesc(tags, PostStatus.PUBLISHED, tags.size());
return paginate(sortByPinnedAndCreated(posts), page, pageSize);
}
public List<Post> listPostsByCategoriesAndTags(java.util.List<Long> categoryIds,
@@ -290,21 +259,14 @@ public class PostService {
return java.util.List.of();
}
Pageable pageable = null;
if (page != null && pageSize != null) {
pageable = PageRequest.of(page, pageSize);
}
java.util.List<Category> categories = categoryRepository.findAllById(categoryIds);
java.util.List<com.openisle.model.Tag> tags = tagRepository.findAllById(tagIds);
if (categories.isEmpty() || tags.isEmpty()) {
return java.util.List.of();
}
if (pageable != null) {
return postRepository.findByCategoriesAndAllTagsOrderByCreatedAtDesc(categories, tags, PostStatus.PUBLISHED, tags.size(), pageable);
}
return postRepository.findByCategoriesAndAllTagsOrderByCreatedAtDesc(categories, tags, PostStatus.PUBLISHED, tags.size());
java.util.List<Post> posts = postRepository.findByCategoriesAndAllTagsOrderByCreatedAtDesc(categories, tags, PostStatus.PUBLISHED, tags.size());
return paginate(sortByPinnedAndCreated(posts), page, pageSize);
}
public List<Post> listPendingPosts() {
@@ -347,6 +309,20 @@ public class PostService {
return post;
}
public Post pinPost(Long id) {
Post post = postRepository.findById(id)
.orElseThrow(() -> new com.openisle.exception.NotFoundException("Post not found"));
post.setPinnedAt(java.time.LocalDateTime.now());
return postRepository.save(post);
}
public Post unpinPost(Long id) {
Post post = postRepository.findById(id)
.orElseThrow(() -> new com.openisle.exception.NotFoundException("Post not found"));
post.setPinnedAt(null);
return postRepository.save(post);
}
@org.springframework.transaction.annotation.Transactional
public void deletePost(Long id, String username) {
Post post = postRepository.findById(id)
@@ -377,4 +353,32 @@ public class PostService {
public long countPostsByTag(Long tagId) {
return postRepository.countDistinctByTags_Id(tagId);
}
private java.util.List<Post> sortByPinnedAndCreated(java.util.List<Post> posts) {
return posts.stream()
.sorted(java.util.Comparator
.comparing(Post::getPinnedAt, java.util.Comparator.nullsLast(java.util.Comparator.reverseOrder()))
.thenComparing(Post::getCreatedAt, java.util.Comparator.reverseOrder()))
.toList();
}
private java.util.List<Post> sortByPinnedAndViews(java.util.List<Post> posts) {
return posts.stream()
.sorted(java.util.Comparator
.comparing(Post::getPinnedAt, java.util.Comparator.nullsLast(java.util.Comparator.reverseOrder()))
.thenComparing(Post::getViews, java.util.Comparator.reverseOrder()))
.toList();
}
private java.util.List<Post> paginate(java.util.List<Post> posts, Integer page, Integer pageSize) {
if (page == null || pageSize == null) {
return posts;
}
int from = page * pageSize;
if (from >= posts.size()) {
return java.util.List.of();
}
int to = Math.min(from + pageSize, posts.size());
return posts.subList(from, to);
}
}