Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
lzx
2026-01-09 15:30:30 +08:00
2 changed files with 59 additions and 1 deletions

View File

@@ -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<S3FileClientConfig> {
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<S3FileClientConfig> {
return StringUtils.EMPTY;
}
// 1. 尝试从 Redis 缓存中获取
String cacheKey = CACHE_KEY_PREFIX + path;
RedisTemplate<String, Object> 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<S3FileClientConfig> {
.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<String, Object> 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<String,Object> redisTemplate;
public S3FileClient(Long id, S3FileClientConfig config) {
super(id, config);
}
@@ -115,6 +162,13 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
.key(path)
.build();
client.deleteObject(deleteRequest);
// 删除文件后,清除对应的缓存
String cacheKey = CACHE_KEY_PREFIX + path;
RedisTemplate<String, Object> redis = getRedisTemplate();
if (redis != null) {
redis.delete(cacheKey);
}
}
@Override

View File

@@ -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;