diff --git a/viewsh-framework/viewsh-common/src/main/java/com/viewsh/framework/common/biz/infra/file/OssPresignUrlApi.java b/viewsh-framework/viewsh-common/src/main/java/com/viewsh/framework/common/biz/infra/file/OssPresignUrlApi.java
new file mode 100644
index 0000000..b6c0f10
--- /dev/null
+++ b/viewsh-framework/viewsh-common/src/main/java/com/viewsh/framework/common/biz/infra/file/OssPresignUrlApi.java
@@ -0,0 +1,20 @@
+package com.viewsh.framework.common.biz.infra.file;
+
+import java.util.List;
+
+/**
+ * OSS 预签名 URL 通用接口
+ *
+ * 由 infra 模块提供实现,供 {@code OssPresignResponseBodyAdvice} 等框架组件使用
+ */
+public interface OssPresignUrlApi {
+
+ /**
+ * 批量生成文件预签名地址
+ *
+ * @param urls 原始 URL 列表
+ * @return 签名后的 URL 列表(与入参顺序一致)
+ */
+ List presignGetUrls(List urls);
+
+}
diff --git a/viewsh-framework/viewsh-spring-boot-starter-web/src/main/java/com/viewsh/framework/web/config/ViewshWebAutoConfiguration.java b/viewsh-framework/viewsh-spring-boot-starter-web/src/main/java/com/viewsh/framework/web/config/ViewshWebAutoConfiguration.java
index 4ccbf08..3b36aaf 100644
--- a/viewsh-framework/viewsh-spring-boot-starter-web/src/main/java/com/viewsh/framework/web/config/ViewshWebAutoConfiguration.java
+++ b/viewsh-framework/viewsh-spring-boot-starter-web/src/main/java/com/viewsh/framework/web/config/ViewshWebAutoConfiguration.java
@@ -1,12 +1,14 @@
package com.viewsh.framework.web.config;
import cn.hutool.core.util.StrUtil;
+import com.viewsh.framework.common.biz.infra.file.OssPresignUrlApi;
import com.viewsh.framework.common.biz.infra.logger.ApiErrorLogCommonApi;
import com.viewsh.framework.common.enums.WebFilterOrderEnum;
import com.viewsh.framework.web.core.filter.CacheRequestBodyFilter;
import com.viewsh.framework.web.core.filter.DemoFilter;
import com.viewsh.framework.web.core.handler.GlobalExceptionHandler;
import com.viewsh.framework.web.core.handler.GlobalResponseBodyHandler;
+import com.viewsh.framework.web.core.presign.core.OssPresignResponseBodyAdvice;
import com.viewsh.framework.web.core.util.WebFrameworkUtils;
import com.google.common.collect.Maps;
import jakarta.servlet.Filter;
@@ -17,6 +19,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
@@ -34,6 +37,8 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
import java.util.Map;
import java.util.function.Predicate;
+// ⚠ 此类被 OssPresignUrlApiAutoConfiguration 通过 beforeName 字符串引用,
+// 重命名/移动时须同步修改。搜索关键字:PRESIGN_AUTO_CONFIG_ORDERING
@AutoConfiguration(beforeName = {
"com.fhs.trans.config.TransServiceConfig" // cloud 独有:避免一键改包后,RestTemplate 初始化的冲突。可见 https://t.zsxq.com/T4yj7 帖子
})
@@ -96,6 +101,12 @@ public class ViewshWebAutoConfiguration {
return new GlobalResponseBodyHandler();
}
+ @Bean
+ @ConditionalOnBean(OssPresignUrlApi.class)
+ public OssPresignResponseBodyAdvice ossPresignResponseBodyAdvice(OssPresignUrlApi ossPresignUrlApi) {
+ return new OssPresignResponseBodyAdvice(ossPresignUrlApi);
+ }
+
@Bean
@SuppressWarnings("InstantiationOfUtilityClass")
public WebFrameworkUtils webFrameworkUtils(WebProperties webProperties) {
diff --git a/viewsh-framework/viewsh-spring-boot-starter-web/src/main/java/com/viewsh/framework/web/core/presign/annotation/OssPresignUrl.java b/viewsh-framework/viewsh-spring-boot-starter-web/src/main/java/com/viewsh/framework/web/core/presign/annotation/OssPresignUrl.java
new file mode 100644
index 0000000..08efbb6
--- /dev/null
+++ b/viewsh-framework/viewsh-spring-boot-starter-web/src/main/java/com/viewsh/framework/web/core/presign/annotation/OssPresignUrl.java
@@ -0,0 +1,17 @@
+package com.viewsh.framework.web.core.presign.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 标记需要自动预签名的 OSS URL 字段
+ *
+ * 使用方式:在 VO 的 String 类型字段上添加此注解,
+ * {@code OssPresignResponseBodyAdvice} 会在响应写出前批量替换为签名后的 URL。
+ */
+@Target({ElementType.FIELD, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface OssPresignUrl {
+}
diff --git a/viewsh-framework/viewsh-spring-boot-starter-web/src/main/java/com/viewsh/framework/web/core/presign/core/OssPresignResponseBodyAdvice.java b/viewsh-framework/viewsh-spring-boot-starter-web/src/main/java/com/viewsh/framework/web/core/presign/core/OssPresignResponseBodyAdvice.java
new file mode 100644
index 0000000..4d94d95
--- /dev/null
+++ b/viewsh-framework/viewsh-spring-boot-starter-web/src/main/java/com/viewsh/framework/web/core/presign/core/OssPresignResponseBodyAdvice.java
@@ -0,0 +1,345 @@
+package com.viewsh.framework.web.core.presign.core;
+
+import cn.hutool.core.util.StrUtil;
+import com.viewsh.framework.common.biz.infra.file.OssPresignUrlApi;
+import com.viewsh.framework.common.pojo.CommonResult;
+import com.viewsh.framework.common.pojo.PageResult;
+import com.viewsh.framework.web.core.presign.annotation.OssPresignUrl;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.MethodParameter;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 自动对 {@link CommonResult} 响应体中带 {@link OssPresignUrl} 注解的字段进行批量预签名
+ *
+ * 流程:
+ *
+ * - 反射递归扫描返回值中的 {@code @OssPresignUrl} 字段,收集原始 URL
+ * - 去重后调用 {@link OssPresignUrlApi#presignGetUrls} — 1 次批量 RPC
+ * - 将签名结果回填到对应字段
+ *
+ *
+ * 只扫描项目自身包({@code com.viewsh.})下的类,避免反射 JDK / 第三方库内部类导致
+ * Java 17 模块系统 {@link InaccessibleObjectException}。
+ */
+@Slf4j
+@ControllerAdvice
+public class OssPresignResponseBodyAdvice implements ResponseBodyAdvice