diff --git a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/framework/file/core/client/s3/S3FileClient.java b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/framework/file/core/client/s3/S3FileClient.java index 7b716ba8..7e9fd653 100644 --- a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/framework/file/core/client/s3/S3FileClient.java +++ b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/framework/file/core/client/s3/S3FileClient.java @@ -6,6 +6,7 @@ import cn.hutool.http.HttpUtil; import com.zt.plat.framework.common.util.spring.SpringUtils; import com.zt.plat.module.infra.framework.file.core.client.AbstractFileClient; import org.apache.commons.lang3.StringUtils; +import org.springframework.data.redis.core.RedisTemplate; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; @@ -22,6 +23,7 @@ import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignReques import java.net.URI; import java.time.Duration; +import java.util.concurrent.TimeUnit; /** * 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务 @@ -32,6 +34,15 @@ public class S3FileClient extends AbstractFileClient { private static final Duration DEFAULT_PRESIGNED_EXPIRATION = Duration.ofHours(24); private static final String PRESIGN_EXPIRE_SECONDS_PROPERTY = "zt.file.download-expire-seconds"; + /** + * Redis 缓存 key 前缀:文件预签名 URL + */ + private static final String CACHE_KEY_PREFIX = "file:presigned:url:"; + /** + * Redis 缓存时间:50 分钟 + */ + private static final long CACHE_EXPIRE_MINUTES = 50L; + /** * 生成临时下载地址(预签名下载 URL) * @param path 文件路径 @@ -45,6 +56,17 @@ public class S3FileClient extends AbstractFileClient { return StringUtils.EMPTY; } + // 1. 尝试从 Redis 缓存中获取 + String cacheKey = CACHE_KEY_PREFIX + path; + RedisTemplate redis = getRedisTemplate(); + if (redis != null) { + Object cachedUrl = redis.opsForValue().get(cacheKey); + if (cachedUrl instanceof String) { + return (String) cachedUrl; + } + } + + // 2. 缓存未命中,调用 S3 生成预签名 URL // 使用 S3 官方支持的 responseCacheControl 参数,强制浏览器和代理不缓存 GetObjectRequest.Builder getObjectRequestBuilder = GetObjectRequest.builder() .bucket(config.getBucket()) @@ -54,12 +76,37 @@ public class S3FileClient extends AbstractFileClient { .signatureDuration(realExpiration) .getObjectRequest(getObjectRequestBuilder.build()) .build(); - return presigner.presignGetObject(getObjectPresignRequest).url().toString(); + String presignedUrl = presigner.presignGetObject(getObjectPresignRequest).url().toString(); + + // 3. 将生成的 URL 存入 Redis 缓存 + if (redis != null && presignedUrl != null && !presignedUrl.isEmpty()) { + redis.opsForValue().set(cacheKey, presignedUrl, CACHE_EXPIRE_MINUTES, TimeUnit.MINUTES); + } + + return presignedUrl; + } + + /** + * 获取 RedisTemplate 实例(延迟加载,避免初始化时循环依赖) + */ + @SuppressWarnings("unchecked") + private RedisTemplate getRedisTemplate() { + if (redisTemplate == null) { + try { + redisTemplate = SpringUtils.getBean("redisTemplate", RedisTemplate.class); + } catch (Exception e) { + // Redis 不可用时,不影响正常功能,只是不使用缓存 + return null; + } + } + return redisTemplate; } private S3Client client; private S3Presigner presigner; + private RedisTemplate redisTemplate; + public S3FileClient(Long id, S3FileClientConfig config) { super(id, config); } @@ -115,6 +162,13 @@ public class S3FileClient extends AbstractFileClient { .key(path) .build(); client.deleteObject(deleteRequest); + + // 删除文件后,清除对应的缓存 + String cacheKey = CACHE_KEY_PREFIX + path; + RedisTemplate redis = getRedisTemplate(); + if (redis != null) { + redis.delete(cacheKey); + } } @Override diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/user/vo/user/UserRespVO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/user/vo/user/UserRespVO.java index c20e1ebd..4fc49f77 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/user/vo/user/UserRespVO.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/user/vo/user/UserRespVO.java @@ -58,6 +58,10 @@ public class UserRespVO{ @DictFormat(DictTypeConstants.USER_SEX) private Integer sex; + @Schema(description = "用户来源,参见 UserSourceEnum 枚举类", example = "1") + @ExcelProperty("用户来源") + private Integer userSource; + @Schema(description = "用户头像", example = "123456789") private String avatar;