refactor: 新增 Amazon S3 依赖,完善加载、卸载存储库配置
This commit is contained in:
parent
5a2c5be088
commit
dea64e62e1
continew-admin-common
continew-admin-system/src/main/java/top/charles7c/continew/admin/system
config
enums
model
service
continew-admin-webapi/src/main/resources/db/changelog/v2.2.0
@ -87,6 +87,11 @@
|
||||
<groupId>org.dromara.x-file-storage</groupId>
|
||||
<artifactId>x-file-storage-spring</artifactId>
|
||||
</dependency>
|
||||
<!-- Amazon Simple Storage Service(亚马逊简单存储服务,通用存储协议 S3,兼容主流云厂商对象存储) -->
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-s3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- FreeMarker(模板引擎) -->
|
||||
<dependency>
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.admin.system.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
|
||||
import top.charles7c.continew.admin.system.model.req.StorageReq;
|
||||
import top.charles7c.continew.admin.system.model.resp.StorageResp;
|
||||
import top.charles7c.continew.admin.system.service.StorageService;
|
||||
|
||||
/**
|
||||
* 文件存储配置加载器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/12/24 22:31
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class FileStorageConfigLoader implements ApplicationRunner {
|
||||
|
||||
private final StorageService storageService;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
List<StorageResp> storageList = storageService.list(null, null);
|
||||
if (CollUtil.isEmpty(storageList)) {
|
||||
return;
|
||||
}
|
||||
storageList.forEach(s -> storageService.load(BeanUtil.copyProperties(s, StorageReq.class)));
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ import cn.hutool.core.util.StrUtil;
|
||||
import top.charles7c.continew.starter.data.mybatis.plus.base.IBaseEnum;
|
||||
|
||||
/**
|
||||
* 文件分类枚举
|
||||
* 文件类型枚举
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/12/23 13:38
|
||||
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.continew.admin.system.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import top.charles7c.continew.starter.data.mybatis.plus.base.IBaseEnum;
|
||||
|
||||
/**
|
||||
* 存储类型枚举
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/12/27 21:45
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum StorageTypeEnum implements IBaseEnum<Integer> {
|
||||
|
||||
/**
|
||||
* 兼容S3协议存储
|
||||
*/
|
||||
S3(1, "兼容S3协议存储"),
|
||||
|
||||
/**
|
||||
* 本地存储
|
||||
*/
|
||||
LOCAL(2, "本地存储"),;
|
||||
|
||||
private final Integer value;
|
||||
private final String description;
|
||||
}
|
@ -22,6 +22,7 @@ import lombok.Data;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import top.charles7c.continew.admin.system.enums.StorageTypeEnum;
|
||||
import top.charles7c.continew.starter.extension.crud.base.BaseDO;
|
||||
|
||||
/**
|
||||
@ -47,6 +48,11 @@ public class StorageDO extends BaseDO {
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private StorageTypeEnum type;
|
||||
|
||||
/**
|
||||
* Access Key
|
||||
*/
|
||||
|
@ -25,6 +25,7 @@ import lombok.Data;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import top.charles7c.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
import top.charles7c.continew.admin.system.enums.StorageTypeEnum;
|
||||
import top.charles7c.continew.starter.extension.crud.base.BaseReq;
|
||||
|
||||
/**
|
||||
@ -54,6 +55,13 @@ public class StorageReq extends BaseReq {
|
||||
@NotBlank(message = "编码不能为空")
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
@Schema(description = "类型")
|
||||
@NotNull(message = "类型非法")
|
||||
private StorageTypeEnum type;
|
||||
|
||||
/**
|
||||
* Access Key
|
||||
*/
|
||||
|
@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
|
||||
import top.charles7c.continew.admin.system.enums.StorageTypeEnum;
|
||||
import top.charles7c.continew.starter.extension.crud.base.BaseDetailResp;
|
||||
|
||||
/**
|
||||
@ -55,6 +56,12 @@ public class StorageDetailResp extends BaseDetailResp {
|
||||
@ExcelProperty(value = "编码")
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
@Schema(description = "类型")
|
||||
private StorageTypeEnum type;
|
||||
|
||||
/**
|
||||
* Access Key
|
||||
*/
|
||||
|
@ -22,6 +22,7 @@ import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import top.charles7c.continew.admin.system.enums.StorageTypeEnum;
|
||||
import top.charles7c.continew.starter.extension.crud.base.BaseResp;
|
||||
|
||||
/**
|
||||
@ -49,6 +50,12 @@ public class StorageResp extends BaseResp {
|
||||
@Schema(description = "编码")
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
@Schema(description = "类型")
|
||||
private StorageTypeEnum type;
|
||||
|
||||
/**
|
||||
* Access Key
|
||||
*/
|
||||
|
@ -34,6 +34,16 @@ import top.charles7c.continew.starter.extension.crud.base.BaseService;
|
||||
*/
|
||||
public interface FileService extends BaseService<FileResp, FileDetailResp, FileQuery, FileReq> {
|
||||
|
||||
/**
|
||||
* 上传
|
||||
*
|
||||
* @param file
|
||||
* 文件信息
|
||||
*/
|
||||
default void upload(MultipartFile file) {
|
||||
upload(file, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传
|
||||
*
|
||||
|
@ -46,4 +46,20 @@ public interface StorageService extends BaseService<StorageResp, StorageDetailRe
|
||||
* @return 存储库信息
|
||||
*/
|
||||
StorageDO getByCode(String code);
|
||||
|
||||
/**
|
||||
* 加载存储库
|
||||
*
|
||||
* @param req
|
||||
* 存储库信息
|
||||
*/
|
||||
void load(StorageReq req);
|
||||
|
||||
/**
|
||||
* 卸载存储库
|
||||
*
|
||||
* @param code
|
||||
* 编码
|
||||
*/
|
||||
void unload(String code);
|
||||
}
|
@ -42,6 +42,7 @@ import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import top.charles7c.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
import top.charles7c.continew.admin.system.enums.StorageTypeEnum;
|
||||
import top.charles7c.continew.admin.system.mapper.StorageMapper;
|
||||
import top.charles7c.continew.admin.system.model.entity.StorageDO;
|
||||
import top.charles7c.continew.admin.system.model.query.StorageQuery;
|
||||
@ -51,6 +52,7 @@ import top.charles7c.continew.admin.system.model.resp.StorageResp;
|
||||
import top.charles7c.continew.admin.system.service.FileService;
|
||||
import top.charles7c.continew.admin.system.service.StorageService;
|
||||
import top.charles7c.continew.starter.core.util.validate.CheckUtils;
|
||||
import top.charles7c.continew.starter.core.util.validate.ValidationUtils;
|
||||
import top.charles7c.continew.starter.extension.crud.base.BaseServiceImpl;
|
||||
|
||||
/**
|
||||
@ -65,17 +67,17 @@ public class StorageServiceImpl
|
||||
extends BaseServiceImpl<StorageMapper, StorageDO, StorageResp, StorageDetailResp, StorageQuery, StorageReq>
|
||||
implements StorageService {
|
||||
|
||||
@Resource
|
||||
private FileService fileService;
|
||||
private final ApplicationContext applicationContext;
|
||||
private final FileStorageService fileStorageService;
|
||||
@Resource
|
||||
private final FileService fileService;
|
||||
|
||||
@Override
|
||||
public Long add(StorageReq req) {
|
||||
String code = req.getCode();
|
||||
CheckUtils.throwIf(this.isCodeExists(code, null), "新增失败,[{}] 已存在", code);
|
||||
req.setStatus(DisEnableStatusEnum.ENABLE);
|
||||
this.loadFileStorage(req);
|
||||
this.load(req);
|
||||
return super.add(req);
|
||||
}
|
||||
|
||||
@ -87,7 +89,8 @@ public class StorageServiceImpl
|
||||
CheckUtils.throwIf(
|
||||
Boolean.TRUE.equals(oldStorage.getIsDefault()) && DisEnableStatusEnum.DISABLE.equals(req.getStatus()),
|
||||
"[{}] 是默认存储库,不允许禁用", oldStorage.getName());
|
||||
this.loadFileStorage(req);
|
||||
this.unload(oldStorage.getCode());
|
||||
this.load(req);
|
||||
super.update(req, id);
|
||||
}
|
||||
|
||||
@ -95,7 +98,7 @@ public class StorageServiceImpl
|
||||
public void delete(List<Long> ids) {
|
||||
CheckUtils.throwIf(fileService.countByStorageIds(ids) > 0, "所选存储库存在文件关联,请删除文件后重试");
|
||||
List<StorageDO> storageList = baseMapper.lambdaQuery().in(StorageDO::getId, ids).list();
|
||||
storageList.forEach(s -> this.removeFileStorage(s.getCode()));
|
||||
storageList.forEach(s -> this.unload(s.getCode()));
|
||||
super.delete(ids);
|
||||
}
|
||||
|
||||
@ -109,29 +112,44 @@ public class StorageServiceImpl
|
||||
return baseMapper.lambdaQuery().eq(StorageDO::getCode, code).one();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载存储配置
|
||||
*
|
||||
* @param req
|
||||
* 存储配置信息
|
||||
*/
|
||||
private void loadFileStorage(StorageReq req) {
|
||||
@Override
|
||||
public void load(StorageReq req) {
|
||||
CopyOnWriteArrayList<FileStorage> fileStorageList = fileStorageService.getFileStorageList();
|
||||
FileStorageProperties.LocalPlusConfig localPlusConfig = new FileStorageProperties.LocalPlusConfig();
|
||||
localPlusConfig.setPlatform(req.getCode());
|
||||
localPlusConfig.setStoragePath(req.getBucketName());
|
||||
localPlusConfig.setDomain(req.getDomain());
|
||||
fileStorageList
|
||||
.addAll(FileStorageServiceBuilder.buildLocalPlusFileStorage(Collections.singletonList(localPlusConfig)));
|
||||
String bucketName = req.getBucketName();
|
||||
StorageTypeEnum type = req.getType();
|
||||
switch (type) {
|
||||
case LOCAL -> {
|
||||
ValidationUtils.throwIfBlank(bucketName, "存储路径不能为空");
|
||||
FileStorageProperties.LocalPlusConfig config = new FileStorageProperties.LocalPlusConfig();
|
||||
config.setPlatform(req.getCode());
|
||||
config.setStoragePath(bucketName);
|
||||
config.setDomain(req.getDomain());
|
||||
fileStorageList
|
||||
.addAll(FileStorageServiceBuilder.buildLocalPlusFileStorage(Collections.singletonList(config)));
|
||||
}
|
||||
case S3 -> {
|
||||
String accessKey = req.getAccessKey();
|
||||
String secretKey = req.getSecretKey();
|
||||
String endpoint = req.getEndpoint();
|
||||
ValidationUtils.throwIfBlank(accessKey, "Access Key不能为空");
|
||||
ValidationUtils.throwIfBlank(secretKey, "Secret Key不能为空");
|
||||
ValidationUtils.throwIfBlank(endpoint, "Endpoint不能为空");
|
||||
ValidationUtils.throwIfBlank(bucketName, "桶名称不能为空");
|
||||
FileStorageProperties.AmazonS3Config config = new FileStorageProperties.AmazonS3Config();
|
||||
config.setPlatform(req.getCode());
|
||||
config.setAccessKey(accessKey);
|
||||
config.setSecretKey(secretKey);
|
||||
config.setEndPoint(endpoint);
|
||||
config.setBucketName(bucketName);
|
||||
config.setDomain(req.getDomain());
|
||||
fileStorageList.addAll(
|
||||
FileStorageServiceBuilder.buildAmazonS3FileStorage(Collections.singletonList(config), null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除存储配置
|
||||
*
|
||||
* @param code
|
||||
* 存储配置编码
|
||||
*/
|
||||
private void removeFileStorage(String code) {
|
||||
@Override
|
||||
public void unload(String code) {
|
||||
CopyOnWriteArrayList<FileStorage> fileStorageList = fileStorageService.getFileStorageList();
|
||||
FileStorage fileStorage = fileStorageService.getFileStorage(code);
|
||||
fileStorageList.remove(fileStorage);
|
||||
|
@ -15,3 +15,9 @@ VALUES
|
||||
(1102, '存储修改', 1100, 3, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'system:storage:update', 2, 1, 1, NOW(), NULL, NULL),
|
||||
(1103, '存储删除', 1100, 3, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'system:storage:delete', 3, 1, 1, NOW(), NULL, NULL),
|
||||
(1104, '存储导出', 1100, 3, NULL, NULL, NULL, NULL, b'0', b'0', b'0', 'system:storage:export', 4, 1, 1, NOW(), NULL, NULL);
|
||||
|
||||
-- 初始化默认存储库
|
||||
INSERT IGNORE INTO `sys_storage`
|
||||
(`id`, `name`, `code`, `type`, `access_key`, `secret_key`, `endpoint`, `bucket_name`, `domain`, `description`, `is_default`, `sort`, `status`, `create_user`, `create_time`, `update_user`, `update_time`)
|
||||
VALUES
|
||||
(1, '本地存储', 'local', 2, NULL, NULL, NULL, '/data/file/', '', '本地存储', b'1', 1, 1, 1, NOW(), NULL, NULL);
|
@ -24,11 +24,12 @@ CREATE TABLE IF NOT EXISTS `sys_storage` (
|
||||
`id` bigint(20) AUTO_INCREMENT COMMENT 'ID',
|
||||
`name` varchar(100) NOT NULL COMMENT '名称',
|
||||
`code` varchar(30) NOT NULL COMMENT '编码',
|
||||
`type` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(1:兼容S3协议存储;2:本地存储)',
|
||||
`access_key` varchar(255) DEFAULT NULL COMMENT 'Access Key(访问密钥)',
|
||||
`secret_key` varchar(255) DEFAULT NULL COMMENT 'Secret Key(私有访问密钥)',
|
||||
`endpoint` varchar(255) DEFAULT NULL COMMENT 'Endpoint(终端节点)',
|
||||
`bucket_name` varchar(255) DEFAULT NULL COMMENT '桶名称',
|
||||
`domain` varchar(255) DEFAULT NULL COMMENT '自定义域名',
|
||||
`domain` varchar(255) NOT NULL DEFAULT '' COMMENT '自定义域名',
|
||||
`description` varchar(200) DEFAULT NULL COMMENT '描述',
|
||||
`is_default` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否为默认存储',
|
||||
`sort` int NOT NULL DEFAULT 999 COMMENT '排序',
|
||||
|
Loading…
Reference in New Issue
Block a user