From fa5fa75b0dd8440bc34657b4e94998547cc91506 Mon Sep 17 00:00:00 2001 From: luke Date: Mon, 16 Jun 2025 19:14:20 +0800 Subject: [PATCH] fix --- Readme.md | 1 + config/mysql/init.txt | 15 +++++++ .../application/service/DocAppService.java | 8 ++++ .../config/ObjectStorageProperties.java | 7 ++++ .../north/controller/FileWriteController.java | 3 +- .../south/MinioOSGatewayImpl.java | 40 ++++++++++++++++++- .../south/ObjectStorageGateway.java | 8 ++++ src/main/resources/application.properties | 2 + 8 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/knowledge/base/application/service/DocAppService.java diff --git a/Readme.md b/Readme.md index 9a2088e..6d49d05 100644 --- a/Readme.md +++ b/Readme.md @@ -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 | 上传文档记录入库 | diff --git a/config/mysql/init.txt b/config/mysql/init.txt index 2dd1992..be4d633 100644 --- a/config/mysql/init.txt +++ b/config/mysql/init.txt @@ -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='对象存储上传记录表'; diff --git a/src/main/java/com/knowledge/base/application/service/DocAppService.java b/src/main/java/com/knowledge/base/application/service/DocAppService.java new file mode 100644 index 0000000..117ec3b --- /dev/null +++ b/src/main/java/com/knowledge/base/application/service/DocAppService.java @@ -0,0 +1,8 @@ +package com.knowledge.base.application.service; + +/** + * @author Luke.ye + * @date 2025/6/16 19:13 + */ +public interface DocAppService { +} diff --git a/src/main/java/com/knowledge/base/infrastructure/config/ObjectStorageProperties.java b/src/main/java/com/knowledge/base/infrastructure/config/ObjectStorageProperties.java index 7f7038a..f75c507 100644 --- a/src/main/java/com/knowledge/base/infrastructure/config/ObjectStorageProperties.java +++ b/src/main/java/com/knowledge/base/infrastructure/config/ObjectStorageProperties.java @@ -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 publicBuckets; } \ No newline at end of file diff --git a/src/main/java/com/knowledge/base/infrastructure/north/controller/FileWriteController.java b/src/main/java/com/knowledge/base/infrastructure/north/controller/FileWriteController.java index c09dd86..e665bf5 100644 --- a/src/main/java/com/knowledge/base/infrastructure/north/controller/FileWriteController.java +++ b/src/main/java/com/knowledge/base/infrastructure/north/controller/FileWriteController.java @@ -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( diff --git a/src/main/java/com/knowledge/base/infrastructure/south/MinioOSGatewayImpl.java b/src/main/java/com/knowledge/base/infrastructure/south/MinioOSGatewayImpl.java index be2be35..8297296 100644 --- a/src/main/java/com/knowledge/base/infrastructure/south/MinioOSGatewayImpl.java +++ b/src/main/java/com/knowledge/base/infrastructure/south/MinioOSGatewayImpl.java @@ -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 publicBuckets = objectStorageProperties.getPublicBuckets(); + return publicBuckets != null && publicBuckets.contains(bucket); + } + } diff --git a/src/main/java/com/knowledge/base/infrastructure/south/ObjectStorageGateway.java b/src/main/java/com/knowledge/base/infrastructure/south/ObjectStorageGateway.java index e8e2cf4..e786542 100644 --- a/src/main/java/com/knowledge/base/infrastructure/south/ObjectStorageGateway.java +++ b/src/main/java/com/knowledge/base/infrastructure/south/ObjectStorageGateway.java @@ -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; + } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 84adbaf..b248cac 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -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