diff --git a/pom.xml b/pom.xml index b21416366..7a53b326f 100755 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,6 @@ 0.0.4 1.18.4 3.6.3 - 1.0 4.4.2 4.0.1 7.2.18 @@ -120,13 +119,6 @@ ${ehcache.version} - - - rome - rome - ${rome.version} - - cn.hutool diff --git a/src/main/java/cc/ryanc/halo/Application.java b/src/main/java/cc/ryanc/halo/Application.java index f67e743eb..0f65eb888 100755 --- a/src/main/java/cc/ryanc/halo/Application.java +++ b/src/main/java/cc/ryanc/halo/Application.java @@ -5,6 +5,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.ApplicationContext; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; /** *
@@ -17,6 +18,7 @@ import org.springframework.context.ApplicationContext;
 @Slf4j
 @SpringBootApplication
 @EnableCaching
+@EnableJpaAuditing
 public class Application {
     public static void main(String[] args) {
         ApplicationContext context = SpringApplication.run(Application.class, args);
diff --git a/src/main/java/cc/ryanc/halo/config/WebMvcAutoConfiguration.java b/src/main/java/cc/ryanc/halo/config/WebMvcAutoConfiguration.java
index 621ffe815..27e4737c8 100644
--- a/src/main/java/cc/ryanc/halo/config/WebMvcAutoConfiguration.java
+++ b/src/main/java/cc/ryanc/halo/config/WebMvcAutoConfiguration.java
@@ -83,8 +83,7 @@ public class WebMvcAutoConfiguration implements WebMvcConfigurer {
         registry.addResourceHandler("/static/**")
                 .addResourceLocations("classpath:/static/");
         registry.addResourceHandler("/**")
-                .addResourceLocations("classpath:/templates/themes/")
-                .addResourceLocations("classpath:/robots.txt");
+                .addResourceLocations("classpath:/templates/themes/");
         registry.addResourceHandler("/upload/**")
                 .addResourceLocations("file:///" + System.getProperties().getProperty("user.home") + "/halo/upload/");
         registry.addResourceHandler("/favicon.ico")
diff --git a/src/main/java/cc/ryanc/halo/listener/StartedListener.java b/src/main/java/cc/ryanc/halo/listener/StartedListener.java
index afa2e249e..24a56b99b 100644
--- a/src/main/java/cc/ryanc/halo/listener/StartedListener.java
+++ b/src/main/java/cc/ryanc/halo/listener/StartedListener.java
@@ -1,6 +1,5 @@
 package cc.ryanc.halo.listener;
 
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.dto.Theme;
 import cc.ryanc.halo.model.enums.BlogPropertiesEnum;
 import cc.ryanc.halo.service.OptionsService;
@@ -19,6 +18,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import static cc.ryanc.halo.model.dto.HaloConst.*;
+
 /**
  * 
  *     应用启动完成后所执行的方法
@@ -72,7 +73,7 @@ public class StartedListener implements ApplicationListener options = optionsService.findAllOptions();
         if (options != null && !options.isEmpty()) {
-            HaloConst.OPTIONS = options;
+            OPTIONS = options;
         }
     }
 
@@ -80,10 +81,10 @@ public class StartedListener implements ApplicationListener themes = HaloUtils.getThemes();
         if (null != themes) {
-            HaloConst.THEMES = themes;
+            THEMES = themes;
         }
     }
 
@@ -227,6 +228,6 @@ public class StartedListener implements ApplicationListener");
         map.put("@(黑线)", "黑线.png");
         map.put("@(鼓掌)", "鼓掌.png");
-        HaloConst.OWO = map;
+        OWO = map;
     }
 }
diff --git a/src/main/java/cc/ryanc/halo/model/domain/Attachment.java b/src/main/java/cc/ryanc/halo/model/domain/Attachment.java
index 365e00c29..b7db4d068 100644
--- a/src/main/java/cc/ryanc/halo/model/domain/Attachment.java
+++ b/src/main/java/cc/ryanc/halo/model/domain/Attachment.java
@@ -1,11 +1,10 @@
 package cc.ryanc.halo.model.domain;
 
 import lombok.Data;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.Id;
-import javax.persistence.Table;
+import javax.persistence.*;
 import java.io.Serializable;
 import java.util.Date;
 
@@ -20,6 +19,7 @@ import java.util.Date;
 @Data
 @Entity
 @Table(name = "halo_attachment")
+@EntityListeners(AuditingEntityListener.class)
 public class Attachment implements Serializable {
 
     private static final long serialVersionUID = 3060117944880138064L;
@@ -59,6 +59,7 @@ public class Attachment implements Serializable {
     /**
      * 上传时间
      */
+    @CreatedDate
     private Date attachCreated;
 
     /**
diff --git a/src/main/java/cc/ryanc/halo/model/domain/Comment.java b/src/main/java/cc/ryanc/halo/model/domain/Comment.java
index b1c3c2c90..d1dd17b04 100644
--- a/src/main/java/cc/ryanc/halo/model/domain/Comment.java
+++ b/src/main/java/cc/ryanc/halo/model/domain/Comment.java
@@ -2,6 +2,8 @@ package cc.ryanc.halo.model.domain;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import lombok.Data;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 
 import javax.persistence.*;
 import javax.validation.constraints.Email;
@@ -21,6 +23,7 @@ import java.util.List;
 @Data
 @Entity
 @Table(name = "halo_comment")
+@EntityListeners(AuditingEntityListener.class)
 public class Comment implements Serializable {
 
     private static final long serialVersionUID = -6639021627094260505L;
@@ -72,6 +75,7 @@ public class Comment implements Serializable {
     /**
      * 评论时间
      */
+    @CreatedDate
     private Date commentDate;
 
     /**
diff --git a/src/main/java/cc/ryanc/halo/model/domain/Logs.java b/src/main/java/cc/ryanc/halo/model/domain/Logs.java
index be550604a..604f026f3 100644
--- a/src/main/java/cc/ryanc/halo/model/domain/Logs.java
+++ b/src/main/java/cc/ryanc/halo/model/domain/Logs.java
@@ -1,11 +1,10 @@
 package cc.ryanc.halo.model.domain;
 
 import lombok.Data;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.Id;
-import javax.persistence.Table;
+import javax.persistence.*;
 import java.io.Serializable;
 import java.util.Date;
 
@@ -20,6 +19,7 @@ import java.util.Date;
 @Data
 @Entity
 @Table(name = "halo_logs")
+@EntityListeners(AuditingEntityListener.class)
 public class Logs implements Serializable {
 
     private static final long serialVersionUID = -2571815432301283171L;
@@ -49,6 +49,7 @@ public class Logs implements Serializable {
     /**
      * 产生的时间
      */
+    @CreatedDate
     private Date logCreated;
 
     public Logs() {
diff --git a/src/main/java/cc/ryanc/halo/model/domain/Post.java b/src/main/java/cc/ryanc/halo/model/domain/Post.java
index 777b85641..4f54480e5 100755
--- a/src/main/java/cc/ryanc/halo/model/domain/Post.java
+++ b/src/main/java/cc/ryanc/halo/model/domain/Post.java
@@ -2,6 +2,9 @@ package cc.ryanc.halo.model.domain;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 
 import javax.persistence.*;
 import java.io.Serializable;
@@ -20,6 +23,7 @@ import java.util.List;
 @Data
 @Entity
 @Table(name = "halo_post")
+@EntityListeners(AuditingEntityListener.class)
 public class Post implements Serializable {
 
     private static final long serialVersionUID = -6019684584665869629L;
@@ -105,11 +109,13 @@ public class Post implements Serializable {
     /**
      * 发表日期
      */
+    @CreatedDate
     private Date postDate;
 
     /**
      * 最后一次更新时间
      */
+    @LastModifiedDate
     private Date postUpdate;
 
     /**
diff --git a/src/main/java/cc/ryanc/halo/repository/PostRepository.java b/src/main/java/cc/ryanc/halo/repository/PostRepository.java
index e5a10bb95..6c619b71d 100644
--- a/src/main/java/cc/ryanc/halo/repository/PostRepository.java
+++ b/src/main/java/cc/ryanc/halo/repository/PostRepository.java
@@ -5,7 +5,9 @@ import cc.ryanc.halo.model.domain.Post;
 import cc.ryanc.halo.model.domain.Tag;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.query.Param;
 
@@ -20,7 +22,7 @@ import java.util.List;
  * @author : RYAN0UP
  * @date : 2017/11/14
  */
-public interface PostRepository extends JpaRepository {
+public interface PostRepository extends JpaRepository, JpaSpecificationExecutor {
 
     /**
      * 查询前五条文章
@@ -50,6 +52,7 @@ public interface PostRepository extends JpaRepository {
      * @param pageable    pageable
      * @return Page
      */
+    @Deprecated
     Page findByPostTypeAndPostStatusAndPostTitleLikeOrPostTypeAndPostStatusAndPostContentLike(
             String postType0,
             Integer postStatus0,
diff --git a/src/main/java/cc/ryanc/halo/service/PostService.java b/src/main/java/cc/ryanc/halo/service/PostService.java
index 2a5bd1131..c83ab0329 100755
--- a/src/main/java/cc/ryanc/halo/service/PostService.java
+++ b/src/main/java/cc/ryanc/halo/service/PostService.java
@@ -6,6 +6,8 @@ import cc.ryanc.halo.model.domain.Tag;
 import cc.ryanc.halo.model.dto.Archive;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
+import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
 import org.springframework.web.bind.annotation.RequestParam;
 
 import java.util.Date;
@@ -70,9 +72,24 @@ public interface PostService {
      * @param postStatus 文章状态
      * @param pageable   分页信息
      * @return Page
+     * @see PostService#searchPostsBy(java.lang.String, java.lang.String, java.lang.Integer, org.springframework.data.domain.Pageable)
      */
+    @Deprecated
     Page searchPosts(String keyword, String postType, Integer postStatus, Pageable pageable);
 
+    /**
+     * 模糊查询文章
+     *
+     * @param keyword    关键词
+     * @param postType   文章类型
+     * @param postStatus 文章状态
+     * @param pageable   分页信息
+     * @return a page of posts
+     */
+    @NonNull
+    Page searchPostsBy(@Nullable String keyword, @Nullable String postType, @Nullable Integer postStatus, @NonNull Pageable pageable);
+
+
     /**
      * 根据文章状态查询 分页,用于后台管理
      *
@@ -248,22 +265,6 @@ public interface PostService {
      */
     Integer getCountByStatus(Integer status);
 
-    /**
-     * 生成rss
-     *
-     * @param posts posts
-     * @return String
-     */
-    String buildRss(List posts);
-
-    /**
-     * 生成sitemap
-     *
-     * @param posts posts
-     * @return String
-     */
-    String buildSiteMap(List posts);
-
     /**
      * 缓存阅读数
      *
diff --git a/src/main/java/cc/ryanc/halo/service/impl/AttachmentServiceImpl.java b/src/main/java/cc/ryanc/halo/service/impl/AttachmentServiceImpl.java
index 2431be8c8..dbf567331 100644
--- a/src/main/java/cc/ryanc/halo/service/impl/AttachmentServiceImpl.java
+++ b/src/main/java/cc/ryanc/halo/service/impl/AttachmentServiceImpl.java
@@ -1,7 +1,6 @@
 package cc.ryanc.halo.service.impl;
 
 import cc.ryanc.halo.model.domain.Attachment;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.dto.QiNiuPutSet;
 import cc.ryanc.halo.model.enums.AttachLocationEnum;
 import cc.ryanc.halo.model.enums.BlogPropertiesEnum;
@@ -42,6 +41,8 @@ import java.io.IOException;
 import java.nio.file.Paths;
 import java.util.*;
 
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+
 /**
  * 
  *     附件业务逻辑实现类
@@ -62,6 +63,7 @@ public class AttachmentServiceImpl implements AttachmentService {
      * 新增附件信息
      *
      * @param attachment attachment
+     *
      * @return Attachment
      */
     @Override
@@ -85,6 +87,7 @@ public class AttachmentServiceImpl implements AttachmentService {
      * 获取所有附件信息 分页
      *
      * @param pageable pageable
+     *
      * @return Page
      */
     @Override
@@ -96,6 +99,7 @@ public class AttachmentServiceImpl implements AttachmentService {
      * 根据附件id查询附件
      *
      * @param attachId attachId
+     *
      * @return Optional
      */
     @Override
@@ -107,6 +111,7 @@ public class AttachmentServiceImpl implements AttachmentService {
      * 根据编号移除附件
      *
      * @param attachId attachId
+     *
      * @return Attachment
      */
     @Override
@@ -122,12 +127,13 @@ public class AttachmentServiceImpl implements AttachmentService {
      *
      * @param file    file
      * @param request request
+     *
      * @return Map
      */
     @Override
     public Map upload(MultipartFile file, HttpServletRequest request) {
         Map resultMap;
-        String attachLoc = HaloConst.OPTIONS.get(BlogPropertiesEnum.ATTACH_LOC.getProp());
+        String attachLoc = OPTIONS.get(BlogPropertiesEnum.ATTACH_LOC.getProp());
         if (StrUtil.isEmpty(attachLoc)) {
             attachLoc = "server";
         }
@@ -153,6 +159,7 @@ public class AttachmentServiceImpl implements AttachmentService {
      *
      * @param file    file
      * @param request request
+     *
      * @return Map
      */
     @Override
@@ -244,6 +251,7 @@ public class AttachmentServiceImpl implements AttachmentService {
      *
      * @param file    file
      * @param request request
+     *
      * @return Map
      */
     @Override
@@ -252,11 +260,11 @@ public class AttachmentServiceImpl implements AttachmentService {
         try {
             final Configuration cfg = new Configuration(Zone.zone0());
             final String key = Md5Util.getMD5Checksum(file);
-            final String accessKey = HaloConst.OPTIONS.get("qiniu_access_key");
-            final String secretKey = HaloConst.OPTIONS.get("qiniu_secret_key");
-            final String domain = HaloConst.OPTIONS.get("qiniu_domain");
-            final String bucket = HaloConst.OPTIONS.get("qiniu_bucket");
-            final String smallUrl = HaloConst.OPTIONS.get("qiniu_small_url");
+            final String accessKey = OPTIONS.get("qiniu_access_key");
+            final String secretKey = OPTIONS.get("qiniu_secret_key");
+            final String domain = OPTIONS.get("qiniu_domain");
+            final String bucket = OPTIONS.get("qiniu_bucket");
+            final String smallUrl = OPTIONS.get("qiniu_small_url");
             if (StrUtil.isEmpty(accessKey) || StrUtil.isEmpty(secretKey) || StrUtil.isEmpty(domain) || StrUtil.isEmpty(bucket)) {
                 return resultMap;
             }
@@ -304,6 +312,7 @@ public class AttachmentServiceImpl implements AttachmentService {
      *
      * @param file    file
      * @param request request
+     *
      * @return Map
      */
     @Override
@@ -311,12 +320,12 @@ public class AttachmentServiceImpl implements AttachmentService {
         final Map resultMap = new HashMap<>(6);
         try {
             final String key = Md5Util.getMD5Checksum(file);
-            final String ossSrc = HaloConst.OPTIONS.get("upyun_oss_src");
-            final String ossPwd = HaloConst.OPTIONS.get("upyun_oss_pwd");
-            final String bucket = HaloConst.OPTIONS.get("upyun_oss_bucket");
-            final String domain = HaloConst.OPTIONS.get("upyun_oss_domain");
-            final String operator = HaloConst.OPTIONS.get("upyun_oss_operator");
-            final String smallUrl = HaloConst.OPTIONS.get("upyun_oss_small");
+            final String ossSrc = OPTIONS.get("upyun_oss_src");
+            final String ossPwd = OPTIONS.get("upyun_oss_pwd");
+            final String bucket = OPTIONS.get("upyun_oss_bucket");
+            final String domain = OPTIONS.get("upyun_oss_domain");
+            final String operator = OPTIONS.get("upyun_oss_operator");
+            final String smallUrl = OPTIONS.get("upyun_oss_small");
             if (StrUtil.isEmpty(ossSrc) || StrUtil.isEmpty(ossPwd) || StrUtil.isEmpty(domain) || StrUtil.isEmpty(bucket) || StrUtil.isEmpty(operator)) {
                 return resultMap;
             }
@@ -353,15 +362,16 @@ public class AttachmentServiceImpl implements AttachmentService {
      * 七牛云删除附件
      *
      * @param key key
+     *
      * @return boolean
      */
     @Override
     public boolean deleteQiNiuAttachment(String key) {
         boolean flag = true;
         final Configuration cfg = new Configuration(Zone.zone0());
-        final String accessKey = HaloConst.OPTIONS.get("qiniu_access_key");
-        final String secretKey = HaloConst.OPTIONS.get("qiniu_secret_key");
-        final String bucket = HaloConst.OPTIONS.get("qiniu_bucket");
+        final String accessKey = OPTIONS.get("qiniu_access_key");
+        final String secretKey = OPTIONS.get("qiniu_secret_key");
+        final String bucket = OPTIONS.get("qiniu_bucket");
         if (StrUtil.isEmpty(accessKey) || StrUtil.isEmpty(secretKey) || StrUtil.isEmpty(bucket)) {
             return false;
         }
@@ -381,15 +391,16 @@ public class AttachmentServiceImpl implements AttachmentService {
      * 又拍云删除附件
      *
      * @param fileName fileName
+     *
      * @return boolean
      */
     @Override
     public boolean deleteUpYunAttachment(String fileName) {
         boolean flag = true;
-        final String ossSrc = HaloConst.OPTIONS.get("upyun_oss_src");
-        final String ossPwd = HaloConst.OPTIONS.get("upyun_oss_pwd");
-        final String bucket = HaloConst.OPTIONS.get("upyun_oss_bucket");
-        final String operator = HaloConst.OPTIONS.get("upyun_oss_operator");
+        final String ossSrc = OPTIONS.get("upyun_oss_src");
+        final String ossPwd = OPTIONS.get("upyun_oss_pwd");
+        final String bucket = OPTIONS.get("upyun_oss_bucket");
+        final String operator = OPTIONS.get("upyun_oss_operator");
         if (StrUtil.isEmpty(ossSrc) || StrUtil.isEmpty(ossPwd) || StrUtil.isEmpty(bucket) || StrUtil.isEmpty(operator)) {
             return false;
         }
diff --git a/src/main/java/cc/ryanc/halo/service/impl/LogsServiceImpl.java b/src/main/java/cc/ryanc/halo/service/impl/LogsServiceImpl.java
index 078296b26..cf0cd042d 100644
--- a/src/main/java/cc/ryanc/halo/service/impl/LogsServiceImpl.java
+++ b/src/main/java/cc/ryanc/halo/service/impl/LogsServiceImpl.java
@@ -39,7 +39,6 @@ public class LogsServiceImpl implements LogsService {
         final Logs logs = new Logs();
         logs.setLogTitle(logTitle);
         logs.setLogContent(logContent);
-        logs.setLogCreated(new Date());
         logs.setLogIp(ServletUtil.getClientIP(request));
         logsRepository.save(logs);
     }
diff --git a/src/main/java/cc/ryanc/halo/service/impl/MailServiceImpl.java b/src/main/java/cc/ryanc/halo/service/impl/MailServiceImpl.java
index 56e69011f..3bbab106a 100644
--- a/src/main/java/cc/ryanc/halo/service/impl/MailServiceImpl.java
+++ b/src/main/java/cc/ryanc/halo/service/impl/MailServiceImpl.java
@@ -1,6 +1,5 @@
 package cc.ryanc.halo.service.impl;
 
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.enums.BlogPropertiesEnum;
 import cc.ryanc.halo.service.MailService;
 import cc.ryanc.halo.utils.HaloUtils;
@@ -15,6 +14,8 @@ import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
 import java.io.File;
 import java.util.Map;
 
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+
 /**
  * 
  *     邮件发送业务逻辑实现类
@@ -40,12 +41,12 @@ public class MailServiceImpl implements MailService {
     public void sendMail(String to, String subject, String content) {
         //配置邮件服务器
         HaloUtils.configMail(
-                HaloConst.OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_HOST.getProp()),
-                HaloConst.OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_USERNAME.getProp()),
-                HaloConst.OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_PASSWORD.getProp()));
+                OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_HOST.getProp()),
+                OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_USERNAME.getProp()),
+                OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_PASSWORD.getProp()));
         try {
             OhMyEmail.subject(subject)
-                    .from(HaloConst.OPTIONS.get(BlogPropertiesEnum.MAIL_FROM_NAME.getProp()))
+                    .from(OPTIONS.get(BlogPropertiesEnum.MAIL_FROM_NAME.getProp()))
                     .to(to)
                     .text(content)
                     .send();
@@ -66,15 +67,15 @@ public class MailServiceImpl implements MailService {
     public void sendTemplateMail(String to, String subject, Map content, String templateName) {
         //配置邮件服务器
         HaloUtils.configMail(
-                HaloConst.OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_HOST.getProp()),
-                HaloConst.OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_USERNAME.getProp()),
-                HaloConst.OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_PASSWORD.getProp()));
+                OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_HOST.getProp()),
+                OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_USERNAME.getProp()),
+                OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_PASSWORD.getProp()));
         StrBuilder text = new StrBuilder();
         try {
             final Template template = freeMarker.getConfiguration().getTemplate(templateName);
             text.append(FreeMarkerTemplateUtils.processTemplateIntoString(template, content));
             OhMyEmail.subject(subject)
-                    .from(HaloConst.OPTIONS.get(BlogPropertiesEnum.MAIL_FROM_NAME.getProp()))
+                    .from(OPTIONS.get(BlogPropertiesEnum.MAIL_FROM_NAME.getProp()))
                     .to(to)
                     .html(text.toString())
                     .send();
@@ -96,16 +97,16 @@ public class MailServiceImpl implements MailService {
     public void sendAttachMail(String to, String subject, Map content, String templateName, String attachSrc) {
         //配置邮件服务器
         HaloUtils.configMail(
-                HaloConst.OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_HOST.getProp()),
-                HaloConst.OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_USERNAME.getProp()),
-                HaloConst.OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_PASSWORD.getProp()));
+                OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_HOST.getProp()),
+                OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_USERNAME.getProp()),
+                OPTIONS.get(BlogPropertiesEnum.MAIL_SMTP_PASSWORD.getProp()));
         File file = new File(attachSrc);
         StrBuilder text = new StrBuilder();
         try {
             final Template template = freeMarker.getConfiguration().getTemplate(templateName);
             text.append(FreeMarkerTemplateUtils.processTemplateIntoString(template, content));
             OhMyEmail.subject(subject)
-                    .from(HaloConst.OPTIONS.get(BlogPropertiesEnum.MAIL_FROM_NAME.getProp()))
+                    .from(OPTIONS.get(BlogPropertiesEnum.MAIL_FROM_NAME.getProp()))
                     .to(to)
                     .html(text.toString())
                     .attach(file, file.getName())
diff --git a/src/main/java/cc/ryanc/halo/service/impl/PostServiceImpl.java b/src/main/java/cc/ryanc/halo/service/impl/PostServiceImpl.java
index e4fb1ab3d..ece6c6386 100755
--- a/src/main/java/cc/ryanc/halo/service/impl/PostServiceImpl.java
+++ b/src/main/java/cc/ryanc/halo/service/impl/PostServiceImpl.java
@@ -4,7 +4,6 @@ import cc.ryanc.halo.model.domain.Category;
 import cc.ryanc.halo.model.domain.Post;
 import cc.ryanc.halo.model.domain.Tag;
 import cc.ryanc.halo.model.dto.Archive;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.enums.BlogPropertiesEnum;
 import cc.ryanc.halo.model.enums.PostStatusEnum;
 import cc.ryanc.halo.model.enums.PostTypeEnum;
@@ -13,7 +12,6 @@ import cc.ryanc.halo.service.CategoryService;
 import cc.ryanc.halo.service.PostService;
 import cc.ryanc.halo.service.TagService;
 import cc.ryanc.halo.utils.HaloUtils;
-import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.HtmlUtil;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -22,12 +20,24 @@ import org.springframework.cache.annotation.CachePut;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.data.jpa.domain.Specifications;
+import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
 
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Optional;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import java.util.*;
+
+import static org.springframework.data.jpa.domain.Specification.where;
+
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+import static cc.ryanc.halo.model.dto.HaloConst.POSTS_VIEWS;
 
 /**
  * 
@@ -57,14 +67,15 @@ public class PostServiceImpl implements PostService {
      * 保存文章
      *
      * @param post Post
+     *
      * @return Post
      */
     @Override
     @CacheEvict(value = {POSTS_CACHE_NAME, COMMENTS_CACHE_NAME}, allEntries = true, beforeInvocation = true)
     public Post save(Post post) {
         int postSummary = 50;
-        if (StrUtil.isNotEmpty(HaloConst.OPTIONS.get(BlogPropertiesEnum.POST_SUMMARY.getProp()))) {
-            postSummary = Integer.parseInt(HaloConst.OPTIONS.get(BlogPropertiesEnum.POST_SUMMARY.getProp()));
+        if (StrUtil.isNotEmpty(OPTIONS.get(BlogPropertiesEnum.POST_SUMMARY.getProp()))) {
+            postSummary = Integer.parseInt(OPTIONS.get(BlogPropertiesEnum.POST_SUMMARY.getProp()));
         }
         final String summaryText = StrUtil.cleanBlank(HtmlUtil.cleanHtmlTag(post.getPostContent()));
         if (summaryText.length() > postSummary) {
@@ -73,7 +84,6 @@ public class PostServiceImpl implements PostService {
         } else {
             post.setPostSummary(summaryText);
         }
-        post.setPostUpdate(DateUtil.date());
         return postRepository.save(post);
     }
 
@@ -81,6 +91,7 @@ public class PostServiceImpl implements PostService {
      * 根据编号移除文章
      *
      * @param postId postId
+     *
      * @return Post
      */
     @Override
@@ -96,6 +107,7 @@ public class PostServiceImpl implements PostService {
      *
      * @param postId postId
      * @param status status
+     *
      * @return Post
      */
     @Override
@@ -130,6 +142,7 @@ public class PostServiceImpl implements PostService {
      * 获取文章列表 不分页
      *
      * @param postType post or page
+     *
      * @return List
      */
     @Override
@@ -145,11 +158,12 @@ public class PostServiceImpl implements PostService {
      * @param postType   文章类型
      * @param postStatus 文章状态
      * @param pageable   分页信息
+     *
      * @return Page
      */
     @Override
     public Page searchPosts(String keyword, String postType, Integer postStatus, Pageable pageable) {
-        Page posts = postRepository.findByPostTypeAndPostStatusAndPostTitleLikeOrPostTypeAndPostStatusAndPostContentLike(
+        return postRepository.findByPostTypeAndPostStatusAndPostTitleLikeOrPostTypeAndPostStatusAndPostContentLike(
                 postType,
                 postStatus,
                 "%" + keyword + "%",
@@ -157,13 +171,23 @@ public class PostServiceImpl implements PostService {
                 postStatus,
                 "%" + keyword + "%",
                 pageable
-        );
-        for (Post post : posts.getContent()) {
+        ).map(post -> {
             if (StrUtil.isNotEmpty(post.getPostPassword())) {
                 post.setPostSummary("该文章为加密文章");
             }
-        }
-        return posts;
+            return post;
+        });
+    }
+
+    @Override
+    public Page searchPostsBy(String keyword, String postType, Integer postStatus, Pageable pageable) {
+        return postRepository.findAll(buildSearchSepcification(keyword, postType, postStatus), pageable)
+                .map(post -> {
+                    if (StrUtil.isNotEmpty(post.getPostPassword())) {
+                        post.setPostSummary("该文章为加密文章");
+                    }
+                    return post;
+                });
     }
 
     /**
@@ -172,35 +196,35 @@ public class PostServiceImpl implements PostService {
      * @param status   0,1,2
      * @param postType post or page
      * @param pageable 分页信息
+     *
      * @return Page
      */
     @Override
     public Page findPostByStatus(Integer status, String postType, Pageable pageable) {
-        Page posts = postRepository.findPostsByPostStatusAndPostType(status, postType, pageable);
-        for (Post post : posts.getContent()) {
+        return postRepository.findPostsByPostStatusAndPostType(status, postType, pageable).map(post -> {
             if (StrUtil.isNotEmpty(post.getPostPassword())) {
                 post.setPostSummary("该文章为加密文章");
             }
-        }
-        return posts;
+            return post;
+        });
     }
 
     /**
      * 根据文章状态查询 分页,首页分页
      *
      * @param pageable pageable
+     *
      * @return Page
      */
     @Override
     @Cacheable(value = POSTS_CACHE_NAME, key = "'posts_page_'+#pageable.pageNumber")
     public Page findPostByStatus(Pageable pageable) {
-        Page posts = postRepository.findPostsByPostStatusAndPostType(PostStatusEnum.PUBLISHED.getCode(), PostTypeEnum.POST_TYPE_POST.getDesc(), pageable);
-        for (Post post : posts.getContent()) {
+        return postRepository.findPostsByPostStatusAndPostType(PostStatusEnum.PUBLISHED.getCode(), PostTypeEnum.POST_TYPE_POST.getDesc(), pageable).map(post -> {
             if (StrUtil.isNotEmpty(post.getPostPassword())) {
                 post.setPostSummary("该文章为加密文章");
             }
-        }
-        return posts;
+            return post;
+        });
     }
 
     /**
@@ -208,6 +232,7 @@ public class PostServiceImpl implements PostService {
      *
      * @param status   0,1,2
      * @param postType post or page
+     *
      * @return List
      */
     @Override
@@ -220,6 +245,7 @@ public class PostServiceImpl implements PostService {
      * 根据编号查询文章
      *
      * @param postId postId
+     *
      * @return Optional
      */
     @Override
@@ -231,6 +257,7 @@ public class PostServiceImpl implements PostService {
      * 根据编号和类型查询文章
      *
      * @param postId postId
+     *
      * @return Post
      */
     @Override
@@ -243,6 +270,7 @@ public class PostServiceImpl implements PostService {
      *
      * @param postUrl  路径
      * @param postType post or page
+     *
      * @return Post
      */
     @Override
@@ -266,6 +294,7 @@ public class PostServiceImpl implements PostService {
      * 获取下一篇文章 较新
      *
      * @param postDate postDate
+     *
      * @return Post
      */
     @Override
@@ -277,6 +306,7 @@ public class PostServiceImpl implements PostService {
      * 获取下一篇文章 较老
      *
      * @param postDate postDate
+     *
      * @return Post
      */
     @Override
@@ -329,6 +359,7 @@ public class PostServiceImpl implements PostService {
 
     /**
      * @return List
+     *
      * @Author Aquan
      * @Description 查询归档信息 返回所有文章
      * @Date 2019.1.4 11:16
@@ -355,6 +386,7 @@ public class PostServiceImpl implements PostService {
      *
      * @param year  year
      * @param month month
+     *
      * @return List
      */
     @Override
@@ -367,6 +399,7 @@ public class PostServiceImpl implements PostService {
      * 根据年份查询文章
      *
      * @param year year
+     *
      * @return List
      */
     @Override
@@ -381,57 +414,55 @@ public class PostServiceImpl implements PostService {
      * @param year     year year
      * @param month    month month
      * @param pageable pageable pageable
+     *
      * @return Page
      */
     @Override
     public Page findPostByYearAndMonth(String year, String month, Pageable pageable) {
-        Page posts = postRepository.findPostByYearAndMonth(year, month, null);
-        for (Post post : posts.getContent()) {
+        return postRepository.findPostByYearAndMonth(year, month, null).map(post -> {
             if (StrUtil.isNotEmpty(post.getPostPassword())) {
                 post.setPostSummary("该文章为加密文章");
             }
-        }
-        return posts;
+            return post;
+        });
     }
 
     /**
      * 根据分类目录查询文章
      *
      * @param category category
-     * @param status   status
      * @param pageable pageable
+     *
      * @return Page
      */
     @Override
     @CachePut(value = POSTS_CACHE_NAME, key = "'posts_category_'+#category.cateId+'_'+#pageable.pageNumber")
     public Page findPostByCategories(Category category, Pageable pageable) {
-        Page posts = postRepository.findPostByCategoriesAndPostStatus(category, PostStatusEnum.PUBLISHED.getCode(), pageable);
-        for (Post post : posts.getContent()) {
+        return postRepository.findPostByCategoriesAndPostStatus(category, PostStatusEnum.PUBLISHED.getCode(), pageable).map(post -> {
             if (StrUtil.isNotEmpty(post.getPostPassword())) {
                 post.setPostSummary("该文章为加密文章");
             }
-        }
-        return posts;
+            return post;
+        });
     }
 
     /**
      * 根据标签查询文章,分页
      *
      * @param tag      tag
-     * @param status   status
      * @param pageable pageable
+     *
      * @return Page
      */
     @Override
     @CachePut(value = POSTS_CACHE_NAME, key = "'posts_tag_'+#tag.tagId+'_'+#pageable.pageNumber")
     public Page findPostsByTags(Tag tag, Pageable pageable) {
-        Page posts = postRepository.findPostsByTagsAndPostStatus(tag, PostStatusEnum.PUBLISHED.getCode(), pageable);
-        for (Post post : posts.getContent()) {
+        return postRepository.findPostsByTagsAndPostStatus(tag, PostStatusEnum.PUBLISHED.getCode(), pageable).map(post -> {
             if (StrUtil.isNotEmpty(post.getPostPassword())) {
                 post.setPostSummary("该文章为加密文章");
             }
-        }
-        return posts;
+            return post;
+        });
     }
 
     /**
@@ -449,6 +480,7 @@ public class PostServiceImpl implements PostService {
      * 当前文章的相似文章
      *
      * @param post post
+     *
      * @return List
      */
     @Override
@@ -486,6 +518,7 @@ public class PostServiceImpl implements PostService {
      * 根据文章状态查询数量
      *
      * @param status 文章状态
+     *
      * @return 文章数量
      */
     @Override
@@ -493,34 +526,6 @@ public class PostServiceImpl implements PostService {
         return postRepository.countAllByPostStatusAndPostType(status, PostTypeEnum.POST_TYPE_POST.getDesc());
     }
 
-    /**
-     * 生成rss
-     *
-     * @param posts posts
-     * @return String
-     */
-    @Override
-    public String buildRss(List posts) {
-        String rss = "";
-        try {
-            rss = HaloUtils.getRss(posts);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        return rss;
-    }
-
-    /**
-     * 生成sitemap
-     *
-     * @param posts posts
-     * @return String
-     */
-    @Override
-    public String buildSiteMap(List posts) {
-        return HaloUtils.getSiteMap(posts);
-    }
-
     /**
      * 缓存阅读数
      *
@@ -528,10 +533,10 @@ public class PostServiceImpl implements PostService {
      */
     @Override
     public void cacheViews(Long postId) {
-        if (null != HaloConst.POSTS_VIEWS.get(postId)) {
-            HaloConst.POSTS_VIEWS.put(postId, HaloConst.POSTS_VIEWS.get(postId) + 1);
+        if (null != POSTS_VIEWS.get(postId)) {
+            POSTS_VIEWS.put(postId, POSTS_VIEWS.get(postId) + 1);
         } else {
-            HaloConst.POSTS_VIEWS.put(postId, 1L);
+            POSTS_VIEWS.put(postId, 1L);
         }
     }
 
@@ -541,6 +546,7 @@ public class PostServiceImpl implements PostService {
      * @param post     post
      * @param cateList cateList
      * @param tagList  tagList
+     *
      * @return Post Post
      */
     @Override
@@ -558,10 +564,58 @@ public class PostServiceImpl implements PostService {
      * 获取最近的文章
      *
      * @param limit 条数
+     *
      * @return List
      */
     @Override
     public List getRecentPosts(int limit) {
         return postRepository.getPostsByLimit(limit);
     }
+
+    @NonNull
+    private Specification buildSearchSepcification(@NonNull String keyword,
+                                                         @NonNull String postType,
+                                                         @NonNull Integer postStatus) {
+        return Specification.where(postTitleLike(keyword)).or(postContentLike(keyword)).and(postTypeEqual(postType)).and(postStatusEqual(postStatus));
+//        return (root, criteriaQuery, criteriaBuilder) -> {
+//            List predicates = new LinkedList<>();
+//
+//            if (StringUtils.hasText(keyword)) {
+//                predicates.add(criteriaBuilder.like(root.get("postContent"), keyword));
+//                predicates.add(criteriaBuilder.or(criteriaBuilder.like(root.get("postTitle"), keyword)));
+//            }
+//
+//            if (StringUtils.hasText(postType)) {
+//                predicates.add(criteriaBuilder.equal(root.get("postType"), postType));
+//            }
+//
+//            if (postStatus != null) {
+//                predicates.add(criteriaBuilder.equal(root.get("postStatus"), postStatus));
+//            }
+//
+//            return criteriaQuery.where(predicates.toArray(new Predicate[0])).getRestriction();
+//        };
+    }
+
+    private Specification postContentLike(@NonNull String keyword) {
+        Assert.hasText(keyword, "Keyword must not be blank");
+
+        return (root, criteriaQuery, criteriaBuilder) ->
+                criteriaBuilder.like(criteriaBuilder.lower(root.get("postContent")), "%" + keyword.toLowerCase() + "%");
+    }
+
+    private Specification postTitleLike(@NonNull String keyword) {
+        Assert.hasText(keyword, "Keyword must not be blank");
+
+        return (root, criteriaQuery, criteriaBuilder) ->
+                criteriaBuilder.like(criteriaBuilder.lower(root.get("postTitle")), "%" + keyword.toLowerCase() + "%");
+    }
+
+    private Specification postTypeEqual(@NonNull String postType) {
+        return (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.equal(root.get("postType"), postType);
+    }
+
+    private Specification postStatusEqual(@NonNull Integer postStatus) {
+        return (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.equal(root.get("postStatus"), postStatus);
+    }
 }
diff --git a/src/main/java/cc/ryanc/halo/task/PostSyncTask.java b/src/main/java/cc/ryanc/halo/task/PostSyncTask.java
index bb01797eb..0a40111fc 100644
--- a/src/main/java/cc/ryanc/halo/task/PostSyncTask.java
+++ b/src/main/java/cc/ryanc/halo/task/PostSyncTask.java
@@ -1,11 +1,12 @@
 package cc.ryanc.halo.task;
 
 import cc.ryanc.halo.model.domain.Post;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.service.PostService;
 import cc.ryanc.halo.utils.SpringUtil;
 import lombok.extern.slf4j.Slf4j;
 
+import static cc.ryanc.halo.model.dto.HaloConst.POSTS_VIEWS;
+
 /**
  * @author : RYAN0UP
  * @date : 2018/12/5
@@ -20,15 +21,15 @@ public class PostSyncTask {
         final PostService postService = SpringUtil.getBean(PostService.class);
         Post post = null;
         int count = 0;
-        for (Long key : HaloConst.POSTS_VIEWS.keySet()) {
+        for (Long key : POSTS_VIEWS.keySet()) {
             post = postService.findByPostId(key).orElse(null);
             if (null != post) {
-                post.setPostViews(post.getPostViews() + HaloConst.POSTS_VIEWS.get(key));
+                post.setPostViews(post.getPostViews() + POSTS_VIEWS.get(key));
                 postService.save(post);
                 count++;
             }
         }
         log.info("The number of visits to {} posts has been updated", count);
-        HaloConst.POSTS_VIEWS.clear();
+        POSTS_VIEWS.clear();
     }
 }
diff --git a/src/main/java/cc/ryanc/halo/utils/HaloUtils.java b/src/main/java/cc/ryanc/halo/utils/HaloUtils.java
index d11273f71..381df8fe2 100755
--- a/src/main/java/cc/ryanc/halo/utils/HaloUtils.java
+++ b/src/main/java/cc/ryanc/halo/utils/HaloUtils.java
@@ -1,20 +1,11 @@
 package cc.ryanc.halo.utils;
 
-import cc.ryanc.halo.model.domain.Post;
 import cc.ryanc.halo.model.dto.BackupDto;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.dto.Theme;
-import cc.ryanc.halo.model.enums.BlogPropertiesEnum;
 import cc.ryanc.halo.model.enums.CommonParamsEnum;
-import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.text.StrBuilder;
 import cn.hutool.core.util.StrUtil;
-import com.sun.syndication.feed.rss.Channel;
-import com.sun.syndication.feed.rss.Content;
-import com.sun.syndication.feed.rss.Item;
-import com.sun.syndication.io.FeedException;
-import com.sun.syndication.io.WireFeedOutput;
 import io.github.biezhi.ome.OhMyEmail;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.util.Assert;
@@ -284,84 +275,6 @@ public class HaloUtils {
         }
     }
 
-    /**
-     * 生成rss
-     *
-     * @param posts posts
-     *
-     * @return String
-     *
-     * @throws FeedException FeedException
-     */
-    public static String getRss(List posts) throws FeedException {
-        Assert.notEmpty(posts, "posts must not be empty");
-
-        final Channel channel = new Channel("rss_2.0");
-        if (null == HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_TITLE.getProp())) {
-            channel.setTitle("");
-        } else {
-            channel.setTitle(HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_TITLE.getProp()));
-        }
-        if (null == HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp())) {
-            channel.setLink("");
-        } else {
-            channel.setLink(HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp()));
-        }
-        if (null == HaloConst.OPTIONS.get(BlogPropertiesEnum.SEO_DESC.getProp())) {
-            channel.setDescription("");
-        } else {
-            channel.setDescription(HaloConst.OPTIONS.get(BlogPropertiesEnum.SEO_DESC.getProp()));
-        }
-        channel.setLanguage("zh-CN");
-        final List items = new ArrayList<>();
-        for (Post post : posts) {
-            final Item item = new Item();
-            item.setTitle(post.getPostTitle());
-            final Content content = new Content();
-            String value = post.getPostContent();
-            final char[] xmlChar = value.toCharArray();
-            for (int i = 0; i < xmlChar.length; ++i) {
-                if (xmlChar[i] > 0xFFFD) {
-                    xmlChar[i] = ' ';
-                } else if (xmlChar[i] < 0x20 && xmlChar[i] != 't' & xmlChar[i] != 'n' & xmlChar[i] != 'r') {
-                    xmlChar[i] = ' ';
-                }
-            }
-            value = new String(xmlChar);
-            content.setValue(value);
-            item.setContent(content);
-            item.setLink(HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp()) + "/archives/" + post.getPostUrl());
-            item.setPubDate(post.getPostDate());
-            items.add(item);
-        }
-        channel.setItems(items);
-        final WireFeedOutput out = new WireFeedOutput();
-        return out.outputString(channel);
-    }
-
-    /**
-     * 获取sitemap
-     *
-     * @param posts posts
-     *
-     * @return String
-     */
-    public static String getSiteMap(List posts) {
-        Assert.notEmpty(posts, "post mut not be empty");
-        final StrBuilder head = new StrBuilder("\n");
-        final StrBuilder urlBody = new StrBuilder();
-        final String urlPath = HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp()) + "/archives/";
-        for (Post post : posts) {
-            urlBody.append("");
-            urlBody.append(urlPath);
-            urlBody.append(post.getPostUrl());
-            urlBody.append("");
-            urlBody.append(DateUtil.format(post.getPostDate(), "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"));
-            urlBody.append("");
-        }
-        return head.append(urlBody).append("").toString();
-    }
-
     /**
      * 配置邮件
      *
diff --git a/src/main/java/cc/ryanc/halo/utils/OwoUtil.java b/src/main/java/cc/ryanc/halo/utils/OwoUtil.java
index ac08b31e8..3e527eb1e 100644
--- a/src/main/java/cc/ryanc/halo/utils/OwoUtil.java
+++ b/src/main/java/cc/ryanc/halo/utils/OwoUtil.java
@@ -1,8 +1,9 @@
 package cc.ryanc.halo.utils;
 
-import cc.ryanc.halo.model.dto.HaloConst;
 import lombok.extern.slf4j.Slf4j;
 
+import static cc.ryanc.halo.model.dto.HaloConst.OWO;
+
 /**
  * 
  *     OwO表情工具类
@@ -18,11 +19,12 @@ public class OwoUtil {
      * 将表情标志转化为图片地址
      *
      * @param mark 表情标志
+     *
      * @return 表情图片地址
      */
     public static String markToImg(String mark) {
-        for (String key : HaloConst.OWO.keySet()) {
-            mark = mark.replace(key, HaloConst.OWO.get(key));
+        for (String key : OWO.keySet()) {
+            mark = mark.replace(key, OWO.get(key));
         }
         return mark;
     }
diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/AdminController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/AdminController.java
index 6069aabbb..f853a323c 100755
--- a/src/main/java/cc/ryanc/halo/web/controller/admin/AdminController.java
+++ b/src/main/java/cc/ryanc/halo/web/controller/admin/AdminController.java
@@ -1,7 +1,6 @@
 package cc.ryanc.halo.web.controller.admin;
 
 import cc.ryanc.halo.model.domain.*;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.dto.JsonResult;
 import cc.ryanc.halo.model.dto.LogsRecord;
 import cc.ryanc.halo.model.enums.*;
@@ -20,9 +19,9 @@ import cn.hutool.http.HtmlUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 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.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.*;
@@ -33,6 +32,9 @@ import javax.servlet.http.HttpSession;
 import java.io.IOException;
 import java.util.*;
 
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+import static cc.ryanc.halo.model.dto.HaloConst.USER_SESSION_KEY;
+
 /**
  * 
  *     后台首页控制器
@@ -77,7 +79,6 @@ public class AdminController extends BaseController {
      * 请求后台页面
      *
      * @param model   model
-     * @param session session
      * @return 模板路径admin/admin_index
      */
     @GetMapping(value = {"", "/index"})
@@ -107,7 +108,7 @@ public class AdminController extends BaseController {
         model.addAttribute("postViewsSum", postViewsSum);
 
         //成立天数
-        final Date blogStart = DateUtil.parse(HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_START.getProp()));
+        final Date blogStart = DateUtil.parse(OPTIONS.get(BlogPropertiesEnum.BLOG_START.getProp()));
         final long hadDays = DateUtil.between(blogStart, DateUtil.date(), DateUnit.DAY);
         model.addAttribute("hadDays", hadDays);
         return "admin/admin_index";
@@ -117,11 +118,12 @@ public class AdminController extends BaseController {
      * 处理跳转到登录页的请求
      *
      * @param session session
+     *
      * @return 模板路径admin/admin_login
      */
     @GetMapping(value = "/login")
     public String login(HttpSession session) {
-        final User user = (User) session.getAttribute(HaloConst.USER_SESSION_KEY);
+        final User user = (User) session.getAttribute(USER_SESSION_KEY);
         //如果session存在,跳转到后台首页
         if (null != user) {
             return "redirect:/admin";
@@ -135,6 +137,7 @@ public class AdminController extends BaseController {
      * @param loginName 登录名:邮箱/用户名
      * @param loginPwd  loginPwd 密码
      * @param session   session session
+     *
      * @return JsonResult JsonResult
      */
     @PostMapping(value = "/getLogin")
@@ -163,7 +166,7 @@ public class AdminController extends BaseController {
         userService.updateUserLoginLast(DateUtil.date());
         //判断User对象是否相等
         if (ObjectUtil.equal(aUser, user)) {
-            session.setAttribute(HaloConst.USER_SESSION_KEY, aUser);
+            session.setAttribute(USER_SESSION_KEY, aUser);
             //重置用户的登录状态为正常
             userService.updateUserNormal();
             logsService.save(LogsRecord.LOGIN, LogsRecord.LOGIN_SUCCESS, request);
@@ -186,12 +189,13 @@ public class AdminController extends BaseController {
      * 退出登录 销毁session
      *
      * @param session session
+     *
      * @return 重定向到/admin/login
      */
     @GetMapping(value = "/logOut")
     public String logOut(HttpSession session) {
-        final User user = (User) session.getAttribute(HaloConst.USER_SESSION_KEY);
-        session.removeAttribute(HaloConst.USER_SESSION_KEY);
+        final User user = (User) session.getAttribute(USER_SESSION_KEY);
+        session.removeAttribute(USER_SESSION_KEY);
         logsService.save(LogsRecord.LOGOUT, user.getUserName(), request);
         log.info("User {} has logged out", user.getUserName());
         return "redirect:/admin/login";
@@ -201,16 +205,11 @@ public class AdminController extends BaseController {
      * 查看所有日志
      *
      * @param model model model
-     * @param page  page 当前页码
-     * @param size  size 每页条数
      * @return 模板路径admin/widget/_logs-all
      */
     @GetMapping(value = "/logs")
-    public String logs(Model model,
-                       @RequestParam(value = "page", defaultValue = "0") Integer page,
-                       @RequestParam(value = "size", defaultValue = "10") Integer size) {
+    public String logs(Model model, @PageableDefault Pageable pageable) {
         final Sort sort = new Sort(Sort.Direction.DESC, "logId");
-        final Pageable pageable = PageRequest.of(page, size, sort);
         final Page logs = logsService.findAll(pageable);
         model.addAttribute("logs", logs);
         return "admin/widget/_logs-all";
@@ -279,6 +278,7 @@ public class AdminController extends BaseController {
      *
      * @param file    file
      * @param request request
+     *
      * @return JsonResult
      */
     @PostMapping(value = "/tools/markdownImport")
@@ -286,7 +286,7 @@ public class AdminController extends BaseController {
     public JsonResult markdownImport(@RequestParam("file") MultipartFile file,
                                      HttpServletRequest request,
                                      HttpSession session) throws IOException {
-        final User user = (User) session.getAttribute(HaloConst.USER_SESSION_KEY);
+        final User user = (User) session.getAttribute(USER_SESSION_KEY);
         final String markdown = IoUtil.read(file.getInputStream(), "UTF-8");
         final String content = MarkdownUtils.renderMarkdown(markdown);
         final Map> frontMatters = MarkdownUtils.getFrontMatter(markdown);
diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/AttachmentController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/AttachmentController.java
index e41d2f94d..389558198 100755
--- a/src/main/java/cc/ryanc/halo/web/controller/admin/AttachmentController.java
+++ b/src/main/java/cc/ryanc/halo/web/controller/admin/AttachmentController.java
@@ -8,14 +8,14 @@ import cc.ryanc.halo.model.enums.ResultCodeEnum;
 import cc.ryanc.halo.service.AttachmentService;
 import cc.ryanc.halo.service.LogsService;
 import cc.ryanc.halo.utils.LocaleMessageUtil;
-import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.text.StrBuilder;
 import cn.hutool.core.util.StrUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 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.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.*;
@@ -55,15 +55,11 @@ public class AttachmentController {
      * 复印件列表
      *
      * @param model model
-     *
      * @return 模板路径admin/admin_attachment
      */
     @GetMapping
     public String attachments(Model model,
-                              @RequestParam(value = "page", defaultValue = "0") Integer page,
-                              @RequestParam(value = "size", defaultValue = "18") Integer size) {
-        final Sort sort = new Sort(Sort.Direction.DESC, "attachId");
-        final Pageable pageable = PageRequest.of(page, size, sort);
+                              @PageableDefault(size = 18, sort = "attachId", direction = Sort.Direction.DESC) Pageable pageable) {
         final Page attachments = attachmentService.findAll(pageable);
         model.addAttribute("attachments", attachments);
         return "admin/admin_attachment";
@@ -73,17 +69,13 @@ public class AttachmentController {
      * 跳转选择附件页面
      *
      * @param model model
-     * @param page  page 当前页码
-     *
      * @return 模板路径admin/widget/_attachment-select
      */
     @GetMapping(value = "/select")
     public String selectAttachment(Model model,
-                                   @RequestParam(value = "page", defaultValue = "0") Integer page,
+                                   @PageableDefault(size = 18, sort = "attachId", direction = Sort.Direction.DESC) Pageable pageable,
                                    @RequestParam(value = "id", defaultValue = "none") String id,
                                    @RequestParam(value = "type", defaultValue = "normal") String type) {
-        final Sort sort = new Sort(Sort.Direction.DESC, "attachId");
-        final Pageable pageable = PageRequest.of(page, 18, sort);
         final Page attachments = attachmentService.findAll(pageable);
         model.addAttribute("attachments", attachments);
         model.addAttribute("id", id);
@@ -109,7 +101,6 @@ public class AttachmentController {
      *
      * @param file    file
      * @param request request
-     *
      * @return Map
      */
     @PostMapping(value = "/upload", produces = {"application/json;charset=UTF-8"})
@@ -133,7 +124,6 @@ public class AttachmentController {
                 attachment.setAttachSmallPath(resultMap.get("smallPath"));
                 attachment.setAttachType(file.getContentType());
                 attachment.setAttachSuffix(resultMap.get("suffix"));
-                attachment.setAttachCreated(DateUtil.date());
                 attachment.setAttachSize(resultMap.get("size"));
                 attachment.setAttachWh(resultMap.get("wh"));
                 attachment.setAttachLocation(resultMap.get("location"));
@@ -160,7 +150,6 @@ public class AttachmentController {
      *
      * @param model    model
      * @param attachId 附件编号
-     *
      * @return 模板路径admin/widget/_attachment-detail
      */
     @GetMapping(value = "/attachment")
@@ -175,55 +164,52 @@ public class AttachmentController {
      *
      * @param attachId 附件编号
      * @param request  request
-     *
      * @return JsonResult
      */
     @GetMapping(value = "/remove")
     @ResponseBody
     public JsonResult removeAttachment(@RequestParam("attachId") Long attachId,
                                        HttpServletRequest request) {
-        Optional attachment = attachmentService.findByAttachId(attachId);
-        String attachLocation = attachment.get().getAttachLocation();
-        String delFileName = attachment.get().getAttachName();
+        final Attachment attachment = attachmentService.findByAttachId(attachId).orElse(new Attachment());
+        final String attachLocation = attachment.getAttachLocation();
+        final String attachName = attachment.getAttachName();
+        final String attachPath = attachment.getAttachPath();
         boolean flag = true;
         try {
-            //删除数据库中的内容
-            attachmentService.remove(attachId);
             if (attachLocation != null) {
                 if (attachLocation.equals(SERVER.getDesc())) {
-                    String delSmallFileName = delFileName.substring(0, delFileName.lastIndexOf('.')) + "_small" + attachment.get().getAttachSuffix();
-                    //删除文件
-                    String userPath = System.getProperties().getProperty("user.home") + "/halo";
-                    File mediaPath = new File(userPath, attachment.get().getAttachPath().substring(0, attachment.get().getAttachPath().lastIndexOf('/')));
-                    File delFile = new File(new StringBuffer(mediaPath.getAbsolutePath()).append("/").append(delFileName).toString());
-                    File delSmallFile = new File(new StringBuffer(mediaPath.getAbsolutePath()).append("/").append(delSmallFileName).toString());
+                    StrBuilder userPath = new StrBuilder(System.getProperties().getProperty("user.home"));
+                    userPath.append("/halo");
+                    //图片物理地址
+                    StrBuilder delPath = new StrBuilder(userPath);
+                    delPath.append(attachPath);
+                    //缩略图物理地址
+                    StrBuilder delSmallPath = new StrBuilder(userPath);
+                    delSmallPath.append(attachment.getAttachSmallPath());
+                    File delFile = new File(delPath.toString());
+                    File delSmallFile = new File(delSmallPath.toString());
                     if (delFile.exists() && delFile.isFile()) {
                         flag = delFile.delete() && delSmallFile.delete();
                     }
                 } else if (attachLocation.equals(QINIU.getDesc())) {
-                    //七牛删除
-                    String attachPath = attachment.get().getAttachPath();
                     String key = attachPath.substring(attachPath.lastIndexOf("/") + 1);
                     flag = attachmentService.deleteQiNiuAttachment(key);
                 } else if (attachLocation.equals(UPYUN.getDesc())) {
-                    //又拍删除
-                    String attachPath = attachment.get().getAttachPath();
                     String fileName = attachPath.substring(attachPath.lastIndexOf("/") + 1);
                     flag = attachmentService.deleteUpYunAttachment(fileName);
-                } else {
-                    //..
                 }
             }
             if (flag) {
-                log.info("Delete file {} successfully!", delFileName);
-                logsService.save(LogsRecord.REMOVE_FILE, delFileName, request);
+                attachmentService.remove(attachId);
+                log.info("Delete file {} successfully!", attachName);
+                logsService.save(LogsRecord.REMOVE_FILE, attachName, request);
             } else {
-                log.error("Deleting attachment {} failed!", delFileName);
+                log.error("Deleting attachment {} failed!", attachName);
                 return new JsonResult(ResultCodeEnum.FAIL.getCode(), localeMessageUtil.getMessage("code.admin.common.delete-failed"));
             }
         } catch (Exception e) {
             e.printStackTrace();
-            log.error("Deleting attachment {} failed: {}", delFileName, e.getMessage());
+            log.error("Deleting attachment {} failed: {}", attachName, e.getMessage());
             return new JsonResult(ResultCodeEnum.FAIL.getCode(), localeMessageUtil.getMessage("code.admin.common.delete-failed"));
         }
         return new JsonResult(ResultCodeEnum.SUCCESS.getCode(), localeMessageUtil.getMessage("code.admin.common.delete-success"));
diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/BackupController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/BackupController.java
index 4162489ba..414b2f589 100644
--- a/src/main/java/cc/ryanc/halo/web/controller/admin/BackupController.java
+++ b/src/main/java/cc/ryanc/halo/web/controller/admin/BackupController.java
@@ -5,7 +5,6 @@ import cc.ryanc.halo.model.domain.Post;
 import cc.ryanc.halo.model.domain.Tag;
 import cc.ryanc.halo.model.domain.User;
 import cc.ryanc.halo.model.dto.BackupDto;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.dto.JsonResult;
 import cc.ryanc.halo.model.enums.*;
 import cc.ryanc.halo.service.MailService;
@@ -34,6 +33,9 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+import static cc.ryanc.halo.model.dto.HaloConst.USER_SESSION_KEY;
+
 /**
  * 
  *     后台备份控制器
@@ -170,14 +172,14 @@ public class BackupController {
                 content.append("date: ").append(post.getPostDate()).append("\n");
                 content.append("updated: ").append(post.getPostUpdate()).append("\n");
                 content.append("thumbnail: ").append(post.getPostThumbnail()).append("\n");
-                if(post.getTags().size()>0){
+                if (post.getTags().size() > 0) {
                     content.append("tags:").append("\n");
                     final List tags = post.getTags();
                     for (Tag tag : tags) {
                         content.append("  - ").append(tag.getTagName()).append("\n");
                     }
                 }
-                if(post.getCategories().size()>0){
+                if (post.getCategories().size() > 0) {
                     content.append("categories:").append("\n");
                     final List categories = post.getCategories();
                     for (Category category : categories) {
@@ -236,11 +238,11 @@ public class BackupController {
                                   @RequestParam("type") String type,
                                   HttpSession session) {
         final String srcPath = System.getProperties().getProperty("user.home") + "/halo/backup/" + type + "/" + fileName;
-        final User user = (User) session.getAttribute(HaloConst.USER_SESSION_KEY);
+        final User user = (User) session.getAttribute(USER_SESSION_KEY);
         if (null == user.getUserEmail() || StrUtil.isEmpty(user.getUserEmail())) {
             return new JsonResult(ResultCodeEnum.FAIL.getCode(), localeMessageUtil.getMessage("code.admin.backup.no-email"));
         }
-        if (StrUtil.equals(HaloConst.OPTIONS.get(BlogPropertiesEnum.SMTP_EMAIL_ENABLE.getProp()), TrueFalseEnum.FALSE.getDesc())) {
+        if (StrUtil.equals(OPTIONS.get(BlogPropertiesEnum.SMTP_EMAIL_ENABLE.getProp()), TrueFalseEnum.FALSE.getDesc())) {
             return new JsonResult(ResultCodeEnum.FAIL.getCode(), localeMessageUtil.getMessage("code.admin.common.no-post"));
         }
         new EmailToAdmin(srcPath, user).start();
diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/CommentController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/CommentController.java
index 675929a2d..d919b2979 100755
--- a/src/main/java/cc/ryanc/halo/web/controller/admin/CommentController.java
+++ b/src/main/java/cc/ryanc/halo/web/controller/admin/CommentController.java
@@ -3,7 +3,6 @@ package cc.ryanc.halo.web.controller.admin;
 import cc.ryanc.halo.model.domain.Comment;
 import cc.ryanc.halo.model.domain.Post;
 import cc.ryanc.halo.model.domain.User;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.dto.JsonResult;
 import cc.ryanc.halo.model.enums.*;
 import cc.ryanc.halo.service.CommentService;
@@ -11,7 +10,6 @@ import cc.ryanc.halo.service.MailService;
 import cc.ryanc.halo.service.PostService;
 import cc.ryanc.halo.utils.OwoUtil;
 import cc.ryanc.halo.web.controller.core.BaseController;
-import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.lang.Validator;
 import cn.hutool.core.text.StrBuilder;
 import cn.hutool.core.util.StrUtil;
@@ -24,6 +22,7 @@ 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.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.*;
@@ -33,6 +32,9 @@ import javax.servlet.http.HttpSession;
 import java.util.HashMap;
 import java.util.Map;
 
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+import static cc.ryanc.halo.model.dto.HaloConst.USER_SESSION_KEY;
+
 /**
  * 
  *     后台评论管理控制器
@@ -60,18 +62,12 @@ public class CommentController extends BaseController {
      *
      * @param model  model
      * @param status status 评论状态
-     * @param page   page 当前页码
-     * @param size   size 每页显示条数
-     *
      * @return 模板路径admin/admin_comment
      */
     @GetMapping
     public String comments(Model model,
-                           @RequestParam(value = "status", defaultValue = "0") Integer status,
-                           @RequestParam(value = "page", defaultValue = "0") Integer page,
-                           @RequestParam(value = "size", defaultValue = "10") Integer size) {
-        final Sort sort = new Sort(Sort.Direction.DESC, "commentDate");
-        final Pageable pageable = PageRequest.of(page, size, sort);
+                           @PageableDefault(sort = "commentDate", direction = Sort.Direction.DESC) Pageable pageable,
+                           @RequestParam(value = "status", defaultValue = "0") Integer status) {
         final Page comments = commentService.findAll(status, pageable);
         model.addAttribute("comments", comments);
         model.addAttribute("publicCount", commentService.getCountByStatus(CommentStatusEnum.PUBLISHED.getCode()));
@@ -86,19 +82,18 @@ public class CommentController extends BaseController {
      *
      * @param commentId 评论编号
      * @param status    评论状态
-     *
      * @return 重定向到/admin/comments
      */
     @GetMapping(value = "/throw")
     public String moveToTrash(@RequestParam("commentId") Long commentId,
                               @RequestParam("status") String status,
-                              @RequestParam(value = "page", defaultValue = "0") Integer page) {
+                              @PageableDefault Pageable pageable) {
         try {
             commentService.updateCommentStatus(commentId, CommentStatusEnum.RECYCLE.getCode());
         } catch (Exception e) {
             log.error("Delete comment failed: {}", e.getMessage());
         }
-        return "redirect:/admin/comments?status=" + status + "&page=" + page;
+        return "redirect:/admin/comments?status=" + status + "&page=" + pageable.getPageNumber();
     }
 
     /**
@@ -107,7 +102,6 @@ public class CommentController extends BaseController {
      * @param commentId 评论编号
      * @param status    评论状态
      * @param session   session
-     *
      * @return 重定向到/admin/comments
      */
     @GetMapping(value = "/revert")
@@ -116,7 +110,7 @@ public class CommentController extends BaseController {
                                 HttpSession session) {
         final Comment comment = commentService.updateCommentStatus(commentId, CommentStatusEnum.PUBLISHED.getCode());
         final Post post = comment.getPost();
-        final User user = (User) session.getAttribute(HaloConst.USER_SESSION_KEY);
+        final User user = (User) session.getAttribute(USER_SESSION_KEY);
 
         //判断是否启用邮件服务
         new NoticeToAuthor(comment, post, user, status).start();
@@ -128,20 +122,18 @@ public class CommentController extends BaseController {
      *
      * @param commentId commentId 评论编号
      * @param status    status 评论状态
-     * @param page      当前页码
-     *
      * @return string 重定向到/admin/comments
      */
     @GetMapping(value = "/remove")
     public String moveToAway(@RequestParam("commentId") Long commentId,
                              @RequestParam("status") Integer status,
-                             @RequestParam(value = "page", defaultValue = "0") Integer page) {
+                             @PageableDefault Pageable pageable) {
         try {
             commentService.remove(commentId);
         } catch (Exception e) {
             log.error("Delete comment failed: {}", e.getMessage());
         }
-        return "redirect:/admin/comments?status=" + status + "&page=" + page;
+        return "redirect:/admin/comments?status=" + status + "&page=" + pageable.getPageNumber();
     }
 
 
@@ -150,7 +142,6 @@ public class CommentController extends BaseController {
      *
      * @param commentId      被回复的评论
      * @param commentContent 回复的内容
-     *
      * @return JsonResult
      */
     @PostMapping(value = "/reply")
@@ -165,7 +156,7 @@ public class CommentController extends BaseController {
             final Post post = postService.findByPostId(postId).orElse(new Post());
 
             //博主信息
-            final User user = (User) session.getAttribute(HaloConst.USER_SESSION_KEY);
+            final User user = (User) session.getAttribute(USER_SESSION_KEY);
 
             //被回复的评论
             final Comment lastComment = commentService.findCommentById(commentId).orElse(new Comment());
@@ -179,10 +170,9 @@ public class CommentController extends BaseController {
             comment.setPost(post);
             comment.setCommentAuthor(user.getUserDisplayName());
             comment.setCommentAuthorEmail(user.getUserEmail());
-            comment.setCommentAuthorUrl(HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp()));
+            comment.setCommentAuthorUrl(OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp()));
             comment.setCommentAuthorIp(ServletUtil.getClientIP(request));
             comment.setCommentAuthorAvatarMd5(SecureUtil.md5(user.getUserEmail()));
-            comment.setCommentDate(DateUtil.date());
 
             final StrBuilder buildContent = new StrBuilder("@");
+                buildContent.append(lastComment.getCommentAuthor());
+                buildContent.append(" ");
+                buildContent.append(OwoUtil.markToImg(HtmlUtil.escape(comment.getCommentContent()).replace("<br/>", "
"))); + comment.setCommentContent(buildContent.toString()); + } else { + //将评论内容的字符专为安全字符 + comment.setCommentContent(OwoUtil.markToImg(HtmlUtil.escape(comment.getCommentContent()).replace("<br/>", "
"))); + } + if (StrUtil.isNotEmpty(comment.getCommentAuthorUrl())) { + comment.setCommentAuthorUrl(URLUtil.normalize(comment.getCommentAuthorUrl())); + } + commentService.save(comment); + if (StrUtil.equals(OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NEED_CHECK.getProp()), TrueFalseEnum.TRUE.getDesc()) || OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NEED_CHECK.getProp()) == null) { + return new JsonResult(ResponseStatusEnum.SUCCESS.getCode(), "你的评论已经提交,待博主审核之后可显示。"); + } else { + return new JsonResult(ResponseStatusEnum.SUCCESS.getCode(), "你的评论已经提交,刷新后即可显示。"); + } + } catch (Exception e) { + return new JsonResult(ResponseStatusEnum.ERROR.getCode(), "评论失败!"); + } + } } diff --git a/src/main/java/cc/ryanc/halo/web/controller/api/ApiMetaWeBlog.java b/src/main/java/cc/ryanc/halo/web/controller/api/ApiMetaWeBlog.java index 71a94def6..2ca7c40d9 100644 --- a/src/main/java/cc/ryanc/halo/web/controller/api/ApiMetaWeBlog.java +++ b/src/main/java/cc/ryanc/halo/web/controller/api/ApiMetaWeBlog.java @@ -3,7 +3,6 @@ package cc.ryanc.halo.web.controller.api; import cc.ryanc.halo.model.domain.Category; import cc.ryanc.halo.model.domain.Post; import cc.ryanc.halo.model.domain.User; -import cc.ryanc.halo.model.dto.HaloConst; import cc.ryanc.halo.model.enums.BlogPropertiesEnum; import cc.ryanc.halo.service.CategoryService; import cc.ryanc.halo.service.PostService; @@ -26,6 +25,8 @@ import javax.servlet.http.HttpServletRequest; import java.util.Date; import java.util.List; +import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS; + /** * @author : RYAN0UP * @date : 2018/12/11 @@ -47,6 +48,7 @@ public class ApiMetaWeBlog { /** * @param request request + * * @return String */ @PostMapping @@ -124,7 +126,9 @@ public class ApiMetaWeBlog { /** * @param methodCall + * * @return + * * @throws Exception */ private Post parsetPost(final JSONObject methodCall) throws Exception { @@ -136,20 +140,20 @@ public class ApiMetaWeBlog { for (int i = 0; i < members.length(); i++) { final JSONObject member = members.getJSONObject(i); final String name = member.getString("name"); - if("dateCreated".equals(name)){ + if ("dateCreated".equals(name)) { final String dateString = member.getJSONObject("value").getString("dateTime.iso8601"); Date date = DateUtil.parseDate(dateString); ret.setPostDate(date); - }else if ("title".equals(name)){ + } else if ("title".equals(name)) { ret.setPostTitle(member.getJSONObject("value").getString("string")); - }else if("description".equals(name)){ + } else if ("description".equals(name)) { final String content = member.getJSONObject("value").optString("string"); ret.setPostContent(content); ret.setPostContentMd(content); - }else if("categories".equals(name)){ + } else if ("categories".equals(name)) { final StrBuilder cateBuilder = new StrBuilder(); final JSONObject data = member.getJSONObject("value").getJSONObject("array").getJSONObject("data"); - if(0==data.length()){ + if (0 == data.length()) { throw new Exception("At least one category"); } } @@ -161,6 +165,7 @@ public class ApiMetaWeBlog { * 根据文章编号获取文章信息 * * @param postId 文章编号 + * * @return 文章信息xml格式 */ private String getPost(Long postId) { @@ -175,6 +180,7 @@ public class ApiMetaWeBlog { * 根据文章编号构建文章信息 * * @param postId 文章编号 + * * @return 文章信息xml格式 */ private String buildPost(final Long postId) { @@ -224,13 +230,13 @@ public class ApiMetaWeBlog { * @return 博客信息xml节点 */ private String buildBlogInfo() { - final String blogId = HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp()); - final String blogTitle = HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_TITLE.getProp()); + final String blogId = OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp()); + final String blogTitle = OPTIONS.get(BlogPropertiesEnum.BLOG_TITLE.getProp()); final StrBuilder strBuilder = new StrBuilder("blogid"); strBuilder.append(blogId); strBuilder.append(""); strBuilder.append("url"); - strBuilder.append(HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp())); + strBuilder.append(OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp())); strBuilder.append(""); strBuilder.append("blogName"); strBuilder.append(blogTitle); @@ -242,6 +248,7 @@ public class ApiMetaWeBlog { * 组装分类信息 * * @return 分类信息xml格式 + * * @throws Exception Exception */ private String getCategories() throws Exception { @@ -258,6 +265,7 @@ public class ApiMetaWeBlog { * 构建分类信息 * * @return 分类信息xml节点 + * * @throws Exception Exception */ private String buildCategories() throws Exception { @@ -271,7 +279,7 @@ public class ApiMetaWeBlog { strBuilder.append("description").append("").append(cateName).append(""); strBuilder.append("title").append("").append(cateName).append(""); strBuilder.append("categoryid").append("").append(cateId).append(""); - strBuilder.append("htmlUrl").append("").append(HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp())).append("/categories/").append(cateName).append(""); + strBuilder.append("htmlUrl").append("").append(OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp())).append("/categories/").append(cateName).append(""); strBuilder.append(""); } return strBuilder.toString(); diff --git a/src/main/java/cc/ryanc/halo/web/controller/api/ApiOptionController.java b/src/main/java/cc/ryanc/halo/web/controller/api/ApiOptionController.java index 7af077bb1..5c62edc15 100644 --- a/src/main/java/cc/ryanc/halo/web/controller/api/ApiOptionController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/api/ApiOptionController.java @@ -85,8 +85,8 @@ public class ApiOptionController { * * @return JsonResult */ - @GetMapping(value = "/{optionName}") - public JsonResult option(@PathVariable(value = "optionName") String optionName) { + @GetMapping(value = "/one") + public JsonResult option(@RequestParam(value = "optionName") String optionName) { final String optionValue = optionsService.findOneOption(optionName); return new JsonResult(ResponseStatusEnum.SUCCESS.getCode(), ResponseStatusEnum.SUCCESS.getMsg(), optionValue); } diff --git a/src/main/java/cc/ryanc/halo/web/controller/api/ApiPostController.java b/src/main/java/cc/ryanc/halo/web/controller/api/ApiPostController.java index afdf492e9..e14f3490b 100644 --- a/src/main/java/cc/ryanc/halo/web/controller/api/ApiPostController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/api/ApiPostController.java @@ -1,7 +1,6 @@ package cc.ryanc.halo.web.controller.api; import cc.ryanc.halo.model.domain.Post; -import cc.ryanc.halo.model.dto.HaloConst; import cc.ryanc.halo.model.dto.JsonResult; import cc.ryanc.halo.model.enums.BlogPropertiesEnum; import cc.ryanc.halo.model.enums.PostStatusEnum; @@ -14,8 +13,12 @@ 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.SortDefault; import org.springframework.web.bind.annotation.*; +import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS; +import static org.springframework.data.domain.Sort.Direction.DESC; + /** *
  *     文章API
@@ -95,15 +98,13 @@ public class ApiPostController {
      * 

* * @param page 页码 - * * @return JsonResult */ @GetMapping(value = "/page/{page}") - public JsonResult posts(@PathVariable(value = "page") Integer page) { - final Sort sort = new Sort(Sort.Direction.DESC, "postDate"); + public JsonResult posts(@PathVariable(value = "page") Integer page, @SortDefault(sort = "postDate", direction = DESC) Sort sort) { int size = 10; - if (StrUtil.isNotBlank(HaloConst.OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()))) { - size = Integer.parseInt(HaloConst.OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp())); + if (StrUtil.isNotBlank(OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()))) { + size = Integer.parseInt(OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp())); } final Pageable pageable = PageRequest.of(page - 1, size, sort); final Page posts = postService.findPostByStatus(PostStatusEnum.PUBLISHED.getCode(), PostTypeEnum.POST_TYPE_POST.getDesc(), pageable); @@ -147,7 +148,6 @@ public class ApiPostController { *

* * @param postId 文章编号 - * * @return JsonResult */ @GetMapping(value = "/{postId}") diff --git a/src/main/java/cc/ryanc/halo/web/controller/core/CommonController.java b/src/main/java/cc/ryanc/halo/web/controller/core/CommonController.java index 43b6f1afa..34cc51df9 100644 --- a/src/main/java/cc/ryanc/halo/web/controller/core/CommonController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/core/CommonController.java @@ -41,7 +41,6 @@ public class CommonController implements ErrorController { /** * 渲染404页面 * - * @param model model * @return String */ @GetMapping(value = "/404") @@ -52,7 +51,6 @@ public class CommonController implements ErrorController { /** * 渲染500页面 * - * @param model model * @return String */ @GetMapping(value = "/500") diff --git a/src/main/java/cc/ryanc/halo/web/controller/core/InstallController.java b/src/main/java/cc/ryanc/halo/web/controller/core/InstallController.java index 32478c3c8..d8484f364 100644 --- a/src/main/java/cc/ryanc/halo/web/controller/core/InstallController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/core/InstallController.java @@ -1,13 +1,13 @@ package cc.ryanc.halo.web.controller.core; import cc.ryanc.halo.model.domain.*; -import cc.ryanc.halo.model.dto.HaloConst; import cc.ryanc.halo.model.dto.JsonResult; import cc.ryanc.halo.model.dto.LogsRecord; import cc.ryanc.halo.model.enums.*; import cc.ryanc.halo.service.*; import cc.ryanc.halo.utils.MarkdownUtils; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; import freemarker.template.Configuration; @@ -23,6 +23,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static cc.ryanc.halo.model.dto.HaloConst.*; + /** *
  *     博客初始化控制器
@@ -70,7 +72,7 @@ public class InstallController {
     @GetMapping
     public String install(Model model) {
         try {
-            if (StrUtil.equals(TrueFalseEnum.TRUE.getDesc(), HaloConst.OPTIONS.get(BlogPropertiesEnum.IS_INSTALL.getProp()))) {
+            if (StrUtil.equals(TrueFalseEnum.TRUE.getDesc(), OPTIONS.get(BlogPropertiesEnum.IS_INSTALL.getProp()))) {
                 model.addAttribute("isInstall", true);
             } else {
                 model.addAttribute("isInstall", false);
@@ -85,8 +87,6 @@ public class InstallController {
      * 执行安装
      *
      * @param blogLocale      系统语言
-     * @param siteTitle       博客标题
-     * @param siteUrl         博客网址
      * @param userName        用户名
      * @param userDisplayName 用户名显示名
      * @param userEmail       用户邮箱
@@ -106,7 +106,7 @@ public class InstallController {
                                 @RequestParam("userPwd") String userPwd,
                                 HttpServletRequest request) {
         try {
-            if (StrUtil.equals(TrueFalseEnum.TRUE.getDesc(), HaloConst.OPTIONS.get(BlogPropertiesEnum.IS_INSTALL.getProp()))) {
+            if (StrUtil.equals(TrueFalseEnum.TRUE.getDesc(), OPTIONS.get(BlogPropertiesEnum.IS_INSTALL.getProp()))) {
                 return new JsonResult(ResultCodeEnum.FAIL.getCode(), "该博客已初始化,不能再次安装!");
             }
             //创建新的用户
@@ -137,11 +137,11 @@ public class InstallController {
             post.setPostContent(MarkdownUtils.renderMarkdown(post.getPostContentMd()));
             post.setPostSummary("欢迎使用Halo进行创作,删除这篇文章后赶紧开始吧。");
             post.setPostStatus(0);
-            post.setPostDate(DateUtil.date());
             post.setPostUrl("hello-halo");
             post.setUser(user);
             post.setCategories(categories);
             post.setAllowComment(AllowCommentEnum.ALLOW.getCode());
+            post.setPostThumbnail("/static/halo-frontend/images/thumbnail/thumbnail-" + RandomUtil.randomInt(1, 11) + ".jpg");
             postService.save(post);
 
             //第一个评论
@@ -152,7 +152,6 @@ public class InstallController {
             comment.setCommentAuthorUrl("https://ryanc.cc");
             comment.setCommentAuthorIp("127.0.0.1");
             comment.setCommentAuthorAvatarMd5(SecureUtil.md5("i@ryanc.cc"));
-            comment.setCommentDate(DateUtil.date());
             comment.setCommentContent("欢迎,欢迎!");
             comment.setCommentStatus(0);
             comment.setCommentAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36");
@@ -190,9 +189,9 @@ public class InstallController {
             menuArchive.setMenuIcon(" ");
             menuService.save(menuArchive);
 
-            HaloConst.OPTIONS.clear();
-            HaloConst.OPTIONS = optionsService.findAllOptions();
-            configuration.setSharedVariable("options", HaloConst.OPTIONS);
+            OPTIONS.clear();
+            OPTIONS = optionsService.findAllOptions();
+            configuration.setSharedVariable("options", OPTIONS);
             configuration.setSharedVariable("user", userService.findUser());
         } catch (Exception e) {
             log.error(e.getMessage());
diff --git a/src/main/java/cc/ryanc/halo/web/controller/front/FrontArchiveController.java b/src/main/java/cc/ryanc/halo/web/controller/front/FrontArchiveController.java
index 520afc667..f2ef1e5fd 100644
--- a/src/main/java/cc/ryanc/halo/web/controller/front/FrontArchiveController.java
+++ b/src/main/java/cc/ryanc/halo/web/controller/front/FrontArchiveController.java
@@ -3,7 +3,6 @@ package cc.ryanc.halo.web.controller.front;
 import cc.ryanc.halo.model.domain.Comment;
 import cc.ryanc.halo.model.domain.Post;
 import cc.ryanc.halo.model.domain.Tag;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.dto.ListPage;
 import cc.ryanc.halo.model.enums.*;
 import cc.ryanc.halo.service.CommentService;
@@ -22,6 +21,7 @@ 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.SortDefault;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.*;
@@ -33,6 +33,9 @@ import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
+import static org.springframework.data.domain.Sort.Direction.DESC;
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+
 /**
  * 
  *     前台文章归档控制器
@@ -58,11 +61,12 @@ public class FrontArchiveController extends BaseController {
      * 文章归档
      *
      * @param model model
+     *
      * @return 模板路径
      */
     @GetMapping
     public String archives(Model model) {
-        return this.archives(model, 1);
+        return this.archives(model, 1, Sort.by(DESC, "postDate"));
     }
 
     /**
@@ -70,14 +74,14 @@ public class FrontArchiveController extends BaseController {
      *
      * @param model model
      * @param page  page 当前页码
+     *
      * @return 模板路径/themes/{theme}/archives
      */
     @GetMapping(value = "page/{page}")
     public String archives(Model model,
-                           @PathVariable(value = "page") Integer page) {
-
+                           @PathVariable(value = "page") Integer page,
+                           @SortDefault(sort = "postDate", direction = DESC) Sort sort) {
         //所有文章数据,分页,material主题适用
-        final Sort sort = new Sort(Sort.Direction.DESC, "postDate");
         final Pageable pageable = PageRequest.of(page - 1, 5, sort);
         final Page posts = postService.findPostByStatus(PostStatusEnum.PUBLISHED.getCode(), PostTypeEnum.POST_TYPE_POST.getDesc(), pageable);
         if (null == posts) {
@@ -94,6 +98,7 @@ public class FrontArchiveController extends BaseController {
      * @param model model
      * @param year  year 年份
      * @param month month 月份
+     *
      * @return 模板路径/themes/{theme}/archives
      */
     @GetMapping(value = "{year}/{month}")
@@ -114,6 +119,7 @@ public class FrontArchiveController extends BaseController {
      *
      * @param postUrl 文章路径名
      * @param model   model
+     *
      * @return 模板路径/themes/{theme}/post
      */
     @GetMapping(value = "{postUrl}")
@@ -140,7 +146,7 @@ public class FrontArchiveController extends BaseController {
             model.addAttribute("nextPost", nextPost);
         }
         List comments = null;
-        if (StrUtil.equals(HaloConst.OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NEED_CHECK.getProp()), TrueFalseEnum.TRUE.getDesc()) || HaloConst.OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NEED_CHECK.getProp()) == null) {
+        if (StrUtil.equals(OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NEED_CHECK.getProp()), TrueFalseEnum.TRUE.getDesc()) || OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NEED_CHECK.getProp()) == null) {
             comments = commentService.findCommentsByPostAndCommentStatus(post, CommentStatusEnum.PUBLISHED.getCode());
         } else {
             comments = commentService.findCommentsByPostAndCommentStatusNot(post, CommentStatusEnum.RECYCLE.getCode());
@@ -156,8 +162,8 @@ public class FrontArchiveController extends BaseController {
         //默认显示10条
         int size = 10;
         //获取每页评论条数
-        if (StrUtil.isNotBlank(HaloConst.OPTIONS.get(BlogPropertiesEnum.INDEX_COMMENTS.getProp()))) {
-            size = Integer.parseInt(HaloConst.OPTIONS.get(BlogPropertiesEnum.INDEX_COMMENTS.getProp()));
+        if (StrUtil.isNotBlank(OPTIONS.get(BlogPropertiesEnum.INDEX_COMMENTS.getProp()))) {
+            size = Integer.parseInt(OPTIONS.get(BlogPropertiesEnum.INDEX_COMMENTS.getProp()));
         }
         //评论分页
         final ListPage commentsPage = new ListPage(CommentUtil.getComments(comments), cp, size);
@@ -187,6 +193,7 @@ public class FrontArchiveController extends BaseController {
      * @param postId       postId
      * @param postPassword postPassword
      * @param response     response
+     *
      * @return String
      */
     @PostMapping(value = "/verifyPostPassword")
diff --git a/src/main/java/cc/ryanc/halo/web/controller/front/FrontCategoryController.java b/src/main/java/cc/ryanc/halo/web/controller/front/FrontCategoryController.java
index c0c385111..5630b519f 100644
--- a/src/main/java/cc/ryanc/halo/web/controller/front/FrontCategoryController.java
+++ b/src/main/java/cc/ryanc/halo/web/controller/front/FrontCategoryController.java
@@ -2,7 +2,6 @@ package cc.ryanc.halo.web.controller.front;
 
 import cc.ryanc.halo.model.domain.Category;
 import cc.ryanc.halo.model.domain.Post;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.enums.BlogPropertiesEnum;
 import cc.ryanc.halo.service.CategoryService;
 import cc.ryanc.halo.service.PostService;
@@ -14,6 +13,7 @@ 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.SortDefault;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -22,6 +22,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
 
 import java.util.List;
 
+import static org.springframework.data.domain.Sort.Direction.DESC;
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+
 /**
  * 
  *     前台文章分类控制器
@@ -44,7 +47,6 @@ public class FrontCategoryController extends BaseController {
      * 分类列表页面
      *
      * @param model model
-     *
      * @return String
      */
     @GetMapping
@@ -59,13 +61,12 @@ public class FrontCategoryController extends BaseController {
      *
      * @param model   model
      * @param cateUrl cateUrl
-     *
      * @return string
      */
     @GetMapping(value = "{cateUrl}")
     public String categories(Model model,
                              @PathVariable("cateUrl") String cateUrl) {
-        return this.categories(model, cateUrl, 1);
+        return this.categories(model, cateUrl, 1, Sort.by(DESC, "postDate"));
     }
 
     /**
@@ -74,21 +75,20 @@ public class FrontCategoryController extends BaseController {
      * @param model   model
      * @param cateUrl 分类目录路径
      * @param page    页码
-     *
      * @return String
      */
     @GetMapping("{cateUrl}/page/{page}")
     public String categories(Model model,
                              @PathVariable("cateUrl") String cateUrl,
-                             @PathVariable("page") Integer page) {
+                             @PathVariable("page") Integer page,
+                             @SortDefault(sort = "postDate", direction = DESC) Sort sort) {
         final Category category = categoryService.findByCateUrl(cateUrl);
         if (null == category) {
             return this.renderNotFound();
         }
-        final Sort sort = new Sort(Sort.Direction.DESC, "postDate");
         int size = 10;
-        if (StrUtil.isNotBlank(HaloConst.OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()))) {
-            size = Integer.parseInt(HaloConst.OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()));
+        if (StrUtil.isNotBlank(OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()))) {
+            size = Integer.parseInt(OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()));
         }
         final Pageable pageable = PageRequest.of(page - 1, size, sort);
         final Page posts = postService.findPostByCategories(category, pageable);
diff --git a/src/main/java/cc/ryanc/halo/web/controller/front/FrontCommentController.java b/src/main/java/cc/ryanc/halo/web/controller/front/FrontCommentController.java
index 6b757c77b..d730d09b0 100644
--- a/src/main/java/cc/ryanc/halo/web/controller/front/FrontCommentController.java
+++ b/src/main/java/cc/ryanc/halo/web/controller/front/FrontCommentController.java
@@ -2,16 +2,16 @@ package cc.ryanc.halo.web.controller.front;
 
 import cc.ryanc.halo.model.domain.Comment;
 import cc.ryanc.halo.model.domain.Post;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.dto.JsonResult;
-import cc.ryanc.halo.model.enums.*;
+import cc.ryanc.halo.model.enums.BlogPropertiesEnum;
+import cc.ryanc.halo.model.enums.PostTypeEnum;
+import cc.ryanc.halo.model.enums.ResultCodeEnum;
+import cc.ryanc.halo.model.enums.TrueFalseEnum;
 import cc.ryanc.halo.service.CommentService;
 import cc.ryanc.halo.service.MailService;
 import cc.ryanc.halo.service.PostService;
 import cc.ryanc.halo.service.UserService;
-import cc.ryanc.halo.utils.CommentUtil;
 import cc.ryanc.halo.utils.OwoUtil;
-import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.lang.Validator;
 import cn.hutool.core.text.StrBuilder;
 import cn.hutool.core.util.StrUtil;
@@ -21,21 +21,19 @@ import cn.hutool.extra.servlet.ServletUtil;
 import cn.hutool.http.HtmlUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
-import org.springframework.http.MediaType;
 import org.springframework.stereotype.Controller;
 import org.springframework.validation.BindingResult;
 import org.springframework.validation.ObjectError;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.validation.Valid;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
-import java.util.Optional;
+
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
 
 /**
  * 
@@ -86,7 +84,6 @@ public class FrontCommentController {
             post = postService.findByPostId(post.getPostId()).orElse(new Post());
             comment.setCommentAuthorEmail(HtmlUtil.escape(comment.getCommentAuthorEmail()).toLowerCase());
             comment.setPost(post);
-            comment.setCommentDate(DateUtil.date());
             comment.setCommentAuthorIp(ServletUtil.getClientIP(request));
             comment.setIsAdmin(0);
             comment.setCommentAuthor(HtmlUtil.escape(comment.getCommentAuthor()));
@@ -116,7 +113,7 @@ public class FrontCommentController {
             } else {
                 new EmailToAdmin(comment, post).start();
             }
-            if (StrUtil.equals(HaloConst.OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NEED_CHECK.getProp()), TrueFalseEnum.TRUE.getDesc()) || HaloConst.OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NEED_CHECK.getProp()) == null) {
+            if (StrUtil.equals(OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NEED_CHECK.getProp()), TrueFalseEnum.TRUE.getDesc()) || OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NEED_CHECK.getProp()) == null) {
                 return new JsonResult(ResultCodeEnum.SUCCESS.getCode(), "你的评论已经提交,待博主审核之后可显示。");
             } else {
                 return new JsonResult(ResultCodeEnum.SUCCESS.getCode(), "你的评论已经提交,刷新后即可显示。");
@@ -140,11 +137,11 @@ public class FrontCommentController {
 
         @Override
         public void run() {
-            if (StrUtil.equals(HaloConst.OPTIONS.get(BlogPropertiesEnum.SMTP_EMAIL_ENABLE.getProp()), TrueFalseEnum.TRUE.getDesc()) && StrUtil.equals(HaloConst.OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NOTICE.getProp()), TrueFalseEnum.TRUE.getDesc())) {
+            if (StrUtil.equals(OPTIONS.get(BlogPropertiesEnum.SMTP_EMAIL_ENABLE.getProp()), TrueFalseEnum.TRUE.getDesc()) && StrUtil.equals(OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NOTICE.getProp()), TrueFalseEnum.TRUE.getDesc())) {
                 try {
                     //发送邮件到博主
                     final Map map = new HashMap<>(5);
-                    final StrBuilder pageUrl = new StrBuilder(HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp()));
+                    final StrBuilder pageUrl = new StrBuilder(OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp()));
                     if (StrUtil.equals(post.getPostType(), PostTypeEnum.POST_TYPE_POST.getDesc())) {
                         pageUrl.append("/archives/");
                     } else {
@@ -183,10 +180,10 @@ public class FrontCommentController {
         @Override
         public void run() {
             //发送通知给对方
-            if (StrUtil.equals(HaloConst.OPTIONS.get(BlogPropertiesEnum.SMTP_EMAIL_ENABLE.getProp()), TrueFalseEnum.TRUE.getDesc()) && StrUtil.equals(HaloConst.OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NOTICE.getProp()), TrueFalseEnum.TRUE.getDesc())) {
+            if (StrUtil.equals(OPTIONS.get(BlogPropertiesEnum.SMTP_EMAIL_ENABLE.getProp()), TrueFalseEnum.TRUE.getDesc()) && StrUtil.equals(OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NOTICE.getProp()), TrueFalseEnum.TRUE.getDesc())) {
                 if (Validator.isEmail(lastComment.getCommentAuthorEmail())) {
                     final Map map = new HashMap<>(8);
-                    final StrBuilder pageUrl = new StrBuilder(HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp()));
+                    final StrBuilder pageUrl = new StrBuilder(OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp()));
                     if (StrUtil.equals(post.getPostType(), PostTypeEnum.POST_TYPE_POST.getDesc())) {
                         pageUrl.append("/archives/");
 
@@ -197,15 +194,15 @@ public class FrontCommentController {
                     pageUrl.append("#comment-id-");
                     pageUrl.append(comment.getCommentId());
                     map.put("pageUrl", pageUrl.toString());
-                    map.put("blogTitle", HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_TITLE.getProp()));
+                    map.put("blogTitle", OPTIONS.get(BlogPropertiesEnum.BLOG_TITLE.getProp()));
                     map.put("commentAuthor", lastComment.getCommentAuthor());
                     map.put("pageName", lastComment.getPost().getPostTitle());
                     map.put("commentContent", lastComment.getCommentContent());
                     map.put("replyAuthor", comment.getCommentAuthor());
                     map.put("replyContent", comment.getCommentContent());
-                    map.put("blogUrl", HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp()));
+                    map.put("blogUrl", OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp()));
                     mailService.sendTemplateMail(
-                            lastComment.getCommentAuthorEmail(), "您在" + HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_TITLE.getProp()) + "的评论有了新回复", map, "common/mail_template/mail_reply.ftl");
+                            lastComment.getCommentAuthorEmail(), "您在" + OPTIONS.get(BlogPropertiesEnum.BLOG_TITLE.getProp()) + "的评论有了新回复", map, "common/mail_template/mail_reply.ftl");
                 }
             }
         }
diff --git a/src/main/java/cc/ryanc/halo/web/controller/front/FrontIndexController.java b/src/main/java/cc/ryanc/halo/web/controller/front/FrontIndexController.java
index c7e6751cf..c6146d3f6 100644
--- a/src/main/java/cc/ryanc/halo/web/controller/front/FrontIndexController.java
+++ b/src/main/java/cc/ryanc/halo/web/controller/front/FrontIndexController.java
@@ -1,7 +1,6 @@
 package cc.ryanc.halo.web.controller.front;
 
 import cc.ryanc.halo.model.domain.Post;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.enums.BlogPropertiesEnum;
 import cc.ryanc.halo.service.PostService;
 import cc.ryanc.halo.web.controller.core.BaseController;
@@ -13,12 +12,16 @@ 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.SortDefault;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 
+import static org.springframework.data.domain.Sort.Direction.DESC;
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+
 /**
  * 
  *     前台首页控制器
@@ -40,11 +43,12 @@ public class FrontIndexController extends BaseController {
      * 请求首页
      *
      * @param model model
+     *
      * @return 模板路径
      */
     @GetMapping
     public String index(Model model) {
-        return this.index(model, 1);
+        return this.index(model, 1, Sort.by(DESC, "postDate"));
     }
 
     /**
@@ -52,17 +56,16 @@ public class FrontIndexController extends BaseController {
      *
      * @param model model
      * @param page  当前页码
-     * @param size  每页数量
      * @return 模板路径/themes/{theme}/index
      */
     @GetMapping(value = "page/{page}")
     public String index(Model model,
-                        @PathVariable(value = "page") Integer page) {
-        final Sort sort = new Sort(Sort.Direction.DESC, "postDate");
+                        @PathVariable(value = "page") Integer page,
+                        @SortDefault(sort = "postDate", direction = DESC) Sort sort) {
         //默认显示10条
         int size = 10;
-        if (StrUtil.isNotBlank(HaloConst.OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()))) {
-            size = Integer.parseInt(HaloConst.OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()));
+        if (StrUtil.isNotBlank(OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()))) {
+            size = Integer.parseInt(OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()));
         }
         //所有文章数据,分页
         final Pageable pageable = PageRequest.of(page - 1, size, sort);
diff --git a/src/main/java/cc/ryanc/halo/web/controller/front/FrontOthersController.java b/src/main/java/cc/ryanc/halo/web/controller/front/FrontOthersController.java
index 4b3001046..956914b9f 100644
--- a/src/main/java/cc/ryanc/halo/web/controller/front/FrontOthersController.java
+++ b/src/main/java/cc/ryanc/halo/web/controller/front/FrontOthersController.java
@@ -1,22 +1,29 @@
 package cc.ryanc.halo.web.controller.front;
 
 import cc.ryanc.halo.model.domain.Post;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.enums.BlogPropertiesEnum;
 import cc.ryanc.halo.model.enums.PostTypeEnum;
 import cc.ryanc.halo.service.PostService;
 import cn.hutool.core.util.StrUtil;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
 import org.springframework.beans.factory.annotation.Autowired;
 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.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
 
+import java.io.IOException;
 import java.util.List;
 
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+
 /**
  * 
  *     sitemap,rss页面控制器
@@ -31,49 +38,102 @@ public class FrontOthersController {
     @Autowired
     private PostService postService;
 
+    @Autowired
+    private FreeMarkerConfigurer freeMarker;
+
     /**
      * 获取文章rss
      *
-     * @return rss
+     * @param model model
+     *
+     * @return String
+     *
+     * @throws IOException       IOException
+     * @throws TemplateException TemplateException
      */
     @GetMapping(value = {"feed", "feed.xml", "atom", "atom.xml"}, produces = "application/xml;charset=UTF-8")
     @ResponseBody
-    public String feed() {
-        String rssPosts = HaloConst.OPTIONS.get(BlogPropertiesEnum.RSS_POSTS.getProp());
+    public String feed(Model model) throws IOException, TemplateException {
+        String rssPosts = OPTIONS.get(BlogPropertiesEnum.RSS_POSTS.getProp());
         if (StrUtil.isBlank(rssPosts)) {
             rssPosts = "20";
         }
-        //获取文章列表并根据时间排序
         final Sort sort = new Sort(Sort.Direction.DESC, "postDate");
         final Pageable pageable = PageRequest.of(0, Integer.parseInt(rssPosts), sort);
-        final Page postsPage = postService.findPostByStatus(0, PostTypeEnum.POST_TYPE_POST.getDesc(), pageable);
-        final List posts = postsPage.getContent();
-        for (Post post : posts) {
-            if(StrUtil.isNotEmpty(post.getPostPassword())){
+        final Page postsPage = postService.findPostByStatus(0, PostTypeEnum.POST_TYPE_POST.getDesc(), pageable).map(post -> {
+            if (StrUtil.isNotEmpty(post.getPostPassword())) {
                 post.setPostContent("该文章为加密文章");
+                post.setPostSummary("该文章为加密文章");
             }
-        }
-        return postService.buildRss(posts);
+            return post;
+        });
+        final List posts = postsPage.getContent();
+        model.addAttribute("posts", posts);
+        final Template template = freeMarker.getConfiguration().getTemplate("common/web/rss.ftl");
+        return FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
     }
 
     /**
-     * 获取sitemap
+     * 获取 XML 格式的站点地图
      *
-     * @return sitemap
+     * @param model model
+     *
+     * @return String
+     *
+     * @throws IOException       IOException
+     * @throws TemplateException TemplateException
      */
     @GetMapping(value = {"sitemap", "sitemap.xml"}, produces = "application/xml;charset=UTF-8")
     @ResponseBody
-    public String siteMap() {
-        //获取文章列表并根据时间排序
-        final Sort sort = new Sort(Sort.Direction.DESC, "postDate");
-        final Pageable pageable = PageRequest.of(0, 999, sort);
-        final Page postsPage = postService.findPostByStatus(0, PostTypeEnum.POST_TYPE_POST.getDesc(), pageable);
-        final List posts = postsPage.getContent();
-        for (Post post : posts) {
-            if(StrUtil.isNotEmpty(post.getPostPassword())){
+    public String sitemapXml(Model model) throws IOException, TemplateException {
+        final Page postsPage = postService.findPostByStatus(0, PostTypeEnum.POST_TYPE_POST.getDesc(), null).map(post -> {
+            if (StrUtil.isNotEmpty(post.getPostPassword())) {
                 post.setPostContent("该文章为加密文章");
+                post.setPostSummary("该文章为加密文章");
             }
-        }
-        return postService.buildSiteMap(posts);
+            return post;
+        });
+        final List posts = postsPage.getContent();
+        model.addAttribute("posts", posts);
+        final Template template = freeMarker.getConfiguration().getTemplate("common/web/sitemap_xml.ftl");
+        return FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
+    }
+
+    /**
+     * 获取 HTML 格式的站点地图
+     *
+     * @param model model
+     *
+     * @return String
+     */
+    @GetMapping(value = "sitemap.html", produces = {"text/html"})
+    public String sitemapHtml(Model model) {
+        final Page postsPage = postService.findPostByStatus(0, PostTypeEnum.POST_TYPE_POST.getDesc(), null).map(post -> {
+            if (StrUtil.isNotEmpty(post.getPostPassword())) {
+                post.setPostContent("该文章为加密文章");
+                post.setPostSummary("该文章为加密文章");
+            }
+            return post;
+        });
+        final List posts = postsPage.getContent();
+        model.addAttribute("posts", posts);
+        return "common/web/sitemap_html";
+    }
+
+    /**
+     * robots
+     *
+     * @param model model
+     *
+     * @return String
+     *
+     * @throws IOException       IOException
+     * @throws TemplateException TemplateException
+     */
+    @GetMapping(value = "robots.txt", produces = {"text/plain"})
+    @ResponseBody
+    public String robots(Model model) throws IOException, TemplateException {
+        final Template template = freeMarker.getConfiguration().getTemplate("common/web/robots.ftl");
+        return FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
     }
 }
diff --git a/src/main/java/cc/ryanc/halo/web/controller/front/FrontPageController.java b/src/main/java/cc/ryanc/halo/web/controller/front/FrontPageController.java
index c46bcd552..4dd7b258a 100644
--- a/src/main/java/cc/ryanc/halo/web/controller/front/FrontPageController.java
+++ b/src/main/java/cc/ryanc/halo/web/controller/front/FrontPageController.java
@@ -3,7 +3,6 @@ package cc.ryanc.halo.web.controller.front;
 import cc.ryanc.halo.model.domain.Comment;
 import cc.ryanc.halo.model.domain.Gallery;
 import cc.ryanc.halo.model.domain.Post;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.dto.ListPage;
 import cc.ryanc.halo.model.enums.*;
 import cc.ryanc.halo.service.CommentService;
@@ -22,6 +21,8 @@ import org.springframework.web.bind.annotation.RequestParam;
 
 import java.util.List;
 
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+
 /**
  * 
  *     前台内置页面,自定义页面控制器
@@ -81,15 +82,15 @@ public class FrontPageController extends BaseController {
             return this.renderNotFound();
         }
         List comments = null;
-        if (StrUtil.equals(HaloConst.OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NEED_CHECK.getProp()), TrueFalseEnum.TRUE.getDesc()) || HaloConst.OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NEED_CHECK.getProp()) == null) {
+        if (StrUtil.equals(OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NEED_CHECK.getProp()), TrueFalseEnum.TRUE.getDesc()) || OPTIONS.get(BlogPropertiesEnum.NEW_COMMENT_NEED_CHECK.getProp()) == null) {
             comments = commentService.findCommentsByPostAndCommentStatus(post, CommentStatusEnum.PUBLISHED.getCode());
         } else {
             comments = commentService.findCommentsByPostAndCommentStatusNot(post, CommentStatusEnum.RECYCLE.getCode());
         }
         //默认显示10条
         int size = 10;
-        if (StrUtil.isNotBlank(HaloConst.OPTIONS.get(BlogPropertiesEnum.INDEX_COMMENTS.getProp()))) {
-            size = Integer.parseInt(HaloConst.OPTIONS.get(BlogPropertiesEnum.INDEX_COMMENTS.getProp()));
+        if (StrUtil.isNotBlank(OPTIONS.get(BlogPropertiesEnum.INDEX_COMMENTS.getProp()))) {
+            size = Integer.parseInt(OPTIONS.get(BlogPropertiesEnum.INDEX_COMMENTS.getProp()));
         }
         //评论分页
         final ListPage commentsPage = new ListPage(CommentUtil.getComments(comments), cp, size);
diff --git a/src/main/java/cc/ryanc/halo/web/controller/front/FrontSearchController.java b/src/main/java/cc/ryanc/halo/web/controller/front/FrontSearchController.java
index f231767ed..5044553fb 100644
--- a/src/main/java/cc/ryanc/halo/web/controller/front/FrontSearchController.java
+++ b/src/main/java/cc/ryanc/halo/web/controller/front/FrontSearchController.java
@@ -1,7 +1,6 @@
 package cc.ryanc.halo.web.controller.front;
 
 import cc.ryanc.halo.model.domain.Post;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.enums.BlogPropertiesEnum;
 import cc.ryanc.halo.model.enums.PostStatusEnum;
 import cc.ryanc.halo.model.enums.PostTypeEnum;
@@ -16,6 +15,7 @@ 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.SortDefault;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -23,6 +23,9 @@ import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 
+import static org.springframework.data.domain.Sort.Direction.DESC;
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+
 /**
  * 
  *     文章检索
@@ -44,12 +47,13 @@ public class FrontSearchController extends BaseController {
      *
      * @param model   model
      * @param keyword 关键词
+     *
      * @return 模板路径/themes/{theme}/search
      */
     @GetMapping
     public String search(Model model,
                          @RequestParam(value = "keyword") String keyword) {
-        return this.search(model, HtmlUtil.escape(keyword), 1);
+        return this.search(model, HtmlUtil.escape(keyword), 1, Sort.by(DESC, "postDate"));
     }
 
     /**
@@ -58,19 +62,20 @@ public class FrontSearchController extends BaseController {
      * @param model   model
      * @param keyword 关键词
      * @param page    当前页码
+     *
      * @return 模板路径/themes/{theme}/search
      */
     @GetMapping(value = "page/{page}")
     public String search(Model model,
                          @RequestParam(value = "keyword") String keyword,
-                         @PathVariable(value = "page") Integer page) {
-        final Sort sort = new Sort(Sort.Direction.DESC, "postDate");
+                         @PathVariable(value = "page") Integer page,
+                         @SortDefault(sort = "postDate", direction = DESC) Sort sort) {
         int size = 10;
-        if (StrUtil.isNotBlank(HaloConst.OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()))) {
-            size = Integer.parseInt(HaloConst.OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()));
+        if (StrUtil.isNotBlank(OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()))) {
+            size = Integer.parseInt(OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()));
         }
         final Pageable pageable = PageRequest.of(page - 1, size, sort);
-        final Page posts = postService.searchPosts(HtmlUtil.escape(keyword),PostTypeEnum.POST_TYPE_POST.getDesc(),PostStatusEnum.PUBLISHED.getCode(),pageable);
+        final Page posts = postService.searchPostsBy(HtmlUtil.escape(keyword), PostTypeEnum.POST_TYPE_POST.getDesc(), PostStatusEnum.PUBLISHED.getCode(), pageable);
         final int[] rainbow = PageUtil.rainbow(page, posts.getTotalPages(), 3);
         model.addAttribute("is_search", true);
         model.addAttribute("keyword", keyword);
diff --git a/src/main/java/cc/ryanc/halo/web/controller/front/FrontTagController.java b/src/main/java/cc/ryanc/halo/web/controller/front/FrontTagController.java
index fce93bbd2..eb2402831 100644
--- a/src/main/java/cc/ryanc/halo/web/controller/front/FrontTagController.java
+++ b/src/main/java/cc/ryanc/halo/web/controller/front/FrontTagController.java
@@ -2,7 +2,6 @@ package cc.ryanc.halo.web.controller.front;
 
 import cc.ryanc.halo.model.domain.Post;
 import cc.ryanc.halo.model.domain.Tag;
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.enums.BlogPropertiesEnum;
 import cc.ryanc.halo.service.PostService;
 import cc.ryanc.halo.service.TagService;
@@ -14,12 +13,16 @@ 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.SortDefault;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 
+import static org.springframework.data.domain.Sort.Direction.DESC;
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+
 /**
  * 
  *     前台标签控制器
@@ -53,13 +56,12 @@ public class FrontTagController extends BaseController {
      *
      * @param tagUrl 标签路径
      * @param model  model
-     *
      * @return String
      */
     @GetMapping(value = "{tagUrl}")
     public String tags(Model model,
                        @PathVariable("tagUrl") String tagUrl) {
-        return this.tags(model, tagUrl, 1);
+        return this.tags(model, tagUrl, 1, Sort.by(DESC, "postDate"));
     }
 
     /**
@@ -68,21 +70,20 @@ public class FrontTagController extends BaseController {
      * @param model  model
      * @param tagUrl 标签路径
      * @param page   页码
-     *
      * @return String
      */
     @GetMapping(value = "{tagUrl}/page/{page}")
     public String tags(Model model,
                        @PathVariable("tagUrl") String tagUrl,
-                       @PathVariable("page") Integer page) {
+                       @PathVariable("page") Integer page,
+                       @SortDefault(sort = "postDate", direction = DESC) Sort sort) {
         final Tag tag = tagService.findByTagUrl(tagUrl);
         if (null == tag) {
             return this.renderNotFound();
         }
-        final Sort sort = new Sort(Sort.Direction.DESC, "postDate");
         int size = 10;
-        if (StrUtil.isNotBlank(HaloConst.OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()))) {
-            size = Integer.parseInt(HaloConst.OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()));
+        if (StrUtil.isNotBlank(OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()))) {
+            size = Integer.parseInt(OPTIONS.get(BlogPropertiesEnum.INDEX_POSTS.getProp()));
         }
         final Pageable pageable = PageRequest.of(page - 1, size, sort);
         final Page posts = postService.findPostsByTags(tag, pageable);
diff --git a/src/main/java/cc/ryanc/halo/web/interceptor/ApiInterceptor.java b/src/main/java/cc/ryanc/halo/web/interceptor/ApiInterceptor.java
index 89841c385..06c7b4b4d 100644
--- a/src/main/java/cc/ryanc/halo/web/interceptor/ApiInterceptor.java
+++ b/src/main/java/cc/ryanc/halo/web/interceptor/ApiInterceptor.java
@@ -1,6 +1,5 @@
 package cc.ryanc.halo.web.interceptor;
 
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.enums.BlogPropertiesEnum;
 import cc.ryanc.halo.model.enums.TrueFalseEnum;
 import cn.hutool.core.util.StrUtil;
@@ -14,6 +13,8 @@ import javax.servlet.http.HttpServletResponse;
 import java.util.HashMap;
 import java.util.Map;
 
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+
 /**
  * 
  *     API接口拦截器,用户可自己选择关闭或者开启
@@ -29,8 +30,8 @@ public class ApiInterceptor implements HandlerInterceptor {
 
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        if (StrUtil.equals(TrueFalseEnum.TRUE.getDesc(), HaloConst.OPTIONS.get(BlogPropertiesEnum.API_STATUS.getProp()))) {
-            if (StrUtil.equals(request.getHeader(TOKEN), HaloConst.OPTIONS.get(BlogPropertiesEnum.API_TOKEN.getProp()))) {
+        if (StrUtil.equals(TrueFalseEnum.TRUE.getDesc(), OPTIONS.get(BlogPropertiesEnum.API_STATUS.getProp()))) {
+            if (StrUtil.equals(request.getHeader(TOKEN), OPTIONS.get(BlogPropertiesEnum.API_TOKEN.getProp()))) {
                 return true;
             } else {
                 response.setCharacterEncoding("UTF-8");
diff --git a/src/main/java/cc/ryanc/halo/web/interceptor/InstallInterceptor.java b/src/main/java/cc/ryanc/halo/web/interceptor/InstallInterceptor.java
index d69166598..c418a9217 100644
--- a/src/main/java/cc/ryanc/halo/web/interceptor/InstallInterceptor.java
+++ b/src/main/java/cc/ryanc/halo/web/interceptor/InstallInterceptor.java
@@ -1,6 +1,5 @@
 package cc.ryanc.halo.web.interceptor;
 
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.enums.BlogPropertiesEnum;
 import cc.ryanc.halo.model.enums.TrueFalseEnum;
 import cn.hutool.core.util.StrUtil;
@@ -11,6 +10,8 @@ import org.springframework.web.servlet.ModelAndView;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+
 /**
  * 
  *     博客初始化拦截器
@@ -24,7 +25,7 @@ public class InstallInterceptor implements HandlerInterceptor {
 
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
-        if (StrUtil.equals(TrueFalseEnum.TRUE.getDesc(), HaloConst.OPTIONS.get(BlogPropertiesEnum.IS_INSTALL.getProp()))) {
+        if (StrUtil.equals(TrueFalseEnum.TRUE.getDesc(), OPTIONS.get(BlogPropertiesEnum.IS_INSTALL.getProp()))) {
             return true;
         }
         response.sendRedirect("/install");
diff --git a/src/main/java/cc/ryanc/halo/web/interceptor/LocaleInterceptor.java b/src/main/java/cc/ryanc/halo/web/interceptor/LocaleInterceptor.java
index 73b301ecb..736262570 100644
--- a/src/main/java/cc/ryanc/halo/web/interceptor/LocaleInterceptor.java
+++ b/src/main/java/cc/ryanc/halo/web/interceptor/LocaleInterceptor.java
@@ -1,6 +1,5 @@
 package cc.ryanc.halo.web.interceptor;
 
-import cc.ryanc.halo.model.dto.HaloConst;
 import cc.ryanc.halo.model.enums.BlogPropertiesEnum;
 import cc.ryanc.halo.model.enums.LocaleEnum;
 import cn.hutool.core.util.StrUtil;
@@ -13,6 +12,8 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.util.Locale;
 
+import static cc.ryanc.halo.model.dto.HaloConst.OPTIONS;
+
 /**
  * @author : wangry
  * @version : 1.0
@@ -27,7 +28,7 @@ public class LocaleInterceptor implements HandlerInterceptor {
         if (null != attribute) {
             return true;
         }
-        if (StrUtil.equals(LocaleEnum.EN_US.getValue(), HaloConst.OPTIONS.get(BlogPropertiesEnum.BLOG_LOCALE.getProp()))) {
+        if (StrUtil.equals(LocaleEnum.EN_US.getValue(), OPTIONS.get(BlogPropertiesEnum.BLOG_LOCALE.getProp()))) {
             request.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, new Locale("en", "US"));
         } else {
             request.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, new Locale("zh", "CN"));
diff --git a/src/main/java/cc/ryanc/halo/web/interceptor/LoginInterceptor.java b/src/main/java/cc/ryanc/halo/web/interceptor/LoginInterceptor.java
index 11e618b4c..c1d17595d 100755
--- a/src/main/java/cc/ryanc/halo/web/interceptor/LoginInterceptor.java
+++ b/src/main/java/cc/ryanc/halo/web/interceptor/LoginInterceptor.java
@@ -1,6 +1,5 @@
 package cc.ryanc.halo.web.interceptor;
 
-import cc.ryanc.halo.model.dto.HaloConst;
 import org.springframework.stereotype.Component;
 import org.springframework.web.servlet.HandlerInterceptor;
 import org.springframework.web.servlet.ModelAndView;
@@ -8,6 +7,8 @@ import org.springframework.web.servlet.ModelAndView;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import static cc.ryanc.halo.model.dto.HaloConst.USER_SESSION_KEY;
+
 /**
  * 
  *     后台登录控制器
@@ -21,7 +22,7 @@ public class LoginInterceptor implements HandlerInterceptor {
 
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        final Object obj = request.getSession().getAttribute(HaloConst.USER_SESSION_KEY);
+        final Object obj = request.getSession().getAttribute(USER_SESSION_KEY);
         //如果user不为空则放行
         if (null != obj) {
             return true;
diff --git a/src/main/resources/application-dev.yaml b/src/main/resources/application-dev.yaml
new file mode 100755
index 000000000..3f2d6f807
--- /dev/null
+++ b/src/main/resources/application-dev.yaml
@@ -0,0 +1,57 @@
+server:
+  port: 8090
+  use-forward-headers: true
+  undertow:
+    io-threads: 2
+    worker-threads: 36
+    buffer-size: 1024
+    directBuffers: true
+  servlet:
+    session:
+      timeout: 86400s
+spring:
+  datasource:
+    type: com.zaxxer.hikari.HikariDataSource
+
+    # H2database 配置
+    driver-class-name: org.h2.Driver
+    url: jdbc:h2:file:~/halo/halo-dev
+    username: admin
+    password: 123456
+
+    #MySql配置
+#    driver-class-name: com.mysql.cj.jdbc.Driver
+#    url: jdbc:mysql://127.0.0.1:3306/halodb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
+#    username: root
+#    password: 123456
+
+  h2:
+    console:
+      settings:
+        web-allow-others: true
+      path: /h2-console
+      enabled: true
+  jpa:
+    hibernate:
+      ddl-auto: update
+    show-sql: true
+  freemarker:
+    allow-request-override: false
+    cache: false
+    check-template-location: true
+    charset: utf-8
+    content-type: text/html
+    expose-request-attributes: false
+    expose-session-attributes: false
+    expose-spring-macro-helpers: true
+    suffix: .ftl
+    settings:
+      auto_import: /spring.ftl as spring
+  servlet:
+    multipart:
+      max-file-size: 10MB
+      max-request-size: 10MB
+
+  # 多语言资源文件路径
+  messages:
+    basename: i18n/messages
\ No newline at end of file
diff --git a/src/main/resources/application-prod.yaml b/src/main/resources/application-prod.yaml
new file mode 100755
index 000000000..d5f36e489
--- /dev/null
+++ b/src/main/resources/application-prod.yaml
@@ -0,0 +1,61 @@
+server:
+  port: 8090
+  use-forward-headers: true
+  undertow:
+    io-threads: 2
+    worker-threads: 36
+    buffer-size: 1024
+    directBuffers: true
+  servlet:
+    session:
+      timeout: 86400s
+spring:
+  datasource:
+    type: com.zaxxer.hikari.HikariDataSource
+
+    # H2database 配置
+    driver-class-name: org.h2.Driver
+    url: jdbc:h2:file:~/halo/halo
+    username: admin
+    password: 123456
+
+    #MySql配置
+#    driver-class-name: com.mysql.cj.jdbc.Driver
+#    url: jdbc:mysql://127.0.0.1:3306/halodb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
+#    username: root
+#    password: 123456
+
+  h2:
+    console:
+      settings:
+        web-allow-others: true
+      path: /h2-console
+      enabled: true
+  jpa:
+    hibernate:
+      ddl-auto: update
+    show-sql: false
+  freemarker:
+    allow-request-override: false
+    cache: false
+    check-template-location: true
+    charset: utf-8
+    content-type: text/html
+    expose-request-attributes: false
+    expose-session-attributes: false
+    expose-spring-macro-helpers: true
+    suffix: .ftl
+    settings:
+      auto_import: /spring.ftl as spring
+  servlet:
+    multipart:
+      max-file-size: 10MB
+      max-request-size: 10MB
+
+  # 多语言资源文件路径
+  messages:
+    basename: i18n/messages
+logging:
+  file: ./logs/log.log
+
+
diff --git a/src/main/resources/application-test.yaml b/src/main/resources/application-test.yaml
new file mode 100755
index 000000000..abcb4a717
--- /dev/null
+++ b/src/main/resources/application-test.yaml
@@ -0,0 +1,46 @@
+server:
+  use-forward-headers: true
+  undertow:
+    io-threads: 2
+    worker-threads: 36
+    buffer-size: 1024
+    directBuffers: true
+  servlet:
+    session:
+      timeout: 86400s
+spring:
+  datasource:
+    type: com.zaxxer.hikari.HikariDataSource
+    # H2database 配置
+    driver-class-name: org.h2.Driver
+    url: jdbc:h2:file:~/halo/halo-test
+    username: admin
+    password: 123456
+  jpa:
+    hibernate:
+      ddl-auto: update
+    show-sql: true
+  freemarker:
+    allow-request-override: false
+    cache: false
+    check-template-location: true
+    charset: utf-8
+    content-type: text/html
+    expose-request-attributes: false
+    expose-session-attributes: false
+    expose-spring-macro-helpers: true
+    suffix: .ftl
+    settings:
+      auto_import: /spring.ftl as spring
+  servlet:
+    multipart:
+      max-file-size: 10MB
+      max-request-size: 10MB
+
+  # 多语言资源文件路径
+  messages:
+    basename: i18n/messages
+logging:
+  file: ./logs/log.log
+
+
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index d5f36e489..90385b2bc 100755
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -1,61 +1,3 @@
-server:
-  port: 8090
-  use-forward-headers: true
-  undertow:
-    io-threads: 2
-    worker-threads: 36
-    buffer-size: 1024
-    directBuffers: true
-  servlet:
-    session:
-      timeout: 86400s
 spring:
-  datasource:
-    type: com.zaxxer.hikari.HikariDataSource
-
-    # H2database 配置
-    driver-class-name: org.h2.Driver
-    url: jdbc:h2:file:~/halo/halo
-    username: admin
-    password: 123456
-
-    #MySql配置
-#    driver-class-name: com.mysql.cj.jdbc.Driver
-#    url: jdbc:mysql://127.0.0.1:3306/halodb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
-#    username: root
-#    password: 123456
-
-  h2:
-    console:
-      settings:
-        web-allow-others: true
-      path: /h2-console
-      enabled: true
-  jpa:
-    hibernate:
-      ddl-auto: update
-    show-sql: false
-  freemarker:
-    allow-request-override: false
-    cache: false
-    check-template-location: true
-    charset: utf-8
-    content-type: text/html
-    expose-request-attributes: false
-    expose-session-attributes: false
-    expose-spring-macro-helpers: true
-    suffix: .ftl
-    settings:
-      auto_import: /spring.ftl as spring
-  servlet:
-    multipart:
-      max-file-size: 10MB
-      max-request-size: 10MB
-
-  # 多语言资源文件路径
-  messages:
-    basename: i18n/messages
-logging:
-  file: ./logs/log.log
-
-
+  profiles:
+    active: prod
\ No newline at end of file
diff --git a/src/main/resources/robots.txt b/src/main/resources/robots.txt
deleted file mode 100644
index f9b88f793..000000000
--- a/src/main/resources/robots.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-User-agent: *
-Disallow: /admin/
-Sitemap: /sitemap.xml
\ No newline at end of file
diff --git a/src/main/resources/templates/admin/admin_option.ftl b/src/main/resources/templates/admin/admin_option.ftl
index 2f25bc13d..9ac6a2fbd 100755
--- a/src/main/resources/templates/admin/admin_option.ftl
+++ b/src/main/resources/templates/admin/admin_option.ftl
@@ -322,6 +322,23 @@
                                             
                                         
                                     
+                                    
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+