From a6c9d33024ea70bb3dbe11981cbc9a3f9027bcd2 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Thu, 2 Jan 2025 20:33:33 +0800 Subject: [PATCH 01/15] =?UTF-8?q?refactor(log):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E5=A4=84=E7=90=86=E5=99=A8=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=20description=E3=80=81module=20=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../starter/log/handler/AopLogHandler.java | 20 ----------- .../continew-starter-log-core/pom.xml | 8 +++++ .../starter/log/AbstractLogHandler.java | 17 ++++++++++ .../continew-starter-log-interceptor/pom.xml | 6 ---- .../log/handler/InterceptorLogHandler.java | 34 ------------------- 5 files changed, 25 insertions(+), 60 deletions(-) diff --git a/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/handler/AopLogHandler.java b/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/handler/AopLogHandler.java index 2ce621f0..203b3926 100644 --- a/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/handler/AopLogHandler.java +++ b/continew-starter-log/continew-starter-log-aop/src/main/java/top/continew/starter/log/handler/AopLogHandler.java @@ -16,11 +16,7 @@ package top.continew.starter.log.handler; -import cn.hutool.core.text.CharSequenceUtil; import top.continew.starter.log.AbstractLogHandler; -import top.continew.starter.log.model.LogRecord; - -import java.lang.reflect.Method; /** * 日志处理器-AOP 版实现 @@ -29,20 +25,4 @@ import java.lang.reflect.Method; * @since 2.8.0 */ public class AopLogHandler extends AbstractLogHandler { - - @Override - public void logDescription(LogRecord logRecord, Method targetMethod) { - super.logDescription(logRecord, targetMethod); - if (CharSequenceUtil.isBlank(logRecord.getDescription())) { - logRecord.setDescription("请在该接口方法上指定日志描述"); - } - } - - @Override - public void logModule(LogRecord logRecord, Method targetMethod, Class targetClass) { - super.logModule(logRecord, targetMethod, targetClass); - if (CharSequenceUtil.isBlank(logRecord.getModule())) { - logRecord.setModule("请在该接口类上指定所属模块"); - } - } } diff --git a/continew-starter-log/continew-starter-log-core/pom.xml b/continew-starter-log/continew-starter-log-core/pom.xml index a972b57f..52e44894 100644 --- a/continew-starter-log/continew-starter-log-core/pom.xml +++ b/continew-starter-log/continew-starter-log-core/pom.xml @@ -11,4 +11,12 @@ continew-starter-log-core ContiNew Starter 日志模块 - 核心模块 + + + + + io.swagger.core.v3 + swagger-annotations-jakarta + + \ No newline at end of file diff --git a/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/AbstractLogHandler.java b/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/AbstractLogHandler.java index 33a46d71..9a3df38b 100644 --- a/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/AbstractLogHandler.java +++ b/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/AbstractLogHandler.java @@ -18,8 +18,11 @@ package top.continew.starter.log; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.text.CharSequenceUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import top.continew.starter.core.validation.ValidationUtils; import top.continew.starter.log.annotation.Log; import top.continew.starter.log.enums.Include; import top.continew.starter.log.http.servlet.RecordableServletHttpRequest; @@ -85,6 +88,13 @@ public abstract class AbstractLogHandler implements LogHandler { if (null != methodLog && CharSequenceUtil.isNotBlank(methodLog.value())) { logRecord.setDescription(methodLog.value()); } + // 例如:@Operation(summary="新增部门") -> 新增部门 + Operation methodOperation = AnnotationUtil.getAnnotation(targetMethod, Operation.class); + if (null != methodOperation) { + logRecord.setDescription(CharSequenceUtil.blankToDefault(methodOperation + .summary(), "请在该接口方法的 @Operation 上添加 summary 来指定日志描述")); + } + ValidationUtils.throwIfBlank(logRecord.getDescription(), "请在该接口方法上添加 @Log 来指定日志描述"); } /** @@ -107,6 +117,13 @@ public abstract class AbstractLogHandler implements LogHandler { if (null != classLog && CharSequenceUtil.isNotBlank(classLog.module())) { logRecord.setModule(classLog.module()); } + // 例如:@Tag(name = "部门管理") -> 部门管理 + Tag classTag = AnnotationUtil.getAnnotation(targetClass, Tag.class); + if (null != classTag) { + String name = classTag.name(); + logRecord.setModule(CharSequenceUtil.blankToDefault(name, "请在该接口类的 @Tag 上添加 name 来指定所属模块")); + } + ValidationUtils.throwIfBlank(logRecord.getModule(), "请在该接口方法或接口类上添加 @Log 来指定所属模块"); } @Override diff --git a/continew-starter-log/continew-starter-log-interceptor/pom.xml b/continew-starter-log/continew-starter-log-interceptor/pom.xml index 8f96ceaf..68277947 100644 --- a/continew-starter-log/continew-starter-log-interceptor/pom.xml +++ b/continew-starter-log/continew-starter-log-interceptor/pom.xml @@ -13,12 +13,6 @@ ContiNew Starter 日志模块 - 基于拦截器实现(Spring Boot Actuator HttpTrace 增强版) - - - io.swagger.core.v3 - swagger-annotations-jakarta - - com.alibaba diff --git a/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/handler/InterceptorLogHandler.java b/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/handler/InterceptorLogHandler.java index adffab87..1f3a4bdd 100644 --- a/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/handler/InterceptorLogHandler.java +++ b/continew-starter-log/continew-starter-log-interceptor/src/main/java/top/continew/starter/log/handler/InterceptorLogHandler.java @@ -16,14 +16,7 @@ package top.continew.starter.log.handler; -import cn.hutool.core.annotation.AnnotationUtil; -import cn.hutool.core.text.CharSequenceUtil; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; import top.continew.starter.log.AbstractLogHandler; -import top.continew.starter.log.model.LogRecord; - -import java.lang.reflect.Method; /** * 日志处理器-拦截器版实现 @@ -32,31 +25,4 @@ import java.lang.reflect.Method; * @since 2.8.0 */ public class InterceptorLogHandler extends AbstractLogHandler { - - @Override - public void logDescription(LogRecord logRecord, Method targetMethod) { - super.logDescription(logRecord, targetMethod); - if (CharSequenceUtil.isNotBlank(logRecord.getDescription())) { - return; - } - // 例如:@Operation(summary="新增部门") -> 新增部门 - Operation methodOperation = AnnotationUtil.getAnnotation(targetMethod, Operation.class); - if (null != methodOperation) { - logRecord.setDescription(CharSequenceUtil.blankToDefault(methodOperation.summary(), "请在该接口方法上指定日志描述")); - } - } - - @Override - public void logModule(LogRecord logRecord, Method targetMethod, Class targetClass) { - super.logModule(logRecord, targetMethod, targetClass); - if (CharSequenceUtil.isNotBlank(logRecord.getModule())) { - return; - } - // 例如:@Tag(name = "部门管理") -> 部门管理 - Tag classTag = AnnotationUtil.getAnnotation(targetClass, Tag.class); - if (null != classTag) { - String name = classTag.name(); - logRecord.setModule(CharSequenceUtil.blankToDefault(name, "请在该接口类上指定所属模块")); - } - } } From b9779e894464ec534bebdd230a7239b6d1964ddb Mon Sep 17 00:00:00 2001 From: Solution-Lin <148463711+Solution-Lin@users.noreply.github.com> Date: Fri, 3 Jan 2025 11:45:24 +0800 Subject: [PATCH 02/15] =?UTF-8?q?refactor(file/excel):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=20BaseEnum=20=E8=BD=AC=E6=8D=A2=E5=99=A8=20(#10)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../excel/converter/ExcelBaseEnumConverter.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/continew-starter-file/continew-starter-file-excel/src/main/java/top/continew/starter/file/excel/converter/ExcelBaseEnumConverter.java b/continew-starter-file/continew-starter-file-excel/src/main/java/top/continew/starter/file/excel/converter/ExcelBaseEnumConverter.java index bc6f6c91..7ebe1317 100644 --- a/continew-starter-file/continew-starter-file-excel/src/main/java/top/continew/starter/file/excel/converter/ExcelBaseEnumConverter.java +++ b/continew-starter-file/continew-starter-file-excel/src/main/java/top/continew/starter/file/excel/converter/ExcelBaseEnumConverter.java @@ -16,7 +16,6 @@ package top.continew.starter.file.excel.converter; -import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ClassUtil; import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.CellDataTypeEnum; @@ -34,7 +33,7 @@ import top.continew.starter.core.enums.BaseEnum; * @see BaseEnum * @since 1.2.0 */ -public class ExcelBaseEnumConverter implements Converter> { +public class ExcelBaseEnumConverter implements Converter> { @Override public Class supportJavaTypeKey() { @@ -50,17 +49,17 @@ public class ExcelBaseEnumConverter implements Converter> { * 转换为 Java 数据(读取 Excel) */ @Override - public BaseEnum convertToJavaData(ReadCellData cellData, - ExcelContentProperty contentProperty, - GlobalConfiguration globalConfiguration) { - return this.getEnum(BaseEnum.class, Convert.toStr(cellData.getData())); + public BaseEnum convertToJavaData(ReadCellData cellData, + ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return this.getEnum(contentProperty.getField().getType(), cellData.getStringValue()); } /** * 转换为 Excel 数据(写入 Excel) */ @Override - public WriteCellData convertToExcelData(BaseEnum value, + public WriteCellData convertToExcelData(BaseEnum value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { if (null == value) { @@ -76,11 +75,11 @@ public class ExcelBaseEnumConverter implements Converter> { * @param description 描述 * @return 对应枚举 ,获取不到时为 {@code null} */ - private BaseEnum getEnum(Class enumType, String description) { + private BaseEnum getEnum(Class enumType, String description) { Object[] enumConstants = enumType.getEnumConstants(); for (Object enumConstant : enumConstants) { if (ClassUtil.isAssignable(BaseEnum.class, enumType)) { - BaseEnum baseEnum = (BaseEnum)enumConstant; + BaseEnum baseEnum = (BaseEnum)enumConstant; if (baseEnum.getDescription().equals(description)) { return baseEnum; } From 80c070093498abb8dff5529d177e1e2519577bf0 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Sat, 4 Jan 2025 19:42:03 +0800 Subject: [PATCH 03/15] =?UTF-8?q?refactor:=20=E8=A7=A3=E5=86=B3=20Sonar=20?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=8C=E6=9B=BF=E6=8D=A2=E9=83=A8=E5=88=86?= =?UTF-8?q?=E8=BF=87=E6=9C=9F=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apidoc/handler/OpenApiHandler.java | 15 ++++++------- .../cache/redisson/util/RedisUtils.java | 20 +++++++++++++++-- .../threadpool/AsyncAutoConfiguration.java | 1 - .../data/mf/util/QueryWrapperHelper.java | 9 ++++---- .../security/crypto/enums/Algorithm.java | 4 ++-- .../limiter/annotation/RateLimiter.java | 4 ++-- .../limiter/core/RateLimiterAspect.java | 22 +++++++++---------- .../starter/security/mask/enums/MaskType.java | 18 +++++++-------- .../web/autoconfigure/xss/XssFilter.java | 6 ++--- .../xss/XssServletRequestWrapper.java | 4 ++-- 10 files changed, 57 insertions(+), 46 deletions(-) diff --git a/continew-starter-api-doc/src/main/java/top/continew/starter/apidoc/handler/OpenApiHandler.java b/continew-starter-api-doc/src/main/java/top/continew/starter/apidoc/handler/OpenApiHandler.java index c782fb1d..217e2410 100644 --- a/continew-starter-api-doc/src/main/java/top/continew/starter/apidoc/handler/OpenApiHandler.java +++ b/continew-starter-api-doc/src/main/java/top/continew/starter/apidoc/handler/OpenApiHandler.java @@ -36,7 +36,6 @@ import org.springdoc.core.service.SecurityService; import org.springdoc.core.utils.PropertyResolverUtils; import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.util.CollectionUtils; import org.springframework.web.method.HandlerMethod; import java.io.StringReader; @@ -152,7 +151,7 @@ public class OpenApiHandler extends OpenAPIService { if (this.openAPI.getPaths() == null) { this.openAPI.setPaths(new Paths()); } - if (!CollectionUtils.isEmpty(this.openAPI.getServers())) { + if (CollUtil.isNotEmpty(this.openAPI.getServers())) { this.isServersPresent = true; } } @@ -176,7 +175,7 @@ public class OpenApiHandler extends OpenAPIService { buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale); buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale); - if (!CollectionUtils.isEmpty(tagsStr)) { + if (CollUtil.isNotEmpty(tagsStr)) { tagsStr = tagsStr.stream() .map(str -> propertyResolverUtils.resolve(str, locale)) .collect(Collectors.toSet()); @@ -190,8 +189,8 @@ public class OpenApiHandler extends OpenAPIService { } } - if (!CollectionUtils.isEmpty(tagsStr)) { - if (CollectionUtils.isEmpty(operation.getTags())) { + if (CollUtil.isNotEmpty(tagsStr)) { + if (CollUtil.isEmpty(operation.getTags())) { operation.setTags(new ArrayList<>(tagsStr)); } else { Set operationTagsSet = new HashSet<>(operation.getTags()); @@ -225,10 +224,10 @@ public class OpenApiHandler extends OpenAPIService { } } - if (!CollectionUtils.isEmpty(tags)) { + if (CollUtil.isNotEmpty(tags)) { // Existing tags List openApiTags = openAPI.getTags(); - if (!CollectionUtils.isEmpty(openApiTags)) { + if (CollUtil.isNotEmpty(openApiTags)) { tags.addAll(openApiTags); } openAPI.setTags(new ArrayList<>(tags)); @@ -256,7 +255,7 @@ public class OpenApiHandler extends OpenAPIService { .collect(Collectors.toSet()); methodTags.addAll(AnnotatedElementUtils .findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class)); - if (!CollectionUtils.isEmpty(methodTags)) { + if (CollUtil.isNotEmpty(methodTags)) { tagsStr.addAll(toSet(methodTags, tag -> propertyResolverUtils.resolve(tag.name(), locale))); List allTags = new ArrayList<>(methodTags); addTags(allTags, tags, locale); diff --git a/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/continew/starter/cache/redisson/util/RedisUtils.java b/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/continew/starter/cache/redisson/util/RedisUtils.java index c7b04510..a9458bf3 100644 --- a/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/continew/starter/cache/redisson/util/RedisUtils.java +++ b/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/continew/starter/cache/redisson/util/RedisUtils.java @@ -19,6 +19,7 @@ package top.continew.starter.cache.redisson.util; import cn.hutool.core.util.ArrayUtil; import cn.hutool.extra.spring.SpringUtil; import org.redisson.api.*; +import org.redisson.api.options.KeysScanOptions; import top.continew.starter.core.constant.StringConstants; import java.time.Duration; @@ -191,7 +192,9 @@ public class RedisUtils { * @return 缓存列表 */ public static Collection keys(String pattern) { - return CLIENT.getKeys().getKeysStreamByPattern(pattern).toList(); + KeysScanOptions options = KeysScanOptions.defaults(); + options.pattern(pattern); + return CLIENT.getKeys().getKeysStream(options).toList(); } /** @@ -365,8 +368,21 @@ public class RedisUtils { * @return true:成功;false:失败 */ public static boolean rateLimit(String key, RateType rateType, int rate, int rateInterval) { + return rateLimit(key, rateType, rate, Duration.ofSeconds(rateInterval)); + } + + /** + * 限流 + * + * @param key 键 + * @param rateType 限流类型(OVERALL:全局限流;PER_CLIENT:单机限流) + * @param rate 速率(指定时间间隔产生的令牌数) + * @param rateInterval 速率间隔(时间间隔) + * @return true:成功;false:失败 + */ + public static boolean rateLimit(String key, RateType rateType, int rate, Duration rateInterval) { RRateLimiter rateLimiter = CLIENT.getRateLimiter(key); - rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS); + rateLimiter.trySetRate(rateType, rate, rateInterval); return rateLimiter.tryAcquire(1); } diff --git a/continew-starter-core/src/main/java/top/continew/starter/core/autoconfigure/threadpool/AsyncAutoConfiguration.java b/continew-starter-core/src/main/java/top/continew/starter/core/autoconfigure/threadpool/AsyncAutoConfiguration.java index 6265eeac..a645a9ba 100644 --- a/continew-starter-core/src/main/java/top/continew/starter/core/autoconfigure/threadpool/AsyncAutoConfiguration.java +++ b/continew-starter-core/src/main/java/top/continew/starter/core/autoconfigure/threadpool/AsyncAutoConfiguration.java @@ -67,7 +67,6 @@ public class AsyncAutoConfiguration implements AsyncConfigurer { @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (throwable, method, objects) -> { - throwable.printStackTrace(); StringBuilder sb = new StringBuilder(); sb.append("Exception message: ") .append(throwable.getMessage()) diff --git a/continew-starter-data/continew-starter-data-mf/src/main/java/top/continew/starter/data/mf/util/QueryWrapperHelper.java b/continew-starter-data/continew-starter-data-mf/src/main/java/top/continew/starter/data/mf/util/QueryWrapperHelper.java index a47d314c..f76b219f 100644 --- a/continew-starter-data/continew-starter-data-mf/src/main/java/top/continew/starter/data/mf/util/QueryWrapperHelper.java +++ b/continew-starter-data/continew-starter-data-mf/src/main/java/top/continew/starter/data/mf/util/QueryWrapperHelper.java @@ -183,12 +183,11 @@ public class QueryWrapperHelper { * @param queryType 查询类型 * @param columnName 列名 * @param fieldValue 字段值 - * @param 查询数据类型 */ - private static void parse(QueryType queryType, - String columnName, - Object fieldValue, - List> consumers) { + private static void parse(QueryType queryType, + String columnName, + Object fieldValue, + List> consumers) { switch (queryType) { case EQ -> consumers.add(q -> q.eq(columnName, fieldValue)); case NE -> consumers.add(q -> q.ne(columnName, fieldValue)); diff --git a/continew-starter-security/continew-starter-security-crypto/src/main/java/top/continew/starter/security/crypto/enums/Algorithm.java b/continew-starter-security/continew-starter-security-crypto/src/main/java/top/continew/starter/security/crypto/enums/Algorithm.java index 549be9fa..ab20db04 100644 --- a/continew-starter-security/continew-starter-security-crypto/src/main/java/top/continew/starter/security/crypto/enums/Algorithm.java +++ b/continew-starter-security/continew-starter-security-crypto/src/main/java/top/continew/starter/security/crypto/enums/Algorithm.java @@ -37,9 +37,9 @@ public enum Algorithm { DES(DesEncryptor.class), /** - * PBEWithMD5AndDES + * PBE With MD5 And DES */ - PBEWithMD5AndDES(PbeWithMd5AndDesEncryptor.class), + PBE_WITH_MD5_AND_DES(PbeWithMd5AndDesEncryptor.class), /** * RSA diff --git a/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/annotation/RateLimiter.java b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/annotation/RateLimiter.java index 03ad6efd..9cfd9bab 100644 --- a/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/annotation/RateLimiter.java +++ b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/annotation/RateLimiter.java @@ -16,10 +16,10 @@ package top.continew.starter.security.limiter.annotation; -import org.redisson.api.RateIntervalUnit; import top.continew.starter.security.limiter.enums.LimitType; import java.lang.annotation.*; +import java.util.concurrent.TimeUnit; /** * 限流注解 @@ -60,7 +60,7 @@ public @interface RateLimiter { /** * 速率间隔时间单位(默认:毫秒) */ - RateIntervalUnit unit() default RateIntervalUnit.MILLISECONDS; + TimeUnit unit() default TimeUnit.MILLISECONDS; /** * 提示信息 diff --git a/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/core/RateLimiterAspect.java b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/core/RateLimiterAspect.java index 4ad5f035..8642d1e9 100644 --- a/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/core/RateLimiterAspect.java +++ b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/core/RateLimiterAspect.java @@ -39,6 +39,7 @@ import top.continew.starter.security.limiter.exception.RateLimiterException; import top.continew.starter.web.util.SpringWebUtils; import java.lang.reflect.Method; +import java.time.Duration; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -129,12 +130,11 @@ public class RateLimiterAspect { // 限流器配置 RateType rateType = rateLimiter.type() == LimitType.CLUSTER ? RateType.PER_CLIENT : RateType.OVERALL; int rate = rateLimiter.rate(); - int rateInterval = rateLimiter.interval(); - RateIntervalUnit rateIntervalUnit = rateLimiter.unit(); + Duration rateInterval = Duration.ofMillis(rateLimiter.unit().toMillis(rateLimiter.interval())); // 判断是否需要更新限流器 - if (this.isConfigurationUpdateNeeded(rRateLimiter, rateType, rate, rateInterval, rateIntervalUnit)) { + if (this.isConfigurationUpdateNeeded(rRateLimiter, rateType, rate, rateInterval)) { // 更新限流器 - rRateLimiter.setRate(rateType, rate, rateInterval, rateIntervalUnit); + rRateLimiter.setRate(rateType, rate, rateInterval); } // 尝试获取令牌 return !rRateLimiter.tryAcquire(); @@ -181,20 +181,18 @@ public class RateLimiterAspect { /** * 判断是否需要更新限流器配置 * - * @param rRateLimiter 限流器 - * @param rateType 限流类型(OVERALL:全局限流;PER_CLIENT:单机限流) - * @param rate 速率(指定时间间隔产生的令牌数) - * @param rateInterval 速率间隔 - * @param rateIntervalUnit 时间单位 + * @param rRateLimiter 限流器 + * @param rateType 限流类型(OVERALL:全局限流;PER_CLIENT:单机限流) + * @param rate 速率(指定时间间隔产生的令牌数) + * @param rateInterval 速率间隔 * @return 是否需要更新配置 */ private boolean isConfigurationUpdateNeeded(RRateLimiter rRateLimiter, RateType rateType, long rate, - long rateInterval, - RateIntervalUnit rateIntervalUnit) { + Duration rateInterval) { RateLimiterConfig config = rRateLimiter.getConfig(); return !Objects.equals(config.getRateType(), rateType) || !Objects.equals(config.getRate(), rate) || !Objects - .equals(config.getRateInterval(), rateIntervalUnit.toMillis(rateInterval)); + .equals(config.getRateInterval(), rateInterval.toMillis()); } } diff --git a/continew-starter-security/continew-starter-security-mask/src/main/java/top/continew/starter/security/mask/enums/MaskType.java b/continew-starter-security/continew-starter-security-mask/src/main/java/top/continew/starter/security/mask/enums/MaskType.java index 56874d15..c1d66339 100644 --- a/continew-starter-security/continew-starter-security-mask/src/main/java/top/continew/starter/security/mask/enums/MaskType.java +++ b/continew-starter-security/continew-starter-security-mask/src/main/java/top/continew/starter/security/mask/enums/MaskType.java @@ -34,7 +34,7 @@ public enum MaskType implements IMaskStrategy { CUSTOM { @Override public String mask(String str, char character, int left, int right) { - return CharSequenceUtil.replace(str, left, str.length() - right, character); + return CharSequenceUtil.replaceByCodePoint(str, left, str.length() - right, character); } }, @@ -45,7 +45,7 @@ public enum MaskType implements IMaskStrategy { MOBILE_PHONE { @Override public String mask(String str, char character, int left, int right) { - return CharSequenceUtil.replace(str, 3, str.length() - 4, character); + return CharSequenceUtil.replaceByCodePoint(str, 3, str.length() - 4, character); } }, @@ -58,7 +58,7 @@ public enum MaskType implements IMaskStrategy { FIXED_PHONE { @Override public String mask(String str, char character, int left, int right) { - return CharSequenceUtil.replace(str, 4, str.length() - 2, character); + return CharSequenceUtil.replaceByCodePoint(str, 4, str.length() - 2, character); } }, @@ -76,7 +76,7 @@ public enum MaskType implements IMaskStrategy { if (index <= 1) { return str; } - return CharSequenceUtil.replace(str, 1, index, character); + return CharSequenceUtil.replaceByCodePoint(str, 1, index, character); } }, @@ -89,7 +89,7 @@ public enum MaskType implements IMaskStrategy { ID_CARD { @Override public String mask(String str, char character, int left, int right) { - return CharSequenceUtil.replace(str, 1, str.length() - 2, character); + return CharSequenceUtil.replaceByCodePoint(str, 1, str.length() - 2, character); } }, @@ -140,11 +140,11 @@ public enum MaskType implements IMaskStrategy { // 普通车牌 int length = str.length(); if (length == 7) { - return CharSequenceUtil.replace(str, 3, 6, character); + return CharSequenceUtil.replaceByCodePoint(str, 3, 6, character); } // 新能源车牌 if (length == 8) { - return CharSequenceUtil.replace(str, 3, 7, character); + return CharSequenceUtil.replaceByCodePoint(str, 3, 7, character); } return str; } @@ -159,7 +159,7 @@ public enum MaskType implements IMaskStrategy { CHINESE_NAME { @Override public String mask(String str, char character, int left, int right) { - return CharSequenceUtil.replace(str, 1, str.length(), character); + return CharSequenceUtil.replaceByCodePoint(str, 1, str.length(), character); } }, @@ -186,7 +186,7 @@ public enum MaskType implements IMaskStrategy { @Override public String mask(String str, char character, int left, int right) { int length = str.length(); - return CharSequenceUtil.replace(str, length - 8, length, character); + return CharSequenceUtil.replaceByCodePoint(str, length - 8, length, character); } }, diff --git a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/xss/XssFilter.java b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/xss/XssFilter.java index c79fef45..e26317f6 100644 --- a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/xss/XssFilter.java +++ b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/xss/XssFilter.java @@ -16,7 +16,7 @@ package top.continew.starter.web.autoconfigure.xss; -import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.CollUtil; import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import org.slf4j.Logger; @@ -55,14 +55,14 @@ public class XssFilter implements Filter { if (servletRequest instanceof HttpServletRequest request && xssProperties.isEnabled()) { // 放行路由:忽略 XSS 过滤 List excludePatterns = xssProperties.getExcludePatterns(); - if (CollectionUtil.isNotEmpty(excludePatterns) && SpringWebUtils.isMatch(request + if (CollUtil.isNotEmpty(excludePatterns) && SpringWebUtils.isMatch(request .getServletPath(), excludePatterns)) { filterChain.doFilter(request, servletResponse); return; } // 拦截路由:执行 XSS 过滤 List includePatterns = xssProperties.getIncludePatterns(); - if (CollectionUtil.isNotEmpty(includePatterns)) { + if (CollUtil.isNotEmpty(includePatterns)) { if (SpringWebUtils.isMatch(request.getServletPath(), includePatterns)) { filterChain.doFilter(new XssServletRequestWrapper(request, xssProperties), servletResponse); } else { diff --git a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/xss/XssServletRequestWrapper.java b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/xss/XssServletRequestWrapper.java index a8b82cc0..0094ed44 100644 --- a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/xss/XssServletRequestWrapper.java +++ b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/xss/XssServletRequestWrapper.java @@ -16,7 +16,7 @@ package top.continew.starter.web.autoconfigure.xss; -import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.util.ArrayUtil; @@ -110,7 +110,7 @@ public class XssServletRequestWrapper extends HttpServletRequestWrapper { // 转义 if (XssMode.ESCAPE.equals(mode)) { List reStr = ReUtil.findAllGroup0(HtmlUtil.RE_HTML_MARK, content); - if (CollectionUtil.isEmpty(reStr)) { + if (CollUtil.isEmpty(reStr)) { return content; } for (String s : reStr) { From 279d72b7242bf996f9b88d38ed0ea7aa0a0d1c46 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Mon, 6 Jan 2025 20:56:16 +0800 Subject: [PATCH 04/15] =?UTF-8?q?feat(core):=20BaseEnum=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20getByValue=E3=80=81getByDescription=E3=80=81isValid?= =?UTF-8?q?Value=20=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../continew/starter/core/enums/BaseEnum.java | 47 +++++++++++++++++++ .../converter/ExcelBaseEnumConverter.java | 23 +-------- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/continew-starter-core/src/main/java/top/continew/starter/core/enums/BaseEnum.java b/continew-starter-core/src/main/java/top/continew/starter/core/enums/BaseEnum.java index 5c5893d8..bbc112e1 100644 --- a/continew-starter-core/src/main/java/top/continew/starter/core/enums/BaseEnum.java +++ b/continew-starter-core/src/main/java/top/continew/starter/core/enums/BaseEnum.java @@ -17,6 +17,7 @@ package top.continew.starter.core.enums; import java.io.Serializable; +import java.util.Objects; /** * 枚举接口 @@ -49,4 +50,50 @@ public interface BaseEnum { default String getColor() { return null; } + + /** + * 根据枚举值获取 + * + * @param value 枚举值 + * @param clazz 枚举类 + * @return 枚举对象 + * @since 2.8.1 + */ + static & BaseEnum, T> E getByValue(T value, Class clazz) { + for (E e : clazz.getEnumConstants()) { + if (Objects.equals(e.getValue(), value)) { + return e; + } + } + return null; + } + + /** + * 根据枚举描述获取 + * + * @param description 枚举描述 + * @param clazz 枚举类 + * @return 枚举对象 + * @since 2.8.1 + */ + static & BaseEnum> E getByDescription(String description, Class clazz) { + for (Object e : clazz.getEnumConstants()) { + if (e instanceof BaseEnum baseEnum && Objects.equals(baseEnum.getDescription(), description)) { + return (E)baseEnum; + } + } + return null; + } + + /** + * 判断枚举值是否有效 + * + * @param value 枚举值 + * @param clazz 枚举类 + * @return 是否有效 + * @since 2.8.1 + */ + static & BaseEnum, T> boolean isValidValue(T value, Class clazz) { + return getByValue(value, clazz) != null; + } } diff --git a/continew-starter-file/continew-starter-file-excel/src/main/java/top/continew/starter/file/excel/converter/ExcelBaseEnumConverter.java b/continew-starter-file/continew-starter-file-excel/src/main/java/top/continew/starter/file/excel/converter/ExcelBaseEnumConverter.java index 7ebe1317..e6a09222 100644 --- a/continew-starter-file/continew-starter-file-excel/src/main/java/top/continew/starter/file/excel/converter/ExcelBaseEnumConverter.java +++ b/continew-starter-file/continew-starter-file-excel/src/main/java/top/continew/starter/file/excel/converter/ExcelBaseEnumConverter.java @@ -16,7 +16,6 @@ package top.continew.starter.file.excel.converter; -import cn.hutool.core.util.ClassUtil; import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.GlobalConfiguration; @@ -52,7 +51,7 @@ public class ExcelBaseEnumConverter implements Converter> { public BaseEnum convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return this.getEnum(contentProperty.getField().getType(), cellData.getStringValue()); + return BaseEnum.getByDescription(cellData.getStringValue(), contentProperty.getField().getType()); } /** @@ -67,24 +66,4 @@ public class ExcelBaseEnumConverter implements Converter> { } return new WriteCellData<>(value.getDescription()); } - - /** - * 通过 value 获取枚举对象,获取不到时为 {@code null} - * - * @param enumType 枚举类型 - * @param description 描述 - * @return 对应枚举 ,获取不到时为 {@code null} - */ - private BaseEnum getEnum(Class enumType, String description) { - Object[] enumConstants = enumType.getEnumConstants(); - for (Object enumConstant : enumConstants) { - if (ClassUtil.isAssignable(BaseEnum.class, enumType)) { - BaseEnum baseEnum = (BaseEnum)enumConstant; - if (baseEnum.getDescription().equals(description)) { - return baseEnum; - } - } - } - return null; - } } From 31e1d52ea4e2b0bfeba2cff4f6606b14c478a1f4 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Mon, 6 Jan 2025 21:00:21 +0800 Subject: [PATCH 05/15] release: v2.8.1 --- CHANGELOG.md | 16 ++++++++++++++++ continew-starter-dependencies/pom.xml | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5f35eaa..472fd2e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## [v2.8.1](https://github.com/continew-org/continew-starter/compare/v2.8.0...v2.8.1) (2025-01-06) + +### ✨ 新特性 + +- 【core】BaseEnum 新增 getByValue、getByDescription、isValidValue 方法 ([279d72b](https://github.com/continew-org/continew-starter/commit/279d72b7242bf996f9b88d38ed0ea7aa0a0d1c46)) + +### 💎 功能优化 + +- 【extension/crud】移除 BaseResp、BaseDetailResp(已移动到 Admin 项目内) ([eb2cac5](https://github.com/continew-org/continew-starter/commit/eb2cac54f75b2850f2957b32190d12e63377c185)) +- 【log】优化日志处理器解析 description、module 方法 ([a6c9d33](https://github.com/continew-org/continew-starter/commit/a6c9d33024ea70bb3dbe11981cbc9a3f9027bcd2)) +- 解决 Sonar 问题,替换部分过期 API ([80c0700](https://github.com/continew-org/continew-starter/commit/80c070093498abb8dff5529d177e1e2519577bf0)) + +### 🐛 问题修复 + +- 【file/excel】优化 BaseEnum 转换器 (GitHub#10@Solution-Lin) ([b9779e8](https://github.com/continew-org/continew-starter/commit/b9779e894464ec534bebdd230a7239b6d1964ddb)) + ## [v2.8.0](https://github.com/continew-org/continew-starter/compare/v2.7.5...v2.8.0) (2024-12-25) ### ✨ 新特性 diff --git a/continew-starter-dependencies/pom.xml b/continew-starter-dependencies/pom.xml index b3c97d51..1a93b68f 100644 --- a/continew-starter-dependencies/pom.xml +++ b/continew-starter-dependencies/pom.xml @@ -43,7 +43,7 @@ - 2.8.0 + 2.8.1 1.2.0 1.39.0 1.16.7 From 0b342d5c73e95b809337b939b4e1e957374bad85 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Thu, 9 Jan 2025 20:32:20 +0800 Subject: [PATCH 06/15] =?UTF-8?q?chore(extension/crud):=20=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=20CommonUserService=E3=80=81ContainerPool=EF=BC=88?= =?UTF-8?q?=E5=B7=B2=E7=A7=BB=E5=8A=A8=E5=88=B0=20Admin=20=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E5=86=85=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crud/constant/ContainerPool.java | 34 ---------------- .../crud/service/CommonUserService.java | 39 ------------------- 2 files changed, 73 deletions(-) delete mode 100644 continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/constant/ContainerPool.java delete mode 100644 continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/service/CommonUserService.java diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/constant/ContainerPool.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/constant/ContainerPool.java deleted file mode 100644 index 6d311298..00000000 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/constant/ContainerPool.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - *

- * 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 top.continew.starter.extension.crud.constant; - -/** - * 数据源容器相关常量(Crane4j 数据填充组件使用) - * - * @author Charles7c - * @since 1.2.0 - */ -public class ContainerPool { - - /** - * 用户昵称 - */ - public static final String USER_NICKNAME = "UserNickname"; - - protected ContainerPool() { - } -} \ No newline at end of file diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/service/CommonUserService.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/service/CommonUserService.java deleted file mode 100644 index 72f6b010..00000000 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/service/CommonUserService.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - *

- * 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 top.continew.starter.extension.crud.service; - -import cn.crane4j.annotation.ContainerMethod; -import cn.crane4j.annotation.MappingType; -import top.continew.starter.extension.crud.constant.ContainerPool; - -/** - * 公共用户业务接口 - * - * @author Charles7c - * @since 1.0.0 - */ -public interface CommonUserService { - - /** - * 根据 ID 查询昵称 - * - * @param id ID - * @return 昵称 - */ - @ContainerMethod(namespace = ContainerPool.USER_NICKNAME, type = MappingType.ORDER_OF_KEYS) - String getNicknameById(Long id); -} From 4fe067a889f00617f03caf7ae3598466560dce33 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Thu, 9 Jan 2025 20:32:29 +0800 Subject: [PATCH 07/15] =?UTF-8?q?fix(log):=20=E4=BF=AE=E5=A4=8D=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=AE=B0=E5=BD=95=E6=97=B6=E6=89=80=E5=B1=9E=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=92=8C=E6=8F=8F=E8=BF=B0=E5=8F=96=E5=80=BC=E4=BC=98?= =?UTF-8?q?=E5=85=88=E7=BA=A7=E5=A4=B1=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/top/continew/starter/log/AbstractLogHandler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/AbstractLogHandler.java b/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/AbstractLogHandler.java index 9a3df38b..9a01e30f 100644 --- a/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/AbstractLogHandler.java +++ b/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/AbstractLogHandler.java @@ -87,6 +87,7 @@ public abstract class AbstractLogHandler implements LogHandler { // 例如:@Log("新增部门") -> 新增部门 if (null != methodLog && CharSequenceUtil.isNotBlank(methodLog.value())) { logRecord.setDescription(methodLog.value()); + return; } // 例如:@Operation(summary="新增部门") -> 新增部门 Operation methodOperation = AnnotationUtil.getAnnotation(targetMethod, Operation.class); @@ -116,6 +117,7 @@ public abstract class AbstractLogHandler implements LogHandler { Log classLog = AnnotationUtil.getAnnotation(targetClass, Log.class); if (null != classLog && CharSequenceUtil.isNotBlank(classLog.module())) { logRecord.setModule(classLog.module()); + return; } // 例如:@Tag(name = "部门管理") -> 部门管理 Tag classTag = AnnotationUtil.getAnnotation(targetClass, Tag.class); From aa463dff37b658d1cb2a69e68f54790e03c4103d Mon Sep 17 00:00:00 2001 From: Charles7c Date: Thu, 9 Jan 2025 20:37:37 +0800 Subject: [PATCH 08/15] =?UTF-8?q?chore:=20graceful-response=205.0.4-boot3?= =?UTF-8?q?=20=3D>=205.0.5-boot3=EF=BC=88=E4=BF=AE=E5=A4=8D=E7=88=B6?= =?UTF-8?q?=E7=B1=BB=E5=8F=82=E6=95=B0=E6=A0=A1=E9=AA=8C=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- continew-starter-dependencies/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/continew-starter-dependencies/pom.xml b/continew-starter-dependencies/pom.xml index 1a93b68f..bca9915e 100644 --- a/continew-starter-dependencies/pom.xml +++ b/continew-starter-dependencies/pom.xml @@ -61,7 +61,7 @@ 15.5 2.2.1 1.12.780 - 5.0.4-boot3 + 5.0.5-boot3 2.9.0 4.5.0 1.5.2 From 5f6822742fd0f032bcc351155f0b966d24b05346 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Thu, 9 Jan 2025 20:42:18 +0800 Subject: [PATCH 09/15] =?UTF-8?q?feat(core):=20SpringUtils=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E8=8E=B7=E5=8F=96=E4=BB=A3=E7=90=86=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../starter/core/util/SpringUtils.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 continew-starter-core/src/main/java/top/continew/starter/core/util/SpringUtils.java diff --git a/continew-starter-core/src/main/java/top/continew/starter/core/util/SpringUtils.java b/continew-starter-core/src/main/java/top/continew/starter/core/util/SpringUtils.java new file mode 100644 index 00000000..087e4ffd --- /dev/null +++ b/continew-starter-core/src/main/java/top/continew/starter/core/util/SpringUtils.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.core.util; + +import cn.hutool.extra.spring.SpringUtil; + +/** + * Spring 工具类 + * + * @author Charles7c + * @since 2.8.2 + */ +public class SpringUtils { + + private SpringUtils() { + } + + /** + * 获取代理对象 + * + * @param target 目标对象 + * @param 目标对象类型 + * @return 代理对象 + * @since 2.8.2 + */ + public static T getProxy(T target) { + return (T)SpringUtil.getBean(target.getClass()); + } +} From c72259028f75b55c2da3f8f7856aa50cb829fb1f Mon Sep 17 00:00:00 2001 From: Charles7c Date: Thu, 9 Jan 2025 20:45:42 +0800 Subject: [PATCH 10/15] release: v2.8.2 --- CHANGELOG.md | 18 ++++++++++++++++++ continew-starter-dependencies/pom.xml | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 472fd2e4..96e1b5d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## [v2.8.2](https://github.com/continew-org/continew-starter/compare/v2.8.1...v2.8.2) (2025-01-09) + +### ✨ 新特性 + +- 【core】SpringUtils 新增获取代理对象方法 ([5f68227](https://github.com/continew-org/continew-starter/commit/5f6822742fd0f032bcc351155f0b966d24b05346)) + +### 💎 功能优化 + +- 【extension/crud】移除 CommonUserService、ContainerPool(已移动到 Admin 项目内) ([0b342d5](https://github.com/continew-org/continew-starter/commit/0b342d5c73e95b809337b939b4e1e957374bad85)) + +### 🐛 问题修复 + +- 【log】修复日志记录时所属模块和描述取值优先级失效的问题 ([4fe067a](https://github.com/continew-org/continew-starter/commit/4fe067a889f00617f03caf7ae3598466560dce33)) + +### 📦 依赖升级 + +- graceful-response 5.0.4-boot3 => 5.0.5-boot3(修复父类参数校验异常) ([aa463df](https://github.com/continew-org/continew-starter/commit/aa463dff37b658d1cb2a69e68f54790e03c4103d)) + ## [v2.8.1](https://github.com/continew-org/continew-starter/compare/v2.8.0...v2.8.1) (2025-01-06) ### ✨ 新特性 diff --git a/continew-starter-dependencies/pom.xml b/continew-starter-dependencies/pom.xml index bca9915e..05686b27 100644 --- a/continew-starter-dependencies/pom.xml +++ b/continew-starter-dependencies/pom.xml @@ -43,7 +43,7 @@ - 2.8.1 + 2.8.2 1.2.0 1.39.0 1.16.7 From 326dd76c34476141c39add5348da052bdb8c27cd Mon Sep 17 00:00:00 2001 From: Charles7c Date: Sun, 12 Jan 2025 22:58:29 +0800 Subject: [PATCH 11/15] =?UTF-8?q?refactor(log):=20=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E6=89=80=E5=B1=9E=E6=A8=A1=E5=9D=97=E3=80=81=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E9=BB=98=E8=AE=A4=E6=8F=90=E7=A4=BA=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E7=9B=B4=E6=8E=A5=E6=8A=9B=E5=87=BA=E5=BC=82?= =?UTF-8?q?=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../continew/starter/log/AbstractLogHandler.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/AbstractLogHandler.java b/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/AbstractLogHandler.java index 9a01e30f..b385719e 100644 --- a/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/AbstractLogHandler.java +++ b/continew-starter-log/continew-starter-log-core/src/main/java/top/continew/starter/log/AbstractLogHandler.java @@ -22,7 +22,6 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import top.continew.starter.core.validation.ValidationUtils; import top.continew.starter.log.annotation.Log; import top.continew.starter.log.enums.Include; import top.continew.starter.log.http.servlet.RecordableServletHttpRequest; @@ -83,6 +82,7 @@ public abstract class AbstractLogHandler implements LogHandler { */ @Override public void logDescription(LogRecord logRecord, Method targetMethod) { + logRecord.setDescription("请在该接口方法上添加 @top.continew.starter.log.annotation.Log(value) 来指定日志描述"); Log methodLog = AnnotationUtil.getAnnotation(targetMethod, Log.class); // 例如:@Log("新增部门") -> 新增部门 if (null != methodLog && CharSequenceUtil.isNotBlank(methodLog.value())) { @@ -91,11 +91,9 @@ public abstract class AbstractLogHandler implements LogHandler { } // 例如:@Operation(summary="新增部门") -> 新增部门 Operation methodOperation = AnnotationUtil.getAnnotation(targetMethod, Operation.class); - if (null != methodOperation) { - logRecord.setDescription(CharSequenceUtil.blankToDefault(methodOperation - .summary(), "请在该接口方法的 @Operation 上添加 summary 来指定日志描述")); + if (null != methodOperation && CharSequenceUtil.isNotBlank(methodOperation.summary())) { + logRecord.setDescription(methodOperation.summary()); } - ValidationUtils.throwIfBlank(logRecord.getDescription(), "请在该接口方法上添加 @Log 来指定日志描述"); } /** @@ -107,6 +105,7 @@ public abstract class AbstractLogHandler implements LogHandler { */ @Override public void logModule(LogRecord logRecord, Method targetMethod, Class targetClass) { + logRecord.setModule("请在该接口方法或类上添加 @top.continew.starter.log.annotation.Log(module) 来指定所属模块"); Log methodLog = AnnotationUtil.getAnnotation(targetMethod, Log.class); // 例如:@Log(module = "部门管理") -> 部门管理 // 方法级注解优先级高于类级注解 @@ -121,11 +120,9 @@ public abstract class AbstractLogHandler implements LogHandler { } // 例如:@Tag(name = "部门管理") -> 部门管理 Tag classTag = AnnotationUtil.getAnnotation(targetClass, Tag.class); - if (null != classTag) { - String name = classTag.name(); - logRecord.setModule(CharSequenceUtil.blankToDefault(name, "请在该接口类的 @Tag 上添加 name 来指定所属模块")); + if (null != classTag && CharSequenceUtil.isNotBlank(classTag.name())) { + logRecord.setModule(classTag.name()); } - ValidationUtils.throwIfBlank(logRecord.getModule(), "请在该接口方法或接口类上添加 @Log 来指定所属模块"); } @Override From 6e621bc4597996c8f1f65c542f5faa922b95a900 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Sun, 12 Jan 2025 23:15:49 +0800 Subject: [PATCH 12/15] =?UTF-8?q?chore(extension/crud):=20=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=20BaseDO=E3=80=81BaseCreateDO=E3=80=81BaseUpdateDO=20?= =?UTF-8?q?=E7=AD=89=EF=BC=88=E5=B7=B2=E7=A7=BB=E5=8A=A8=E5=88=B0=20Admin?= =?UTF-8?q?=20=E9=A1=B9=E7=9B=AE=E5=86=85=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AbstractBaseController.java | 3 +- .../extension/crud/model/req/BaseReq.java | 32 ------- .../crud/model/entity/BaseCreateDO.java | 62 ------------- .../extension/crud/model/entity/BaseDO.java | 84 ----------------- .../crud/model/entity/BaseUpdateDO.java | 62 ------------- .../crud/model/entity/BaseCreateDO.java | 67 -------------- .../extension/crud/model/entity/BaseDO.java | 91 ------------------- .../crud/model/entity/BaseUpdateDO.java | 67 -------------- 8 files changed, 1 insertion(+), 467 deletions(-) delete mode 100644 continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/model/req/BaseReq.java delete mode 100644 continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/model/entity/BaseCreateDO.java delete mode 100644 continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/model/entity/BaseDO.java delete mode 100644 continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/model/entity/BaseUpdateDO.java delete mode 100644 continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/model/entity/BaseCreateDO.java delete mode 100644 continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/model/entity/BaseDO.java delete mode 100644 continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/model/entity/BaseUpdateDO.java diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/controller/AbstractBaseController.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/controller/AbstractBaseController.java index 1578ccfe..a9c5e9db 100644 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/controller/AbstractBaseController.java +++ b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/controller/AbstractBaseController.java @@ -30,7 +30,6 @@ import top.continew.starter.extension.crud.enums.Api; import top.continew.starter.extension.crud.handler.CrudApiHandler; import top.continew.starter.extension.crud.model.query.PageQuery; import top.continew.starter.extension.crud.model.query.SortQuery; -import top.continew.starter.extension.crud.model.req.BaseReq; import top.continew.starter.extension.crud.model.resp.BaseIdResp; import top.continew.starter.extension.crud.model.resp.BasePageResp; import top.continew.starter.extension.crud.service.BaseService; @@ -49,7 +48,7 @@ import java.util.List; * @author Charles7c * @since 1.0.0 */ -public abstract class AbstractBaseController, L, D, Q, C extends BaseReq> implements CrudApiHandler { +public abstract class AbstractBaseController, L, D, Q, C> implements CrudApiHandler { @Autowired protected S baseService; diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/model/req/BaseReq.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/model/req/BaseReq.java deleted file mode 100644 index cb7cbf89..00000000 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/model/req/BaseReq.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - *

- * 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 top.continew.starter.extension.crud.model.req; - -import java.io.Serial; -import java.io.Serializable; - -/** - * 请求参数基类 - * - * @author Charles7c - * @since 1.0.0 - */ -public class BaseReq implements Serializable { - - @Serial - private static final long serialVersionUID = 1L; -} diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/model/entity/BaseCreateDO.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/model/entity/BaseCreateDO.java deleted file mode 100644 index eef1af83..00000000 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/model/entity/BaseCreateDO.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - *

- * 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 top.continew.starter.extension.crud.model.entity; - -import java.io.Serial; -import java.time.LocalDateTime; - -/** - * 实体类基类 - * - *

- * 通用字段:创建人、创建时间 - *

- * - * @author Charles7c - * @since 2.0.1 - */ -public class BaseCreateDO extends BaseIdDO { - - @Serial - private static final long serialVersionUID = 1L; - - /** - * 创建人 - */ - private Long createUser; - - /** - * 创建时间 - */ - private LocalDateTime createTime; - - public Long getCreateUser() { - return createUser; - } - - public void setCreateUser(Long createUser) { - this.createUser = createUser; - } - - public LocalDateTime getCreateTime() { - return createTime; - } - - public void setCreateTime(LocalDateTime createTime) { - this.createTime = createTime; - } -} diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/model/entity/BaseDO.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/model/entity/BaseDO.java deleted file mode 100644 index 5b62bd58..00000000 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/model/entity/BaseDO.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - *

- * 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 top.continew.starter.extension.crud.model.entity; - -import java.io.Serial; -import java.time.LocalDateTime; - -/** - * 实体类基类 - * - * @author Charles7c - * @since 1.0.0 - */ -public class BaseDO extends BaseIdDO { - - @Serial - private static final long serialVersionUID = 1L; - - /** - * 创建人 - */ - private Long createUser; - - /** - * 创建时间 - */ - private LocalDateTime createTime; - - /** - * 修改人 - */ - private Long updateUser; - - /** - * 修改时间 - */ - private LocalDateTime updateTime; - - public Long getCreateUser() { - return createUser; - } - - public void setCreateUser(Long createUser) { - this.createUser = createUser; - } - - public LocalDateTime getCreateTime() { - return createTime; - } - - public void setCreateTime(LocalDateTime createTime) { - this.createTime = createTime; - } - - public Long getUpdateUser() { - return updateUser; - } - - public void setUpdateUser(Long updateUser) { - this.updateUser = updateUser; - } - - public LocalDateTime getUpdateTime() { - return updateTime; - } - - public void setUpdateTime(LocalDateTime updateTime) { - this.updateTime = updateTime; - } -} diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/model/entity/BaseUpdateDO.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/model/entity/BaseUpdateDO.java deleted file mode 100644 index c9c2e46a..00000000 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mf/src/main/java/top/continew/starter/extension/crud/model/entity/BaseUpdateDO.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - *

- * 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 top.continew.starter.extension.crud.model.entity; - -import java.io.Serial; -import java.time.LocalDateTime; - -/** - * 实体类基类 - * - *

- * 通用字段:创建人、创建时间 - *

- * - * @author Charles7c - * @since 2.0.1 - */ -public class BaseUpdateDO extends BaseIdDO { - - @Serial - private static final long serialVersionUID = 1L; - - /** - * 修改人 - */ - private Long updateUser; - - /** - * 修改时间 - */ - private LocalDateTime updateTime; - - public Long getUpdateUser() { - return updateUser; - } - - public void setUpdateUser(Long updateUser) { - this.updateUser = updateUser; - } - - public LocalDateTime getUpdateTime() { - return updateTime; - } - - public void setUpdateTime(LocalDateTime updateTime) { - this.updateTime = updateTime; - } -} diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/model/entity/BaseCreateDO.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/model/entity/BaseCreateDO.java deleted file mode 100644 index 6f6282b5..00000000 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/model/entity/BaseCreateDO.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - *

- * 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 top.continew.starter.extension.crud.model.entity; - -import com.baomidou.mybatisplus.annotation.FieldFill; -import com.baomidou.mybatisplus.annotation.TableField; - -import java.io.Serial; -import java.time.LocalDateTime; - -/** - * 实体类基类 - * - *

- * 通用字段:创建人、创建时间 - *

- * - * @author Charles7c - * @since 2.0.1 - */ -public class BaseCreateDO extends BaseIdDO { - - @Serial - private static final long serialVersionUID = 1L; - - /** - * 创建人 - */ - @TableField(fill = FieldFill.INSERT) - private Long createUser; - - /** - * 创建时间 - */ - @TableField(fill = FieldFill.INSERT) - private LocalDateTime createTime; - - public Long getCreateUser() { - return createUser; - } - - public void setCreateUser(Long createUser) { - this.createUser = createUser; - } - - public LocalDateTime getCreateTime() { - return createTime; - } - - public void setCreateTime(LocalDateTime createTime) { - this.createTime = createTime; - } -} diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/model/entity/BaseDO.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/model/entity/BaseDO.java deleted file mode 100644 index 3e17461c..00000000 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/model/entity/BaseDO.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - *

- * 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 top.continew.starter.extension.crud.model.entity; - -import com.baomidou.mybatisplus.annotation.FieldFill; -import com.baomidou.mybatisplus.annotation.TableField; - -import java.io.Serial; -import java.time.LocalDateTime; - -/** - * 实体类基类 - * - * @author Charles7c - * @since 1.0.0 - */ -public class BaseDO extends BaseIdDO { - - @Serial - private static final long serialVersionUID = 1L; - - /** - * 创建人 - */ - @TableField(fill = FieldFill.INSERT) - private Long createUser; - - /** - * 创建时间 - */ - @TableField(fill = FieldFill.INSERT) - private LocalDateTime createTime; - - /** - * 修改人 - */ - @TableField(fill = FieldFill.UPDATE) - private Long updateUser; - - /** - * 修改时间 - */ - @TableField(fill = FieldFill.UPDATE) - private LocalDateTime updateTime; - - public Long getCreateUser() { - return createUser; - } - - public void setCreateUser(Long createUser) { - this.createUser = createUser; - } - - public LocalDateTime getCreateTime() { - return createTime; - } - - public void setCreateTime(LocalDateTime createTime) { - this.createTime = createTime; - } - - public Long getUpdateUser() { - return updateUser; - } - - public void setUpdateUser(Long updateUser) { - this.updateUser = updateUser; - } - - public LocalDateTime getUpdateTime() { - return updateTime; - } - - public void setUpdateTime(LocalDateTime updateTime) { - this.updateTime = updateTime; - } -} diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/model/entity/BaseUpdateDO.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/model/entity/BaseUpdateDO.java deleted file mode 100644 index c1c85cdf..00000000 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/model/entity/BaseUpdateDO.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - *

- * 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 top.continew.starter.extension.crud.model.entity; - -import com.baomidou.mybatisplus.annotation.FieldFill; -import com.baomidou.mybatisplus.annotation.TableField; - -import java.io.Serial; -import java.time.LocalDateTime; - -/** - * 实体类基类 - * - *

- * 通用字段:创建人、创建时间 - *

- * - * @author Charles7c - * @since 2.0.1 - */ -public class BaseUpdateDO extends BaseIdDO { - - @Serial - private static final long serialVersionUID = 1L; - - /** - * 修改人 - */ - @TableField(fill = FieldFill.UPDATE) - private Long updateUser; - - /** - * 修改时间 - */ - @TableField(fill = FieldFill.UPDATE) - private LocalDateTime updateTime; - - public Long getUpdateUser() { - return updateUser; - } - - public void setUpdateUser(Long updateUser) { - this.updateUser = updateUser; - } - - public LocalDateTime getUpdateTime() { - return updateTime; - } - - public void setUpdateTime(LocalDateTime updateTime) { - this.updateTime = updateTime; - } -} From d771e128399851fa78f1041fa4ffcd6af3332fcd Mon Sep 17 00:00:00 2001 From: Charles7c Date: Thu, 16 Jan 2025 20:50:52 +0800 Subject: [PATCH 13/15] =?UTF-8?q?fix(extension/crud):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=9D=A1=E4=BB=B6=E6=A0=A1=E9=AA=8C=E6=97=A0?= =?UTF-8?q?=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extension/crud/controller/AbstractBaseController.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/controller/AbstractBaseController.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/controller/AbstractBaseController.java index a9c5e9db..79aa3243 100644 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/controller/AbstractBaseController.java +++ b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-core/src/main/java/top/continew/starter/extension/crud/controller/AbstractBaseController.java @@ -64,7 +64,7 @@ public abstract class AbstractBaseController, @Operation(summary = "分页查询列表", description = "分页查询列表") @ResponseBody @GetMapping - public BasePageResp page(Q query, @Validated PageQuery pageQuery) { + public BasePageResp page(@Validated Q query, @Validated PageQuery pageQuery) { return baseService.page(query, pageQuery); } @@ -79,7 +79,7 @@ public abstract class AbstractBaseController, @Operation(summary = "查询列表", description = "查询列表") @ResponseBody @GetMapping("/list") - public List list(Q query, SortQuery sortQuery) { + public List list(@Validated Q query, @Validated SortQuery sortQuery) { return baseService.list(query, sortQuery); } @@ -94,7 +94,7 @@ public abstract class AbstractBaseController, @Operation(summary = "查询树列表", description = "查询树列表") @ResponseBody @GetMapping("/tree") - public List> tree(Q query, SortQuery sortQuery) { + public List> tree(@Validated Q query, @Validated SortQuery sortQuery) { return baseService.tree(query, sortQuery, false); } @@ -167,7 +167,7 @@ public abstract class AbstractBaseController, @ExcludeFromGracefulResponse @Operation(summary = "导出数据", description = "导出数据") @GetMapping("/export") - public void export(Q query, SortQuery sortQuery, HttpServletResponse response) { + public void export(@Validated Q query, @Validated SortQuery sortQuery, HttpServletResponse response) { baseService.export(query, sortQuery, response); } } From 13c4253b292f96c663dd468e0cbc66a0acb5136d Mon Sep 17 00:00:00 2001 From: Charles7c Date: Thu, 16 Jan 2025 21:25:33 +0800 Subject: [PATCH 14/15] release: v2.8.3 --- CHANGELOG.md | 11 +++++++++++ continew-starter-dependencies/pom.xml | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96e1b5d3..5db066c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## [v2.8.3](https://github.com/continew-org/continew-starter/compare/v2.8.2...v2.8.3) (2025-01-16) + +### 💎 功能优化 + +- 【log】调整所属模块、日志描述默认提示,不再直接抛出异常 ([326dd76](https://github.com/continew-org/continew-starter/commit/326dd76c34476141c39add5348da052bdb8c27cd)) +- 【extension/crud】移除 BaseDO、BaseCreateDO、BaseUpdateDO 等(已移动到 Admin 项目内) ([6e621bc](https://github.com/continew-org/continew-starter/commit/6e621bc4597996c8f1f65c542f5faa922b95a900)) + +### 🐛 问题修复 + +- 【extension/crud】修复查询条件校验无效的问题 ([d771e12](https://github.com/continew-org/continew-starter/commit/d771e128399851fa78f1041fa4ffcd6af3332fcd)) + ## [v2.8.2](https://github.com/continew-org/continew-starter/compare/v2.8.1...v2.8.2) (2025-01-09) ### ✨ 新特性 diff --git a/continew-starter-dependencies/pom.xml b/continew-starter-dependencies/pom.xml index 05686b27..93fd378d 100644 --- a/continew-starter-dependencies/pom.xml +++ b/continew-starter-dependencies/pom.xml @@ -43,7 +43,7 @@ - 2.8.2 + 2.8.3 1.2.0 1.39.0 1.16.7 From e11c7a0d8c2bfb3d6532312eec6a10a098bb3f4c Mon Sep 17 00:00:00 2001 From: liquor <958142070@qq.com> Date: Thu, 16 Jan 2025 13:36:48 +0000 Subject: [PATCH 15/15] =?UTF-8?q?feat(storage):=20=E6=96=B0=E5=A2=9E=20S3?= =?UTF-8?q?=20=E5=AD=98=E5=82=A8=E6=A8=A1=E5=9D=97=EF=BC=8C=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E6=9C=AC=E5=9C=B0=E5=AD=98=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- continew-starter-dependencies/pom.xml | 49 ++- .../continew-starter-storage-core/pom.xml | 30 ++ .../storage/constant/StorageConstant.java | 42 ++ .../starter/storage/dao/StorageDao.java | 35 ++ .../dao/impl/StorageDaoDefaultImpl.java | 33 ++ .../decorator/AbstractStorageDecorator.java | 106 +++++ .../starter/storage/enums/FileTypeEnum.java | 102 +++++ .../storage/manger/StorageManager.java | 80 ++++ .../storage/model/req/StorageProperties.java | 152 +++++++ .../storage/model/resp/ThumbnailResp.java | 61 +++ .../storage/model/resp/UploadResp.java | 215 ++++++++++ .../storage/strategy/StorageStrategy.java | 151 +++++++ .../storage/util/ImageThumbnailUtils.java | 81 ++++ .../starter/storage/util/StorageUtils.java | 128 ++++++ .../continew-starter-storage-local/pom.xml | 6 +- .../LocalStorageAutoconfigure.java | 39 ++ .../starter/storage/client/LocalClient.java | 85 ++++ .../LocalStorageAutoConfiguration.java | 79 ---- .../autoconfigure/LocalStorageProperties.java | 105 ----- .../strategy/LocalStorageStrategy.java | 275 ++++++++++++ .../starter/storage/util/LocalUtils.java | 46 ++ ...ot.autoconfigure.AutoConfiguration.imports | 2 +- .../continew-starter-storage-oss/pom.xml | 63 +++ .../OssStorageAutoconfigure.java | 40 ++ .../starter/storage/client/OssClient.java | 163 +++++++ .../storage/strategy/OssStorageStrategy.java | 401 ++++++++++++++++++ .../starter/storage/util/OssUtils.java | 61 +++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + continew-starter-storage/pom.xml | 2 + 29 files changed, 2437 insertions(+), 196 deletions(-) create mode 100644 continew-starter-storage/continew-starter-storage-core/pom.xml create mode 100644 continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/constant/StorageConstant.java create mode 100644 continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/dao/StorageDao.java create mode 100644 continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/dao/impl/StorageDaoDefaultImpl.java create mode 100644 continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/decorator/AbstractStorageDecorator.java create mode 100644 continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/enums/FileTypeEnum.java create mode 100644 continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/manger/StorageManager.java create mode 100644 continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/model/req/StorageProperties.java create mode 100644 continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/model/resp/ThumbnailResp.java create mode 100644 continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/model/resp/UploadResp.java create mode 100644 continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/strategy/StorageStrategy.java create mode 100644 continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/util/ImageThumbnailUtils.java create mode 100644 continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/util/StorageUtils.java create mode 100644 continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/autoconfigure/LocalStorageAutoconfigure.java create mode 100644 continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/client/LocalClient.java delete mode 100644 continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/local/autoconfigure/LocalStorageAutoConfiguration.java delete mode 100644 continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/local/autoconfigure/LocalStorageProperties.java create mode 100644 continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/strategy/LocalStorageStrategy.java create mode 100644 continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/util/LocalUtils.java create mode 100644 continew-starter-storage/continew-starter-storage-oss/pom.xml create mode 100644 continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/autoconfigure/OssStorageAutoconfigure.java create mode 100644 continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/client/OssClient.java create mode 100644 continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/strategy/OssStorageStrategy.java create mode 100644 continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/util/OssUtils.java create mode 100644 continew-starter-storage/continew-starter-storage-oss/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/continew-starter-dependencies/pom.xml b/continew-starter-dependencies/pom.xml index 93fd378d..238e538c 100644 --- a/continew-starter-dependencies/pom.xml +++ b/continew-starter-dependencies/pom.xml @@ -70,6 +70,11 @@ 2.14.5 3.2.12 5.8.34 + + 2.29.23 + 0.33.5 + + 0.4.20 1.6.0 2.43.0 @@ -253,18 +258,32 @@ ${easy-excel.version}
- + - org.dromara.x-file-storage - x-file-storage-spring - ${x-file-storage.version} + software.amazon.awssdk + s3 + ${s3.version} - + - com.amazonaws - aws-java-sdk-s3 - ${aws-s3.version} + software.amazon.awssdk.crt + aws-crt + ${s3-crt.version} + + + + + software.amazon.awssdk + s3-transfer-manager + ${s3.version} + + + + + net.coobird + thumbnailator + ${thumbnails.version} @@ -476,6 +495,13 @@ ${revision} + + + top.continew + continew-starter-storage-core + ${revision} + + top.continew @@ -483,6 +509,13 @@ ${revision} + + + top.continew + continew-starter-storage-oss + ${revision} + + top.continew diff --git a/continew-starter-storage/continew-starter-storage-core/pom.xml b/continew-starter-storage/continew-starter-storage-core/pom.xml new file mode 100644 index 00000000..2c54c40b --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-core/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + top.continew + continew-starter-storage + ${revision} + + + continew-starter-storage-core + ContiNew Starter 存储模块 - 核心模块 + + + + + + top.continew + continew-starter-cache-redisson + + + + + net.coobird + thumbnailator + + + + + diff --git a/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/constant/StorageConstant.java b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/constant/StorageConstant.java new file mode 100644 index 00000000..06c6151b --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/constant/StorageConstant.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.constant; + +/** + * 存储 常量 + * + * @author echo + * @date 2024/12/16 19:09 + */ +public class StorageConstant { + + /** + * 默认存储 Key + */ + public static final String DEFAULT_KEY = "storage:default_config"; + + /** + * 云服务商 域名前缀 + *

目前只支持 阿里云-oss 华为云-obs 腾讯云-cos

+ */ + public static final String[] CLOUD_SERVICE_PREFIX = new String[] {"oss", "cos", "obs"}; + + /** + * 缩略图后缀 + */ + public static final String SMALL_SUFFIX = "small"; +} diff --git a/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/dao/StorageDao.java b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/dao/StorageDao.java new file mode 100644 index 00000000..ed55e9f3 --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/dao/StorageDao.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.dao; + +import top.continew.starter.storage.model.resp.UploadResp; + +/** + * 存储记录持久层接口 + * + * @author echo + * @date 2024/12/17 16:49 + */ +public interface StorageDao { + + /** + * 记录上传信息 + * + * @param uploadResp 上传信息 + */ + void add(UploadResp uploadResp); +} diff --git a/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/dao/impl/StorageDaoDefaultImpl.java b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/dao/impl/StorageDaoDefaultImpl.java new file mode 100644 index 00000000..0f29d672 --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/dao/impl/StorageDaoDefaultImpl.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.dao.impl; + +import top.continew.starter.storage.dao.StorageDao; +import top.continew.starter.storage.model.resp.UploadResp; + +/** + * 默认记录实现,此类并不能真正保存记录,只是用来脱离数据库运行,保证文件上传功能可以正常使用 + * + * @author echo + * @date 2024/12/18 08:48 + **/ +public class StorageDaoDefaultImpl implements StorageDao { + @Override + public void add(UploadResp uploadResp) { + + } +} diff --git a/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/decorator/AbstractStorageDecorator.java b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/decorator/AbstractStorageDecorator.java new file mode 100644 index 00000000..e9fdabcb --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/decorator/AbstractStorageDecorator.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.decorator; + +import top.continew.starter.storage.model.resp.ThumbnailResp; +import top.continew.starter.storage.model.resp.UploadResp; +import top.continew.starter.storage.strategy.StorageStrategy; + +import java.io.InputStream; + +/** + * 装饰器基类 - 用于重写 + * + * @author echo + * @date 2024/12/30 19:33 + */ +public abstract class AbstractStorageDecorator implements StorageStrategy { + + protected StorageStrategy delegate; + + protected AbstractStorageDecorator(StorageStrategy delegate) { + this.delegate = delegate; + } + + @Override + public C getClient() { + return delegate.getClient(); + } + + @Override + public boolean bucketExists(String bucketName) { + return delegate.bucketExists(bucketName); + } + + @Override + public void createBucket(String bucketName) { + delegate.createBucket(bucketName); + } + + @Override + public UploadResp upload(String fileName, InputStream inputStream, String fileType) { + return delegate.upload(fileName, inputStream, fileType); + } + + @Override + public UploadResp upload(String fileName, + String path, + InputStream inputStream, + String fileType, + boolean isThumbnail) { + return delegate.upload(fileName, path, inputStream, fileType, isThumbnail); + } + + @Override + public UploadResp upload(String bucketName, + String fileName, + String path, + InputStream inputStream, + String fileType, + boolean isThumbnail) { + return delegate.upload(bucketName, fileName, path, inputStream, fileType, isThumbnail); + } + + @Override + public void upload(String bucketName, String fileName, String path, InputStream inputStream, String fileType) { + delegate.upload(bucketName, fileName, path, inputStream, fileType); + } + + @Override + public ThumbnailResp uploadThumbnail(String bucketName, + String fileName, + String path, + InputStream inputStream, + String fileType) { + return delegate.uploadThumbnail(bucketName, fileName, path, inputStream, fileType); + } + + @Override + public InputStream download(String bucketName, String fileName) { + return delegate.download(bucketName, fileName); + } + + @Override + public void delete(String bucketName, String fileName) { + delegate.delete(bucketName, fileName); + } + + @Override + public String getImageBase64(String bucketName, String fileName) { + return delegate.getImageBase64(bucketName, fileName); + } +} diff --git a/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/enums/FileTypeEnum.java b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/enums/FileTypeEnum.java new file mode 100644 index 00000000..8dd112fa --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/enums/FileTypeEnum.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.enums; + +import cn.hutool.core.util.StrUtil; +import top.continew.starter.core.enums.BaseEnum; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * 文件类型枚举 + * + * @author Charles7c + * @since 2023/12/23 13:38 + */ +public enum FileTypeEnum implements BaseEnum { + + /** + * 其他 + */ + UNKNOWN(1, "其他", Collections.emptyList()), + + /** + * 图片 + */ + IMAGE(2, "图片", List + .of("jpg", "jpeg", "png", "gif", "bmp", "webp", "ico", "psd", "tiff", "dwg", "jxr", "apng", "xcf")), + + /** + * 文档 + */ + DOC(3, "文档", List.of("txt", "pdf", "doc", "xls", "ppt", "docx", "xlsx", "pptx")), + + /** + * 视频 + */ + VIDEO(4, "视频", List.of("mp4", "avi", "mkv", "flv", "webm", "wmv", "m4v", "mov", "mpg", "rmvb", "3gp")), + + /** + * 音频 + */ + AUDIO(5, "音频", List.of("mp3", "flac", "wav", "ogg", "midi", "m4a", "aac", "amr", "ac3", "aiff")),; + + private final Integer value; + private final String description; + private final List extensions; + + /** + * 根据扩展名查询 + * + * @param extension 扩展名 + * @return 文件类型 + */ + public static FileTypeEnum getByExtension(String extension) { + return Arrays.stream(FileTypeEnum.values()) + .filter(t -> t.getExtensions().contains(StrUtil.emptyIfNull(extension).toLowerCase())) + .findFirst() + .orElse(FileTypeEnum.UNKNOWN); + } + + FileTypeEnum(Integer value, String description, List extensions) { + this.value = value; + this.description = description; + this.extensions = extensions; + } + + public List getExtensions() { + return this.extensions; + } + + @Override + public Integer getValue() { + return this.value; + } + + @Override + public String getDescription() { + return this.description; + } + + @Override + public String getColor() { + return BaseEnum.super.getColor(); + } + +} \ No newline at end of file diff --git a/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/manger/StorageManager.java b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/manger/StorageManager.java new file mode 100644 index 00000000..8390bd3c --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/manger/StorageManager.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.manger; + +import top.continew.starter.cache.redisson.util.RedisUtils; +import top.continew.starter.core.validation.ValidationUtils; +import top.continew.starter.storage.constant.StorageConstant; +import top.continew.starter.storage.strategy.StorageStrategy; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 存储策略管理器 + * + * @author echo + * @date 2024/12/16 + */ +public class StorageManager { + + /** + * 存储策略连接信息 + */ + private static final Map> STORAGE_STRATEGY = new ConcurrentHashMap<>(); + + /** + * 加载存储策略 + * + * @param code 存储码 + * @param strategy 对应存储策略 + */ + public static void load(String code, StorageStrategy strategy) { + STORAGE_STRATEGY.put(code, strategy); + } + + /** + * 卸载存储策略 + * + * @param code 存储码 + */ + public static void unload(String code) { + STORAGE_STRATEGY.remove(code); + } + + /** + * 根据 存储 code 获取对应存储策略 + * + * @param code 代码 + * @return {@link StorageStrategy } + */ + public static StorageStrategy instance(String code) { + StorageStrategy strategy = STORAGE_STRATEGY.get(code); + ValidationUtils.throwIfEmpty(strategy, "未找到存储配置:" + code); + return strategy; + } + + /** + * 获取默认存储策略 + * + * @return {@link StorageStrategy } + */ + public static StorageStrategy instance() { + return instance(RedisUtils.get(StorageConstant.DEFAULT_KEY)); + } + +} \ No newline at end of file diff --git a/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/model/req/StorageProperties.java b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/model/req/StorageProperties.java new file mode 100644 index 00000000..2583e293 --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/model/req/StorageProperties.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.model.req; + +/** + * 存储配置信息 + * + * @author echo + * @date 2024/11/04 15:13 + **/ +public class StorageProperties { + + /** + * 编码 + */ + private String code; + + /** + * 访问密钥 + */ + private String accessKey; + + /** + * 私有密钥 + */ + private String secretKey; + + /** + * 终端节点 + */ + private String endpoint; + + /** + * 桶名称 + */ + private String bucketName; + + /** + * 域名 + */ + private String domain; + + /** + * 作用域 + */ + private String region; + + /** + * 是否是默认存储 + */ + private Boolean isDefault; + + public StorageProperties() { + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getAccessKey() { + return accessKey; + } + + public void setAccessKey(String accessKey) { + this.accessKey = accessKey; + } + + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public String getBucketName() { + return bucketName; + } + + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public String getRegion() { + return region; + } + + public void setRegion(String region) { + this.region = region; + } + + public Boolean getIsDefault() { + return isDefault; + } + + public void setIsDefault(Boolean isDefault) { + this.isDefault = isDefault; + } + + public StorageProperties(String code, + String accessKey, + String secretKey, + String endpoint, + String bucketName, + String domain, + String region, + Boolean isDefault) { + this.code = code; + this.accessKey = accessKey; + this.secretKey = secretKey; + this.endpoint = endpoint; + this.bucketName = bucketName; + this.domain = domain; + this.region = region; + this.isDefault = isDefault; + + } +} diff --git a/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/model/resp/ThumbnailResp.java b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/model/resp/ThumbnailResp.java new file mode 100644 index 00000000..5233b4cb --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/model/resp/ThumbnailResp.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.model.resp; + +/** + * 缩略图 + * + * @author echo + * @date 2024/12/20 17:00 + */ +public class ThumbnailResp { + + /** + * 缩略图大小(字节) + */ + private Long thumbnailSize; + + /** + * 缩略图地址 格式 xxx/xxx/xxx.small.jpg + */ + private String thumbnailPath; + + public ThumbnailResp() { + } + + public ThumbnailResp(Long thumbnailSize, String thumbnailPath) { + this.thumbnailSize = thumbnailSize; + this.thumbnailPath = thumbnailPath; + } + + public Long getThumbnailSize() { + return thumbnailSize; + } + + public void setThumbnailSize(Long thumbnailSize) { + this.thumbnailSize = thumbnailSize; + } + + public String getThumbnailPath() { + return thumbnailPath; + } + + public void setThumbnailPath(String thumbnailPath) { + this.thumbnailPath = thumbnailPath; + } + +} diff --git a/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/model/resp/UploadResp.java b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/model/resp/UploadResp.java new file mode 100644 index 00000000..d08f3d6c --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/model/resp/UploadResp.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.model.resp; + +import java.time.LocalDateTime; + +/** + * 上传结果 + * + * @author echo + * @date 2024/12/10 + */ +public class UploadResp { + + /** + * 存储 code + */ + private String code; + + /** + * 访问地址 + *

如果桶为私有,则提供临时链接,时间默认为 12 小时

+ */ + private String url; + + /** + * 文件基础路径 + */ + private String basePath; + + /** + * 原始 文件名 + */ + private String originalFilename; + + /** + * 扩展名 + */ + private String ext; + + /** + * 文件大小(字节) + */ + private long size; + + /** + * 已上传对象的实体标记(用来校验文件)-S3 + */ + private String eTag; + + /** + * 存储路径 + *

格式 桶/文件名 continew/2024/12/24/1234.jpg + */ + private String path; + + /** + * 存储桶 + */ + private String bucketName; + + /** + * 缩略图大小(字节) + */ + private Long thumbnailSize; + + /** + * 缩略图URL + */ + private String thumbnailUrl; + + /** + * 上传时间 + */ + private LocalDateTime createTime; + + public UploadResp() { + } + + public UploadResp(String code, + String url, + String basePath, + String originalFilename, + String ext, + long size, + String eTag, + String path, + String bucketName, + Long thumbnailSize, + String thumbnailUrl, + LocalDateTime createTime) { + this.code = code; + this.url = url; + this.basePath = basePath; + this.originalFilename = originalFilename; + this.ext = ext; + this.size = size; + this.eTag = eTag; + this.path = path; + this.bucketName = bucketName; + this.thumbnailSize = thumbnailSize; + this.thumbnailUrl = thumbnailUrl; + this.createTime = createTime; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getBasePath() { + return basePath; + } + + public void setBasePath(String basePath) { + this.basePath = basePath; + } + + public String getOriginalFilename() { + return originalFilename; + } + + public void setOriginalFilename(String originalFilename) { + this.originalFilename = originalFilename; + } + + public String getExt() { + return ext; + } + + public void setExt(String ext) { + this.ext = ext; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + public String geteTag() { + return eTag; + } + + public void seteTag(String eTag) { + this.eTag = eTag; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getBucketName() { + return bucketName; + } + + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + public Long getThumbnailSize() { + return thumbnailSize; + } + + public void setThumbnailSize(Long thumbnailSize) { + this.thumbnailSize = thumbnailSize; + } + + public String getThumbnailUrl() { + return thumbnailUrl; + } + + public void setThumbnailUrl(String thumbnailUrl) { + this.thumbnailUrl = thumbnailUrl; + } + + public LocalDateTime getCreateTime() { + return createTime; + } + + public void setCreateTime(LocalDateTime createTime) { + this.createTime = createTime; + } +} diff --git a/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/strategy/StorageStrategy.java b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/strategy/StorageStrategy.java new file mode 100644 index 00000000..ad6d8596 --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/strategy/StorageStrategy.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.strategy; + +import top.continew.starter.storage.model.resp.ThumbnailResp; +import top.continew.starter.storage.model.resp.UploadResp; + +import java.io.InputStream; + +/** + * 存储策略接口 + * + * @author echo + * @date 2024/12/16 11:19 + */ +public interface StorageStrategy { + + /** + * 获得客户端 - 用于重写时 获取对应存储 code 客户端 + * + * @return {@link Object } + */ + C getClient(); + + /** + * 检查桶是否存在 + *

S3: 检查桶是否存在

+ *

local: 检查 默认路径 是否存在

+ * + * @param bucketName 桶名称 + * @return true 存在 false 不存在 + */ + boolean bucketExists(String bucketName); + + /** + * 创建桶 + *

S3: 创建桶

+ *

local: 创建 默认路径下 指定文件夹

+ * + * @param bucketName 桶名称 + */ + void createBucket(String bucketName); + + /** + * 上传文件 - 默认桶 + * + * @param fileName 文件名 + * @param inputStream 输入流 + * @param fileType 文件类型 + * @return 上传响应 + */ + UploadResp upload(String fileName, InputStream inputStream, String fileType); + + /** + * 上传文件 - 默认桶 + * + * @param fileName 文件名 + * @param path 路径 + * @param inputStream 输入流 + * @param fileType 文件类型 + * @param isThumbnail 是缩略图 + * @return {@link UploadResp } + */ + UploadResp upload(String fileName, String path, InputStream inputStream, String fileType, boolean isThumbnail); + + /** + * 上传文件 + * + * @param bucketName 桶名称 + * @param fileName 文件名 + * @param path 路径 + * @param inputStream 输入流 + * @param fileType 文件类型 + * @param isThumbnail 是缩略图 + * @return 上传响应 + */ + UploadResp upload(String bucketName, + String fileName, + String path, + InputStream inputStream, + String fileType, + boolean isThumbnail); + + /** + * 文件上传-基础上传 + * + * @param bucketName 桶名称 - 基础上传不做处理 + * @param fileName 文件名 - 基础上传不做处理 + * @param path 路径 - 基础上传不做处理 + * @param inputStream 输入流 + * @param fileType 文件类型 + * @return {@link UploadResp } + */ + void upload(String bucketName, String fileName, String path, InputStream inputStream, String fileType); + + /** + * 上传缩略图 + * + * @param bucketName 桶名称 + * @param fileName 文件名 + * @param inputStream 输入流 + * @param fileType 文件类型 + * @return {@link UploadResp } + */ + ThumbnailResp uploadThumbnail(String bucketName, + String fileName, + String path, + InputStream inputStream, + String fileType); + + /** + * 下载文件 + * + * @param bucketName 桶名称 + * @param fileName 文件名 + * @return 文件输入流 + */ + InputStream download(String bucketName, String fileName); + + /** + * 删除文件 + * + * @param bucketName 桶名称 + * @param fileName 文件名 + */ + void delete(String bucketName, String fileName); + + /** + * 获取图像Base64 + * + * @param bucketName 桶名称 + * @param fileName 文件名 + * @return Base64编码的图像 + */ + String getImageBase64(String bucketName, String fileName); + +} diff --git a/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/util/ImageThumbnailUtils.java b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/util/ImageThumbnailUtils.java new file mode 100644 index 00000000..67e040d7 --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/util/ImageThumbnailUtils.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.util; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * 图像缩略图工具 + * + * @author echo + * @date 2024/12/20 16:49 + */ +public class ImageThumbnailUtils { + + // 默认缩略图尺寸:100x100 + private static final int DEFAULT_WIDTH = 100; + private static final int DEFAULT_HEIGHT = 100; + + /** + * 根据输入流生成默认大小(100x100)的缩略图并写入输出流 + * + * @param inputStream 原始图片的输入流 + * @param outputStream 缩略图输出流 + * @param suffix 后缀 + * @throws IOException IOException + */ + public static void generateThumbnail(InputStream inputStream, + OutputStream outputStream, + String suffix) throws IOException { + generateThumbnail(inputStream, outputStream, DEFAULT_WIDTH, DEFAULT_HEIGHT, suffix); + } + + /** + * 根据输入流和自定义尺寸生成缩略图并写入输出流 + * + * @param inputStream 原始图片的输入流 + * @param outputStream 缩略图输出流 + * @param width 缩略图宽度 + * @param height 缩略图高度 + * @param suffix 后缀 + * @throws IOException IOException + */ + public static void generateThumbnail(InputStream inputStream, + OutputStream outputStream, + int width, + int height, + String suffix) throws IOException { + // 读取原始图片 + BufferedImage originalImage = ImageIO.read(inputStream); + + // 调整图片大小 + Image tmp = originalImage.getScaledInstance(width, height, Image.SCALE_SMOOTH); + BufferedImage thumbnail = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + + // 画出缩略图 + Graphics2D g2d = thumbnail.createGraphics(); + g2d.drawImage(tmp, 0, 0, null); + g2d.dispose(); + // 写入输出流 + ImageIO.write(thumbnail, suffix, outputStream); + } +} diff --git a/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/util/StorageUtils.java b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/util/StorageUtils.java new file mode 100644 index 00000000..4cfca27a --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-core/src/main/java/top/continew/starter/storage/util/StorageUtils.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.util; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.io.file.FileNameUtil; +import cn.hutool.core.util.StrUtil; +import top.continew.starter.core.constant.StringConstants; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.Paths; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * 储存工具 + * + * @author echo + * @date 2024/12/16 19:55 + */ +public class StorageUtils { + public StorageUtils() { + } + + /** + * 格式文件名 + * + * @param fileName 文件名 + * @return {@link String } + */ + public static String formatFileName(String fileName) { + // 获取文件后缀名 + String suffix = FileUtil.extName(fileName); + // 获取当前时间的年月日时分秒格式 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); + String datetime = LocalDateTime.now().format(formatter); + // 获取当前时间戳 + String timestamp = String.valueOf(System.currentTimeMillis()); + // 生成新的文件名 + return datetime + timestamp + "." + suffix; + } + + /** + * 默认文件目录 + * + * @param fileName 文件名 + * @return {@link String } + */ + public static String defaultFileDir(String fileName) { + LocalDate today = LocalDate.now(); + return Paths.get(String.valueOf(today.getYear()), String.valueOf(today.getMonthValue()), String.valueOf(today + .getDayOfMonth()), fileName).toString(); + } + + /** + * 默认路径地址 格式 2024/03/10/ + * + * @return {@link String } + */ + public static String defaultPath() { + LocalDate today = LocalDate.now(); + return Paths.get(String.valueOf(today.getYear()), String.valueOf(today.getMonthValue()), String.valueOf(today + .getDayOfMonth())) + StringConstants.SLASH; + } + + /** + * 根据 endpoint 判断是否带有 http 或 https,如果没有则加上 http 前缀。 + * + * @param endpoint 输入的 endpoint 字符串 + * @return URI 对象 + */ + public static URI createUriWithProtocol(String endpoint) { + // 判断 endpoint 是否包含 http:// 或 https:// 前缀 + if (!endpoint.startsWith("http://") && !endpoint.startsWith("https://")) { + // 如果没有协议前缀,则加上 http:// + endpoint = "http://" + endpoint; + } + // 返回 URI 对象 + return URI.create(endpoint); + } + + /** + * 生成缩略图文件名 + * + * @param fileName 文件名 + * @param suffix 后缀 + * @return {@link String } + */ + public static String buildThumbnailFileName(String fileName, String suffix) { + // 获取文件的扩展名 + String extName = FileNameUtil.extName(fileName); + // 去掉扩展名 + String baseName = StrUtil.subBefore(fileName, StringConstants.DOT, true); + // 拼接新的路径:原始路径 + .缩略图后缀 + .扩展名 + return baseName + "." + suffix + "." + extName; + } + + /** + * 可重复读流 + * + * @param inputStream 输入流 + * @return {@link InputStream } + */ + public static InputStream ensureByteArrayStream(InputStream inputStream) { + return (inputStream instanceof ByteArrayInputStream) + ? inputStream + : new ByteArrayInputStream(IoUtil.readBytes(inputStream)); + } + +} diff --git a/continew-starter-storage/continew-starter-storage-local/pom.xml b/continew-starter-storage/continew-starter-storage-local/pom.xml index 0ee4e8ea..8d2638ea 100644 --- a/continew-starter-storage/continew-starter-storage-local/pom.xml +++ b/continew-starter-storage/continew-starter-storage-local/pom.xml @@ -13,10 +13,10 @@ ContiNew Starter 存储模块 - 本地存储 - + - org.springframework - spring-webmvc + top.continew + continew-starter-storage-core \ No newline at end of file diff --git a/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/autoconfigure/LocalStorageAutoconfigure.java b/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/autoconfigure/LocalStorageAutoconfigure.java new file mode 100644 index 00000000..86c8612d --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/autoconfigure/LocalStorageAutoconfigure.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.autoconfigure; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import top.continew.starter.storage.dao.StorageDao; +import top.continew.starter.storage.dao.impl.StorageDaoDefaultImpl; + +/** + * 本地存储 - 存储自动配置 + * + * @author echo + * @date 2024/12/17 20:23 + */ +@AutoConfiguration +public class LocalStorageAutoconfigure { + + @Bean + @ConditionalOnMissingBean + public StorageDao storageDao() { + return new StorageDaoDefaultImpl(); + } +} diff --git a/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/client/LocalClient.java b/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/client/LocalClient.java new file mode 100644 index 00000000..7bec647b --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/client/LocalClient.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.client; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import top.continew.starter.storage.model.req.StorageProperties; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * 本地客户端 + * + * @author echo + * @date 2024/12/16 19:37 + */ +public class LocalClient { + private static final Logger log = LoggerFactory.getLogger(LocalClient.class); + + /** + * 配置属性 + */ + private final StorageProperties properties; + + /** + * 构造函数 + * + * @param properties 配置属性 + */ + public LocalClient(StorageProperties properties) { + this.properties = properties; + // 判断是否是默认存储,若不存在桶目录,则创建 + if (Boolean.TRUE.equals(properties.getIsDefault())) { + String bucketName = properties.getBucketName(); + if (bucketName != null && !bucketName.isEmpty()) { + createBucketDirectory(bucketName); + } else { + log.info("默认存储-存储桶已存在 => {}", bucketName); + } + } + log.info("加载 Local 存储 => {}", properties.getCode()); + } + + /** + * 获取属性 + * + * @return {@link StorageProperties } + */ + public StorageProperties getProperties() { + return properties; + } + + /** + * 创建桶目录 + * + * @param bucketName 桶名称 + */ + private void createBucketDirectory(String bucketName) { + Path bucketPath = Path.of(bucketName); + try { + if (Files.notExists(bucketPath)) { + Files.createDirectories(bucketPath); + log.info("默认存储-存储桶创建成功 : {}", bucketPath.toAbsolutePath()); + } + } catch (IOException e) { + log.error("创建默认存储-存储桶失败 => 路径: {}", bucketPath.toAbsolutePath(), e); + } + } +} diff --git a/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/local/autoconfigure/LocalStorageAutoConfiguration.java b/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/local/autoconfigure/LocalStorageAutoConfiguration.java deleted file mode 100644 index 38f8f66d..00000000 --- a/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/local/autoconfigure/LocalStorageAutoConfiguration.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - *

- * 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 top.continew.starter.storage.local.autoconfigure; - -import cn.hutool.core.text.CharSequenceUtil; -import jakarta.annotation.PostConstruct; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import top.continew.starter.core.constant.PropertiesConstants; -import top.continew.starter.core.constant.StringConstants; - -import java.util.Map; - -/** - * 本地文件自动配置 - * - * @author Charles7c - * @since 1.1.0 - */ -@EnableWebMvc -@AutoConfiguration -@EnableConfigurationProperties(LocalStorageProperties.class) -@ConditionalOnProperty(prefix = PropertiesConstants.STORAGE_LOCAL, name = PropertiesConstants.ENABLED, havingValue = "true", matchIfMissing = true) -public class LocalStorageAutoConfiguration implements WebMvcConfigurer { - - private static final Logger log = LoggerFactory.getLogger(LocalStorageAutoConfiguration.class); - private final LocalStorageProperties properties; - - public LocalStorageAutoConfiguration(LocalStorageProperties properties) { - this.properties = properties; - } - - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - Map mappingMap = properties.getMapping(); - for (Map.Entry mappingEntry : mappingMap.entrySet()) { - LocalStorageProperties.LocalStorageMapping mapping = mappingEntry.getValue(); - String pathPattern = mapping.getPathPattern(); - String location = mapping.getLocation(); - if (CharSequenceUtil.isBlank(location)) { - throw new IllegalArgumentException("Path pattern [%s] location is null.".formatted(pathPattern)); - } - registry.addResourceHandler(CharSequenceUtil.appendIfMissing(pathPattern, StringConstants.PATH_PATTERN)) - .addResourceLocations(!location.startsWith("file:") - ? "file:%s".formatted(this.format(location)) - : this.format(location)) - .setCachePeriod(0); - } - } - - private String format(String location) { - return location.replace(StringConstants.BACKSLASH, StringConstants.SLASH); - } - - @PostConstruct - public void postConstruct() { - log.debug("[ContiNew Starter] - Auto Configuration 'Storage-Local' completed initialization."); - } -} diff --git a/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/local/autoconfigure/LocalStorageProperties.java b/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/local/autoconfigure/LocalStorageProperties.java deleted file mode 100644 index 182b5716..00000000 --- a/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/local/autoconfigure/LocalStorageProperties.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - *

- * 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 top.continew.starter.storage.local.autoconfigure; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.util.unit.DataSize; -import top.continew.starter.core.constant.PropertiesConstants; - -import java.util.HashMap; -import java.util.Map; - -/** - * 本地存储配置属性 - * - * @author Charles7c - * @since 1.1.0 - */ -@ConfigurationProperties(PropertiesConstants.STORAGE_LOCAL) -public class LocalStorageProperties { - - /** - * 是否启用本地存储 - */ - private boolean enabled = true; - - /** - * 存储映射 - */ - private Map mapping = new HashMap<>(); - - /** - * 本地存储映射 - */ - public static class LocalStorageMapping { - - /** - * 路径模式 - */ - private String pathPattern; - - /** - * 资源路径 - */ - private String location; - - /** - * 单文件上传大小限制 - */ - private DataSize maxFileSize = DataSize.ofMegabytes(1); - - public String getPathPattern() { - return pathPattern; - } - - public void setPathPattern(String pathPattern) { - this.pathPattern = pathPattern; - } - - public String getLocation() { - return location; - } - - public void setLocation(String location) { - this.location = location; - } - - public DataSize getMaxFileSize() { - return maxFileSize; - } - - public void setMaxFileSize(DataSize maxFileSize) { - this.maxFileSize = maxFileSize; - } - } - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public Map getMapping() { - return mapping; - } - - public void setMapping(Map mapping) { - this.mapping = mapping; - } -} diff --git a/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/strategy/LocalStorageStrategy.java b/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/strategy/LocalStorageStrategy.java new file mode 100644 index 00000000..39bc36d5 --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/strategy/LocalStorageStrategy.java @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.strategy; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.io.file.FileNameUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.exception.BusinessException; +import top.continew.starter.core.validation.CheckUtils; +import top.continew.starter.core.validation.ValidationUtils; +import top.continew.starter.storage.client.LocalClient; +import top.continew.starter.storage.constant.StorageConstant; +import top.continew.starter.storage.dao.StorageDao; +import top.continew.starter.storage.enums.FileTypeEnum; +import top.continew.starter.storage.model.req.StorageProperties; +import top.continew.starter.storage.model.resp.ThumbnailResp; +import top.continew.starter.storage.model.resp.UploadResp; +import top.continew.starter.storage.util.ImageThumbnailUtils; +import top.continew.starter.storage.util.LocalUtils; +import top.continew.starter.storage.util.StorageUtils; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.NoSuchAlgorithmException; +import java.time.LocalDateTime; +import java.util.Base64; + +/** + * 本地存储策略 + * + * @author echo + * @date 2024/12/16 19:48 + */ +public class LocalStorageStrategy implements StorageStrategy { + + private final LocalClient client; + private final StorageDao storageDao; + + public LocalStorageStrategy(LocalClient client, StorageDao storageDao) { + this.client = client; + this.storageDao = storageDao; + } + + private StorageProperties getStorageProperties() { + return client.getProperties(); + } + + @Override + public LocalClient getClient() { + return client; + } + + @Override + public boolean bucketExists(String bucketName) { + try { + return Files.exists(Path.of(bucketName)); + } catch (RuntimeException e) { + throw new BusinessException("local存储 查询桶 失败", e); + } + } + + @Override + public void createBucket(String bucketName) { + if (!bucketExists(bucketName)) { + try { + Files.createDirectories(Path.of(bucketName)); + } catch (IOException e) { + throw new BusinessException("local存储 创建桶 失败", e); + } + } + } + + @Override + public UploadResp upload(String fileName, InputStream inputStream, String fileType) { + String bucketName = getStorageProperties().getBucketName(); + return this.upload(bucketName, fileName, null, inputStream, fileType, false); + } + + @Override + public UploadResp upload(String fileName, + String path, + InputStream inputStream, + String fileType, + boolean isThumbnail) { + String bucketName = getStorageProperties().getBucketName(); + return this.upload(bucketName, fileName, path, inputStream, fileType, isThumbnail); + } + + @Override + public UploadResp upload(String bucketName, + String fileName, + String path, + InputStream inputStream, + String fileType, + boolean isThumbnail) { + try { + // 可重复读流 + inputStream = StorageUtils.ensureByteArrayStream(inputStream); + // 获取流大小 + byte[] originalBytes = IoUtil.readBytes(inputStream); + ValidationUtils.throwIf(originalBytes.length == 0, "输入流内容长度不可用或无效"); + + // 获取文件扩展名 + String fileExtension = FileNameUtil.extName(fileName); + // 格式化文件名 防止上传后重复 + String formatFileName = StorageUtils.formatFileName(fileName); + // 判断文件路径是否为空 为空给默认路径 格式 2024/12/30/ + if (StrUtil.isEmpty(path)) { + path = StorageUtils.defaultPath(); + } + // 判断文件夹是否存在 不存在则创建 + Path folderPath = Paths.get(bucketName, path); + if (!Files.exists(folderPath)) { + Files.createDirectories(folderPath); + } + ThumbnailResp thumbnailResp = null; + //判断是否需要上传缩略图 前置条件 文件必须为图片 + boolean contains = FileTypeEnum.IMAGE.getExtensions().contains(fileExtension); + if (contains && isThumbnail) { + try (InputStream thumbnailStream = new ByteArrayInputStream(originalBytes)) { + thumbnailResp = this.uploadThumbnail(bucketName, formatFileName, path, thumbnailStream, fileType); + } + } + + // 上传文件 + try (InputStream uploadStream = new ByteArrayInputStream(originalBytes)) { + this.upload(bucketName, formatFileName, path, uploadStream, fileType); + } + + // 构建文件 md5 + String eTag = LocalUtils.calculateMD5(inputStream); + // 构建 上传后的文件路径地址 格式 xxx/xxx/xxx.jpg + String filePath = Paths.get(path, formatFileName).toString(); + // 构建 文件上传记录 并返回 + return buildStorageRecord(bucketName, fileName, filePath, eTag, originalBytes.length, thumbnailResp); + } catch (NoSuchAlgorithmException | IOException e) { + throw new BusinessException("文件上传异常", e); + } + + } + + @Override + public void upload(String bucketName, String fileName, String path, InputStream inputStream, String fileType) { + byte[] fileBytes = IoUtil.readBytes(inputStream); + // 拼接完整地址 + String filePath = Paths.get(bucketName, path, fileName).toString(); + try { + //上传文件 + File targetFile = new File(filePath); + try (FileOutputStream fos = new FileOutputStream(targetFile)) { + fos.write(fileBytes); + } + } catch (IOException e) { + throw new BusinessException("文件上传异常", e); + } + } + + @Override + public ThumbnailResp uploadThumbnail(String bucketName, + String fileName, + String path, + InputStream inputStream, + String fileType) { + // 获取文件扩展名 + String fileExtension = FileNameUtil.extName(fileName); + // 生成缩略图文件名 + String thumbnailFileName = StorageUtils.buildThumbnailFileName(fileName, StorageConstant.SMALL_SUFFIX); + // 处理文件为缩略图 + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + ImageThumbnailUtils.generateThumbnail(inputStream, outputStream, fileExtension); + inputStream = new ByteArrayInputStream(outputStream.toByteArray()); + // 上传文件 + this.upload(bucketName, thumbnailFileName, path, inputStream, null); + + return new ThumbnailResp((long)outputStream.size(), Paths.get(path, thumbnailFileName).toString()); + } catch (IOException e) { + throw new BusinessException("缩略图处理异常", e); + } + } + + @Override + public InputStream download(String bucketName, String fileName) { + String fullPath = Paths.get(bucketName, fileName).toString(); + File file = new File(fullPath); + try { + return new FileInputStream(file); + } catch (IOException e) { + throw new BusinessException("下载文件异常", e); + } + } + + @Override + public void delete(String bucketName, String fileName) { + try { + String fullPath = Paths.get(bucketName, fileName).toString(); + Files.delete(Paths.get(fullPath)); + } catch (Exception e) { + throw new BusinessException("删除文件异常", e); + } + } + + @Override + public String getImageBase64(String bucketName, String fileName) { + try (InputStream inputStream = download(bucketName, fileName)) { + if (ObjectUtil.isEmpty(inputStream)) { + return null; + } + String extName = FileUtil.extName(fileName); + CheckUtils.throwIf(!FileTypeEnum.IMAGE.getExtensions().contains(extName), "{} 不是图像格式", extName); + return Base64.getEncoder().encodeToString(inputStream.readAllBytes()); + } catch (Exception e) { + throw new BusinessException("无法查看图片", e); + } + } + + /** + * 构建存储记录 + * + * @param bucketName 桶名称 + * @param fileName 原始文件名 + * @param filePath 文件路径 xx/xx/xxx.jpg + * @param eTag 标签 - md5 + * @param size 文件大小 + * @param thumbnailResp 缩略图信息 + * @return {@link UploadResp } + */ + private UploadResp buildStorageRecord(String bucketName, + String fileName, + String filePath, + String eTag, + long size, + ThumbnailResp thumbnailResp) { + // 获取当前存储 code + String code = client.getProperties().getCode(); + // 构建访问地址前缀 + String baseUrl = "http://" + getStorageProperties().getEndpoint() + StringConstants.SLASH; + + UploadResp resp = new UploadResp(); + resp.setCode(code); + resp.setUrl(baseUrl + filePath); + resp.setBasePath(filePath); + resp.setOriginalFilename(fileName); + resp.setExt(FileNameUtil.extName(fileName)); + resp.setSize(size); + resp.seteTag(eTag); + resp.setPath(filePath); + resp.setBucketName(bucketName); + resp.setCreateTime(LocalDateTime.now()); + if (ObjectUtil.isNotEmpty(thumbnailResp)) { + resp.setThumbnailUrl(baseUrl + thumbnailResp.getThumbnailPath()); + resp.setThumbnailSize(thumbnailResp.getThumbnailSize()); + } + storageDao.add(resp); + return resp; + } +} diff --git a/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/util/LocalUtils.java b/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/util/LocalUtils.java new file mode 100644 index 00000000..07520b12 --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-local/src/main/java/top/continew/starter/storage/util/LocalUtils.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.util; + +import cn.hutool.core.io.IoUtil; +import net.dreamlu.mica.core.utils.DigestUtil; + +import java.io.InputStream; +import java.security.NoSuchAlgorithmException; + +/** + * 本地存储工具 + * + * @author echo + * @date 2024/12/27 11:58 + */ +public class LocalUtils { + public LocalUtils() { + } + + /** + * 计算MD5 + * + * @param inputStream 输入流 + * @return {@link String } + * @throws NoSuchAlgorithmException 没有这样算法例外 + */ + public static String calculateMD5(InputStream inputStream) throws NoSuchAlgorithmException { + byte[] fileBytes = IoUtil.readBytes(inputStream); + return DigestUtil.md5Hex(fileBytes); + } +} diff --git a/continew-starter-storage/continew-starter-storage-local/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/continew-starter-storage/continew-starter-storage-local/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 93613475..169edcb2 100644 --- a/continew-starter-storage/continew-starter-storage-local/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/continew-starter-storage/continew-starter-storage-local/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1 +1 @@ -top.continew.starter.storage.local.autoconfigure.LocalStorageAutoConfiguration \ No newline at end of file +top.continew.starter.storage.autoconfigure.LocalStorageAutoconfigure \ No newline at end of file diff --git a/continew-starter-storage/continew-starter-storage-oss/pom.xml b/continew-starter-storage/continew-starter-storage-oss/pom.xml new file mode 100644 index 00000000..d4d8734b --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-oss/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + top.continew + continew-starter-storage + ${revision} + + + continew-starter-storage-oss + ContiNew Starter 存储模块 - 对象存储 + + + + + software.amazon.awssdk + s3 + + + + software.amazon.awssdk + netty-nio-client + + + + software.amazon.awssdk + aws-crt-client + + + + software.amazon.awssdk + apache-client + + + + software.amazon.awssdk + url-connection-client + + + + + + + software.amazon.awssdk.crt + aws-crt + + + + + software.amazon.awssdk + s3-transfer-manager + + + + + top.continew + continew-starter-storage-core + + + + + diff --git a/continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/autoconfigure/OssStorageAutoconfigure.java b/continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/autoconfigure/OssStorageAutoconfigure.java new file mode 100644 index 00000000..4dc512c4 --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/autoconfigure/OssStorageAutoconfigure.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.autoconfigure; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import top.continew.starter.storage.dao.StorageDao; +import top.continew.starter.storage.dao.impl.StorageDaoDefaultImpl; + +/** + * 对象存储 - 存储自动配置 + * + * @author echo + * @date 2024/12/17 20:23 + */ +@AutoConfiguration +public class OssStorageAutoconfigure { + + @Bean + @ConditionalOnMissingBean + public StorageDao storageDao() { + return new StorageDaoDefaultImpl(); + } + +} diff --git a/continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/client/OssClient.java b/continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/client/OssClient.java new file mode 100644 index 00000000..b1b519bc --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/client/OssClient.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.client; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.crt.S3CrtHttpConfiguration; +import software.amazon.awssdk.services.s3.model.CreateBucketRequest; +import software.amazon.awssdk.services.s3.model.HeadBucketRequest; +import software.amazon.awssdk.services.s3.model.NoSuchBucketException; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.transfer.s3.S3TransferManager; +import top.continew.starter.core.exception.BusinessException; +import top.continew.starter.storage.model.req.StorageProperties; +import top.continew.starter.storage.util.OssUtils; +import top.continew.starter.storage.util.StorageUtils; + +import java.net.URI; +import java.time.Duration; + +/** + * S3客户端 + * + * @author echo + * @date 2024/12/16 + */ +public class OssClient { + + private static final Logger log = LoggerFactory.getLogger(OssClient.class); + + /** + * 配置属性 + */ + private final StorageProperties properties; + + /** + * s3 异步客户端 + */ + private final S3AsyncClient client; + + /** + * S3 数据传输的高级工具 + */ + private final S3TransferManager transferManager; + + /** + * S3 预签名 + */ + private final S3Presigner presigner; + + /** + * 获取属性 + * + * @return {@link StorageProperties } + */ + public StorageProperties getProperties() { + return properties; + } + + /** + * 构造方法 + * + * @param s3PropertiesReq 微型性能要求 + */ + public OssClient(StorageProperties s3PropertiesReq) { + this.properties = s3PropertiesReq; + + // 创建认证信息 + StaticCredentialsProvider auth = StaticCredentialsProvider.create(AwsBasicCredentials.create(properties + .getAccessKey(), properties.getSecretKey())); + + URI uriWithProtocol = StorageUtils.createUriWithProtocol(properties.getEndpoint()); + + // 创建 客户端连接 + client = S3AsyncClient.crtBuilder() + .credentialsProvider(auth) // 认证信息 + .endpointOverride(uriWithProtocol) // 连接端点 + .region(OssUtils.getRegion(properties.getRegion())) + .targetThroughputInGbps(20.0) //吞吐量 + .minimumPartSizeInBytes(10 * 1025 * 1024L) + .checksumValidationEnabled(false) + .httpConfiguration(S3CrtHttpConfiguration.builder() + .connectionTimeout(Duration.ofSeconds(60)) // 设置连接超时 + .build()) + .build(); + + // 基于 CRT 创建 S3 Transfer Manager 的实例 + this.transferManager = S3TransferManager.builder().s3Client(this.client).build(); + + this.presigner = S3Presigner.builder() + .region(OssUtils.getRegion(properties.getRegion())) + .credentialsProvider(auth) + .endpointOverride(uriWithProtocol) + .build(); + + // 只创建 默认存储的的桶 + if (s3PropertiesReq.getIsDefault()) { + try { + // 检查存储桶是否存在 + client.headBucket(HeadBucketRequest.builder().bucket(properties.getBucketName()).build()); + log.info("默认存储-存储桶 {} 已存在", properties.getBucketName()); + } catch (NoSuchBucketException e) { + log.info("默认存储桶 {} 不存在,尝试创建...", properties.getBucketName()); + try { + // 创建存储桶 + client.createBucket(CreateBucketRequest.builder().bucket(properties.getBucketName()).build()); + log.info("默认存储-存储桶 {} 创建成功", properties.getBucketName()); + } catch (Exception createException) { + log.error("创建默认存储-存储桶 {} 失败", properties.getBucketName(), createException); + throw new BusinessException("创建默认存储-桶出错", createException); + } + } catch (Exception e) { + log.error("检查默认存储-存储桶 {} 时出错", properties.getBucketName(), e); + throw new BusinessException("检查默认存储-桶时出错", e); + } + } + log.info("加载 S3 存储 => {}", properties.getCode()); + } + + /** + * 获得客户端 + * + * @return {@link S3TransferManager } + */ + public S3AsyncClient getClient() { + return client; + } + + /** + * 获得 高效连接客户端 主要用于 上传下载 复制 删除 + * + * @return {@link S3TransferManager } + */ + public S3TransferManager getTransferManager() { + return transferManager; + } + + /** + * 获得 S3 预签名 + * + * @return {@link S3Presigner } + */ + public S3Presigner getPresigner() { + return presigner; + } +} diff --git a/continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/strategy/OssStorageStrategy.java b/continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/strategy/OssStorageStrategy.java new file mode 100644 index 00000000..f6eb6f40 --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/strategy/OssStorageStrategy.java @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.strategy; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.io.file.FileNameUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.core.ResponseInputStream; +import software.amazon.awssdk.core.async.AsyncResponseTransformer; +import software.amazon.awssdk.core.async.BlockingInputStreamAsyncRequestBody; +import software.amazon.awssdk.services.s3.model.*; +import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest; +import software.amazon.awssdk.transfer.s3.model.CompletedUpload; +import software.amazon.awssdk.transfer.s3.model.Download; +import software.amazon.awssdk.transfer.s3.model.DownloadRequest; +import software.amazon.awssdk.transfer.s3.model.Upload; +import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.exception.BusinessException; +import top.continew.starter.core.validation.CheckUtils; +import top.continew.starter.core.validation.ValidationUtils; +import top.continew.starter.storage.client.OssClient; +import top.continew.starter.storage.constant.StorageConstant; +import top.continew.starter.storage.dao.StorageDao; +import top.continew.starter.storage.enums.FileTypeEnum; +import top.continew.starter.storage.model.req.StorageProperties; +import top.continew.starter.storage.model.resp.ThumbnailResp; +import top.continew.starter.storage.model.resp.UploadResp; +import top.continew.starter.storage.util.ImageThumbnailUtils; +import top.continew.starter.storage.util.OssUtils; +import top.continew.starter.storage.util.StorageUtils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.nio.file.Paths; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.Base64; +import java.util.List; +import java.util.concurrent.CompletionException; + +/** + * OSS存储策略 + *

...

+ * + * @author echo + * @date 2024/12/16 20:29 + */ +public class OssStorageStrategy implements StorageStrategy { + private final static Logger log = LoggerFactory.getLogger(OssStorageStrategy.class); + + private final OssClient client; + private final StorageDao storageDao; + private String etag; + + public OssStorageStrategy(OssClient ossClient, StorageDao storageDao) { + this.client = ossClient; + this.storageDao = storageDao; + } + + private StorageProperties getStorageProperties() { + return client.getProperties(); + } + + @Override + public OssClient getClient() { + return client; + } + + @Override + public boolean bucketExists(String bucketName) { + try { + // 调用 headBucket 请求,检查桶是否存在 + client.getClient().headBucket(HeadBucketRequest.builder().bucket(bucketName).build()).join(); + return true; // 桶存在 + } catch (Exception e) { + // 捕获异常,详细判断具体原因 + if (e.getCause() instanceof NoSuchBucketException) { + // 桶不存在 + return false; + } else if (e.getCause() instanceof S3Exception s3Exception) { + // 检查是否是其他人创建的桶(403 Forbidden 错误) + if (s3Exception.statusCode() == HttpURLConnection.HTTP_FORBIDDEN) { + throw new BusinessException("全局重复:存储桶名称已被他人创建:" + bucketName); + } + } + // 捕获其他所有异常,并抛出 + throw new BusinessException("S3 存储桶查询失败,存储桶名称:" + bucketName, e); + } + } + + @Override + public void createBucket(String bucketName) { + try { + if (!this.bucketExists(bucketName)) { + client.getClient().createBucket(CreateBucketRequest.builder().bucket(bucketName).build()).join(); + } + } catch (S3Exception e) { + throw new BusinessException("S3 存储桶,创建失败", e); + } + } + + @Override + public UploadResp upload(String fileName, InputStream inputStream, String fileType) { + String bucketName = getStorageProperties().getBucketName(); + return this.upload(bucketName, fileName, null, inputStream, fileType, false); + } + + @Override + public UploadResp upload(String fileName, + String path, + InputStream inputStream, + String fileType, + boolean isThumbnail) { + String bucketName = getStorageProperties().getBucketName(); + return this.upload(bucketName, fileName, path, inputStream, fileType, isThumbnail); + } + + @Override + public UploadResp upload(String bucketName, + String fileName, + String path, + InputStream inputStream, + String fileType, + boolean isThumbnail) { + try { + + // 可重复读流 + inputStream = StorageUtils.ensureByteArrayStream(inputStream); + byte[] fileBytes = IoUtil.readBytes(inputStream); + ValidationUtils.throwIf(fileBytes.length == 0, "输入流内容长度不可用或无效"); + // 获取文件扩展名 + String fileExtension = FileNameUtil.extName(fileName); + // 格式化文件名 防止上传后重复 + String formatFileName = StorageUtils.formatFileName(fileName); + // 判断文件路径是否为空 为空给默认路径 格式 2024/12/30/ + if (StrUtil.isEmpty(path)) { + path = StorageUtils.defaultPath(); + } + ThumbnailResp thumbnailResp = null; + //判断是否需要上传缩略图 前置条件 文件必须为图片 + boolean contains = FileTypeEnum.IMAGE.getExtensions().contains(fileExtension); + if (contains && isThumbnail) { + try (InputStream thumbnailStream = new ByteArrayInputStream(fileBytes)) { + thumbnailResp = this.uploadThumbnail(bucketName, formatFileName, path, thumbnailStream, fileType); + } + } + + // 上传文件 + try (InputStream uploadStream = new ByteArrayInputStream(fileBytes)) { + this.upload(bucketName, formatFileName, path, uploadStream, fileType); + } + String eTag = etag; + // 构建 上传后的文件路径地址 格式 xxx/xxx/xxx.jpg + String filePath = Paths.get(path, formatFileName).toString(); + // 构建 文件上传记录 并返回 + return buildStorageRecord(bucketName, fileName, filePath, eTag, fileBytes.length, thumbnailResp); + } catch (IOException e) { + throw new BusinessException("文件上传异常", e); + } + } + + @Override + public void upload(String bucketName, String fileName, String path, InputStream inputStream, String fileType) { + // 构建 S3 存储 文件路径 + String filePath = Paths.get(path, fileName).toString(); + try { + long available = inputStream.available(); + // 构建异步请求体,指定内容长度 + BlockingInputStreamAsyncRequestBody requestBody = BlockingInputStreamAsyncRequestBody.builder() + .contentLength(available) + .subscribeTimeout(Duration.ofSeconds(30)) + .build(); + + // 初始化上传任务 + Upload upload = client.getTransferManager() + .upload(u -> u.requestBody(requestBody) + .putObjectRequest(b -> b.bucket(bucketName).key(filePath).contentType(fileType).build()) + .build()); + + // 写入输入流内容到请求体 + requestBody.writeInputStream(inputStream); + CompletedUpload uploadResult = upload.completionFuture().join(); + etag = uploadResult.response().eTag(); + } catch (IOException e) { + throw new BusinessException("文件上传异常", e); + } + } + + @Override + public ThumbnailResp uploadThumbnail(String bucketName, + String fileName, + String path, + InputStream inputStream, + String fileType) { + // 获取文件扩展名 + String fileExtension = FileNameUtil.extName(fileName); + // 生成缩略图文件名 + String thumbnailFileName = StorageUtils.buildThumbnailFileName(fileName, StorageConstant.SMALL_SUFFIX); + // 处理文件为缩略图 + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + ImageThumbnailUtils.generateThumbnail(inputStream, outputStream, fileExtension); + inputStream = new ByteArrayInputStream(outputStream.toByteArray()); + // 上传文件 + this.upload(bucketName, thumbnailFileName, path, inputStream, fileType); + return new ThumbnailResp((long)outputStream.size(), Paths.get(path, thumbnailFileName).toString()); + } catch (IOException e) { + throw new BusinessException("缩略图处理异常", e); + } + } + + @Override + public InputStream download(String bucketName, String fileName) { + try { + // 构建下载请求 + DownloadRequest> downloadRequest = DownloadRequest.builder() + .getObjectRequest(req -> req.bucket(bucketName).key(fileName).build()) // 设置桶名和对象名 + .addTransferListener(LoggingTransferListener.create()) // 添加传输监听器 + .responseTransformer(AsyncResponseTransformer.toBlockingInputStream()) // 转换为阻塞输入流 + .build(); + // 执行下载操作 + Download> download = client.getTransferManager() + .download(downloadRequest); + // 直接等待下载完成并返回 InputStream + // 返回输入流 + return download.completionFuture().join().result(); + } catch (CompletionException e) { + // 处理异步执行中的异常 + throw new BusinessException("文件下载失败,错误信息: " + e.getCause().getMessage(), e.getCause()); + } catch (Exception e) { + // 捕获其他异常 + throw new BusinessException("文件下载失败,发生未知错误", e); + } + } + + @Override + public void delete(String bucketName, String fileName) { + try { + client.getClient().deleteObject(DeleteObjectRequest.builder().bucket(bucketName).key(fileName).build()); + } catch (Exception e) { + throw new BusinessException("S3 文件删除失败", e); + } + } + + @Override + public String getImageBase64(String bucketName, String fileName) { + try (InputStream inputStream = download(bucketName, fileName)) { + if (ObjectUtil.isEmpty(inputStream)) { + return null; + } + String extName = FileUtil.extName(fileName); + boolean contains = FileTypeEnum.IMAGE.getExtensions().contains(extName); + CheckUtils.throwIf(!contains, "{}非图片格式,无法获取", extName); + return Base64.getEncoder().encodeToString(inputStream.readAllBytes()); + } catch (Exception e) { + throw new BusinessException("图片查看失败", e); + } + } + + /** + * 构建储存记录 + * + * @param bucketName 桶名称 + * @param fileName 文件名 + * @param filePath 文件路径 + * @param eTag e 标记 + * @param contentLength 内容长度 + * @param thumbnailResp 相应缩略图 + * @return {@link UploadResp } + */ + private UploadResp buildStorageRecord(String bucketName, + String fileName, + String filePath, + String eTag, + long contentLength, + ThumbnailResp thumbnailResp) { + // 获取终端地址 + String endpoint = client.getProperties().getEndpoint(); + // 判断桶策略 + boolean isPrivateBucket = this.isPrivate(bucketName); + // 如果是私有桶 则生成私有URL链接 默认 访问时间为 12 小时 + String url = isPrivateBucket + ? this.getPrivateUrl(bucketName, filePath, 12) + : OssUtils.getUrl(endpoint, bucketName) + StringConstants.SLASH + filePath; + + String thumbnailUrl = ""; + long thumbnailSize = 0; + // 判断缩略图响应是否为空 + if (ObjectUtil.isNotEmpty(thumbnailResp)) { + // 同理按照 访问桶策略构建 缩略图访问地址 + thumbnailUrl = isPrivateBucket + ? this.getPrivateUrl(bucketName, thumbnailResp.getThumbnailPath(), 12) + : OssUtils.getUrl(endpoint, bucketName) + StringConstants.SLASH + thumbnailResp.getThumbnailPath(); + thumbnailSize = thumbnailResp.getThumbnailSize(); + } + + UploadResp uploadResp = new UploadResp(); + uploadResp.setCode(client.getProperties().getCode()); + uploadResp.setUrl(url); + uploadResp.setBasePath(filePath); + uploadResp.setOriginalFilename(fileName); + uploadResp.setExt(FileNameUtil.extName(fileName)); + uploadResp.setSize(contentLength); + uploadResp.setThumbnailUrl(thumbnailUrl); + uploadResp.setThumbnailSize(thumbnailSize); + uploadResp.seteTag(eTag); + uploadResp.setPath(Paths.get(bucketName, filePath).toString()); + uploadResp.setBucketName(bucketName); + uploadResp.setCreateTime(LocalDateTime.now()); + storageDao.add(uploadResp); + return uploadResp; + } + + /** + * 是否为私有桶 + * + * @param bucketName 桶名称 + * @return boolean T 是 F 不是 + */ + private boolean isPrivate(String bucketName) { + try { + // 尝试获取桶的策略 + GetBucketPolicyResponse policyResponse = client.getClient() + .getBucketPolicy(GetBucketPolicyRequest.builder().bucket(bucketName).build()) + .join(); + //转成 json + String policy = policyResponse.policy(); + JSONObject json = new JSONObject(policy); + // 为空则是私有 + return ObjectUtil.isEmpty(json.get("Statement")); + } catch (Exception e) { + // 如果 getBucketPolicy 抛出异常,说明不是 MinIO 或不支持策略 + log.warn("获取桶策略失败,可能是 MinIO,异常信息: {}", e.getMessage()); + } + + try { + // 获取桶的 ACL 信息 + GetBucketAclResponse aclResponse = client.getClient() + .getBucketAcl(GetBucketAclRequest.builder().bucket(bucketName).build()) + .join(); + List grants = aclResponse.grants(); + // 只存在 FULL_CONTROL 权限并且只有一个 Grant,则认为是私有桶 + if (grants.size() == 1 && grants.stream() + .anyMatch(grant -> grant.permission().equals(Permission.FULL_CONTROL))) { + return true; + } + // 如果存在其他权限 (READ 或 WRITE),认为是公开桶 + return grants.stream() + .noneMatch(grant -> grant.permission().equals(Permission.READ) || grant.permission() + .equals(Permission.WRITE)); + } catch (Exception e) { + // 如果 getBucketAcl 失败,可能是权限或连接问题 + log.error("获取桶 ACL 失败: {}", e.getMessage()); + return true; // 出现错误时,默认认为桶是私有的 + } + } + + /** + * 获取私有URL链接 + * + * @param bucketName 桶名称 + * @param fileName 文件名 + * @param second 授权时间 + * @return {@link String } + */ + private String getPrivateUrl(String bucketName, String fileName, Integer second) { + try { + return client.getPresigner() + .presignGetObject(GetObjectPresignRequest.builder() + .signatureDuration(Duration.ofHours(second)) + .getObjectRequest(GetObjectRequest.builder().bucket(bucketName).key(fileName).build()) + .build()) + .url() + .toString(); + } catch (RuntimeException e) { + throw new BusinessException("获取私有链接异常", e); + } + } +} diff --git a/continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/util/OssUtils.java b/continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/util/OssUtils.java new file mode 100644 index 00000000..9e3c5889 --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-oss/src/main/java/top/continew/starter/storage/util/OssUtils.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 top.continew.starter.storage.util; + +import cn.hutool.core.util.StrUtil; +import software.amazon.awssdk.regions.Region; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.storage.constant.StorageConstant; + +/** + * OSS 工具 + * + * @author echo + * @date 2024/12/17 13:48 + */ +public class OssUtils { + public OssUtils() { + } + + /** + * 获取作用域 + *

如果 region 参数非空,使用 Region.of 方法创建对应的 S3 区域对象,否则返回默认区域

+ * + * @param region 区域 + * @return {@link Region } + */ + public static Region getRegion(String region) { + return StrUtil.isEmpty(region) ? Region.US_EAST_1 : Region.of(region); + } + + /** + * 获取url + * + * @param endpoint 端点 + * @param bucketName 桶名称 + * @return {@link String } + */ + public static String getUrl(String endpoint, String bucketName) { + // 如果是云服务商,直接返回域名或终端点 + if (StrUtil.containsAny(endpoint, StorageConstant.CLOUD_SERVICE_PREFIX)) { + return "http://" + bucketName + StringConstants.DOT + endpoint; + } else { + return "http://" + endpoint + StringConstants.SLASH + bucketName; + } + } + +} diff --git a/continew-starter-storage/continew-starter-storage-oss/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/continew-starter-storage/continew-starter-storage-oss/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..f611fce9 --- /dev/null +++ b/continew-starter-storage/continew-starter-storage-oss/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +top.continew.starter.storage.autoconfigure.OssStorageAutoconfigure \ No newline at end of file diff --git a/continew-starter-storage/pom.xml b/continew-starter-storage/pom.xml index 3c34cc51..7f810b7e 100644 --- a/continew-starter-storage/pom.xml +++ b/continew-starter-storage/pom.xml @@ -14,7 +14,9 @@ ContiNew Starter 存储模块 + continew-starter-storage-core continew-starter-storage-local + continew-starter-storage-oss