From 1d6f118774a5a76553c9f1a5697c3a8702763a70 Mon Sep 17 00:00:00 2001 From: smallchill Date: Mon, 17 Jun 2019 00:15:24 +0800 Subject: [PATCH] :tada: 2.3.2.RELEASE --- blade-core-boot/pom.xml | 2 +- .../src/main/resources/bootstrap.yml | 2 +- blade-core-cloud/pom.xml | 2 +- blade-core-develop/pom.xml | 2 +- blade-core-launch/pom.xml | 2 +- .../core/launch/BladeApplication.java | 7 +- .../core/launch/constant/AppConstant.java | 6 +- .../core/launch/service/LauncherService.java | 24 +- blade-core-log/pom.xml | 2 +- blade-core-mybatis/pom.xml | 2 +- blade-core-oss/pom.xml | 32 ++ .../springblade/core/oss/QiniuTemplate.java | 248 +++++++++ .../core/oss/config/OssConfiguration.java | 83 +++ .../springblade/core/oss/model/BladeFile.java | 35 ++ .../springblade/core/oss/model/OssFile.java | 53 ++ .../core/oss/props/OssProperties.java | 65 +++ .../core/oss/rule/BladeOssRule.java | 42 ++ .../springblade/core/oss/rule/OssRule.java | 41 ++ blade-core-secure/pom.xml | 2 +- blade-core-swagger/pom.xml | 2 +- .../core/swagger/SwaggerProperties.java | 2 +- blade-core-test/pom.xml | 31 ++ .../springblade/core/test/BladeBaseTest.java | 30 ++ .../springblade/core/test/BladeBootTest.java | 57 ++ .../core/test/BladeBootTestException.java | 29 + .../core/test/BladeSpringRunner.java | 80 +++ blade-core-tool/pom.xml | 24 +- .../springblade/core/tool/utils/CharPool.java | 36 ++ .../springblade/core/tool/utils/DateUtil.java | 496 +++++++++++++++--- .../springblade/core/tool/utils/FileUtil.java | 385 ++++++++++++++ .../core/tool/utils/StringPool.java | 2 +- pom.xml | 10 +- 32 files changed, 1742 insertions(+), 94 deletions(-) create mode 100644 blade-core-oss/pom.xml create mode 100644 blade-core-oss/src/main/java/org/springblade/core/oss/QiniuTemplate.java create mode 100644 blade-core-oss/src/main/java/org/springblade/core/oss/config/OssConfiguration.java create mode 100644 blade-core-oss/src/main/java/org/springblade/core/oss/model/BladeFile.java create mode 100644 blade-core-oss/src/main/java/org/springblade/core/oss/model/OssFile.java create mode 100644 blade-core-oss/src/main/java/org/springblade/core/oss/props/OssProperties.java create mode 100644 blade-core-oss/src/main/java/org/springblade/core/oss/rule/BladeOssRule.java create mode 100644 blade-core-oss/src/main/java/org/springblade/core/oss/rule/OssRule.java create mode 100644 blade-core-test/pom.xml create mode 100644 blade-core-test/src/main/java/org/springblade/core/test/BladeBaseTest.java create mode 100644 blade-core-test/src/main/java/org/springblade/core/test/BladeBootTest.java create mode 100644 blade-core-test/src/main/java/org/springblade/core/test/BladeBootTestException.java create mode 100644 blade-core-test/src/main/java/org/springblade/core/test/BladeSpringRunner.java create mode 100644 blade-core-tool/src/main/java/org/springblade/core/tool/utils/CharPool.java create mode 100644 blade-core-tool/src/main/java/org/springblade/core/tool/utils/FileUtil.java diff --git a/blade-core-boot/pom.xml b/blade-core-boot/pom.xml index 7e6d247..25dfe7d 100644 --- a/blade-core-boot/pom.xml +++ b/blade-core-boot/pom.xml @@ -5,7 +5,7 @@ org.springblade blade-tool - 2.3.1 + 2.3.2 4.0.0 diff --git a/blade-core-boot/src/main/resources/bootstrap.yml b/blade-core-boot/src/main/resources/bootstrap.yml index d36c5be..612c443 100644 --- a/blade-core-boot/src/main/resources/bootstrap.yml +++ b/blade-core-boot/src/main/resources/bootstrap.yml @@ -75,7 +75,7 @@ mybatis-plus: swagger: title: SpringBlade 接口文档系统 description: SpringBlade 接口文档系统 - version: 2.3.1 + version: 2.3.2 license: Powered By SpringBlade licenseUrl: https://bladex.vip terms-of-service-url: https://bladex.vip diff --git a/blade-core-cloud/pom.xml b/blade-core-cloud/pom.xml index e214e93..93a6fcf 100644 --- a/blade-core-cloud/pom.xml +++ b/blade-core-cloud/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.3.1 + 2.3.2 4.0.0 diff --git a/blade-core-develop/pom.xml b/blade-core-develop/pom.xml index afd7c6d..6448d72 100644 --- a/blade-core-develop/pom.xml +++ b/blade-core-develop/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.3.1 + 2.3.2 4.0.0 diff --git a/blade-core-launch/pom.xml b/blade-core-launch/pom.xml index 710de02..9fd22c3 100644 --- a/blade-core-launch/pom.xml +++ b/blade-core-launch/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.3.1 + 2.3.2 4.0.0 diff --git a/blade-core-launch/src/main/java/org/springblade/core/launch/BladeApplication.java b/blade-core-launch/src/main/java/org/springblade/core/launch/BladeApplication.java index dd9088c..0f84897 100644 --- a/blade-core-launch/src/main/java/org/springblade/core/launch/BladeApplication.java +++ b/blade-core-launch/src/main/java/org/springblade/core/launch/BladeApplication.java @@ -27,6 +27,7 @@ import org.springframework.util.StringUtils; import java.util.*; import java.util.function.Function; +import java.util.stream.Collectors; /** * 项目启动器,搞定环境变量问题 @@ -100,8 +101,10 @@ public class BladeApplication { props.setProperty("spring.cloud.nacos.config.file-extension", NacosConstant.NACOS_CONFIG_FORMAT); props.setProperty("spring.cloud.sentinel.transport.dashboard", SentinelConstant.SENTINEL_ADDR); // 加载自定义组件 - ServiceLoader loader = ServiceLoader.load(LauncherService.class); - loader.forEach(launcherService -> launcherService.launcher(builder, appName, profile)); + List launcherList = new ArrayList<>(); + ServiceLoader.load(LauncherService.class).forEach(launcherList::add); + launcherList.stream().sorted(Comparator.comparing(LauncherService::getOrder)).collect(Collectors.toList()) + .forEach(launcherService -> launcherService.launcher(builder, appName, profile)); return builder; } diff --git a/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java b/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java index 6bedb89..89698a1 100644 --- a/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java +++ b/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java @@ -25,7 +25,7 @@ public interface AppConstant { /** * 应用版本 */ - String APPLICATION_VERSION = "2.3.1"; + String APPLICATION_VERSION = "2.3.2"; /** * 基础包 @@ -68,6 +68,10 @@ public interface AppConstant { * 开发模块名称 */ String APPLICATION_DEVELOP_NAME = APPLICATION_NAME_PREFIX + "develop"; + /** + * 资源模块名称 + */ + String APPLICATION_RESOURCE_NAME = APPLICATION_NAME_PREFIX + "resource"; /** * 测试模块名称 */ diff --git a/blade-core-launch/src/main/java/org/springblade/core/launch/service/LauncherService.java b/blade-core-launch/src/main/java/org/springblade/core/launch/service/LauncherService.java index d89a20a..b8d7010 100644 --- a/blade-core-launch/src/main/java/org/springblade/core/launch/service/LauncherService.java +++ b/blade-core-launch/src/main/java/org/springblade/core/launch/service/LauncherService.java @@ -16,13 +16,14 @@ package org.springblade.core.launch.service; import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.core.Ordered; /** * launcher 扩展 用于一些组件发现 * * @author Chill */ -public interface LauncherService { +public interface LauncherService extends Ordered, Comparable { /** * 启动时 处理 SpringApplicationBuilder @@ -33,4 +34,25 @@ public interface LauncherService { */ void launcher(SpringApplicationBuilder builder, String appName, String profile); + /** + * 获取排列顺序 + * + * @return order + */ + @Override + default int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + + /** + * 对比排序 + * + * @param o LauncherService + * @return compare + */ + @Override + default int compareTo(LauncherService o) { + return Integer.compare(this.getOrder(), o.getOrder()); + } + } diff --git a/blade-core-log/pom.xml b/blade-core-log/pom.xml index 5c823a4..e7fce98 100644 --- a/blade-core-log/pom.xml +++ b/blade-core-log/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.3.1 + 2.3.2 4.0.0 diff --git a/blade-core-mybatis/pom.xml b/blade-core-mybatis/pom.xml index f9fd71d..2f06a40 100644 --- a/blade-core-mybatis/pom.xml +++ b/blade-core-mybatis/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.3.1 + 2.3.2 4.0.0 diff --git a/blade-core-oss/pom.xml b/blade-core-oss/pom.xml new file mode 100644 index 0000000..499d7cb --- /dev/null +++ b/blade-core-oss/pom.xml @@ -0,0 +1,32 @@ + + + + blade-tool + org.springblade + 2.3.2 + + 4.0.0 + + blade-core-oss + ${project.artifactId} + ${blade.tool.version} + jar + + + + + org.springblade + blade-core-tool + ${blade.tool.version} + + + + com.qiniu + qiniu-java-sdk + 7.2.18 + + + + diff --git a/blade-core-oss/src/main/java/org/springblade/core/oss/QiniuTemplate.java b/blade-core-oss/src/main/java/org/springblade/core/oss/QiniuTemplate.java new file mode 100644 index 0000000..46f4c85 --- /dev/null +++ b/blade-core-oss/src/main/java/org/springblade/core/oss/QiniuTemplate.java @@ -0,0 +1,248 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.oss; + +import com.qiniu.common.Zone; +import com.qiniu.http.Response; +import com.qiniu.storage.BucketManager; +import com.qiniu.storage.UploadManager; +import com.qiniu.storage.model.FileInfo; +import com.qiniu.util.Auth; +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; +import org.springblade.core.oss.model.BladeFile; +import org.springblade.core.oss.model.OssFile; +import org.springblade.core.oss.props.OssProperties; +import org.springblade.core.oss.rule.OssRule; +import org.springblade.core.tool.utils.CollectionUtil; +import org.springblade.core.tool.utils.Func; +import org.springblade.core.tool.utils.StringPool; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; +import java.util.Date; +import java.util.List; + +/** + * QiniuTemplate + * + * @author Chill + */ +@AllArgsConstructor +public class QiniuTemplate { + private Auth auth; + private UploadManager uploadManager; + private BucketManager bucketManager; + private OssProperties ossProperties; + private OssRule ossRule; + + + @SneakyThrows + public void makeBucket(String bucketName) { + if (!CollectionUtil.contains(bucketManager.buckets(), getBucketName(bucketName))) { + bucketManager.createBucket(getBucketName(bucketName), Zone.zone0().getRegion()); + } + } + + + @SneakyThrows + public void removeBucket(String bucketName) { + bucketManager.deleteBucket(getBucketName(bucketName)); + } + + + @SneakyThrows + public boolean bucketExists(String bucketName) { + return CollectionUtil.contains(bucketManager.buckets(), getBucketName(bucketName)); + } + + + @SneakyThrows + public void copyFile(String bucketName, String fileName, String destBucketName) { + bucketManager.copy(getBucketName(bucketName), fileName, getBucketName(destBucketName), fileName); + } + + + @SneakyThrows + public void copyFile(String bucketName, String fileName, String destBucketName, String destFileName) { + bucketManager.copy(getBucketName(bucketName), fileName, getBucketName(destBucketName), destFileName); + } + + + @SneakyThrows + public OssFile statFile(String fileName) { + return statFile(ossProperties.getBucketName(), fileName); + } + + + @SneakyThrows + public OssFile statFile(String bucketName, String fileName) { + FileInfo stat = bucketManager.stat(getBucketName(bucketName), fileName); + OssFile ossFile = new OssFile(); + ossFile.setName(stat.key); + ossFile.setName(Func.isEmpty(stat.key) ? fileName : stat.key); + ossFile.setLink(fileLink(ossFile.getName())); + ossFile.setHash(stat.hash); + ossFile.setLength(stat.fsize); + ossFile.setPutTime(new Date(stat.putTime / 10000)); + ossFile.setContentType(stat.mimeType); + return ossFile; + } + + + @SneakyThrows + public String filePath(String fileName) { + return getBucketName().concat(StringPool.SLASH).concat(fileName); + } + + + @SneakyThrows + public String filePath(String bucketName, String fileName) { + return getBucketName(bucketName).concat(StringPool.SLASH).concat(fileName); + } + + + @SneakyThrows + public String fileLink(String fileName) { + return ossProperties.getEndpoint().concat(StringPool.SLASH).concat(fileName); + } + + + @SneakyThrows + public String fileLink(String bucketName, String fileName) { + return ossProperties.getEndpoint().concat(StringPool.SLASH).concat(fileName); + } + + + @SneakyThrows + public BladeFile putFile(MultipartFile file) { + return putFile(ossProperties.getBucketName(), file.getOriginalFilename(), file); + } + + + @SneakyThrows + public BladeFile putFile(String fileName, MultipartFile file) { + return putFile(ossProperties.getBucketName(), fileName, file); + } + + + @SneakyThrows + public BladeFile putFile(String bucketName, String fileName, MultipartFile file) { + return putFile(bucketName, fileName, file); + } + + + @SneakyThrows + public BladeFile putFile(String fileName, InputStream stream) { + return putFile(ossProperties.getBucketName(), fileName, stream); + } + + + @SneakyThrows + public BladeFile putFile(String bucketName, String fileName, InputStream stream) { + return put(bucketName, stream, fileName, false); + } + + @SneakyThrows + public BladeFile put(String bucketName, InputStream stream, String key, boolean cover) { + makeBucket(bucketName); + key = getFileName(key); + // 覆盖上传 + if (cover) { + uploadManager.put(stream, key, getUploadToken(bucketName, key), null, null); + } else { + Response response = uploadManager.put(stream, key, getUploadToken(bucketName), null, null); + int retry = 0; + int retryCount = 5; + while (response.needRetry() && retry < retryCount) { + response = uploadManager.put(stream, key, getUploadToken(bucketName), null, null); + retry++; + } + } + BladeFile file = new BladeFile(); + file.setName(key); + file.setLink(fileLink(bucketName, key)); + return file; + } + + + @SneakyThrows + public void removeFile(String fileName) { + bucketManager.delete(getBucketName(), fileName); + } + + + @SneakyThrows + public void removeFile(String bucketName, String fileName) { + bucketManager.delete(getBucketName(bucketName), fileName); + } + + + @SneakyThrows + public void removeFiles(List fileNames) { + fileNames.forEach(this::removeFile); + } + + + @SneakyThrows + public void removeFiles(String bucketName, List fileNames) { + fileNames.forEach(fileName -> removeFile(getBucketName(bucketName), fileName)); + } + + /** + * 根据规则生成存储桶名称规则 + * + * @return String + */ + private String getBucketName() { + return getBucketName(ossProperties.getBucketName()); + } + + /** + * 根据规则生成存储桶名称规则 + * + * @param bucketName 存储桶名称 + * @return String + */ + private String getBucketName(String bucketName) { + return ossRule.bucketName(bucketName); + } + + /** + * 根据规则生成文件名称规则 + * + * @param originalFilename 原始文件名 + * @return string + */ + private String getFileName(String originalFilename) { + return ossRule.fileName(originalFilename); + } + + /** + * 获取上传凭证,普通上传 + */ + public String getUploadToken(String bucketName) { + return auth.uploadToken(getBucketName(bucketName)); + } + + /** + * 获取上传凭证,覆盖上传 + */ + private String getUploadToken(String bucketName, String key) { + return auth.uploadToken(getBucketName(bucketName), key); + } + +} diff --git a/blade-core-oss/src/main/java/org/springblade/core/oss/config/OssConfiguration.java b/blade-core-oss/src/main/java/org/springblade/core/oss/config/OssConfiguration.java new file mode 100644 index 0000000..c69b614 --- /dev/null +++ b/blade-core-oss/src/main/java/org/springblade/core/oss/config/OssConfiguration.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.oss.config; + +import com.qiniu.common.Zone; +import com.qiniu.storage.BucketManager; +import com.qiniu.storage.UploadManager; +import com.qiniu.util.Auth; +import lombok.AllArgsConstructor; +import org.springblade.core.oss.QiniuTemplate; +import org.springblade.core.oss.props.OssProperties; +import org.springblade.core.oss.rule.BladeOssRule; +import org.springblade.core.oss.rule.OssRule; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Oss配置类 + * + * @author Chill + */ +@Configuration +@AllArgsConstructor +@EnableConfigurationProperties(OssProperties.class) +@ConditionalOnProperty(value = "oss.enable", havingValue = "true") +public class OssConfiguration { + + private OssProperties ossProperties; + + @Bean + @ConditionalOnMissingBean(OssRule.class) + public OssRule ossRule() { + return new BladeOssRule(); + } + + @Bean + public com.qiniu.storage.Configuration qiniuConfiguration() { + return new com.qiniu.storage.Configuration(Zone.zone0()); + } + + @Bean + public Auth auth() { + return Auth.create(ossProperties.getAccessKey(), ossProperties.getSecretKey()); + } + + @Bean + @ConditionalOnBean(com.qiniu.storage.Configuration.class) + public UploadManager uploadManager(com.qiniu.storage.Configuration cfg) { + return new UploadManager(cfg); + } + + @Bean + @ConditionalOnBean(com.qiniu.storage.Configuration.class) + public BucketManager bucketManager(com.qiniu.storage.Configuration cfg) { + return new BucketManager(auth(), cfg); + } + + @Bean + @ConditionalOnMissingBean(QiniuTemplate.class) + @ConditionalOnBean({Auth.class, UploadManager.class, BucketManager.class, OssRule.class}) + public QiniuTemplate qiniuTemplate(Auth auth, UploadManager uploadManager, BucketManager bucketManager, OssRule ossRule) { + return new QiniuTemplate(auth, uploadManager, bucketManager, ossProperties, ossRule); + } + + +} diff --git a/blade-core-oss/src/main/java/org/springblade/core/oss/model/BladeFile.java b/blade-core-oss/src/main/java/org/springblade/core/oss/model/BladeFile.java new file mode 100644 index 0000000..bc02327 --- /dev/null +++ b/blade-core-oss/src/main/java/org/springblade/core/oss/model/BladeFile.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.oss.model; + +import lombok.Data; + +/** + * BladeFile + * + * @author Chill + */ +@Data +public class BladeFile { + /** + * 文件地址 + */ + private String link; + /** + * 文件名 + */ + private String name; +} diff --git a/blade-core-oss/src/main/java/org/springblade/core/oss/model/OssFile.java b/blade-core-oss/src/main/java/org/springblade/core/oss/model/OssFile.java new file mode 100644 index 0000000..999f6cd --- /dev/null +++ b/blade-core-oss/src/main/java/org/springblade/core/oss/model/OssFile.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.oss.model; + +import lombok.Data; + +import java.util.Date; + +/** + * OssFile + * + * @author Chill + */ +@Data +public class OssFile { + /** + * 文件地址 + */ + private String link; + /** + * 文件名 + */ + private String name; + /** + * 文件hash值 + */ + public String hash; + /** + * 文件大小 + */ + private long length; + /** + * 文件上传时间 + */ + private Date putTime; + /** + * 文件contentType + */ + private String contentType; +} diff --git a/blade-core-oss/src/main/java/org/springblade/core/oss/props/OssProperties.java b/blade-core-oss/src/main/java/org/springblade/core/oss/props/OssProperties.java new file mode 100644 index 0000000..1a46887 --- /dev/null +++ b/blade-core-oss/src/main/java/org/springblade/core/oss/props/OssProperties.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.oss.props; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Minio参数配置类 + * + * @author Chill + */ +@Data +@ConfigurationProperties(prefix = "oss") +public class OssProperties { + + /** + * 是否启用 + */ + private Boolean enable; + + /** + * 对象存储名称 + */ + private String name; + + /** + * 是否开启租户模式 + */ + private Boolean tenantMode; + + /** + * 对象存储服务的URL + */ + private String endpoint; + + /** + * Access key就像用户ID,可以唯一标识你的账户 + */ + private String accessKey; + + /** + * Secret key是你账户的密码 + */ + private String secretKey; + + /** + * 默认的存储桶名称 + */ + private String bucketName = "bladex"; + +} diff --git a/blade-core-oss/src/main/java/org/springblade/core/oss/rule/BladeOssRule.java b/blade-core-oss/src/main/java/org/springblade/core/oss/rule/BladeOssRule.java new file mode 100644 index 0000000..4e8294a --- /dev/null +++ b/blade-core-oss/src/main/java/org/springblade/core/oss/rule/BladeOssRule.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.oss.rule; + +import lombok.AllArgsConstructor; +import org.springblade.core.tool.utils.DateUtil; +import org.springblade.core.tool.utils.FileUtil; +import org.springblade.core.tool.utils.StringPool; +import org.springblade.core.tool.utils.StringUtil; + +/** + * 默认存储桶生成规则 + * + * @author Chill + */ +@AllArgsConstructor +public class BladeOssRule implements OssRule { + + @Override + public String bucketName(String bucketName) { + return bucketName; + } + + @Override + public String fileName(String originalFilename) { + return "upload" + StringPool.SLASH + DateUtil.today() + StringPool.SLASH + StringUtil.randomUUID() + StringPool.DOT + FileUtil.getFileExtension(originalFilename); + } + +} diff --git a/blade-core-oss/src/main/java/org/springblade/core/oss/rule/OssRule.java b/blade-core-oss/src/main/java/org/springblade/core/oss/rule/OssRule.java new file mode 100644 index 0000000..6bdecb6 --- /dev/null +++ b/blade-core-oss/src/main/java/org/springblade/core/oss/rule/OssRule.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.oss.rule; + +/** + * Oss通用规则 + * + * @author Chill + */ +public interface OssRule { + + /** + * 获取存储桶规则 + * + * @param bucketName 存储桶名称 + * @return String + */ + String bucketName(String bucketName); + + /** + * 获取文件名规则 + * + * @param originalFilename 文件名 + * @return String + */ + String fileName(String originalFilename); + +} diff --git a/blade-core-secure/pom.xml b/blade-core-secure/pom.xml index 890e82d..18bafc4 100644 --- a/blade-core-secure/pom.xml +++ b/blade-core-secure/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.3.1 + 2.3.2 4.0.0 diff --git a/blade-core-swagger/pom.xml b/blade-core-swagger/pom.xml index 71fc968..2aeed80 100644 --- a/blade-core-swagger/pom.xml +++ b/blade-core-swagger/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.3.1 + 2.3.2 4.0.0 diff --git a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerProperties.java b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerProperties.java index 7e5dbf5..70f81a4 100644 --- a/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerProperties.java +++ b/blade-core-swagger/src/main/java/org/springblade/core/swagger/SwaggerProperties.java @@ -53,7 +53,7 @@ public class SwaggerProperties { /** * 版本 **/ - private String version = "2.3.1"; + private String version = "2.3.2"; /** * 许可证 **/ diff --git a/blade-core-test/pom.xml b/blade-core-test/pom.xml new file mode 100644 index 0000000..0f4ee10 --- /dev/null +++ b/blade-core-test/pom.xml @@ -0,0 +1,31 @@ + + + + org.springblade + blade-tool + 2.3.2 + + 4.0.0 + + blade-core-test + ${project.artifactId} + ${blade.tool.version} + jar + + + + + org.springblade + blade-core-launch + ${blade.tool.version} + + + + org.springframework.boot + spring-boot-starter-test + + + + diff --git a/blade-core-test/src/main/java/org/springblade/core/test/BladeBaseTest.java b/blade-core-test/src/main/java/org/springblade/core/test/BladeBaseTest.java new file mode 100644 index 0000000..67c830d --- /dev/null +++ b/blade-core-test/src/main/java/org/springblade/core/test/BladeBaseTest.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springblade.core.test; + +import org.junit.runner.RunWith; +import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; + +/** + * boot test 基类 + * + * @author L.cm + */ +@RunWith(BladeSpringRunner.class) +public abstract class BladeBaseTest extends AbstractJUnit4SpringContextTests { + +} diff --git a/blade-core-test/src/main/java/org/springblade/core/test/BladeBootTest.java b/blade-core-test/src/main/java/org/springblade/core/test/BladeBootTest.java new file mode 100644 index 0000000..d6d4760 --- /dev/null +++ b/blade-core-test/src/main/java/org/springblade/core/test/BladeBootTest.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springblade.core.test; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.*; + +/** + * 简化 测试 + * + * @author L.cm + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@SpringBootTest +public @interface BladeBootTest { + /** + * 服务名:appName + * @return appName + */ + @AliasFor("appName") + String value() default "blade-test"; + /** + * 服务名:appName + * @return appName + */ + @AliasFor("value") + String appName() default "blade-test"; + /** + * profile + * @return profile + */ + String profile() default "dev"; + /** + * 启用 ServiceLoader 加载 launcherService + * @return 是否启用 + */ + boolean enableLoader() default false; +} diff --git a/blade-core-test/src/main/java/org/springblade/core/test/BladeBootTestException.java b/blade-core-test/src/main/java/org/springblade/core/test/BladeBootTestException.java new file mode 100644 index 0000000..16d68d2 --- /dev/null +++ b/blade-core-test/src/main/java/org/springblade/core/test/BladeBootTestException.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springblade.core.test; + +/** + * blade test 异常 + * + * @author L.cm + */ +class BladeBootTestException extends RuntimeException { + + BladeBootTestException(String message) { + super(message); + } +} diff --git a/blade-core-test/src/main/java/org/springblade/core/test/BladeSpringRunner.java b/blade-core-test/src/main/java/org/springblade/core/test/BladeSpringRunner.java new file mode 100644 index 0000000..bb456c0 --- /dev/null +++ b/blade-core-test/src/main/java/org/springblade/core/test/BladeSpringRunner.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springblade.core.test; + + +import org.junit.runners.model.InitializationError; +import org.springblade.core.launch.BladeApplication; +import org.springblade.core.launch.constant.AppConstant; +import org.springblade.core.launch.constant.NacosConstant; +import org.springblade.core.launch.constant.SentinelConstant; +import org.springblade.core.launch.service.LauncherService; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 设置启动参数 + * + * @author L.cm + */ +public class BladeSpringRunner extends SpringJUnit4ClassRunner { + + public BladeSpringRunner(Class clazz) throws InitializationError { + super(clazz); + setUpTestClass(clazz); + } + + private void setUpTestClass(Class clazz) { + BladeBootTest bladeBootTest = AnnotationUtils.getAnnotation(clazz, BladeBootTest.class); + if (bladeBootTest == null) { + throw new BladeBootTestException(String.format("%s must be @BladeBootTest .", clazz)); + } + String appName = bladeBootTest.appName(); + String profile = bladeBootTest.profile(); + boolean isLocalDev = BladeApplication.isLocalDev(); + Properties props = System.getProperties(); + props.setProperty("blade.env", profile); + props.setProperty("blade.name", appName); + props.setProperty("blade.is-local", String.valueOf(isLocalDev)); + props.setProperty("blade.dev-mode", profile.equals(AppConstant.PROD_CODE) ? "false" : "true"); + props.setProperty("blade.service.version", AppConstant.APPLICATION_VERSION); + props.setProperty("spring.application.name", appName); + props.setProperty("spring.profiles.active", profile); + props.setProperty("info.version", AppConstant.APPLICATION_VERSION); + props.setProperty("info.desc", appName); + props.setProperty("spring.cloud.nacos.discovery.server-addr", NacosConstant.NACOS_ADDR); + props.setProperty("spring.cloud.nacos.config.server-addr", NacosConstant.NACOS_ADDR); + props.setProperty("spring.cloud.nacos.config.prefix", NacosConstant.NACOS_CONFIG_PREFIX); + props.setProperty("spring.cloud.nacos.config.file-extension", NacosConstant.NACOS_CONFIG_FORMAT); + props.setProperty("spring.cloud.sentinel.transport.dashboard", SentinelConstant.SENTINEL_ADDR); + props.setProperty("spring.main.allow-bean-definition-overriding", "true"); + // 加载自定义组件 + if (bladeBootTest.enableLoader()) { + List launcherList = new ArrayList<>(); + SpringApplicationBuilder builder = new SpringApplicationBuilder(clazz); + ServiceLoader.load(LauncherService.class).forEach(launcherList::add); + launcherList.stream().sorted(Comparator.comparing(LauncherService::getOrder)).collect(Collectors.toList()) + .forEach(launcherService -> launcherService.launcher(builder, appName, profile)); + } + System.err.println(String.format("---[junit.test]:[%s]---启动中,读取到的环境变量:[%s]", appName, profile)); + } + +} diff --git a/blade-core-tool/pom.xml b/blade-core-tool/pom.xml index b3d42a7..8f110b3 100644 --- a/blade-core-tool/pom.xml +++ b/blade-core-tool/pom.xml @@ -6,7 +6,7 @@ org.springblade blade-tool - 2.3.1 + 2.3.2 4.0.0 @@ -56,17 +56,17 @@ swagger-models ${swagger.models.version} - - - io.protostuff - protostuff-core - ${protostuff.version} - - - io.protostuff - protostuff-runtime - ${protostuff.version} - + + + io.protostuff + protostuff-core + ${protostuff.version} + + + io.protostuff + protostuff-runtime + ${protostuff.version} + diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/CharPool.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/CharPool.java new file mode 100644 index 0000000..c8ac124 --- /dev/null +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/CharPool.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.tool.utils; + +/** + * char 常量池 + * + * @author L.cm + */ +public interface CharPool { + + char UPPER_A = 'A'; + char LOWER_A = 'a'; + char UPPER_Z = 'Z'; + char LOWER_Z = 'z'; + char DOT = '.'; + char AT = '@'; + char LEFT_BRACE = '{'; + char RIGHT_BRACE = '}'; + char LEFT_BRACKET = '('; + char RIGHT_BRACKET = ')'; + +} diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/DateUtil.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/DateUtil.java index a7c8aff..05db0e9 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/DateUtil.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/DateUtil.java @@ -1,119 +1,264 @@ -/** - * Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com). - *

- * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.gnu.org/licenses/lgpl.html - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package org.springblade.core.tool.utils; +import lombok.experimental.UtilityClass; import org.springframework.util.Assert; import java.text.ParseException; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAmount; +import java.time.temporal.TemporalQuery; import java.util.Calendar; import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; /** * 日期工具类 * * @author L.cm */ +@UtilityClass public class DateUtil { + public static final String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss"; + public static final String PATTERN_DATE = "yyyy-MM-dd"; + public static final String PATTERN_TIME = "HH:mm:ss"; /** - * 设置年 + * 老 date 格式化 + */ + public static final ConcurrentDateFormat DATETIME_FORMAT = ConcurrentDateFormat.of(PATTERN_DATETIME); + public static final ConcurrentDateFormat DATE_FORMAT = ConcurrentDateFormat.of(PATTERN_DATE); + public static final ConcurrentDateFormat TIME_FORMAT = ConcurrentDateFormat.of(PATTERN_TIME); + /** + * java 8 时间格式化 + */ + public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATETIME); + public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATE); + public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_TIME); + + /** + * 添加年 * - * @param date 时间 - * @param amount 年数,-1表示减少 + * @param date 时间 + * @param yearsToAdd 添加的年数 * @return 设置后的时间 */ - public static Date setYears(Date date, int amount) { - return set(date, Calendar.YEAR, amount); + public static Date plusYears(Date date, int yearsToAdd) { + return DateUtil.set(date, Calendar.YEAR, yearsToAdd); } /** - * 设置月 + * 添加月 * - * @param date 时间 - * @param amount 月数,-1表示减少 + * @param date 时间 + * @param monthsToAdd 添加的月数 * @return 设置后的时间 */ - public static Date setMonths(Date date, int amount) { - return set(date, Calendar.MONTH, amount); + public static Date plusMonths(Date date, int monthsToAdd) { + return DateUtil.set(date, Calendar.MONTH, monthsToAdd); } /** - * 设置周 + * 添加周 * - * @param date 时间 - * @param amount 周数,-1表示减少 + * @param date 时间 + * @param weeksToAdd 添加的周数 * @return 设置后的时间 */ - public static Date setWeeks(Date date, int amount) { - return set(date, Calendar.WEEK_OF_YEAR, amount); + public static Date plusWeeks(Date date, int weeksToAdd) { + return DateUtil.plus(date, Period.ofWeeks(weeksToAdd)); } /** - * 设置天 + * 添加天 * - * @param date 时间 - * @param amount 天数,-1表示减少 + * @param date 时间 + * @param daysToAdd 添加的天数 * @return 设置后的时间 */ - public static Date setDays(Date date, int amount) { - return set(date, Calendar.DATE, amount); + public static Date plusDays(Date date, long daysToAdd) { + return DateUtil.plus(date, Duration.ofDays(daysToAdd)); } /** - * 设置小时 + * 添加小时 * - * @param date 时间 - * @param amount 小时数,-1表示减少 + * @param date 时间 + * @param hoursToAdd 添加的小时数 * @return 设置后的时间 */ - public static Date setHours(Date date, int amount) { - return set(date, Calendar.HOUR_OF_DAY, amount); + public static Date plusHours(Date date, long hoursToAdd) { + return DateUtil.plus(date, Duration.ofHours(hoursToAdd)); } /** - * 设置分钟 + * 添加分钟 * - * @param date 时间 - * @param amount 分钟数,-1表示减少 + * @param date 时间 + * @param minutesToAdd 添加的分钟数 * @return 设置后的时间 */ - public static Date setMinutes(Date date, int amount) { - return set(date, Calendar.MINUTE, amount); + public static Date plusMinutes(Date date, long minutesToAdd) { + return DateUtil.plus(date, Duration.ofMinutes(minutesToAdd)); } /** - * 设置秒 + * 添加秒 * - * @param date 时间 - * @param amount 秒数,-1表示减少 + * @param date 时间 + * @param secondsToAdd 添加的秒数 * @return 设置后的时间 */ - public static Date setSeconds(Date date, int amount) { - return set(date, Calendar.SECOND, amount); + public static Date plusSeconds(Date date, long secondsToAdd) { + return DateUtil.plus(date, Duration.ofSeconds(secondsToAdd)); } /** - * 设置毫秒 + * 添加毫秒 * - * @param date 时间 - * @param amount 毫秒数,-1表示减少 + * @param date 时间 + * @param millisToAdd 添加的毫秒数 * @return 设置后的时间 */ - public static Date setMilliseconds(Date date, int amount) { - return set(date, Calendar.MILLISECOND, amount); + public static Date plusMillis(Date date, long millisToAdd) { + return DateUtil.plus(date, Duration.ofMillis(millisToAdd)); + } + + /** + * 添加纳秒 + * + * @param date 时间 + * @param nanosToAdd 添加的纳秒数 + * @return 设置后的时间 + */ + public static Date plusNanos(Date date, long nanosToAdd) { + return DateUtil.plus(date, Duration.ofNanos(nanosToAdd)); + } + + /** + * 日期添加时间量 + * + * @param date 时间 + * @param amount 时间量 + * @return 设置后的时间 + */ + public static Date plus(Date date, TemporalAmount amount) { + Instant instant = date.toInstant(); + return Date.from(instant.plus(amount)); + } + + /** + * 减少年 + * + * @param date 时间 + * @param years 减少的年数 + * @return 设置后的时间 + */ + public static Date minusYears(Date date, int years) { + return DateUtil.set(date, Calendar.YEAR, -years); + } + + /** + * 减少月 + * + * @param date 时间 + * @param months 减少的月数 + * @return 设置后的时间 + */ + public static Date minusMonths(Date date, int months) { + return DateUtil.set(date, Calendar.MONTH, -months); + } + + /** + * 减少周 + * + * @param date 时间 + * @param weeks 减少的周数 + * @return 设置后的时间 + */ + public static Date minusWeeks(Date date, int weeks) { + return DateUtil.minus(date, Period.ofWeeks(weeks)); + } + + /** + * 减少天 + * + * @param date 时间 + * @param days 减少的天数 + * @return 设置后的时间 + */ + public static Date minusDays(Date date, long days) { + return DateUtil.minus(date, Duration.ofDays(days)); + } + + /** + * 减少小时 + * + * @param date 时间 + * @param hours 减少的小时数 + * @return 设置后的时间 + */ + public static Date minusHours(Date date, long hours) { + return DateUtil.minus(date, Duration.ofHours(hours)); + } + + /** + * 减少分钟 + * + * @param date 时间 + * @param minutes 减少的分钟数 + * @return 设置后的时间 + */ + public static Date minusMinutes(Date date, long minutes) { + return DateUtil.minus(date, Duration.ofMinutes(minutes)); + } + + /** + * 减少秒 + * + * @param date 时间 + * @param seconds 减少的秒数 + * @return 设置后的时间 + */ + public static Date minusSeconds(Date date, long seconds) { + return DateUtil.minus(date, Duration.ofSeconds(seconds)); + } + + /** + * 减少毫秒 + * + * @param date 时间 + * @param millis 减少的毫秒数 + * @return 设置后的时间 + */ + public static Date minusMillis(Date date, long millis) { + return DateUtil.minus(date, Duration.ofMillis(millis)); + } + + /** + * 减少纳秒 + * + * @param date 时间 + * @param nanos 减少的纳秒数 + * @return 设置后的时间 + */ + public static Date minusNanos(Date date, long nanos) { + return DateUtil.minus(date, Duration.ofNanos(nanos)); + } + + /** + * 日期减少时间量 + * + * @param date 时间 + * @param amount 时间量 + * @return 设置后的时间 + */ + public static Date minus(Date date, TemporalAmount amount) { + Instant instant = date.toInstant(); + return Date.from(instant.minus(amount)); } /** @@ -133,14 +278,6 @@ public class DateUtil { return c.getTime(); } - public static final String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss"; - public static final String PATTERN_DATE = "yyyy-MM-dd"; - public static final String PATTERN_TIME = "HH:mm:ss"; - - public static final ConcurrentDateFormat DATETIME_FORMAT = ConcurrentDateFormat.of(PATTERN_DATETIME); - public static final ConcurrentDateFormat DATE_FORMAT = ConcurrentDateFormat.of(PATTERN_DATE); - public static final ConcurrentDateFormat TIME_FORMAT = ConcurrentDateFormat.of(PATTERN_TIME); - /** * 日期时间格式化 * @@ -182,6 +319,47 @@ public class DateUtil { return ConcurrentDateFormat.of(pattern).format(date); } + /** + * java8 日期时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 + */ + public static String formatDateTime(TemporalAccessor temporal) { + return DATETIME_FORMATTER.format(temporal); + } + + /** + * java8 日期时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 + */ + public static String formatDate(TemporalAccessor temporal) { + return DATE_FORMATTER.format(temporal); + } + + /** + * java8 时间格式化 + * + * @param temporal 时间 + * @return 格式化后的时间 + */ + public static String formatTime(TemporalAccessor temporal) { + return TIME_FORMATTER.format(temporal); + } + + /** + * java8 日期格式化 + * + * @param temporal 时间 + * @param pattern 表达式 + * @return 格式化后的时间 + */ + public static String format(TemporalAccessor temporal, String pattern) { + return DateTimeFormatter.ofPattern(pattern).format(temporal); + } + /** * 将字符串转换为时间 * @@ -213,4 +391,196 @@ public class DateUtil { } } + /** + * 将字符串转换为时间 + * + * @param dateStr 时间字符串 + * @param pattern 表达式 + * @return 时间 + */ + public static T parse(String dateStr, String pattern, TemporalQuery query) { + return DateTimeFormatter.ofPattern(pattern).parse(dateStr, query); + } + + /** + * 时间转 Instant + * + * @param dateTime 时间 + * @return Instant + */ + public static Instant toInstant(LocalDateTime dateTime) { + return dateTime.atZone(ZoneId.systemDefault()).toInstant(); + } + + /** + * Instant 转 时间 + * + * @param instant Instant + * @return Instant + */ + public static LocalDateTime toDateTime(Instant instant) { + return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + } + + /** + * 转换成 date + * + * @param dateTime LocalDateTime + * @return Date + */ + public static Date toDate(LocalDateTime dateTime) { + return Date.from(DateUtil.toInstant(dateTime)); + } + + /** + * 转换成 date + * + * @param localDate LocalDate + * @return Date + */ + public static Date toDate(final LocalDate localDate) { + return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + } + + /** + * Converts local date time to Calendar. + */ + public static Calendar toCalendar(final LocalDateTime localDateTime) { + return GregorianCalendar.from(ZonedDateTime.of(localDateTime, ZoneId.systemDefault())); + } + + /** + * localDateTime 转换成毫秒数 + * + * @param localDateTime LocalDateTime + * @return long + */ + public static long toMilliseconds(final LocalDateTime localDateTime) { + return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + /** + * localDate 转换成毫秒数 + * + * @param localDate LocalDate + * @return long + */ + public static long toMilliseconds(LocalDate localDate) { + return toMilliseconds(localDate.atStartOfDay()); + } + + /** + * 转换成java8 时间 + * + * @param calendar 日历 + * @return LocalDateTime + */ + public static LocalDateTime fromCalendar(final Calendar calendar) { + TimeZone tz = calendar.getTimeZone(); + ZoneId zid = tz == null ? ZoneId.systemDefault() : tz.toZoneId(); + return LocalDateTime.ofInstant(calendar.toInstant(), zid); + } + + /** + * 转换成java8 时间 + * + * @param instant Instant + * @return LocalDateTime + */ + public static LocalDateTime fromInstant(final Instant instant) { + return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + } + + /** + * 转换成java8 时间 + * + * @param date Date + * @return LocalDateTime + */ + public static LocalDateTime fromDate(final Date date) { + return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); + } + + /** + * 转换成java8 时间 + * + * @param milliseconds 毫秒数 + * @return LocalDateTime + */ + public static LocalDateTime fromMilliseconds(final long milliseconds) { + return LocalDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), ZoneId.systemDefault()); + } + + /** + * 比较2个时间差,跨度比较小 + * + * @param startInclusive 开始时间 + * @param endExclusive 结束时间 + * @return 时间间隔 + */ + public static Duration between(Temporal startInclusive, Temporal endExclusive) { + return Duration.between(startInclusive, endExclusive); + } + + /** + * 比较2个时间差,跨度比较大,年月日为单位 + * + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return 时间间隔 + */ + public static Period between(LocalDate startDate, LocalDate endDate) { + return Period.between(startDate, endDate); + } + + /** + * 比较2个 时间差 + * + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return 时间间隔 + */ + public static Duration between(Date startDate, Date endDate) { + return Duration.between(startDate.toInstant(), endDate.toInstant()); + } + + /** + * 将秒数转换为日时分秒 + * + * @param second 秒数 + * @return 时间 + */ + public static String secondToTime(Long second) { + // 判断是否为空 + if (second == null || second == 0L) { + return StringPool.EMPTY; + } + //转换天数 + long days = second / 86400; + //剩余秒数 + second = second % 86400; + //转换小时 + long hours = second / 3600; + //剩余秒数 + second = second % 3600; + //转换分钟 + long minutes = second / 60; + //剩余秒数 + second = second % 60; + if (days > 0) { + return StringUtil.format("{}天{}小时{}分{}秒", days, hours, minutes, second); + } else { + return StringUtil.format("{}小时{}分{}秒", hours, minutes, second); + } + } + + /** + * 获取今天的日期 + * + * @return 时间 + */ + public static String today() { + return format(new Date(), "yyyyMMdd"); + } + } diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/FileUtil.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/FileUtil.java new file mode 100644 index 0000000..9ade2de --- /dev/null +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/FileUtil.java @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2018-2028, DreamLu All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the dreamlu.net developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: DreamLu 卢春梦 (596392912@qq.com) + */ + +package org.springblade.core.tool.utils; + +import lombok.experimental.UtilityClass; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.FileSystemUtils; +import org.springframework.util.PatternMatchUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +/** + * 文件工具类 + * + * @author L.cm + */ +@UtilityClass +public class FileUtil extends org.springframework.util.FileCopyUtils { + + /** + * 默认为true + * + * @author L.cm + */ + public static class TrueFilter implements FileFilter, Serializable { + private static final long serialVersionUID = -6420452043795072619L; + + public final static TrueFilter TRUE = new TrueFilter(); + + @Override + public boolean accept(File pathname) { + return true; + } + } + + /** + * 扫描目录下的文件 + * + * @param path 路径 + * @return 文件集合 + */ + public static List list(String path) { + File file = new File(path); + return list(file, TrueFilter.TRUE); + } + + /** + * 扫描目录下的文件 + * + * @param path 路径 + * @param fileNamePattern 文件名 * 号 + * @return 文件集合 + */ + public static List list(String path, final String fileNamePattern) { + File file = new File(path); + return list(file, pathname -> { + String fileName = pathname.getName(); + return PatternMatchUtils.simpleMatch(fileNamePattern, fileName); + }); + } + + /** + * 扫描目录下的文件 + * + * @param path 路径 + * @param filter 文件过滤 + * @return 文件集合 + */ + public static List list(String path, FileFilter filter) { + File file = new File(path); + return list(file, filter); + } + + /** + * 扫描目录下的文件 + * + * @param file 文件 + * @return 文件集合 + */ + public static List list(File file) { + List fileList = new ArrayList<>(); + return list(file, fileList, TrueFilter.TRUE); + } + + /** + * 扫描目录下的文件 + * + * @param file 文件 + * @param fileNamePattern Spring AntPathMatcher 规则 + * @return 文件集合 + */ + public static List list(File file, final String fileNamePattern) { + List fileList = new ArrayList<>(); + return list(file, fileList, pathname -> { + String fileName = pathname.getName(); + return PatternMatchUtils.simpleMatch(fileNamePattern, fileName); + }); + } + + /** + * 扫描目录下的文件 + * + * @param file 文件 + * @param filter 文件过滤 + * @return 文件集合 + */ + public static List list(File file, FileFilter filter) { + List fileList = new ArrayList<>(); + return list(file, fileList, filter); + } + + /** + * 扫描目录下的文件 + * + * @param file 文件 + * @param filter 文件过滤 + * @return 文件集合 + */ + private static List list(File file, List fileList, FileFilter filter) { + if (file.isDirectory()) { + File[] files = file.listFiles(); + if (files != null) { + for (File f : files) { + list(f, fileList, filter); + } + } + } else { + // 过滤文件 + boolean accept = filter.accept(file); + if (file.exists() && accept) { + fileList.add(file); + } + } + return fileList; + } + + /** + * 获取文件后缀名 + * @param fullName 文件全名 + * @return {String} + */ + public static String getFileExtension(String fullName) { + Assert.notNull(fullName, "file fullName is null."); + String fileName = new File(fullName).getName(); + int dotIndex = fileName.lastIndexOf('.'); + return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1); + } + + /** + * 获取文件名,去除后缀名 + * @param file 文件 + * @return {String} + */ + public static String getNameWithoutExtension(String file) { + Assert.notNull(file, "file is null."); + String fileName = new File(file).getName(); + int dotIndex = fileName.lastIndexOf(CharPool.DOT); + return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex); + } + + /** + * Returns the path to the system temporary directory. + * + * @return the path to the system temporary directory. + */ + public static String getTempDirPath() { + return System.getProperty("java.io.tmpdir"); + } + + /** + * Returns a {@link File} representing the system temporary directory. + * + * @return the system temporary directory. + */ + public static File getTempDir() { + return new File(getTempDirPath()); + } + + /** + * Reads the contents of a file into a String. + * The file is always closed. + * + * @param file the file to read, must not be {@code null} + * @return the file contents, never {@code null} + */ + public static String readToString(final File file) { + return readToString(file, Charsets.UTF_8); + } + + /** + * Reads the contents of a file into a String. + * The file is always closed. + * + * @param file the file to read, must not be {@code null} + * @param encoding the encoding to use, {@code null} means platform default + * @return the file contents, never {@code null} + */ + public static String readToString(final File file, final Charset encoding) { + try (InputStream in = Files.newInputStream(file.toPath())) { + return IoUtil.toString(in, encoding); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Reads the contents of a file into a String. + * The file is always closed. + * + * @param file the file to read, must not be {@code null} + * @return the file contents, never {@code null} + */ + public static byte[] readToByteArray(final File file) { + try (InputStream in = Files.newInputStream(file.toPath())) { + return IoUtil.toByteArray(in); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Writes a String to a file creating the file if it does not exist. + * + * @param file the file to write + * @param data the content to write to the file + */ + public static void writeToFile(final File file, final String data) { + writeToFile(file, data, Charsets.UTF_8, false); + } + + /** + * Writes a String to a file creating the file if it does not exist. + * + * @param file the file to write + * @param data the content to write to the file + * @param append if {@code true}, then the String will be added to the + * end of the file rather than overwriting + */ + public static void writeToFile(final File file, final String data, final boolean append){ + writeToFile(file, data, Charsets.UTF_8, append); + } + + /** + * Writes a String to a file creating the file if it does not exist. + * + * @param file the file to write + * @param data the content to write to the file + * @param encoding the encoding to use, {@code null} means platform default + */ + public static void writeToFile(final File file, final String data, final Charset encoding) { + writeToFile(file, data, encoding, false); + } + + /** + * Writes a String to a file creating the file if it does not exist. + * + * @param file the file to write + * @param data the content to write to the file + * @param encoding the encoding to use, {@code null} means platform default + * @param append if {@code true}, then the String will be added to the + * end of the file rather than overwriting + */ + public static void writeToFile(final File file, final String data, final Charset encoding, final boolean append) { + try (OutputStream out = new FileOutputStream(file, append)) { + IoUtil.write(data, out, encoding); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * 转成file + * @param multipartFile MultipartFile + * @param file File + */ + public static void toFile(MultipartFile multipartFile, final File file) { + try { + FileUtil.toFile(multipartFile.getInputStream(), file); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * 转成file + * @param in InputStream + * @param file File + */ + public static void toFile(InputStream in, final File file) { + try (OutputStream out = new FileOutputStream(file)) { + FileUtil.copy(in, out); + } catch (IOException e) { + throw Exceptions.unchecked(e); + } + } + + /** + * Moves a file. + *

+ * When the destination file is on another file system, do a "copy and delete". + * + * @param srcFile the file to be moved + * @param destFile the destination file + * @throws NullPointerException if source or destination is {@code null} + * @throws IOException if source or destination is invalid + * @throws IOException if an IO error occurs moving the file + */ + public static void moveFile(final File srcFile, final File destFile) throws IOException { + Assert.notNull(srcFile, "Source must not be null"); + Assert.notNull(destFile, "Destination must not be null"); + if (!srcFile.exists()) { + throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); + } + if (srcFile.isDirectory()) { + throw new IOException("Source '" + srcFile + "' is a directory"); + } + if (destFile.exists()) { + throw new IOException("Destination '" + destFile + "' already exists"); + } + if (destFile.isDirectory()) { + throw new IOException("Destination '" + destFile + "' is a directory"); + } + final boolean rename = srcFile.renameTo(destFile); + if (!rename) { + FileUtil.copy(srcFile, destFile); + if (!srcFile.delete()) { + FileUtil.deleteQuietly(destFile); + throw new IOException("Failed to delete original file '" + srcFile + "' after copy to '" + destFile + "'"); + } + } + } + + /** + * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories. + *

+ * The difference between File.delete() and this method are: + *

    + *
  • A directory to be deleted does not have to be empty.
  • + *
  • No exceptions are thrown when a file or directory cannot be deleted.
  • + *
+ * + * @param file file or directory to delete, can be {@code null} + * @return {@code true} if the file or directory was deleted, otherwise + * {@code false} + */ + public static boolean deleteQuietly(@Nullable final File file) { + if (file == null) { + return false; + } + try { + if (file.isDirectory()) { + FileSystemUtils.deleteRecursively(file); + } + } catch (final Exception ignored) { + } + + try { + return file.delete(); + } catch (final Exception ignored) { + return false; + } + } + +} diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringPool.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringPool.java index b7b282c..c4a964d 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringPool.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringPool.java @@ -27,7 +27,7 @@ public interface StringPool { String AT = "@"; String ASTERISK = "*"; String STAR = ASTERISK; - char SLASH = '/'; + String SLASH = "/"; char BACK_SLASH = '\\'; String DOUBLE_SLASH = "#//"; String COLON = ":"; diff --git a/pom.xml b/pom.xml index 4b4253e..7c675b3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springblade blade-tool - 2.3.1 + 2.3.2 pom blade-tool @@ -36,19 +36,19 @@ - 2.3.1 + 2.3.2 1.8 3.8.0 2.9.2 1.5.21 - 1.9.3 + 1.9.4 3.1.0 4.0.1 1.6.0 3.4.2 2.1.4 - 1.0.1 + 1.1.0 0.9.0.RELEASE 2.1.5.RELEASE @@ -68,7 +68,9 @@ blade-core-mybatis blade-core-secure blade-core-swagger + blade-core-test blade-core-tool + blade-core-oss