fix
This commit is contained in:
parent
d87266b3e2
commit
fa5fa75b0d
@ -7,6 +7,7 @@
|
||||
| 1.0.1 | 2025-06-06 | Luke.Ye | 重构登录Token校验逻辑, |
|
||||
| 1.0.2 | 2025-06-09 | Luke.Ye | 添加用户角色相关逻辑,重构代码 |
|
||||
| 1.0.3 | 2025-06-16 | Luke.Ye | 新增文档上传至对象存储接口 |
|
||||
| 1.0.4 | 2025-06-17 | Luke.Ye | 上传文档记录入库 |
|
||||
|
||||
|
||||
|
||||
|
||||
@ -83,6 +83,21 @@ CREATE TABLE IF NOT EXISTS user_file (
|
||||
UNIQUE INDEX uniq_user_file(user_id, file_id)
|
||||
) COMMENT='用户-文件直接授权关联表(用于特殊文件个别授权)';
|
||||
|
||||
-- 8. 对象存储上传记录表
|
||||
CREATE TABLE IF NOT EXISTS oss_upload_record (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键',
|
||||
file_name VARCHAR(255) NOT NULL COMMENT '文件名',
|
||||
bucket_name VARCHAR(128) NOT NULL COMMENT '目标 bucket 名',
|
||||
uploader VARCHAR(128) NOT NULL COMMENT '上传人用户名',
|
||||
upload_time DATETIME NOT NULL COMMENT '上传时间',
|
||||
object_path VARCHAR(512) NOT NULL COMMENT 'MinIO 存储路径',
|
||||
url VARCHAR(1024) COMMENT '访问链接(如CDN代理后的地址)',
|
||||
add_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
INDEX idx_uploader(uploader),
|
||||
INDEX idx_file_name(file_name),
|
||||
INDEX idx_bucket(bucket_name)
|
||||
) COMMENT='对象存储上传记录表';
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
package com.knowledge.base.application.service;
|
||||
|
||||
/**
|
||||
* @author Luke.ye
|
||||
* @date 2025/6/16 19:13
|
||||
*/
|
||||
public interface DocAppService {
|
||||
}
|
||||
@ -4,9 +4,16 @@ import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "object.storage")
|
||||
@Data
|
||||
public class ObjectStorageProperties {
|
||||
private boolean useUuidPrefix = true;
|
||||
|
||||
/**
|
||||
* 配置哪些 buckets 是公开的(不使用预签名链接)
|
||||
*/
|
||||
private List<String> publicBuckets;
|
||||
}
|
||||
@ -162,7 +162,8 @@ public class FileWriteController {
|
||||
|
||||
// 使用原始文件名作为 key,MinIO 对象路径作为 value
|
||||
String fileKey = Paths.get(originalName).getFileName().toString();
|
||||
filePathMap.put(fileKey, String.format("http://s3.wisdompulse.cn/%s/%s", bucket, safeName));
|
||||
String url = objectStorageGateway.generateUrl(bucket, safeName);
|
||||
filePathMap.put(fileKey, url);
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(Map.of(
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
package com.knowledge.base.infrastructure.south;
|
||||
|
||||
import com.knowledge.base.infrastructure.config.ObjectStorageProperties;
|
||||
import io.minio.*;
|
||||
import io.minio.http.Method;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
@ -14,15 +17,20 @@ public class MinioOSGatewayImpl implements ObjectStorageGateway {
|
||||
|
||||
private final MinioClient minioClient;
|
||||
|
||||
private final ObjectStorageProperties objectStorageProperties;
|
||||
|
||||
|
||||
public MinioOSGatewayImpl(
|
||||
@Value("${minio.endpoint:http://s3.wisdompulse.cn}") String endpoint,
|
||||
@Value("${minio.access-key:lukeye}") String accessKey,
|
||||
@Value("${minio.secret-key:lukeye@6}") String secretKey
|
||||
@Value("${minio.secret-key:lukeye@6}") String secretKey,
|
||||
ObjectStorageProperties objectStorageProperties
|
||||
) {
|
||||
this.minioClient = MinioClient.builder()
|
||||
.endpoint(endpoint)
|
||||
.credentials(accessKey, secretKey)
|
||||
.build();
|
||||
this.objectStorageProperties = objectStorageProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -46,4 +54,34 @@ public class MinioOSGatewayImpl implements ObjectStorageGateway {
|
||||
.build());
|
||||
log.info("上传成功: {}/{}", bucket, objectPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateUrl(String bucket, String objectPath) throws Exception {
|
||||
// 判断 bucket 是否公开(通过策略判断、或提前配置)
|
||||
boolean isPublic = isPublicBucket(bucket);
|
||||
|
||||
if (isPublic) {
|
||||
// 直接拼接公网 URL(你自己的 CDN 域名)
|
||||
return String.format("http://s3.wisdompulse.cn/%s/%s", bucket, objectPath);
|
||||
} else {
|
||||
// 生成预签名链接(最大 7 天)
|
||||
return minioClient.getPresignedObjectUrl(
|
||||
GetPresignedObjectUrlArgs.builder()
|
||||
.method(Method.GET)
|
||||
.bucket(bucket)
|
||||
.object(objectPath)
|
||||
.expiry(60 * 60 * 24 * 7) // 7 天
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 bucket 是否公开(默认策略:通过配置控制)
|
||||
*/
|
||||
private boolean isPublicBucket(String bucket) {
|
||||
List<String> publicBuckets = objectStorageProperties.getPublicBuckets();
|
||||
return publicBuckets != null && publicBuckets.contains(bucket);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -13,4 +13,12 @@ public interface ObjectStorageGateway {
|
||||
* 上传文件到指定 bucket 的路径
|
||||
*/
|
||||
void uploadFile(String bucket, String objectPath, MultipartFile file) throws Exception;
|
||||
|
||||
/**
|
||||
* 根据 bucket 和 object 路径生成可访问链接:
|
||||
* - 公开 bucket:返回拼接的固定访问路径;
|
||||
* - 私有 bucket:生成预签名 URL(最长 7 天)。
|
||||
*/
|
||||
String generateUrl(String bucket, String objectPath) throws Exception;
|
||||
|
||||
}
|
||||
|
||||
@ -27,5 +27,7 @@ import.schedule.cron=0 0 * * * *
|
||||
knowledge.base.redis.enable=false
|
||||
|
||||
object.storage.use-uuid-prefix=true
|
||||
object.storage.public-buckets[0]=kbase
|
||||
object.storage.public-buckets[1]=public-media
|
||||
spring.servlet.multipart.max-file-size=200MB
|
||||
spring.servlet.multipart.max-request-size=500MB
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user