mirror of https://github.com/halo-dev/halo
Complete comment tree view api
parent
8935f7478a
commit
3a1bf64636
|
@ -0,0 +1,34 @@
|
|||
package cc.ryanc.halo.model.support;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Comment page implementation.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 3/25/19
|
||||
*/
|
||||
@Data
|
||||
public class CommentPage<T> extends PageImpl<T> {
|
||||
|
||||
/**
|
||||
* Total comment (Contains sub comments)
|
||||
*/
|
||||
private final long commentCount;
|
||||
|
||||
public CommentPage(List<T> content, Pageable pageable, long topTotal, long commentCount) {
|
||||
super(content, pageable, topTotal);
|
||||
|
||||
this.commentCount = commentCount;
|
||||
}
|
||||
|
||||
public CommentPage(List<T> content, long commentCount) {
|
||||
super(content);
|
||||
|
||||
this.commentCount = commentCount;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package cc.ryanc.halo.model.vo;
|
||||
|
||||
import cc.ryanc.halo.model.dto.CommentOutputDTO;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Comment vo.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 3/25/19
|
||||
*/
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
public class CommentVO extends CommentOutputDTO {
|
||||
|
||||
private List<CommentVO> children;
|
||||
|
||||
}
|
|
@ -48,7 +48,32 @@ public interface CommentRepository extends BaseRepository<Comment, Long> {
|
|||
@NonNull
|
||||
List<Comment> findAllByPostId(@NonNull Integer postId);
|
||||
|
||||
/**
|
||||
* Counts comment count by post id collection.
|
||||
*
|
||||
* @param postIds post id collection must not be null
|
||||
* @return a list of comment count
|
||||
*/
|
||||
@Query("select new cc.ryanc.halo.model.projection.CommentCountProjection(count(comment.id), comment.postId) from Comment comment where comment.postId in ?1 group by comment.postId")
|
||||
@NonNull
|
||||
List<CommentCountProjection> countByPostIds(@NonNull Iterable<Integer> postIds);
|
||||
|
||||
/**
|
||||
* Finds comments by post id, comment status.
|
||||
*
|
||||
* @param postId post id must not be null
|
||||
* @param status comment status must not be null
|
||||
* @return a list of comment
|
||||
*/
|
||||
List<Comment> findAllByPostIdAndStatus(Integer postId, CommentStatus status);
|
||||
|
||||
/**
|
||||
* Finds comments by post id, comment status.
|
||||
*
|
||||
* @param postId post id must not be null
|
||||
* @param status comment status must not be null
|
||||
* @param pageable page info must not be null
|
||||
* @return a page of comment
|
||||
*/
|
||||
Page<Comment> findAllByPostIdAndStatus(Integer postId, CommentStatus status, Pageable pageable);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package cc.ryanc.halo.service;
|
|||
import cc.ryanc.halo.model.entity.Comment;
|
||||
import cc.ryanc.halo.model.enums.CommentStatus;
|
||||
import cc.ryanc.halo.model.vo.CommentListVO;
|
||||
import cc.ryanc.halo.model.vo.CommentVO;
|
||||
import cc.ryanc.halo.service.base.CrudService;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
@ -74,4 +75,14 @@ public interface CommentService extends CrudService<Comment, Long> {
|
|||
*/
|
||||
@NonNull
|
||||
Comment createBy(@NonNull Comment comment, @NonNull HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* Lists comment vos by post id.
|
||||
*
|
||||
* @param postId post id must not be null
|
||||
* @param pageable page info must not be null
|
||||
* @return a page of comment vo
|
||||
*/
|
||||
@NonNull
|
||||
Page<CommentVO> pageVosBy(@NonNull Integer postId, @NonNull Pageable pageable);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,9 @@ import cc.ryanc.halo.model.entity.Post;
|
|||
import cc.ryanc.halo.model.enums.BlogProperties;
|
||||
import cc.ryanc.halo.model.enums.CommentStatus;
|
||||
import cc.ryanc.halo.model.projection.CommentCountProjection;
|
||||
import cc.ryanc.halo.model.support.CommentPage;
|
||||
import cc.ryanc.halo.model.vo.CommentListVO;
|
||||
import cc.ryanc.halo.model.vo.CommentVO;
|
||||
import cc.ryanc.halo.repository.CommentRepository;
|
||||
import cc.ryanc.halo.repository.PostRepository;
|
||||
import cc.ryanc.halo.service.CommentService;
|
||||
|
@ -20,6 +22,7 @@ import cn.hutool.extra.servlet.ServletUtil;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.domain.*;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
@ -131,6 +134,113 @@ public class CommentServiceImpl extends AbstractCrudService<Comment, Long> imple
|
|||
return createdComment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<CommentVO> pageVosBy(Integer postId, Pageable pageable) {
|
||||
Assert.notNull(postId, "Post id must not be null");
|
||||
Assert.notNull(pageable, "Page info must not be null");
|
||||
|
||||
log.debug("Getting comment of post: [{}], page info: [{}]", postId, pageable);
|
||||
|
||||
// List all the top comments (Caution: This list will be cleared)
|
||||
List<Comment> comments = commentRepository.findAllByPostIdAndStatus(postId, CommentStatus.PUBLISHED);
|
||||
|
||||
// Init the top virtual comment
|
||||
CommentVO topVirtualComment = new CommentVO();
|
||||
topVirtualComment.setId(0L);
|
||||
topVirtualComment.setChildren(new LinkedList<>());
|
||||
|
||||
// Concrete the comment tree
|
||||
concreteTree(topVirtualComment, new LinkedList<>(comments), buildCommentComparator(pageable.getSortOr(Sort.by(Sort.Direction.DESC, "createTime"))));
|
||||
|
||||
List<CommentVO> topComments = topVirtualComment.getChildren();
|
||||
|
||||
List<CommentVO> pageContent = null;
|
||||
|
||||
// Calc the shear index
|
||||
int startIndex = pageable.getPageNumber() * pageable.getPageSize();
|
||||
if (startIndex >= topComments.size() || startIndex < 0) {
|
||||
pageContent = Collections.emptyList();
|
||||
} else {
|
||||
int endIndex = startIndex + pageable.getPageSize();
|
||||
if (endIndex > topComments.size()) {
|
||||
endIndex = topComments.size();
|
||||
}
|
||||
|
||||
log.debug("Top comments size: [{}]", topComments.size());
|
||||
log.debug("Start index: [{}]", startIndex);
|
||||
log.debug("End index: [{}]", endIndex);
|
||||
|
||||
pageContent = topComments.subList(startIndex, endIndex);
|
||||
}
|
||||
|
||||
return new CommentPage<>(pageContent, pageable, topComments.size(), comments.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a comment comparator.
|
||||
*
|
||||
* @param sort sort info
|
||||
* @return comment comparator
|
||||
*/
|
||||
private Comparator<CommentVO> buildCommentComparator(Sort sort) {
|
||||
return (currentComment, toCompareComment) -> {
|
||||
Assert.notNull(currentComment, "Current comment must not be null");
|
||||
Assert.notNull(toCompareComment, "Comment to compare must not be null");
|
||||
|
||||
// Get sort order
|
||||
Order order = sort.filter(anOrder -> anOrder.getProperty().equals("createTime"))
|
||||
.get()
|
||||
.findFirst()
|
||||
.orElseGet(() -> Order.desc("createTime"));
|
||||
|
||||
// Init sign
|
||||
int sign = order.getDirection().isAscending() ? 1 : -1;
|
||||
|
||||
// Compare createTime property
|
||||
return sign * currentComment.getCreateTime().compareTo(toCompareComment.getCreateTime());
|
||||
};
|
||||
}
|
||||
|
||||
private void concreteTree(CommentVO parentComment, List<Comment> comments, Comparator<CommentVO> commentComparator) {
|
||||
Assert.notNull(parentComment, "Parent comment must not be null");
|
||||
Assert.notNull(commentComparator, "Comment comparator must not be null");
|
||||
|
||||
if (CollectionUtils.isEmpty(comments)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Comment> children = new LinkedList<>();
|
||||
|
||||
comments.forEach(comment -> {
|
||||
if (parentComment.getId().equals(comment.getParentId())) {
|
||||
// Stage the child comment
|
||||
children.add(comment);
|
||||
|
||||
// Convert to comment vo
|
||||
CommentVO commentVO = new CommentVO().convertFrom(comment);
|
||||
|
||||
// TODO Add template
|
||||
|
||||
// Init children container
|
||||
if (parentComment.getChildren() == null) {
|
||||
parentComment.setChildren(new LinkedList<>());
|
||||
}
|
||||
|
||||
parentComment.getChildren().add(commentVO);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove all children
|
||||
comments.removeAll(children);
|
||||
|
||||
if (!CollectionUtils.isEmpty(parentComment.getChildren())) {
|
||||
// Recursively concrete the children
|
||||
parentComment.getChildren().forEach(childComment -> concreteTree(childComment, comments, commentComparator));
|
||||
// Sort the children
|
||||
parentComment.getChildren().sort(commentComparator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts to comment vo page.
|
||||
*
|
||||
|
|
|
@ -4,16 +4,17 @@ import cc.ryanc.halo.model.dto.post.PostMinimalOutputDTO;
|
|||
import cc.ryanc.halo.model.dto.post.PostSimpleOutputDTO;
|
||||
import cc.ryanc.halo.model.entity.Post;
|
||||
import cc.ryanc.halo.model.enums.PostStatus;
|
||||
import cc.ryanc.halo.model.enums.PostType;
|
||||
import cc.ryanc.halo.model.params.PostParam;
|
||||
import cc.ryanc.halo.model.vo.CommentVO;
|
||||
import cc.ryanc.halo.model.vo.PostDetailVO;
|
||||
import cc.ryanc.halo.service.PostCategoryService;
|
||||
import cc.ryanc.halo.service.PostService;
|
||||
import cc.ryanc.halo.service.PostTagService;
|
||||
import cc.ryanc.halo.service.*;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.web.PageableDefault;
|
||||
import org.springframework.data.web.SortDefault;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
@ -37,10 +38,20 @@ public class PostController {
|
|||
|
||||
private final PostTagService postTagService;
|
||||
|
||||
public PostController(PostService postService, PostCategoryService postCategoryService, PostTagService postTagService) {
|
||||
private final CommentService commentService;
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
public PostController(PostService postService,
|
||||
PostCategoryService postCategoryService,
|
||||
PostTagService postTagService,
|
||||
CommentService commentService,
|
||||
OptionService optionService) {
|
||||
this.postService = postService;
|
||||
this.postCategoryService = postCategoryService;
|
||||
this.postTagService = postTagService;
|
||||
this.commentService = commentService;
|
||||
this.optionService = optionService;
|
||||
}
|
||||
|
||||
@GetMapping("latest")
|
||||
|
@ -91,4 +102,11 @@ public class PostController {
|
|||
postCategoryService.removeByPostId(postId);
|
||||
postTagService.removeByPostId(postId);
|
||||
}
|
||||
|
||||
@GetMapping("{postId:\\d+}/comments")
|
||||
public Page<CommentVO> listComments(@PathVariable("postId") Integer postId,
|
||||
@RequestParam(name = "page", required = false, defaultValue = "0") int page,
|
||||
@SortDefault Sort sort) {
|
||||
return commentService.pageVosBy(postId, PageRequest.of(page, optionService.getCommentPageSize(), sort));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package cc.ryanc.halo.web.controller.support;
|
||||
|
||||
import cc.ryanc.halo.model.support.CommentPage;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
|
@ -30,6 +31,13 @@ public class PageJacksonSerializer extends JsonSerializer<Page> {
|
|||
generator.writeBooleanField("isLast", page.isLast());
|
||||
generator.writeBooleanField("isEmpty", page.isEmpty());
|
||||
generator.writeBooleanField("hasContent", page.hasContent());
|
||||
|
||||
// Handle comment page
|
||||
if (page instanceof CommentPage) {
|
||||
CommentPage commentPage = (CommentPage) page;
|
||||
generator.writeNumberField("commentCount", commentPage.getCommentCount());
|
||||
}
|
||||
|
||||
generator.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue