diff --git a/CHANGELOG.md b/CHANGELOG.md index 612e4c142..dd76c04a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # CHANGELOG +# 1.3.1 + +# Fixed + +- 修复自定义页面设置中的地址预览出现 undefined 的问题。 +- 升级 fastjson 版本为 `1.2.67`。 + # 1.3.0 ## Feature diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3aaeb463c..d2a092b4c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,14 +2,48 @@ ### 开发步骤 -1. `Fork` 本仓库到你自己的 Github。 -2. `Clone` 你刚刚 Fork 的仓库到本地。 -3. 执行 `git checkout dev` 切换到 `dev` 分支并进行开发。 -4. 提交代码到自己的仓库。 -5. 回到自己的仓库页面,选择 `New pull request` 按钮,创建 `Pull request` 到原仓库的 `dev` 分支。 -6. 等待合并。 +#### 1. Fork 此仓库 + +点击右上角的 `fork` 按钮即可。 + +#### 2. Clone 仓库到本地 + +```bash +git clone https://github.com/{YOUR_USERNAME}/halo + +git submodule init + +git submodule update +``` + +#### 3. 创建新的开发分支 + +```bash +git checkout -b {BRANCH_NAME} +``` + +#### 4. 提交代码 + +```bash +git push origin {BRANCH_NAME} +``` + +#### 5. 提交 pull request + +回到自己的仓库页面,选择 `New pull request` 按钮,创建 `Pull request` 到原仓库的 `master` 分支。 + +然后等待我们 Review 即可,如有 `Change Request`,再本地修改之后再次提交即可。 + +#### 6. 更新主仓库代码到自己的仓库 + +```bash +git remote add upstream git@github.com:halo-dev/halo.git + +git pull upstream master + +git push +``` ### 开发规范 -1. 在提交前请使用 IDE 格式化代码。 -2. 不接受创建 `Pull request` 到 `master` 分支。 \ No newline at end of file +请参考 [https://halo.run/archives/code-style](https://halo.run/archives/code-style),请确保所有代码格式化之后再提交。 \ No newline at end of file diff --git a/README.md b/README.md index ce6f0647e..a766a5f38 100755 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ 另外,写给想自己拉代码编译运行的同学: -> 目前我们的开发分支即 master,肯定会有很多小问题,不要运行不起来就跑过来吐槽什么代码开源不完整之类的,多找找自己的原因。同时建议下载最新 release 版本的代码,或者在 master 分支执行 `git checkout v1.3.0`。 +> 目前我们的开发分支即 master,肯定会有很多小问题,不要运行不起来就跑过来吐槽什么代码开源不完整之类的,多找找自己的原因。同时建议下载最新 release 版本的代码,或者在 master 分支执行 `git checkout v1.3.1`。 PS:实在不想写这个声明(影响 README 的美观),但是就目前来看,写在 README 上是有必要的,因为大部分遇到问题的人都不会去仔细阅读文档。 @@ -35,13 +35,13 @@ PS:实在不想写这个声明(影响 README 的美观),但是就目前 ### 下载最新的 Halo 安装包 ```bash -curl -L https://github.com/halo-dev/halo/releases/download/v1.3.0/halo-1.3.0.jar --output halo-latest.jar +curl -L https://github.com/halo-dev/halo/releases/download/v1.3.1/halo-1.3.1.jar --output halo-latest.jar ``` 或者 ```bash -wget https://github.com/halo-dev/halo/releases/download/v1.3.0/halo-1.3.0.jar -O halo-latest.jar +wget https://github.com/halo-dev/halo/releases/download/v1.3.1/halo-1.3.1.jar -O halo-latest.jar ``` ### 启动 Halo diff --git a/build.gradle b/build.gradle index ac3ea2c11..6d0f88b36 100644 --- a/build.gradle +++ b/build.gradle @@ -1,22 +1,22 @@ plugins { - id 'org.springframework.boot' version '2.2.5.RELEASE' - id 'io.spring.dependency-management' version '1.0.9.RELEASE' - id 'checkstyle' - id 'java' + id "org.springframework.boot" version "2.2.5.RELEASE" + id "io.spring.dependency-management" version "1.0.9.RELEASE" + id "checkstyle" + id "java" } -group = 'run.halo.app' -version = '1.3.0' -description = 'Halo, An excellent open source blog publishing application.' +group = "run.halo.app" +version = "1.3.1" +description = "Halo, An excellent open source blog publishing application." java { - archivesBaseName = 'halo' + archivesBaseName = "halo" sourceCompatibility = JavaVersion.VERSION_1_8 } repositories { maven { - url 'https://maven.aliyun.com/nexus/content/groups/public' + url "https://maven.aliyun.com/nexus/content/groups/public" } mavenCentral() jcenter() @@ -24,8 +24,8 @@ repositories { configurations { implementation { - exclude module: 'spring-boot-starter-tomcat' - exclude module: 'slf4j-log4j12' + exclude module: "spring-boot-starter-tomcat" + exclude module: "slf4j-log4j12" } developmentOnly @@ -39,95 +39,100 @@ configurations { } } -bootJar { - manifest { - attributes('Implementation-Title': 'Halo Application', - 'Implementation-Version': archiveVersion) + +springBoot { + buildInfo() +} + +// gradle 的 Property Expansion 的占位符 ${..} 与 springboot 的占位符 冲突, springboot 的需要转义 \${..} +processResources { + filesMatching("application.yaml") { + expand(project.properties) } } ext { - set('hutoolVersion', "5.2.3") - set('upyunSdkVersion', "4.2.0") - set('qiniuSdkVersion', "7.2.28") - set('aliyunSdkVersion', "3.8.1") - set('baiduSdkVersion', "0.10.36") - set('qcloudSdkVersion', "5.6.18") - set('swaggerVersion', "2.9.2") - set('commonsLangVersion', "3.9") - set('httpclientVersion', "4.5.12") - set('dataformatYamlVersion', "2.10.3") - set('jgitVersion', "5.7.0.202003110725-r") - set('flexmarkVersion', "0.60.2") - set('thumbnailatorVersion', "0.4.11") - set('image4jVersion', "0.7zensight1") - set('flywayVersion', "6.3.1") - set('h2Version', "1.4.196") - set('levelDbVersion', "0.12") - set('jsonVersion', "20190722") - set('fastJsonVersion', "1.2.66") - set('annotationsVersion',"3.0.1") + hutoolVersion = "5.2.3" + upyunSdkVersion = "4.2.0" + qiniuSdkVersion = "7.2.28" + aliyunSdkVersion = "3.8.1" + baiduSdkVersion = "0.10.36" + qcloudSdkVersion = "5.6.18" + swaggerVersion = "2.9.2" + commonsLangVersion = "3.9" + httpclientVersion = "4.5.12" + dataformatYamlVersion = "2.10.3" + jgitVersion = "5.7.0.202003110725-r" + flexmarkVersion = "0.60.2" + thumbnailatorVersion = "0.4.11" + image4jVersion = "0.7zensight1" + flywayVersion = "6.3.1" + h2Version = "1.4.196" + levelDbVersion = "0.12" + jsonVersion = "20190722" + fastJsonVersion = "1.2.67" + annotationsVersion = "3.0.1" } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-actuator' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-undertow' - implementation 'org.springframework.boot:spring-boot-starter-freemarker' + implementation "org.springframework.boot:spring-boot-starter-actuator" + implementation "org.springframework.boot:spring-boot-starter-data-jpa" + implementation "org.springframework.boot:spring-boot-starter-web" + implementation "org.springframework.boot:spring-boot-starter-undertow" + implementation "org.springframework.boot:spring-boot-starter-freemarker" implementation "com.sun.mail:jakarta.mail" - implementation "cn.hutool:hutool-core:${hutoolVersion}" - implementation "cn.hutool:hutool-crypto:${hutoolVersion}" - implementation "cn.hutool:hutool-extra:${hutoolVersion}" - implementation "com.upyun:java-sdk:${upyunSdkVersion}" - implementation "com.qiniu:qiniu-java-sdk:${qiniuSdkVersion}" - implementation "com.aliyun.oss:aliyun-sdk-oss:${aliyunSdkVersion}" - implementation "com.baidubce:bce-java-sdk:${baiduSdkVersion}" - implementation "com.qcloud:cos_api:${qcloudSdkVersion}" - implementation "io.springfox:springfox-swagger2:${swaggerVersion}" - implementation "io.springfox:springfox-swagger-ui:${swaggerVersion}" - implementation "org.apache.commons:commons-lang3:${commonsLangVersion}" - implementation "org.apache.httpcomponents:httpclient:${httpclientVersion}" - implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${dataformatYamlVersion}" - implementation "org.eclipse.jgit:org.eclipse.jgit:${jgitVersion}" - implementation "com.google.code.findbugs:annotations:${annotationsVersion}" + implementation "cn.hutool:hutool-core:$hutoolVersion" + implementation "cn.hutool:hutool-crypto:$hutoolVersion" + implementation "cn.hutool:hutool-extra:$hutoolVersion" + implementation "com.upyun:java-sdk:$upyunSdkVersion" + implementation "com.qiniu:qiniu-java-sdk:$qiniuSdkVersion" + implementation "com.aliyun.oss:aliyun-sdk-oss:$aliyunSdkVersion" + implementation "com.baidubce:bce-java-sdk:$baiduSdkVersion" + implementation "com.qcloud:cos_api:$qcloudSdkVersion" + implementation "io.springfox:springfox-swagger2:$swaggerVersion" + implementation "io.springfox:springfox-swagger-ui:$swaggerVersion" + implementation "org.apache.commons:commons-lang3:$commonsLangVersion" + implementation "org.apache.httpcomponents:httpclient:$httpclientVersion" + implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$dataformatYamlVersion" + implementation "org.eclipse.jgit:org.eclipse.jgit:$jgitVersion" + implementation "com.google.code.findbugs:annotations:$annotationsVersion" - implementation "com.vladsch.flexmark:flexmark:${flexmarkVersion}" - implementation "com.vladsch.flexmark:flexmark-ext-attributes:${flexmarkVersion}" - implementation "com.vladsch.flexmark:flexmark-ext-autolink:${flexmarkVersion}" - implementation "com.vladsch.flexmark:flexmark-ext-emoji:${flexmarkVersion}" - implementation "com.vladsch.flexmark:flexmark-ext-escaped-character:${flexmarkVersion}" - implementation "com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:${flexmarkVersion}" - implementation "com.vladsch.flexmark:flexmark-ext-gfm-tasklist:${flexmarkVersion}" - implementation "com.vladsch.flexmark:flexmark-ext-ins:${flexmarkVersion}" - implementation "com.vladsch.flexmark:flexmark-ext-media-tags:${flexmarkVersion}" - implementation "com.vladsch.flexmark:flexmark-ext-tables:${flexmarkVersion}" - implementation "com.vladsch.flexmark:flexmark-ext-toc:${flexmarkVersion}" - implementation "com.vladsch.flexmark:flexmark-ext-superscript:${flexmarkVersion}" - implementation "com.vladsch.flexmark:flexmark-ext-yaml-front-matter:${flexmarkVersion}" - implementation "com.vladsch.flexmark:flexmark-ext-gitlab:${flexmarkVersion}" -// implementation "com.vladsch.flexmark:flexmark-html-parser:${flexmarkVersion}" + implementation "com.vladsch.flexmark:flexmark:$flexmarkVersion" + implementation "com.vladsch.flexmark:flexmark-ext-attributes:$flexmarkVersion" + implementation "com.vladsch.flexmark:flexmark-ext-autolink:$flexmarkVersion" + implementation "com.vladsch.flexmark:flexmark-ext-emoji:$flexmarkVersion" + implementation "com.vladsch.flexmark:flexmark-ext-escaped-character:$flexmarkVersion" + implementation "com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:$flexmarkVersion" + implementation "com.vladsch.flexmark:flexmark-ext-gfm-tasklist:$flexmarkVersion" + implementation "com.vladsch.flexmark:flexmark-ext-ins:$flexmarkVersion" + implementation "com.vladsch.flexmark:flexmark-ext-media-tags:$flexmarkVersion" + implementation "com.vladsch.flexmark:flexmark-ext-tables:$flexmarkVersion" + implementation "com.vladsch.flexmark:flexmark-ext-toc:$flexmarkVersion" + implementation "com.vladsch.flexmark:flexmark-ext-superscript:$flexmarkVersion" + implementation "com.vladsch.flexmark:flexmark-ext-yaml-front-matter:$flexmarkVersion" + implementation "com.vladsch.flexmark:flexmark-ext-gitlab:$flexmarkVersion" +// implementation "com.vladsch.flexmark:flexmark-html-parser:$flexmarkVersion" - implementation "net.coobird:thumbnailator:${thumbnailatorVersion}" - implementation "net.sf.image4j:image4j:${image4jVersion}" - implementation "org.flywaydb:flyway-core:${flywayVersion}" + implementation "net.coobird:thumbnailator:$thumbnailatorVersion" + implementation "net.sf.image4j:image4j:$image4jVersion" + implementation "org.flywaydb:flyway-core:$flywayVersion" - implementation "org.json:json:${jsonVersion}" - implementation "com.alibaba:fastjson:${fastJsonVersion}" + implementation "org.json:json:$jsonVersion" + implementation "com.alibaba:fastjson:$fastJsonVersion" - implementation "org.iq80.leveldb:leveldb:${levelDbVersion}" - runtimeOnly "com.h2database:h2:${h2Version}" - runtimeOnly 'mysql:mysql-connector-java' + implementation "org.iq80.leveldb:leveldb:$levelDbVersion" + runtimeOnly "com.h2database:h2:$h2Version" + runtimeOnly "mysql:mysql-connector-java" - compileOnly 'org.projectlombok:lombok' - annotationProcessor 'org.projectlombok:lombok' + compileOnly "org.projectlombok:lombok" + annotationProcessor "org.projectlombok:lombok" - testCompileOnly 'org.projectlombok:lombok' - testAnnotationProcessor 'org.projectlombok:lombok' + testCompileOnly "org.projectlombok:lombok" + testAnnotationProcessor "org.projectlombok:lombok" - testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation "org.springframework.boot:spring-boot-starter-test" - developmentOnly 'org.springframework.boot:spring-boot-devtools' + developmentOnly "org.springframework.boot:spring-boot-devtools" } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 84a906615..6623300be 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/run/halo/app/config/HaloConfiguration.java b/src/main/java/run/halo/app/config/HaloConfiguration.java index 26e38374a..c330e4e10 100644 --- a/src/main/java/run/halo/app/config/HaloConfiguration.java +++ b/src/main/java/run/halo/app/config/HaloConfiguration.java @@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.info.BuildProperties; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -15,8 +16,10 @@ import run.halo.app.cache.AbstractStringCacheStore; import run.halo.app.cache.InMemoryCacheStore; import run.halo.app.cache.LevelCacheStore; import run.halo.app.config.properties.HaloProperties; +import run.halo.app.model.support.HaloConst; import run.halo.app.utils.HttpClientUtils; +import javax.annotation.PostConstruct; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; @@ -34,6 +37,9 @@ public class HaloConfiguration { @Autowired HaloProperties haloProperties; + @Autowired + BuildProperties buildProperties; + @Bean public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) { builder.failOnEmptyBeans(false); @@ -70,4 +76,8 @@ public class HaloConfiguration { } + @PostConstruct + public void init() { + HaloConst.HALO_VERSION = buildProperties.getVersion(); + } } diff --git a/src/main/java/run/halo/app/handler/theme/config/impl/YamlThemeConfigResolverImpl.java b/src/main/java/run/halo/app/handler/theme/config/impl/YamlThemeConfigResolverImpl.java index 77c2d5c56..aed539068 100644 --- a/src/main/java/run/halo/app/handler/theme/config/impl/YamlThemeConfigResolverImpl.java +++ b/src/main/java/run/halo/app/handler/theme/config/impl/YamlThemeConfigResolverImpl.java @@ -113,8 +113,8 @@ public class YamlThemeConfigResolverImpl implements ThemeConfigResolver { item.setName(itemMap.get("name").toString()); item.setLabel(itemMap.getOrDefault("label", item.getName()).toString()); Object dataType = itemMap.getOrDefault("data-type", itemMap.get("dataType")); - item.setDataType(DataType.typeOf(dataType)); item.setType(InputType.typeOf(itemMap.get("type"))); + item.setDataType(item.getType().equals(InputType.SWITCH) ? DataType.BOOL : DataType.typeOf(dataType)); item.setDefaultValue(itemMap.get("default")); item.setPlaceholder(itemMap.getOrDefault("placeholder", "").toString()); item.setDescription(itemMap.getOrDefault("description", "").toString()); @@ -140,8 +140,8 @@ public class YamlThemeConfigResolverImpl implements ThemeConfigResolver { item.setName(key.toString()); item.setLabel(itemMap.getOrDefault("label", item.getName()).toString()); Object dataType = itemMap.getOrDefault("data-type", itemMap.get("dataType")); - item.setDataType(DataType.typeOf(dataType)); item.setType(InputType.typeOf(itemMap.get("type"))); + item.setDataType(item.getType().equals(InputType.SWITCH) ? DataType.BOOL : DataType.typeOf(dataType)); item.setDefaultValue(itemMap.get("default")); item.setPlaceholder(itemMap.getOrDefault("placeholder", "").toString()); item.setDescription(itemMap.getOrDefault("description", "").toString()); diff --git a/src/main/java/run/halo/app/model/entity/Attachment.java b/src/main/java/run/halo/app/model/entity/Attachment.java index 1ca0ea10e..35c26bb55 100644 --- a/src/main/java/run/halo/app/model/entity/Attachment.java +++ b/src/main/java/run/halo/app/model/entity/Attachment.java @@ -17,7 +17,9 @@ import javax.persistence.*; */ @Data @Entity -@Table(name = "attachments") +@Table(name = "attachments", + indexes = {@Index(name = "attachments_media_type", columnList = "media_type"), + @Index(name = "attachments_create_time", columnList = "create_time")}) @ToString @EqualsAndHashCode(callSuper = true) public class Attachment extends BaseEntity { diff --git a/src/main/java/run/halo/app/model/entity/Menu.java b/src/main/java/run/halo/app/model/entity/Menu.java index ff1f28cc4..579874e04 100644 --- a/src/main/java/run/halo/app/model/entity/Menu.java +++ b/src/main/java/run/halo/app/model/entity/Menu.java @@ -16,7 +16,9 @@ import javax.persistence.*; */ @Data @Entity -@Table(name = "menus", indexes = {@Index(name = "menus_parent_id", columnList = "parent_id")}) +@Table(name = "menus", + indexes = {@Index(name = "menus_parent_id", columnList = "parent_id"), + @Index(name = "menus_name", columnList = "name")}) @ToString @EqualsAndHashCode(callSuper = true) public class Menu extends BaseEntity { diff --git a/src/main/java/run/halo/app/model/entity/Photo.java b/src/main/java/run/halo/app/model/entity/Photo.java index 5c7dc4ea1..b42b0a51f 100644 --- a/src/main/java/run/halo/app/model/entity/Photo.java +++ b/src/main/java/run/halo/app/model/entity/Photo.java @@ -16,7 +16,9 @@ import java.util.Date; */ @Data @Entity -@Table(name = "photos", indexes = {@Index(name = "photos_team", columnList = "team")}) +@Table(name = "photos", + indexes = {@Index(name = "photos_team", columnList = "team"), + @Index(name = "photos_create_time", columnList = "create_time")}) @ToString @EqualsAndHashCode(callSuper = true) public class Photo extends BaseEntity { diff --git a/src/main/java/run/halo/app/model/enums/InputType.java b/src/main/java/run/halo/app/model/enums/InputType.java index b2640b3fc..7b4c6f502 100644 --- a/src/main/java/run/halo/app/model/enums/InputType.java +++ b/src/main/java/run/halo/app/model/enums/InputType.java @@ -44,7 +44,12 @@ public enum InputType { /** * Attachment picker input type */ - ATTACHMENT; + ATTACHMENT, + + /** + * Switch input type, only true or false + */ + SWITCH; /** * Convert type to input type. diff --git a/src/main/java/run/halo/app/model/support/HaloConst.java b/src/main/java/run/halo/app/model/support/HaloConst.java index 042be6cfe..8e915c6b6 100644 --- a/src/main/java/run/halo/app/model/support/HaloConst.java +++ b/src/main/java/run/halo/app/model/support/HaloConst.java @@ -50,10 +50,6 @@ public class HaloConst { */ public final static String DEFAULT_THEME_ID = "caicai_anatole"; - /** - * Version constant. (Available in production environment) - */ - public static final String HALO_VERSION; /** * Path separator. */ @@ -143,8 +139,9 @@ public class HaloConst { */ public static String USER_SESSION_KEY = "user_session"; - static { - // Set version - HALO_VERSION = HaloConst.class.getPackage().getImplementationVersion(); - } + /** + * Version constant. + */ + public static String HALO_VERSION = null; + } diff --git a/src/main/java/run/halo/app/service/impl/CommentBlackListServiceImpl.java b/src/main/java/run/halo/app/service/impl/CommentBlackListServiceImpl.java index f01979412..30f80debc 100644 --- a/src/main/java/run/halo/app/service/impl/CommentBlackListServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/CommentBlackListServiceImpl.java @@ -10,9 +10,9 @@ import run.halo.app.repository.PostCommentRepository; import run.halo.app.service.CommentBlackListService; import run.halo.app.service.OptionService; import run.halo.app.service.base.AbstractCrudService; +import run.halo.app.utils.DateTimeUtils; import java.time.LocalDateTime; -import java.time.ZoneId; import java.util.Date; import java.util.Optional; @@ -26,7 +26,6 @@ import java.util.Optional; @Service @Slf4j public class CommentBlackListServiceImpl extends AbstractCrudService implements CommentBlackListService { - private static final ZoneId ZONE_ID = ZoneId.of("Asia/Shanghai"); private final CommentBlackListRepository commentBlackListRepository; private final PostCommentRepository postCommentRepository; private final OptionService optionService; @@ -50,10 +49,9 @@ public class CommentBlackListServiceImpl extends AbstractCrudService blackList = commentBlackListRepository.findByIpAddress(ipAddress); LocalDateTime now = LocalDateTime.now(); - Date endTime = new Date(now.atZone(ZONE_ID).toInstant().toEpochMilli()); + Date endTime = new Date(DateTimeUtils.toEpochMilli(now)); Integer banTime = optionService.getByPropertyOrDefault(CommentProperties.COMMENT_BAN_TIME, Integer.class, 10); - Date startTime = new Date(now.minusMinutes(banTime) - .atZone(ZONE_ID).toInstant().toEpochMilli()); + Date startTime = new Date(DateTimeUtils.toEpochMilli(now.minusMinutes(banTime))); Integer range = optionService.getByPropertyOrDefault(CommentProperties.COMMENT_RANGE, Integer.class, 30); boolean isPresent = postCommentRepository.countByIpAndTime(ipAddress, startTime, endTime) >= range; if (isPresent && blackList.isPresent()) { @@ -61,10 +59,10 @@ public class CommentBlackListServiceImpl extends AbstractCrudService result <= 0).ifPresent(result -> log.error("更新评论封禁时间失败")); + .filter(result -> result <= 0).ifPresent(result -> log.error("更新评论封禁时间失败")); } private Date getBanTime(LocalDateTime localDateTime, Integer banTime) { - return new Date(localDateTime.plusMinutes(banTime).atZone(ZONE_ID).toInstant().toEpochMilli()); + return new Date(DateTimeUtils.toEpochMilli(localDateTime.plusMinutes(banTime))); } } diff --git a/src/main/java/run/halo/app/utils/DateTimeUtils.java b/src/main/java/run/halo/app/utils/DateTimeUtils.java new file mode 100644 index 000000000..e5c387156 --- /dev/null +++ b/src/main/java/run/halo/app/utils/DateTimeUtils.java @@ -0,0 +1,543 @@ +package run.halo.app.utils; + +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; +import java.time.temporal.Temporal; + +import static cn.hutool.core.date.DatePattern.*; + +/** + * 日期工具 + * + * @author LeiXinXin + * @date 2019/12/10 + */ +public class DateTimeUtils { + /** + * 标准日期格式 {@link DateTimeFormatter}:yyyyMMddHHmmssSSS + */ + public static final DateTimeFormatter PURE_DATETIME_MS_FORMATTER = new DateTimeFormatterBuilder() + .appendPattern(PURE_DATETIME_PATTERN) + .appendValue(ChronoField.MILLI_OF_SECOND, 3) + .toFormatter(); + /** + * 标准日期格式 {@link DateTimeFormatter}:yyyyMMdd + */ + public static final DateTimeFormatter PURE_DATE_FORMATTER = DateTimeFormatter.ofPattern(PURE_DATE_PATTERN); + /** + * 标准日期格式 {@link DateTimeFormatter}:yyyy-MM-dd + */ + public static final DateTimeFormatter NORM_DATE_FORMATTER = DateTimeFormatter.ofPattern(NORM_DATE_PATTERN); + /** + * 标准日期格式:HHmm + */ + public final static String TIME_PATTERN = "HHmm"; + /** + * 标准日期格式 {@link DateTimeFormatter} HHmm + */ + public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(TIME_PATTERN); + /** + * 标准日期格式:HH:mm + */ + public final static String NORM_TIME_PATTERN = "HH:mm"; + /** + * 标准日期格式 {@link DateTimeFormatter} HH:mm + */ + public static final DateTimeFormatter NORM_TIME_FORMATTER = DateTimeFormatter.ofPattern(NORM_TIME_PATTERN); + /** + * 标准日期时间格式,精确到秒 {@link DateTimeFormatter}:yyyy-MM-dd HH:mm:ss + */ + public static final DateTimeFormatter NORM_DATETIME_FORMATTER = DateTimeFormatter.ofPattern(NORM_DATETIME_PATTERN); + /** + * 上海时区格式 + */ + public static final String CTT = ZoneId.SHORT_IDS.get("CTT"); + /** + * 上海时区 + */ + public static final ZoneId CTT_ZONE_ID = ZoneId.of(CTT); + + + private DateTimeUtils() { + } + + /** + * 获取当前时间,默认为上海时区 + * + * @return Now LocalDateTime + */ + public static LocalDateTime now() { + return now(CTT_ZONE_ID); + } + + /** + * 根据时区获取当前时间 + * + * @param zoneId 时区 + * @return Now LocalDateTime + */ + public static LocalDateTime now(ZoneId zoneId) { + return LocalDateTime.now(zoneId); + } + + /** + * 按 yyyyMMdd 格式化 + * + * @param localDateTime 日期时间 + * @return Result + */ + public static String formatDate(LocalDateTime localDateTime) { + return format(localDateTime, PURE_DATE_FORMATTER); + } + + /** + * 按 yyyyMMdd 格式化 + * + * @param localDate 日期 + * @return Result + */ + public static String formatDate(LocalDate localDate) { + return format(localDate, PURE_DATE_FORMATTER); + } + + /** + * 按 HHmm 格式化 + * + * @param localDateTime 时间 + * @return Result + */ + public static String formatTime(LocalDateTime localDateTime) { + return format(localDateTime, TIME_FORMATTER); + } + + /** + * 按 HHmm 格式化 + * + * @param localTime 时间 + * @return Result + */ + public static String formatTime(LocalTime localTime) { + return format(localTime, TIME_FORMATTER); + } + + /** + * 按 yyyy-MM-dd HH:mm:ss 格式格式化 + * + * @param localDateTime 时间 + * @return Result + */ + public static String formatDateTime(LocalDateTime localDateTime) { + return format(localDateTime, NORM_DATETIME_FORMATTER); + } + + /** + * 根据日期格式化时间 + * + * @param localDateTime 时间 + * @param formatter 时间格式 + * @return Result + */ + public static String format(LocalDateTime localDateTime, DateTimeFormatter formatter) { + return localDateTime.format(formatter); + } + + /** + * 根据时间格式,格式化时间 + * + * @param localTime 时间 + * @param formatter 时间格式 + * @return Result + */ + public static String format(LocalTime localTime, DateTimeFormatter formatter) { + return localTime.format(formatter); + } + + /** + * 根据日期格式,格式化日期 + * + * @param localDate 日期 + * @param formatter 日期格式 + * @return Result + */ + public static String format(LocalDate localDate, DateTimeFormatter formatter) { + return localDate.format(formatter); + } + + /** + * 按照上海时区,解析香港格式的时间 + *

+ * 时间格式 yyyyMMddHHmmssSSS + * + * @param time 时间 + * @return LocalDateTime + */ + public static LocalDateTime parseCttDateTime(String time) { + return parse(time, PURE_DATETIME_MS_FORMATTER); + } + + /** + * 根据日期格式解析时间 + * + * @param formatter 时间格式 + * @param time 时间 + * @return LocalDateTime + */ + public static LocalDateTime parse(String time, DateTimeFormatter formatter) { + return LocalDateTime.parse(time, formatter); + } + + /** + * to instant by default zoneId(Shanghai) + * + * @param localDateTime 时间 + * @return Instant + */ + public static Instant toInstant(LocalDateTime localDateTime) { + return toInstant(localDateTime, CTT_ZONE_ID); + } + + /** + * To instant by zoneId + * + * @param localDateTime 时间 + * @return Instant + */ + public static Instant toInstant(LocalDateTime localDateTime, ZoneId zoneId) { + return localDateTime.atZone(zoneId).toInstant(); + } + + /** + * 将 localDateTime 转为秒 + * + * @param localDateTime 时间 + * @return 秒 + */ + public static long getEpochSecond(LocalDateTime localDateTime) { + return toInstant(localDateTime).getEpochSecond(); + } + + /** + * 将localDateTime 转为时间戳 + * + * @param localDateTime 时间 + * @return 时间戳 + */ + public static long toEpochMilli(LocalDateTime localDateTime) { + return toInstant(localDateTime).toEpochMilli(); + } + + /** + * 将秒和毫秒设为0 + * + * @param localDateTime 需要被设置的时间 + * @return 返回被设置的LocalDateTime + */ + public static LocalDateTime secondAndNanoSetZero(LocalDateTime localDateTime) { + return localDateTime.withSecond(0).withNano(0); + } + + /** + * 增加一天时间 + * + * @param localDateTime 日期时间 + * @return 新增一天后的 LocalDateTime + */ + public static LocalDateTime plusOneDayToDateTime(LocalDateTime localDateTime) { + return plusDays(localDateTime, 1); + } + + /** + * 增加一天时间 + * + * @param localDateTime 日期时间 + * @param localTime 时间 + * @return 新增一天后的 LocalDateTime + */ + public static LocalDateTime plusOneDay(LocalDateTime localDateTime, LocalTime localTime) { + return plusOneDay(localDateTime.toLocalDate(), localTime); + } + + /** + * 增加一天时间 + * + * @param localDate 日期 + * @param localTime 时间 + * @return 新增一天后的 LocalDateTime + */ + public static LocalDateTime plusOneDay(LocalDate localDate, LocalTime localTime) { + final LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime); + return plusDays(localDateTime, 1); + } + + + /** + * 增加一天时间 + * + * @param localDateTime 日期时间 + * @return 新增一天后的 LocalDate + */ + public static LocalDate plusOneDayToDate(LocalDateTime localDateTime) { + return plusDays(localDateTime, 1).toLocalDate(); + } + + /** + * 根据days新增天数 + * + * @param localDateTime 日期时间 + * @param days 天数 + * @return 新增 days 后的 LocalDateTime + */ + public static LocalDateTime plusDays(LocalDateTime localDateTime, long days) { + return localDateTime.plusDays(days); + } + + /** + * 新增一天 + * + * @param localDate 日期 + * @return 新增一天后的 LocalDate + */ + public static LocalDate plusOneDayToDate(LocalDate localDate) { + return plusDays(localDate, 1); + } + + /** + * 根据days新增天数 + * + * @param localDate 日期 + * @param days 新增的天数 + * @return 新增 days 后的 LocalDate + */ + public static LocalDate plusDays(LocalDate localDate, long days) { + return localDate.plusDays(days); + } + + /** + * 增加1分钟 + * + * @param localDateTime 日期时间 + * @return 返回新增的 LocalDateTime + */ + public static LocalDateTime plusOneMinute(LocalDateTime localDateTime, LocalTime localTime) { + return plusOneMinute(localDateTime.toLocalDate(), localTime); + } + + /** + * 增加1分钟 + * + * @param localDate 日期 + * @param localTime 时间 + * @return 返回新增的 LocalDateTime + */ + public static LocalDateTime plusOneMinute(LocalDate localDate, LocalTime localTime) { + final LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime); + return plusMinutes(localDateTime, 1); + } + + + /** + * 增加1分钟 + * + * @param localDateTime 日期时间 + * @return 返回新增的 LocalDateTime + */ + public static LocalDateTime plusOneMinute(LocalDateTime localDateTime) { + return plusMinutes(localDateTime, 1); + } + + /** + * 增加30分钟 + * + * @param localDateTime 日期时间 + * @return 返回新增的 LocalDateTime + */ + public static LocalDateTime plusThirtyMinute(LocalDateTime localDateTime) { + return plusMinutes(localDateTime, 30); + } + + /** + * 新增1分钟 + * + * @param localDateTime 日期时间 + * @return 返回新增的 LocalTime + */ + public static LocalTime plusOneMinuteToTime(LocalDateTime localDateTime) { + return plusMinutes(localDateTime, 1).toLocalTime(); + } + + /** + * 新增1分钟 + * + * @param localTime 日期时间 + * @return 返回新增的 LocalTime + */ + public static LocalTime plusOneMinute(LocalTime localTime) { + return plusMinutes(localTime, 1); + } + + /** + * 增加30分钟 + * + * @param localTime 日期时间 + * @return 返回新增的 LocalTime + */ + public static LocalTime plusThirtyMinute(LocalTime localTime) { + return plusMinutes(localTime, 30); + } + + /** + * 根据 minutes 新增分钟 + * + * @param localDateTime 日期时间 + * @param minutes 分钟数 + * @return 返回新增的 LocalDateTime + */ + public static LocalDateTime plusMinutes(LocalDateTime localDateTime, long minutes) { + return localDateTime.plusMinutes(minutes); + } + + /** + * 根据 minutes 新增分钟 + * + * @param localTime 时间 + * @param minutes 分钟数 + * @return 返回新增的 LocalTime + */ + public static LocalTime plusMinutes(LocalTime localTime, long minutes) { + return localTime.plusMinutes(minutes); + } + + /** + * 减少 1 分钟 + * + * @param localDateTime 日期时间 + * @param localTime 时间 + * @return 返回新增的 LocalTime + */ + public static LocalDateTime minusOneMinutes(LocalDateTime localDateTime, LocalTime localTime) { + return minusOneMinutes(localDateTime.toLocalDate(), localTime); + } + + + /** + * 减少 1 分钟 + * + * @param localDate 日期 + * @param localTime 时间 + * @return 返回新增的 LocalTime + */ + public static LocalDateTime minusOneMinutes(LocalDate localDate, LocalTime localTime) { + final LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime); + return minusMinutes(localDateTime, 1); + } + + /** + * 减少 1 分钟 + * + * @param localDateTime 日期时间 + * @return 返回新增的 LocalTime + */ + public static LocalDateTime minusOneMinutes(LocalDateTime localDateTime) { + return minusMinutes(localDateTime, 1); + } + + /** + * 根据 minutes 减少分钟 + * + * @param localDateTime 日期时间 + * @param minutes 分钟数 + * @return 返回新增的 LocalTime + */ + public static LocalDateTime minusMinutes(LocalDateTime localDateTime, long minutes) { + return localDateTime.minusMinutes(minutes); + } + + /** + * 根据 minutes 减少分钟 + * + * @param localTime 时间 + * @param minutes 分钟数 + * @return 返回新增的 LocalTime + */ + public static LocalTime minusMinutes(LocalTime localTime, long minutes) { + return localTime.minusMinutes(minutes); + } + + /** + * 判断是否是中午 + * + * @param startInclusive Start + * @return boolean + */ + public static boolean isNoon(LocalDateTime startInclusive) { + LocalDateTime noonDateTime = LocalDateTime.of(startInclusive.toLocalDate(), LocalTime.NOON); + return Duration.between(startInclusive, noonDateTime).isZero(); + } + + /** + * 判断是否是中午 + * + * @param startInclusive Start + * @return boolean + */ + public static boolean isNoon(LocalTime startInclusive) { + return Duration.between(startInclusive, LocalTime.NOON).isZero(); + } + + /** + * 是否是负数,startInclusive 大于 endInclusive 就是负数 + * + * @param startInclusive Start + * @param endInclusive end + * @return boolean + */ + public static boolean isNegative(Temporal startInclusive, Temporal endInclusive) { + return Duration.between(startInclusive, endInclusive).isNegative(); + } + + /** + * 相比是否是0,两个时间一致就是0 + * + * @param startInclusive Start + * @param endInclusive end + * @return boolean + */ + public static boolean isZero(Temporal startInclusive, Temporal endInclusive) { + return Duration.between(startInclusive, endInclusive).isZero(); + } + + /** + * endInclusive 大于或等于 startInclusive + * + * @param startInclusive Start + * @param endInclusive end + * @return boolean + */ + public static boolean isGreaterOrEqual(Temporal startInclusive, Temporal endInclusive) { + return Duration.between(startInclusive, endInclusive).toNanos() >= 0; + } + + /** + * endInclusive 大于 startInclusive + * + * @param startInclusive Start + * @param endInclusive end + * @return boolean + */ + public static boolean isGreater(Temporal startInclusive, Temporal endInclusive) { + return Duration.between(startInclusive, endInclusive).toNanos() > 0; + } + + /** + * endInclusive 小或等于 startInclusive + * + * @param startInclusive Start + * @param endInclusive end + * @return boolean + */ + public static boolean isLessThanOrEqual(Temporal startInclusive, Temporal endInclusive) { + return Duration.between(startInclusive, endInclusive).toNanos() <= 0; + } +} diff --git a/src/main/resources/admin/index.html b/src/main/resources/admin/index.html index 234a21060..343825a42 100644 --- a/src/main/resources/admin/index.html +++ b/src/main/resources/admin/index.html @@ -1 +1 @@ -Halo Dashboard

\ No newline at end of file +Halo Dashboard
\ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index bc7e1daeb..f74c5943c 100755 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -37,7 +37,7 @@ spring: multipart: max-file-size: 10240MB max-request-size: 10240MB - location: ${java.io.tmpdir} + location: \${java.io.tmpdir} management: endpoints: web: @@ -48,7 +48,10 @@ logging: level: run.halo.app: INFO file: - path: ${user.home}/.halo/logs + path: \${user.home}/.halo/logs halo: - download-timeout: 5m \ No newline at end of file + download-timeout: 5m + +application: + version: ${version} diff --git a/src/test/java/run/halo/app/utils/DateTimeUtilsTest.java b/src/test/java/run/halo/app/utils/DateTimeUtilsTest.java new file mode 100644 index 000000000..8f37e5b22 --- /dev/null +++ b/src/test/java/run/halo/app/utils/DateTimeUtilsTest.java @@ -0,0 +1,156 @@ +package run.halo.app.utils; + + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.junit4.SpringRunner; + +import java.time.*; +import java.time.format.DateTimeFormatter; + +/** + * DateTimeUtils 测试用例 + * + * @author LeiXinXin + * @date 2020/1/9 + */ +@RunWith(SpringRunner.class) +public class DateTimeUtilsTest { + /** + * 获取上海时区的当前时间 + */ + @Test + public void nowTest() { + final LocalDateTime now = DateTimeUtils.now().withNano(0); + Assert.assertNotNull(now); + final LocalDateTime ctt = DateTimeUtils.now(ZoneId.of(ZoneId.SHORT_IDS.get("CTT"))).withNano(0); + Assert.assertNotNull(ctt); + + Assert.assertEquals(Duration.between(now, ctt).toMinutes(), 0); + } + + /** + * 格式化日期时间 + */ + @Test + public void formatTest() { + final LocalDateTime now = LocalDateTime.of(2020, 1, 9, 20, 20, 20); + final String formatDate = DateTimeUtils.formatDate(now); + Assert.assertEquals(formatDate, "20200109"); + + final LocalDate localDate = LocalDate.of(2020, 1, 9); + final String localFormatDate = DateTimeUtils.formatDate(localDate); + Assert.assertEquals(localFormatDate, "20200109"); + + final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd"); + final String formatDateTime1 = DateTimeUtils.format(now, dateTimeFormatter); + Assert.assertEquals(formatDateTime1, "2020/01/09"); + + final LocalTime time1 = LocalTime.of(10, 20); + final String formatTime = DateTimeUtils.formatTime(time1); + Assert.assertEquals(formatTime, "1020"); + + final String formatTime1 = DateTimeUtils.format(time1, DateTimeFormatter.ofPattern("HH:mm")); + Assert.assertEquals(formatTime1, "10:20"); + } + + /** + * 增加时间 + */ + @Test + public void plusTest() { + final LocalDateTime now = LocalDateTime.of(2020, 1, 9, 10, 20); + final LocalDateTime localDateTime = DateTimeUtils.plusOneMinute(now); + Assert.assertEquals(localDateTime.toString(), "2020-01-09T10:21"); + + LocalTime localTime = LocalTime.of(7, 30); + final LocalDateTime localDateTime1 = DateTimeUtils.plusOneMinute(now, localTime); + Assert.assertEquals(localDateTime1.toString(), "2020-01-09T07:31"); + + final LocalDate date = LocalDate.of(2020, 1, 3); + final LocalDateTime localDateTime2 = DateTimeUtils.plusOneMinute(date, localTime); + Assert.assertEquals(localDateTime2.toString(), "2020-01-03T07:31"); + + final LocalTime localTime1 = DateTimeUtils.plusOneMinute(localTime); + Assert.assertEquals(localTime1.toString(), "07:31"); + + final LocalDateTime localDateTime3 = DateTimeUtils.plusDays(now, 10); + Assert.assertEquals(localDateTime3.toString(), "2020-01-19T10:20"); + } + + /** + * 解析时间格式为LocalDateTime + */ + @Test + public void parseTest() { + String time = "20200109135500000"; + final LocalDateTime localDateTime = DateTimeUtils.parseCttDateTime(time); + Assert.assertEquals(localDateTime.toString(), "2020-01-09T13:55"); + + String time2 = "2020/1/9 13:56"; + final LocalDateTime dateTime = DateTimeUtils.parse(time2, DateTimeFormatter.ofPattern("yyyy/M/d HH:mm")); + Assert.assertEquals(dateTime.toString(), "2020-01-09T13:56"); + } + + /** + * 减少日期时间 + */ + @Test + public void minusTest() { + final LocalDateTime now = LocalDateTime.of(2020, 1, 3, 14, 20); + final LocalDateTime localDateTime = DateTimeUtils.minusOneMinutes(now); + Assert.assertEquals(localDateTime.toString(), "2020-01-03T14:19"); + } + + /** + * 转为Instant + */ + @Test + public void toInstantTest() { + final LocalDateTime now = LocalDateTime.of(2020, 1, 3, 14, 20); + + final Instant instant = DateTimeUtils.toInstant(now); + Assert.assertEquals(instant.toString(), "2020-01-03T06:20:00Z"); + + final Instant jst = DateTimeUtils.toInstant(now, ZoneId.of(ZoneId.SHORT_IDS.get("JST"))); + Assert.assertEquals(jst.toString(), "2020-01-03T05:20:00Z"); + } + + /** + * 一些其他的使用方法 + */ + @Test + public void other() { + LocalDateTime startInclusive = LocalDateTime.of(2020, 1, 3, 10, 10, 30); + LocalDateTime endInclusive = LocalDateTime.of(2020, 1, 4, 10, 10, 30); + // End 大于等于 Start + final boolean greaterOrEqual = DateTimeUtils.isGreaterOrEqual(startInclusive, endInclusive); + Assert.assertTrue(greaterOrEqual); + + /* + * 小于等于 + */ + final boolean lessThanOrEqual = DateTimeUtils.isLessThanOrEqual(startInclusive, endInclusive); + Assert.assertFalse(lessThanOrEqual); + + /* + * 两个时间的比较是否为0 + */ + final boolean zero = DateTimeUtils.isZero(startInclusive, endInclusive); + Assert.assertFalse(zero); + + // 是否是负数,startInclusive 大于 endInclusive 就是负数 + final boolean negative = DateTimeUtils.isNegative(startInclusive, endInclusive); + Assert.assertFalse(negative); + + // 是否是中午 + final boolean noon = DateTimeUtils.isNoon(LocalTime.of(12, 0)); + Assert.assertTrue(noon); + + // 把纳秒和秒设置为0 + final LocalDateTime localDateTime = LocalDateTime.of(2020, 1, 5, 6, 40, 30, 999); + final LocalDateTime time = DateTimeUtils.secondAndNanoSetZero(localDateTime); + Assert.assertEquals(time.toString(), "2020-01-05T06:40"); + } +} \ No newline at end of file