diff --git a/pom.xml b/pom.xml
index 42152d9e7..d228f4a0e 100755
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,7 @@
0.4.8
0.12.1
3.8.1
+ 2.3
diff --git a/src/main/java/cc/ryanc/halo/model/dto/TagWithCountOutputDTO.java b/src/main/java/cc/ryanc/halo/model/dto/TagWithCountOutputDTO.java
new file mode 100644
index 000000000..8236c8f32
--- /dev/null
+++ b/src/main/java/cc/ryanc/halo/model/dto/TagWithCountOutputDTO.java
@@ -0,0 +1,16 @@
+package cc.ryanc.halo.model.dto;
+
+import lombok.Data;
+
+/**
+ * Tag with post count output dto.
+ *
+ * @author johnniang
+ * @date 3/20/19
+ */
+@Data
+public class TagWithCountOutputDTO extends TagOutputDTO {
+
+ private Long postCount;
+
+}
diff --git a/src/main/java/cc/ryanc/halo/model/entity/Attachment.java b/src/main/java/cc/ryanc/halo/model/entity/Attachment.java
index bad0f29ee..c65df426a 100644
--- a/src/main/java/cc/ryanc/halo/model/entity/Attachment.java
+++ b/src/main/java/cc/ryanc/halo/model/entity/Attachment.java
@@ -7,7 +7,6 @@ import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import javax.persistence.*;
-import java.time.Instant;
/**
* Attachment entity
diff --git a/src/main/java/cc/ryanc/halo/model/params/TagParam.java b/src/main/java/cc/ryanc/halo/model/params/TagParam.java
new file mode 100644
index 000000000..8901bfeef
--- /dev/null
+++ b/src/main/java/cc/ryanc/halo/model/params/TagParam.java
@@ -0,0 +1,36 @@
+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.SlugUtils;
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+
+/**
+ * Tag param.
+ *
+ * @author johnniang
+ * @date 3/20/19
+ */
+@Data
+public class TagParam implements InputConverter {
+
+ @NotBlank(message = "Tag name must not be blank")
+ @Size(max = 255, message = "Length of tag name must not be more than {max}")
+ private String name;
+
+ @Size(max = 255, message = "Length of tag slug name must not be more than {max}")
+ private String slugName;
+
+ @Override
+ public Tag convertTo() {
+ if (StringUtils.isBlank(slugName)) {
+ // Handle slug name
+ slugName = SlugUtils.slugify(name);
+ }
+ return InputConverter.super.convertTo();
+ }
+}
diff --git a/src/main/java/cc/ryanc/halo/repository/PostTagRepository.java b/src/main/java/cc/ryanc/halo/repository/PostTagRepository.java
index 4423bded4..f4abc2a76 100644
--- a/src/main/java/cc/ryanc/halo/repository/PostTagRepository.java
+++ b/src/main/java/cc/ryanc/halo/repository/PostTagRepository.java
@@ -62,4 +62,5 @@ public interface PostTagRepository extends BaseRepository {
*/
@NonNull
List findAllByPostIdIn(@NonNull Iterable postIds);
+
}
diff --git a/src/main/java/cc/ryanc/halo/repository/TagRepository.java b/src/main/java/cc/ryanc/halo/repository/TagRepository.java
index 43f7796d5..11e1881e5 100644
--- a/src/main/java/cc/ryanc/halo/repository/TagRepository.java
+++ b/src/main/java/cc/ryanc/halo/repository/TagRepository.java
@@ -2,6 +2,7 @@ package cc.ryanc.halo.repository;
import cc.ryanc.halo.model.entity.Tag;
import cc.ryanc.halo.repository.base.BaseRepository;
+import org.springframework.lang.NonNull;
/**
* Tag repository.
@@ -10,4 +11,12 @@ import cc.ryanc.halo.repository.base.BaseRepository;
*/
public interface TagRepository extends BaseRepository {
+ /**
+ * Count by name or slug name.
+ *
+ * @param name tag name must not be null
+ * @param slugName tag slug name must not be null
+ * @return tag count
+ */
+ long countByNameOrSlugName(@NonNull String name, @NonNull String slugName);
}
diff --git a/src/main/java/cc/ryanc/halo/service/PostTagService.java b/src/main/java/cc/ryanc/halo/service/PostTagService.java
index e55708be8..1b45aef1d 100644
--- a/src/main/java/cc/ryanc/halo/service/PostTagService.java
+++ b/src/main/java/cc/ryanc/halo/service/PostTagService.java
@@ -1,9 +1,11 @@
package cc.ryanc.halo.service;
+import cc.ryanc.halo.model.dto.TagWithCountOutputDTO;
import cc.ryanc.halo.model.entity.Post;
import cc.ryanc.halo.model.entity.PostTag;
import cc.ryanc.halo.model.entity.Tag;
import cc.ryanc.halo.service.base.CrudService;
+import org.springframework.data.domain.Sort;
import org.springframework.lang.NonNull;
import java.util.Collection;
@@ -25,10 +27,19 @@ public interface PostTagService extends CrudService {
* @return a list of tag
*/
@NonNull
- List listTagBy(@NonNull Integer postId);
+ List listTagsBy(@NonNull Integer postId);
/**
- * Lists tag list map by post id.
+ * List tag with post count output dtos.
+ *
+ * @param sort sort info
+ * @return a list of tag with post count output dto
+ */
+ @NonNull
+ List listTagWithCountDtos(@NonNull Sort sort);
+
+ /**
+ * Lists tags list map by post id.
*
* @param postIds post id collection
* @return tag map (key: postId, value: a list of tags)
@@ -37,12 +48,12 @@ public interface PostTagService extends CrudService {
Map> listTagListMapBy(Collection postIds);
/**
- * Lists post by tag id.
+ * Lists posts by tag id.
*
* @param tagId tag id must not be null
* @return a list of post
*/
@NonNull
- List listPostBy(@NonNull Integer tagId);
+ List listPostsBy(@NonNull Integer tagId);
}
diff --git a/src/main/java/cc/ryanc/halo/service/impl/PostTagServiceImpl.java b/src/main/java/cc/ryanc/halo/service/impl/PostTagServiceImpl.java
index efbcd6025..c6cb01608 100644
--- a/src/main/java/cc/ryanc/halo/service/impl/PostTagServiceImpl.java
+++ b/src/main/java/cc/ryanc/halo/service/impl/PostTagServiceImpl.java
@@ -1,5 +1,6 @@
package cc.ryanc.halo.service.impl;
+import cc.ryanc.halo.model.dto.TagWithCountOutputDTO;
import cc.ryanc.halo.model.entity.Post;
import cc.ryanc.halo.model.entity.PostTag;
import cc.ryanc.halo.model.entity.Tag;
@@ -9,11 +10,13 @@ import cc.ryanc.halo.repository.TagRepository;
import cc.ryanc.halo.service.PostTagService;
import cc.ryanc.halo.service.base.AbstractCrudService;
import cc.ryanc.halo.utils.ServiceUtils;
+import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import java.util.*;
+import java.util.stream.Collectors;
/**
* Post tag service implementation.
@@ -40,7 +43,7 @@ public class PostTagServiceImpl extends AbstractCrudService im
}
@Override
- public List listTagBy(Integer postId) {
+ public List listTagsBy(Integer postId) {
Assert.notNull(postId, "Post id must not be null");
// Find all tag ids
@@ -49,6 +52,21 @@ public class PostTagServiceImpl extends AbstractCrudService im
return tagRepository.findAllById(tagIds);
}
+ @Override
+ public List listTagWithCountDtos(Sort sort) {
+ Assert.notNull(sort, "Sort info must not be null");
+
+ // Find all tags
+ List tags = tagRepository.findAll(sort);
+
+ // Find post count
+ return tags.stream().map(tag -> {
+ TagWithCountOutputDTO tagOutputDTO = new TagWithCountOutputDTO().convertFrom(tag);
+
+ return tagOutputDTO;
+ }).collect(Collectors.toList());
+ }
+
@Override
public Map> listTagListMapBy(Collection postIds) {
if (CollectionUtils.isEmpty(postIds)) {
@@ -78,7 +96,7 @@ public class PostTagServiceImpl extends AbstractCrudService im
@Override
- public List listPostBy(Integer tagId) {
+ public List listPostsBy(Integer tagId) {
Assert.notNull(tagId, "Tag id must not be null");
// Find all post ids
diff --git a/src/main/java/cc/ryanc/halo/service/impl/TagServiceImpl.java b/src/main/java/cc/ryanc/halo/service/impl/TagServiceImpl.java
index f149acbf6..78a487e52 100644
--- a/src/main/java/cc/ryanc/halo/service/impl/TagServiceImpl.java
+++ b/src/main/java/cc/ryanc/halo/service/impl/TagServiceImpl.java
@@ -1,9 +1,11 @@
package cc.ryanc.halo.service.impl;
+import cc.ryanc.halo.exception.AlreadyExistsException;
import cc.ryanc.halo.model.entity.Tag;
import cc.ryanc.halo.repository.TagRepository;
import cc.ryanc.halo.service.TagService;
import cc.ryanc.halo.service.base.AbstractCrudService;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
@@ -12,6 +14,7 @@ import org.springframework.stereotype.Service;
* @author : RYAN0UP
* @date : 2019-03-14
*/
+@Slf4j
@Service
public class TagServiceImpl extends AbstractCrudService implements TagService {
@@ -29,6 +32,22 @@ public class TagServiceImpl extends AbstractCrudService implements
*/
@Override
public void remove(Integer id) {
- // TODO 删除标签,以及对应的文章关系
+ // TODO 删除标签,以及对应的文章关系
+ }
+
+ @Override
+ public Tag create(Tag tag) {
+ // Check if the tag is exist
+ long count = tagRepository.countByNameOrSlugName(tag.getName(), tag.getSlugName());
+
+ log.debug("Tag count: [{}]", count);
+
+ if (count > 0) {
+ // If the tag has exist already
+ throw new AlreadyExistsException("The tag has already exist").setErrorData(tag);
+ }
+
+ // Get tag name
+ return super.create(tag);
}
}
diff --git a/src/main/java/cc/ryanc/halo/utils/SlugUtils.java b/src/main/java/cc/ryanc/halo/utils/SlugUtils.java
new file mode 100644
index 000000000..6efd7d435
--- /dev/null
+++ b/src/main/java/cc/ryanc/halo/utils/SlugUtils.java
@@ -0,0 +1,29 @@
+package cc.ryanc.halo.utils;
+
+import org.springframework.lang.NonNull;
+import org.springframework.util.Assert;
+
+import java.text.Normalizer;
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+/**
+ * Slugify utilities.
+ *
+ * @author johnniang
+ * @date 3/20/19
+ */
+public class SlugUtils {
+
+ private static final Pattern NON_LATIN = Pattern.compile("[^\\w-]");
+ private static final Pattern WHITESPACE = Pattern.compile("[\\s]");
+
+ public static String slugify(@NonNull String input) {
+ Assert.hasText(input, "Input string must not be blank");
+
+ String withoutWhitespace = WHITESPACE.matcher(input).replaceAll("-");
+ String normalized = Normalizer.normalize(withoutWhitespace, Normalizer.Form.NFKD);
+ String slug = NON_LATIN.matcher(normalized).replaceAll("");
+ return slug.toLowerCase(Locale.ENGLISH);
+ }
+}
diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/api/TagController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/api/TagController.java
new file mode 100644
index 000000000..c896a1e79
--- /dev/null
+++ b/src/main/java/cc/ryanc/halo/web/controller/admin/api/TagController.java
@@ -0,0 +1,48 @@
+package cc.ryanc.halo.web.controller.admin.api;
+
+import cc.ryanc.halo.model.dto.TagOutputDTO;
+import cc.ryanc.halo.model.entity.Tag;
+import cc.ryanc.halo.model.params.TagParam;
+import cc.ryanc.halo.service.TagService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.web.SortDefault;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * Tag controller.
+ *
+ * @author johnniang
+ * @date 3/20/19
+ */
+@Slf4j
+@RestController
+@RequestMapping("/admin/api/tags")
+public class TagController {
+
+ private final TagService tagService;
+
+ public TagController(TagService tagService) {
+ this.tagService = tagService;
+ }
+
+ @GetMapping
+ public List listTags(@SortDefault(sort = "updateTime", direction = Sort.Direction.DESC) Sort sort) {
+
+ return null;
+ }
+
+ @PostMapping
+ public TagOutputDTO createTag(@Valid @RequestBody TagParam tagParam) {
+ // Convert to tag
+ Tag tag = tagParam.convertTo();
+
+ log.debug("Tag to be created: [{}]", tag);
+
+ // Create and convert
+ return new TagOutputDTO().convertFrom(tagService.create(tag));
+ }
+}
diff --git a/src/test/java/cc/ryanc/halo/utils/SlugUtilsTest.java b/src/test/java/cc/ryanc/halo/utils/SlugUtilsTest.java
new file mode 100644
index 000000000..839f297d7
--- /dev/null
+++ b/src/test/java/cc/ryanc/halo/utils/SlugUtilsTest.java
@@ -0,0 +1,21 @@
+package cc.ryanc.halo.utils;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.*;
+
+/**
+ * @author johnniang
+ * @date 3/20/19
+ */
+public class SlugUtilsTest {
+
+ @Test
+ public void makeSlugTest() {
+ String slugResult = SlugUtils.slugify("Hello World");
+
+ Assert.assertThat(slugResult, equalTo("hello-world"));
+ }
+}
\ No newline at end of file