mirror of https://github.com/halo-dev/halo
Complete post creation api
parent
078b6ab9c7
commit
f0651f8c37
|
@ -0,0 +1,73 @@
|
|||
package cc.ryanc.halo.model.params;
|
||||
|
||||
import cc.ryanc.halo.model.dto.base.InputConverter;
|
||||
import cc.ryanc.halo.model.entity.Post;
|
||||
import cc.ryanc.halo.model.enums.PostCreateFrom;
|
||||
import cc.ryanc.halo.model.enums.PostStatus;
|
||||
import cc.ryanc.halo.utils.HaloUtils;
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Post param.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 3/21/19
|
||||
*/
|
||||
@Data
|
||||
public class PostParam implements InputConverter<Post> {
|
||||
|
||||
@NotBlank(message = "Post title must not be blank")
|
||||
@Size(max = 100, message = "Length of post title must not be more than {max}")
|
||||
private String title;
|
||||
|
||||
private PostStatus status = PostStatus.DRAFT;
|
||||
|
||||
private String url;
|
||||
|
||||
@NotBlank(message = "Post original content must not be blank")
|
||||
private String originalContent;
|
||||
|
||||
@Size(max = 255, message = "Length of post thumbnail must not be more than {max}")
|
||||
private String thumbnail;
|
||||
|
||||
private Boolean disallowComment = false;
|
||||
|
||||
@Size(max = 255, message = "Length of post password must not be more than {max}")
|
||||
private String password;
|
||||
|
||||
@Size(max = 255, message = "Length of post template must not be more than {max}")
|
||||
private String template;
|
||||
|
||||
@Min(value = 0, message = "Post top priority must not be less than {value}")
|
||||
private Integer topPriority = 0;
|
||||
|
||||
private PostCreateFrom createFrom = PostCreateFrom.ADMIN;
|
||||
|
||||
private Set<Integer> tagIds;
|
||||
|
||||
private Set<Integer> categoryIds;
|
||||
|
||||
@Override
|
||||
public Post convertTo() {
|
||||
if (StringUtils.isBlank(url)) {
|
||||
url = HaloUtils.normalizeUrl(title);
|
||||
} else {
|
||||
url = HaloUtils.normalizeUrl(url);
|
||||
}
|
||||
|
||||
url = HaloUtils.initializeUrlIfBlank(url);
|
||||
|
||||
Post post = InputConverter.super.convertTo();
|
||||
// Crypt password
|
||||
post.setPassword(BCrypt.hashpw(password, BCrypt.gensalt()));
|
||||
|
||||
return post;
|
||||
}
|
||||
}
|
|
@ -2,9 +2,12 @@ package cc.ryanc.halo.model.params;
|
|||
|
||||
import cc.ryanc.halo.model.dto.base.InputConverter;
|
||||
import cc.ryanc.halo.model.entity.Tag;
|
||||
import cc.ryanc.halo.utils.HaloUtils;
|
||||
import cc.ryanc.halo.utils.SlugUtils;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
@ -31,6 +34,9 @@ public class TagParam implements InputConverter<Tag> {
|
|||
// Handle slug name
|
||||
slugName = SlugUtils.slugify(name);
|
||||
}
|
||||
|
||||
slugName = HaloUtils.initializeUrlIfBlank(slugName);
|
||||
|
||||
return InputConverter.super.convertTo();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,4 +37,12 @@ public interface PostRepository extends BaseRepository<Post, Integer>, JpaSpecif
|
|||
* @return posts count
|
||||
*/
|
||||
long countByStatusAndType(@NonNull PostStatus status, @NonNull PostType type);
|
||||
|
||||
/**
|
||||
* Count by post url.
|
||||
*
|
||||
* @param url post url must not be blank
|
||||
* @return the count
|
||||
*/
|
||||
long countByUrl(@NonNull String url);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.springframework.lang.NonNull;
|
|||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Post category service interface.
|
||||
|
@ -44,4 +45,14 @@ public interface PostCategoryService extends CrudService<PostCategory, Integer>
|
|||
*/
|
||||
@NonNull
|
||||
List<Post> listPostBy(@NonNull Integer categoryId);
|
||||
|
||||
/**
|
||||
* Creates post categories by post id and category id set.
|
||||
*
|
||||
* @param postId post id must not be null
|
||||
* @param categoryIds category id set
|
||||
* @return a list of post category
|
||||
*/
|
||||
@NonNull
|
||||
List<PostCategory> createBy(@NonNull Integer postId, Set<Integer> categoryIds);
|
||||
}
|
||||
|
|
|
@ -12,8 +12,10 @@ import cc.ryanc.halo.service.base.CrudService;
|
|||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Post service.
|
||||
|
@ -111,4 +113,16 @@ public interface PostService extends CrudService<Post, Integer> {
|
|||
* @return posts count
|
||||
*/
|
||||
Long countByStatus(PostStatus status, PostType type);
|
||||
|
||||
/**
|
||||
* Creates post by post param.
|
||||
*
|
||||
* @param post post must not be null
|
||||
* @param tagIds tag id set
|
||||
* @param categoryIds category id set
|
||||
* @return post created
|
||||
*/
|
||||
@NonNull
|
||||
@Transactional
|
||||
Post createBy(@NonNull Post post, Set<Integer> tagIds, Set<Integer> categoryIds);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.springframework.lang.NonNull;
|
|||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Post tag service interface.
|
||||
|
@ -56,4 +57,13 @@ public interface PostTagService extends CrudService<PostTag, Integer> {
|
|||
@NonNull
|
||||
List<Post> listPostsBy(@NonNull Integer tagId);
|
||||
|
||||
/**
|
||||
* Creates post tags by post id and tag id set.
|
||||
*
|
||||
* @param postId post id must not be null
|
||||
* @param tagIds tag id set
|
||||
* @return a list of post tag
|
||||
*/
|
||||
@NonNull
|
||||
List<PostTag> createBy(@NonNull Integer postId, Set<Integer> tagIds);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.springframework.util.Assert;
|
|||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Post category service implementation.
|
||||
|
@ -85,4 +86,24 @@ public class PostCategoryServiceImpl extends AbstractCrudService<PostCategory, I
|
|||
|
||||
return postRepository.findAllById(postIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PostCategory> createBy(Integer postId, Set<Integer> categoryIds) {
|
||||
Assert.notNull(postId, "Post id must not be null");
|
||||
|
||||
if (CollectionUtils.isEmpty(categoryIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// Build post categories
|
||||
List<PostCategory> postCategories = categoryIds.stream().map(categoryId -> {
|
||||
PostCategory postCategory = new PostCategory();
|
||||
postCategory.setPostId(postId);
|
||||
postCategory.setCategoryId(categoryId);
|
||||
return postCategory;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
// Create them
|
||||
return createInBatch(postCategories);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
package cc.ryanc.halo.service.impl;
|
||||
|
||||
import cc.ryanc.halo.exception.AlreadyExistsException;
|
||||
import cc.ryanc.halo.model.dto.CategoryOutputDTO;
|
||||
import cc.ryanc.halo.model.dto.TagOutputDTO;
|
||||
import cc.ryanc.halo.model.dto.post.PostMinimalOutputDTO;
|
||||
import cc.ryanc.halo.model.dto.post.PostSimpleOutputDTO;
|
||||
import cc.ryanc.halo.model.entity.Category;
|
||||
import cc.ryanc.halo.model.entity.Post;
|
||||
import cc.ryanc.halo.model.entity.Tag;
|
||||
import cc.ryanc.halo.model.entity.*;
|
||||
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.PostListVO;
|
||||
import cc.ryanc.halo.repository.PostRepository;
|
||||
import cc.ryanc.halo.service.PostCategoryService;
|
||||
import cc.ryanc.halo.service.PostService;
|
||||
import cc.ryanc.halo.service.PostTagService;
|
||||
import cc.ryanc.halo.service.*;
|
||||
import cc.ryanc.halo.service.base.AbstractCrudService;
|
||||
import cc.ryanc.halo.utils.HaloUtils;
|
||||
import cc.ryanc.halo.utils.MarkdownUtils;
|
||||
import cc.ryanc.halo.utils.ServiceUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
@ -34,20 +36,29 @@ import java.util.stream.Collectors;
|
|||
* @author johnniang
|
||||
* @author RYAN0UP
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PostServiceImpl extends AbstractCrudService<Post, Integer> implements PostService {
|
||||
|
||||
private final PostRepository postRepository;
|
||||
|
||||
private final TagService tagService;
|
||||
|
||||
private final CategoryService categoryService;
|
||||
|
||||
private final PostTagService postTagService;
|
||||
|
||||
private final PostCategoryService postCategoryService;
|
||||
|
||||
public PostServiceImpl(PostRepository postRepository,
|
||||
TagService tagService,
|
||||
CategoryService categoryService,
|
||||
PostTagService postTagService,
|
||||
PostCategoryService postCategoryService) {
|
||||
super(postRepository);
|
||||
this.postRepository = postRepository;
|
||||
this.tagService = tagService;
|
||||
this.categoryService = categoryService;
|
||||
this.postTagService = postTagService;
|
||||
this.postCategoryService = postCategoryService;
|
||||
}
|
||||
|
@ -155,4 +166,42 @@ public class PostServiceImpl extends AbstractCrudService<Post, Integer> implemen
|
|||
public Long countByStatus(PostStatus status, PostType type) {
|
||||
return postRepository.countByStatusAndType(status, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Post createBy(Post post, Set<Integer> tagIds, Set<Integer> categoryIds) {
|
||||
Assert.notNull(post, "Post param must not be null");
|
||||
|
||||
// TODO Check url
|
||||
long count = postRepository.countByUrl(post.getUrl());
|
||||
|
||||
if (count > 0) {
|
||||
throw new AlreadyExistsException("The post url has been exist already").setErrorData(post.getUrl());
|
||||
}
|
||||
|
||||
// Render content
|
||||
post.setFormatContent(MarkdownUtils.renderMarkdown(post.getOriginalContent()));
|
||||
|
||||
// TODO Handle thumbnail
|
||||
|
||||
// Create post
|
||||
create(post);
|
||||
|
||||
// List all tags
|
||||
List<Tag> tags = tagService.listAllByIds(tagIds);
|
||||
|
||||
// List all categories
|
||||
List<Category> categories = categoryService.listAllByIds(categoryIds);
|
||||
|
||||
// Create post tags
|
||||
List<PostTag> postTags = postTagService.createBy(post.getId(), ServiceUtils.fetchProperty(tags, Tag::getId));
|
||||
|
||||
log.debug("Created post tags: [{}]", postTags);
|
||||
|
||||
// Create post categories
|
||||
List<PostCategory> postCategories = postCategoryService.createBy(post.getId(), ServiceUtils.fetchProperty(categories, Category::getId));
|
||||
|
||||
log.debug("Created post categories: [{}]", postCategories);
|
||||
|
||||
return post;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,11 +60,9 @@ public class PostTagServiceImpl extends AbstractCrudService<PostTag, Integer> im
|
|||
List<Tag> tags = tagRepository.findAll(sort);
|
||||
|
||||
// Find post count
|
||||
return tags.stream().map(tag -> {
|
||||
TagWithCountOutputDTO tagOutputDTO = new TagWithCountOutputDTO().convertFrom(tag);
|
||||
|
||||
return tagOutputDTO;
|
||||
}).collect(Collectors.toList());
|
||||
return tags.stream().map(
|
||||
tag -> new TagWithCountOutputDTO().<TagWithCountOutputDTO>convertFrom(tag)
|
||||
).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -104,4 +102,25 @@ public class PostTagServiceImpl extends AbstractCrudService<PostTag, Integer> im
|
|||
|
||||
return postRepository.findAllById(postIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PostTag> createBy(Integer postId, Set<Integer> tagIds) {
|
||||
Assert.notNull(postId, "Post id must not be null");
|
||||
|
||||
if (CollectionUtils.isEmpty(tagIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// Create post tags
|
||||
Set<PostTag> postTags = tagIds.stream().map(tagId -> {
|
||||
// Build post tag
|
||||
PostTag postTag = new PostTag();
|
||||
postTag.setPostId(postId);
|
||||
postTag.setTagId(tagId);
|
||||
return postTag;
|
||||
}).collect(Collectors.toSet());
|
||||
|
||||
// Create in batch
|
||||
return createInBatch(postTags);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@ import cn.hutool.core.util.StrUtil;
|
|||
import com.qiniu.common.Zone;
|
||||
import io.github.biezhi.ome.OhMyEmail;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
@ -40,6 +42,37 @@ public class HaloUtils {
|
|||
|
||||
public final static int DEFAULT_PAGE_SIZE = 10;
|
||||
|
||||
/**
|
||||
* Initialize url if blank.
|
||||
*
|
||||
* @param url url can be blank
|
||||
* @return initial url
|
||||
*/
|
||||
@NonNull
|
||||
public static String initializeUrlIfBlank(@Nullable String url) {
|
||||
if (!StringUtils.isBlank(url)) {
|
||||
return url;
|
||||
}
|
||||
// TODO Consider to UUID
|
||||
return String.valueOf(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize url.
|
||||
*
|
||||
* @param url url must not be blank
|
||||
* @return normalized url
|
||||
*/
|
||||
@NonNull
|
||||
public static String normalizeUrl(@NonNull String url) {
|
||||
Assert.hasText(url, "Url must not be blank");
|
||||
|
||||
StringUtils.removeEnd(url, "html");
|
||||
StringUtils.removeEnd(url, "htm");
|
||||
|
||||
return SlugUtils.slugify(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets machine IP address.
|
||||
*
|
||||
|
@ -200,7 +233,7 @@ public class HaloUtils {
|
|||
final BufferedImage image = ImageIO.read(new FileInputStream(file));
|
||||
return image.getWidth() + "x" + image.getHeight();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to get read image file",e);
|
||||
throw new RuntimeException("Failed to get read image file", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,7 +256,7 @@ public class HaloUtils {
|
|||
bufferedWriter = new BufferedWriter(fileWriter);
|
||||
bufferedWriter.write(data);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to export file",e);
|
||||
throw new RuntimeException("Failed to export file", e);
|
||||
} finally {
|
||||
if (null != bufferedWriter) {
|
||||
bufferedWriter.close();
|
||||
|
@ -297,7 +330,7 @@ public class HaloUtils {
|
|||
result.append(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to push posts to baidu",e);
|
||||
throw new RuntimeException("Failed to push posts to baidu", e);
|
||||
} finally {
|
||||
try {
|
||||
if (null != out) {
|
||||
|
|
|
@ -18,6 +18,13 @@ public class SlugUtils {
|
|||
private static final Pattern NON_LATIN = Pattern.compile("[^\\w-]");
|
||||
private static final Pattern WHITESPACE = Pattern.compile("[\\s]");
|
||||
|
||||
/**
|
||||
* Slugify string.
|
||||
*
|
||||
* @param input input string must not be blank
|
||||
* @return slug string
|
||||
*/
|
||||
@NonNull
|
||||
public static String slugify(@NonNull String input) {
|
||||
Assert.hasText(input, "Input string must not be blank");
|
||||
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
package cc.ryanc.halo.web.controller.admin.api;
|
||||
|
||||
import cc.ryanc.halo.model.dto.post.PostDetailOutputDTO;
|
||||
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.service.PostService;
|
||||
import cc.ryanc.halo.model.params.PostParam;
|
||||
import cc.ryanc.halo.service.*;
|
||||
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.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
|
@ -27,8 +31,24 @@ public class PostController {
|
|||
|
||||
private final PostService postService;
|
||||
|
||||
public PostController(PostService postService) {
|
||||
private final TagService tagService;
|
||||
|
||||
private final CategoryService categoryService;
|
||||
|
||||
private final PostTagService postTagService;
|
||||
|
||||
private final PostCategoryService postCategoryService;
|
||||
|
||||
public PostController(PostService postService,
|
||||
TagService tagService,
|
||||
CategoryService categoryService,
|
||||
PostTagService postTagService,
|
||||
PostCategoryService postCategoryService) {
|
||||
this.postService = postService;
|
||||
this.tagService = tagService;
|
||||
this.categoryService = categoryService;
|
||||
this.postTagService = postTagService;
|
||||
this.postCategoryService = postCategoryService;
|
||||
}
|
||||
|
||||
@GetMapping("latest")
|
||||
|
@ -43,4 +63,13 @@ public class PostController {
|
|||
@PageableDefault(sort = "updateTime", direction = DESC) Pageable pageable) {
|
||||
return postService.pageSimpleDtoByStatus(status, PostType.POST, pageable);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public PostDetailOutputDTO createBy(@Valid @RequestBody PostParam postParam) {
|
||||
// Convert to
|
||||
Post post = postParam.convertTo();
|
||||
|
||||
return new PostDetailOutputDTO().convertFrom(postService.createBy(post, postParam.getTagIds(), postParam.getCategoryIds()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue