Complete comment creation api

pull/137/head
johnniang 2019-03-25 13:21:25 +08:00
parent ee2bc1c5ed
commit 42fced1c02
5 changed files with 120 additions and 22 deletions

View File

@ -103,6 +103,10 @@ public class Comment extends BaseEntity {
public void prePersist() {
super.prePersist();
id = null;
if (parentId == null || parentId < 0) {
parentId = 0L;
}
}
}

View File

@ -165,7 +165,7 @@ public enum BlogProperties implements ValueEnum<String> {
private Class<?> type;
BlogProperties(String value, Class<?> type) {
if (!supportType(type)) {
if (!isSupportedType(type)) {
throw new IllegalArgumentException("Unsupported blog property type: " + type);
}
@ -199,7 +199,7 @@ public enum BlogProperties implements ValueEnum<String> {
public static <T> T convertTo(@NonNull String value, @NonNull Class<T> type) {
Assert.hasText(value, "Property value must not be blank");
if (!supportType(type)) {
if (!isSupportedType(type)) {
throw new IllegalArgumentException("Unsupported blog property type: " + type);
}
@ -245,7 +245,7 @@ public enum BlogProperties implements ValueEnum<String> {
* @param type type to check
* @return true if supports; false else
*/
public static boolean supportType(Class<?> type) {
public static boolean isSupportedType(Class<?> type) {
return type != null && (
type.isAssignableFrom(String.class)
|| type.isAssignableFrom(Number.class)

View File

@ -2,6 +2,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.params.CommentParam;
import cc.ryanc.halo.model.vo.CommentVO;
import cc.ryanc.halo.service.base.CrudService;
import org.springframework.data.domain.Page;
@ -9,6 +10,7 @@ import org.springframework.data.domain.Pageable;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@ -20,6 +22,13 @@ import java.util.Map;
*/
public interface CommentService extends CrudService<Comment, Long> {
/**
* %d: parent commentator id
* %s: parent commentator author name
* %s: comment content
*/
String COMMENT_TEMPLATE = "<a href='#comment-id-%d>@%s</a> %s";
/**
* Lists latest comments.
*
@ -49,11 +58,21 @@ public interface CommentService extends CrudService<Comment, Long> {
List<Comment> listBy(@NonNull Integer postId);
/**
* Count by post id collection.
* Counts by post id collection.
*
* @param postIds post id collection
* @return a count map, key: post id, value: comment count
*/
@NonNull
Map<Integer, Long> countByPostIds(@Nullable Collection<Integer> postIds);
/**
* Creates a comment by comment param.
*
* @param commentParam comment param must not be null and should be validated
* @param request http servlet request must not be null
* @return created comment
*/
@NonNull
Comment createBy(@NonNull CommentParam commentParam, @NonNull HttpServletRequest request);
}

View File

@ -1,23 +1,35 @@
package cc.ryanc.halo.service.impl;
import cc.ryanc.halo.exception.NotFoundException;
import cc.ryanc.halo.model.dto.post.PostMinimalOutputDTO;
import cc.ryanc.halo.model.entity.Comment;
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.params.CommentParam;
import cc.ryanc.halo.model.projection.CommentCountProjection;
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;
import cc.ryanc.halo.service.OptionService;
import cc.ryanc.halo.service.base.AbstractCrudService;
import cc.ryanc.halo.utils.OwoUtil;
import cc.ryanc.halo.utils.ServiceUtils;
import cn.hutool.core.util.URLUtil;
import cn.hutool.crypto.SecureUtil;
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.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.util.HtmlUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
@ -27,6 +39,7 @@ import java.util.stream.Collectors;
* @author : RYAN0UP
* @date : 2019-03-14
*/
@Slf4j
@Service
public class CommentServiceImpl extends AbstractCrudService<Comment, Long> implements CommentService {
@ -34,11 +47,15 @@ public class CommentServiceImpl extends AbstractCrudService<Comment, Long> imple
private final PostRepository postRepository;
private final OptionService optionService;
public CommentServiceImpl(CommentRepository commentRepository,
PostRepository postRepository) {
PostRepository postRepository,
OptionService optionService) {
super(commentRepository);
this.commentRepository = commentRepository;
this.postRepository = postRepository;
this.optionService = optionService;
}
@Override
@ -81,6 +98,64 @@ public class CommentServiceImpl extends AbstractCrudService<Comment, Long> imple
return ServiceUtils.convertToMap(commentCountProjections, CommentCountProjection::getPostId, CommentCountProjection::getCount);
}
@Override
public Comment createBy(CommentParam commentParam, HttpServletRequest request) {
Assert.notNull(commentParam, "Comment param must not be null");
Assert.notNull(request, "Http servlet request must not be null");
// Post id must exist
boolean postExist = postRepository.existsById(commentParam.getPostId());
if (!postExist) {
log.error("Post: [{}] was not found", commentParam.getPostId());
throw new NotFoundException("The post was not found").setErrorData(commentParam.getPostId());
}
// Convert to comment
Comment comment = commentParam.convertTo();
// Set some default value
comment.setIpAddress(ServletUtil.getClientIP(request));
// TODO Check user login status and set this field
comment.setIsAdmin(false);
comment.setAuthor(HtmlUtils.htmlEscape(comment.getAuthor()));
comment.setGavatarMd5(SecureUtil.md5(comment.getEmail()));
if (comment.getParentId() != null && comment.getParentId() > 0) {
// Validate the comment parent id
Comment parentComment = getById(comment.getParentId());
// Format content and set it
String formattedContent = String.format(COMMENT_TEMPLATE,
parentComment.getId(),
parentComment.getAuthor(),
OwoUtil.parseOwo(formatContent(comment.getContent())));
comment.setContent(formattedContent);
} else {
comment.setParentId(0L);
// Top comment
comment.setContent(OwoUtil.parseOwo(formatContent(comment.getContent())));
}
if (StringUtils.isNotBlank(comment.getAuthorUrl())) {
// Normalize the author url and set it
comment.setAuthorUrl(URLUtil.normalize(comment.getAuthorUrl()));
}
// Handle comment status
Boolean needAudit = optionService.getByProperty(BlogProperties.NEW_COMMENT_NEED_CHECK, Boolean.class, true);
if (needAudit) {
comment.setStatus(CommentStatus.AUDITING);
} else {
comment.setStatus(CommentStatus.PUBLISHED);
}
Comment createdComment = create(comment);
// TODO Handle email sending
return createdComment;
}
/**
* Converts to comment vo page.
*
@ -122,4 +197,9 @@ public class CommentServiceImpl extends AbstractCrudService<Comment, Long> imple
return commentVO;
}).collect(Collectors.toList());
}
private String formatContent(@NonNull String content) {
return HtmlUtils.htmlEscape(content).replace("&lt;br/&gt;", "<br/>");
}
}

View File

@ -1,21 +1,16 @@
package cc.ryanc.halo.web.controller.admin.api;
import cc.ryanc.halo.model.dto.CommentOutputDTO;
import cc.ryanc.halo.model.entity.Comment;
import cc.ryanc.halo.model.enums.BlogProperties;
import cc.ryanc.halo.model.enums.CommentStatus;
import cc.ryanc.halo.model.params.CommentParam;
import cc.ryanc.halo.model.support.HaloConst;
import cc.ryanc.halo.model.vo.CommentVO;
import cc.ryanc.halo.service.CommentService;
import cc.ryanc.halo.utils.HaloUtils;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cc.ryanc.halo.service.OptionService;
import cc.ryanc.halo.service.PostService;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@ -36,8 +31,16 @@ public class CommentController {
private final CommentService commentService;
public CommentController(CommentService commentService) {
private final PostService postService;
private final OptionService optionService;
public CommentController(CommentService commentService,
PostService postService,
OptionService optionService) {
this.commentService = commentService;
this.postService = postService;
this.optionService = optionService;
}
@GetMapping("latest")
@ -54,14 +57,6 @@ public class CommentController {
@PostMapping
public CommentOutputDTO createBy(@Valid @RequestBody CommentParam commentParam, HttpServletRequest request) {
Comment comment = commentParam.convertTo();
// Set some default value
comment.setGavatarMd5(SecureUtil.md5(comment.getEmail()));
comment.setIpAddress(ServletUtil.getClientIP(request));
// commentService.createBy(comment)
return null;
return new CommentOutputDTO().convertFrom(commentService.createBy(commentParam, request));
}
}