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.1 | 2025-06-06 | Luke.Ye | 重构登录Token校验逻辑, |
|
||||||
| 1.0.2 | 2025-06-09 | Luke.Ye | 添加用户角色相关逻辑,重构代码 |
|
| 1.0.2 | 2025-06-09 | Luke.Ye | 添加用户角色相关逻辑,重构代码 |
|
||||||
| 1.0.3 | 2025-06-16 | 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)
|
UNIQUE INDEX uniq_user_file(user_id, file_id)
|
||||||
) COMMENT='用户-文件直接授权关联表(用于特殊文件个别授权)';
|
) 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.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties(prefix = "object.storage")
|
@ConfigurationProperties(prefix = "object.storage")
|
||||||
@Data
|
@Data
|
||||||
public class ObjectStorageProperties {
|
public class ObjectStorageProperties {
|
||||||
private boolean useUuidPrefix = true;
|
private boolean useUuidPrefix = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置哪些 buckets 是公开的(不使用预签名链接)
|
||||||
|
*/
|
||||||
|
private List<String> publicBuckets;
|
||||||
}
|
}
|
||||||
@ -162,7 +162,8 @@ public class FileWriteController {
|
|||||||
|
|
||||||
// 使用原始文件名作为 key,MinIO 对象路径作为 value
|
// 使用原始文件名作为 key,MinIO 对象路径作为 value
|
||||||
String fileKey = Paths.get(originalName).getFileName().toString();
|
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(
|
return ResponseEntity.ok(Map.of(
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
package com.knowledge.base.infrastructure.south;
|
package com.knowledge.base.infrastructure.south;
|
||||||
|
|
||||||
|
import com.knowledge.base.infrastructure.config.ObjectStorageProperties;
|
||||||
import io.minio.*;
|
import io.minio.*;
|
||||||
|
import io.minio.http.Method;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@ -14,15 +17,20 @@ public class MinioOSGatewayImpl implements ObjectStorageGateway {
|
|||||||
|
|
||||||
private final MinioClient minioClient;
|
private final MinioClient minioClient;
|
||||||
|
|
||||||
|
private final ObjectStorageProperties objectStorageProperties;
|
||||||
|
|
||||||
|
|
||||||
public MinioOSGatewayImpl(
|
public MinioOSGatewayImpl(
|
||||||
@Value("${minio.endpoint:http://s3.wisdompulse.cn}") String endpoint,
|
@Value("${minio.endpoint:http://s3.wisdompulse.cn}") String endpoint,
|
||||||
@Value("${minio.access-key:lukeye}") String accessKey,
|
@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()
|
this.minioClient = MinioClient.builder()
|
||||||
.endpoint(endpoint)
|
.endpoint(endpoint)
|
||||||
.credentials(accessKey, secretKey)
|
.credentials(accessKey, secretKey)
|
||||||
.build();
|
.build();
|
||||||
|
this.objectStorageProperties = objectStorageProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -46,4 +54,34 @@ public class MinioOSGatewayImpl implements ObjectStorageGateway {
|
|||||||
.build());
|
.build());
|
||||||
log.info("上传成功: {}/{}", bucket, objectPath);
|
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 的路径
|
* 上传文件到指定 bucket 的路径
|
||||||
*/
|
*/
|
||||||
void uploadFile(String bucket, String objectPath, MultipartFile file) throws Exception;
|
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
|
knowledge.base.redis.enable=false
|
||||||
|
|
||||||
object.storage.use-uuid-prefix=true
|
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-file-size=200MB
|
||||||
spring.servlet.multipart.max-request-size=500MB
|
spring.servlet.multipart.max-request-size=500MB
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user