mirror of
https://github.com/continew-org/continew-admin.git
synced 2025-09-09 20:57:21 +08:00
refactor(system/storage): 重构存储管理,新增设置默认存储、修改状态接口
S3 => OSS
This commit is contained in:
@@ -30,15 +30,15 @@ import top.continew.starter.core.enums.BaseEnum;
|
||||
@RequiredArgsConstructor
|
||||
public enum StorageTypeEnum implements BaseEnum<Integer> {
|
||||
|
||||
/**
|
||||
* 兼容S3协议存储
|
||||
*/
|
||||
S3(1, "兼容S3协议存储"),
|
||||
|
||||
/**
|
||||
* 本地存储
|
||||
*/
|
||||
LOCAL(2, "本地存储"),;
|
||||
LOCAL(1, "本地存储"),
|
||||
|
||||
/**
|
||||
* 对象存储
|
||||
*/
|
||||
OSS(2, "对象存储");
|
||||
|
||||
private final Integer value;
|
||||
private final String description;
|
||||
|
@@ -54,24 +54,24 @@ public class StorageDO extends BaseDO {
|
||||
private StorageTypeEnum type;
|
||||
|
||||
/**
|
||||
* Access Key(访问密钥)
|
||||
* Access Key
|
||||
*/
|
||||
@FieldEncrypt
|
||||
private String accessKey;
|
||||
|
||||
/**
|
||||
* Secret Key(私有密钥)
|
||||
* Secret Key
|
||||
*/
|
||||
@FieldEncrypt
|
||||
private String secretKey;
|
||||
|
||||
/**
|
||||
* Endpoint(终端节点)
|
||||
* Endpoint
|
||||
*/
|
||||
private String endpoint;
|
||||
|
||||
/**
|
||||
* 桶名称
|
||||
* Bucket
|
||||
*/
|
||||
private String bucketName;
|
||||
|
||||
|
@@ -19,6 +19,7 @@ package top.continew.admin.system.model.query;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
import top.continew.admin.system.enums.StorageTypeEnum;
|
||||
import top.continew.starter.data.core.annotation.Query;
|
||||
import top.continew.starter.data.core.enums.QueryType;
|
||||
|
||||
@@ -50,4 +51,10 @@ public class StorageQuery implements Serializable {
|
||||
*/
|
||||
@Schema(description = "状态", example = "1")
|
||||
private DisEnableStatusEnum status;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
@Schema(description = "类型", example = "2")
|
||||
private StorageTypeEnum type;
|
||||
}
|
@@ -16,6 +16,7 @@
|
||||
|
||||
package top.continew.admin.system.model.req;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
@@ -67,43 +68,45 @@ public class StorageReq implements Serializable {
|
||||
private StorageTypeEnum type;
|
||||
|
||||
/**
|
||||
* 访问密钥
|
||||
* Access Key
|
||||
*/
|
||||
@Schema(description = "访问密钥", example = "")
|
||||
@Length(max = 255, message = "访问密钥长度不能超过 {max} 个字符")
|
||||
@NotBlank(message = "访问密钥不能为空", groups = ValidationGroup.Storage.S3.class)
|
||||
@Schema(description = "Access Key", example = "")
|
||||
@Length(max = 255, message = "Access Key长度不能超过 {max} 个字符")
|
||||
@NotBlank(message = "Access Key不能为空", groups = ValidationGroup.Storage.OSS.class)
|
||||
private String accessKey;
|
||||
|
||||
/**
|
||||
* 私有密钥
|
||||
* Secret Key
|
||||
*/
|
||||
@Schema(description = "私有密钥", example = "")
|
||||
@NotBlank(message = "私有密钥不能为空", groups = ValidationGroup.Storage.S3.class)
|
||||
@Schema(description = "Secret Key", example = "")
|
||||
@NotBlank(message = "Secret Key不能为空", groups = ValidationGroup.Storage.OSS.class)
|
||||
private String secretKey;
|
||||
|
||||
/**
|
||||
* 终端节点
|
||||
* Endpoint
|
||||
*/
|
||||
@Schema(description = "终端节点", example = "")
|
||||
@Length(max = 255, message = "终端节点长度不能超过 {max} 个字符")
|
||||
@NotBlank(message = "终端节点不能为空", groups = ValidationGroup.Storage.S3.class)
|
||||
@Schema(description = "Endpoint", example = "")
|
||||
@Length(max = 255, message = "Endpoint长度不能超过 {max} 个字符")
|
||||
@NotBlank(message = "Endpoint不能为空", groups = ValidationGroup.Storage.OSS.class)
|
||||
private String endpoint;
|
||||
|
||||
/**
|
||||
* 桶名称
|
||||
* Bucket/存储路径
|
||||
*/
|
||||
@Schema(description = "桶名称", example = "C:/continew-admin/data/file/")
|
||||
@Length(max = 255, message = "桶名称长度不能超过 {max} 个字符")
|
||||
@NotBlank(message = "桶名称不能为空", groups = ValidationGroup.Storage.S3.class)
|
||||
@Schema(description = "Bucket/存储路径", example = "C:/continew-admin/data/file/")
|
||||
@Length(max = 255, message = "Bucket长度不能超过 {max} 个字符", groups = ValidationGroup.Storage.OSS.class)
|
||||
@Length(max = 255, message = "存储路径长度不能超过 {max} 个字符", groups = ValidationGroup.Storage.Local.class)
|
||||
@NotBlank(message = "Bucket不能为空", groups = ValidationGroup.Storage.OSS.class)
|
||||
@NotBlank(message = "存储路径不能为空", groups = ValidationGroup.Storage.Local.class)
|
||||
private String bucketName;
|
||||
|
||||
/**
|
||||
* 域名
|
||||
* 域名/访问路径
|
||||
*/
|
||||
@Schema(description = "域名", example = "http://localhost:8000/file")
|
||||
@Length(max = 255, message = "域名长度不能超过 {max} 个字符")
|
||||
@NotBlank(message = "域名不能为空")
|
||||
@Schema(description = "域名/访问路径", example = "http://localhost:8000/file")
|
||||
@Length(max = 255, message = "域名长度不能超过 {max} 个字符", groups = ValidationGroup.Storage.OSS.class)
|
||||
@Length(max = 255, message = "访问路径长度不能超过 {max} 个字符", groups = ValidationGroup.Storage.Local.class)
|
||||
@NotBlank(message = "访问路径不能为空", groups = ValidationGroup.Storage.Local.class)
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
@@ -119,16 +122,15 @@ public class StorageReq implements Serializable {
|
||||
@Length(max = 200, message = "描述长度不能超过 {max} 个字符")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 是否为默认存储
|
||||
*/
|
||||
@Schema(description = "是否为默认存储", example = "true")
|
||||
@NotNull(message = "是否为默认存储不能为空")
|
||||
private Boolean isDefault;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@Schema(description = "状态", example = "1")
|
||||
private DisEnableStatusEnum status;
|
||||
|
||||
/**
|
||||
* 是否为默认存储
|
||||
*/
|
||||
@JsonIgnore
|
||||
private Boolean isDefault;
|
||||
}
|
@@ -63,32 +63,32 @@ public class StorageResp extends BaseDetailResp {
|
||||
private StorageTypeEnum type;
|
||||
|
||||
/**
|
||||
* 访问密钥
|
||||
* Access Key
|
||||
*/
|
||||
@Schema(description = "访问密钥", example = "")
|
||||
@Schema(description = "Access Key", example = "")
|
||||
private String accessKey;
|
||||
|
||||
/**
|
||||
* 私有密钥
|
||||
* Secret Key
|
||||
*/
|
||||
@Schema(description = "私有密钥", example = "")
|
||||
@Schema(description = "Secret Key", example = "")
|
||||
@JsonMask(left = 4, right = 3)
|
||||
private String secretKey;
|
||||
|
||||
/**
|
||||
* 终端节点
|
||||
* Endpoint
|
||||
*/
|
||||
@Schema(description = "终端节点", example = "")
|
||||
@Schema(description = "Endpoint", example = "")
|
||||
private String endpoint;
|
||||
|
||||
/**
|
||||
* 桶名称
|
||||
* Bucket/存储路径
|
||||
*/
|
||||
@Schema(description = "桶名称", example = "C:/continew-admin/data/file/")
|
||||
@Schema(description = "Bucket/存储路径", example = "C:/continew-admin/data/file/")
|
||||
private String bucketName;
|
||||
|
||||
/**
|
||||
* 域名
|
||||
* 域名/访问路径
|
||||
*/
|
||||
@Schema(description = "域名", example = "http://localhost:8000/file")
|
||||
private String domain;
|
||||
|
@@ -16,6 +16,7 @@
|
||||
|
||||
package top.continew.admin.system.service;
|
||||
|
||||
import top.continew.admin.common.model.req.CommonStatusUpdateReq;
|
||||
import top.continew.admin.system.model.entity.StorageDO;
|
||||
import top.continew.admin.system.model.query.StorageQuery;
|
||||
import top.continew.admin.system.model.req.StorageReq;
|
||||
@@ -31,6 +32,21 @@ import top.continew.starter.extension.crud.service.BaseService;
|
||||
*/
|
||||
public interface StorageService extends BaseService<StorageResp, StorageResp, StorageQuery, StorageReq>, IService<StorageDO> {
|
||||
|
||||
/**
|
||||
* 修改状态
|
||||
*
|
||||
* @param req 状态信息
|
||||
* @param id ID
|
||||
*/
|
||||
void updateStatus(CommonStatusUpdateReq req, Long id);
|
||||
|
||||
/**
|
||||
* 设置默认存储
|
||||
*
|
||||
* @param id ID
|
||||
*/
|
||||
void setDefault(Long id);
|
||||
|
||||
/**
|
||||
* 查询默认存储
|
||||
*
|
||||
|
@@ -27,7 +27,9 @@ import org.dromara.x.file.storage.core.FileStorageService;
|
||||
import org.dromara.x.file.storage.core.FileStorageServiceBuilder;
|
||||
import org.dromara.x.file.storage.core.platform.FileStorage;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
import top.continew.admin.common.model.req.CommonStatusUpdateReq;
|
||||
import top.continew.admin.common.util.SecureUtils;
|
||||
import top.continew.admin.system.enums.StorageTypeEnum;
|
||||
import top.continew.admin.system.mapper.StorageMapper;
|
||||
@@ -67,10 +69,13 @@ public class StorageServiceImpl extends BaseServiceImpl<StorageMapper, StorageDO
|
||||
@Override
|
||||
public void beforeAdd(StorageReq req) {
|
||||
this.decodeSecretKey(req, null);
|
||||
CheckUtils.throwIf(Boolean.TRUE.equals(req.getIsDefault()) && this.isDefaultExists(null), "请先取消原有默认存储");
|
||||
String code = req.getCode();
|
||||
CheckUtils.throwIf(this.isCodeExists(code, null), "新增失败,[{}] 已存在", code);
|
||||
this.load(req);
|
||||
// 单独指定默认存储
|
||||
req.setIsDefault(false);
|
||||
if (DisEnableStatusEnum.ENABLE.equals(req.getStatus())) {
|
||||
this.load(req);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,16 +83,12 @@ public class StorageServiceImpl extends BaseServiceImpl<StorageMapper, StorageDO
|
||||
StorageDO oldStorage = super.getById(id);
|
||||
CheckUtils.throwIfNotEqual(req.getCode(), oldStorage.getCode(), "不允许修改存储编码");
|
||||
CheckUtils.throwIfNotEqual(req.getType(), oldStorage.getType(), "不允许修改存储类型");
|
||||
this.decodeSecretKey(req, oldStorage);
|
||||
DisEnableStatusEnum newStatus = req.getStatus();
|
||||
CheckUtils.throwIf(Boolean.TRUE.equals(oldStorage.getIsDefault()) && DisEnableStatusEnum.DISABLE
|
||||
.equals(newStatus), "[{}] 是默认存储,不允许禁用", oldStorage.getName());
|
||||
this.decodeSecretKey(req, oldStorage);
|
||||
// 重新加载存储引擎
|
||||
DisEnableStatusEnum oldStatus = oldStorage.getStatus();
|
||||
if (Boolean.TRUE.equals(req.getIsDefault())) {
|
||||
CheckUtils.throwIf(this.isDefaultExists(id), "请先取消原有默认存储");
|
||||
CheckUtils.throwIf(!DisEnableStatusEnum.ENABLE.equals(oldStatus) && !DisEnableStatusEnum.ENABLE
|
||||
.equals(newStatus), "请先启用该存储");
|
||||
}
|
||||
// 先卸载
|
||||
if (DisEnableStatusEnum.ENABLE.equals(oldStatus)) {
|
||||
this.unload(BeanUtil.copyProperties(oldStorage, StorageReq.class));
|
||||
@@ -103,7 +104,7 @@ public class StorageServiceImpl extends BaseServiceImpl<StorageMapper, StorageDO
|
||||
CheckUtils.throwIf(fileService.countByStorageIds(ids) > 0, "所选存储存在文件关联,请删除文件后重试");
|
||||
List<StorageDO> storageList = baseMapper.lambdaQuery().in(StorageDO::getId, ids).list();
|
||||
storageList.forEach(s -> {
|
||||
CheckUtils.throwIfEqual(Boolean.TRUE, s.getIsDefault(), "[{}] 是默认存储,不允许禁用", s.getName());
|
||||
CheckUtils.throwIfEqual(Boolean.TRUE, s.getIsDefault(), "[{}] 是默认存储,不允许删除", s.getName());
|
||||
// 卸载启用状态的存储
|
||||
if (DisEnableStatusEnum.ENABLE.equals(s.getStatus())) {
|
||||
this.unload(BeanUtil.copyProperties(s, StorageReq.class));
|
||||
@@ -111,6 +112,43 @@ public class StorageServiceImpl extends BaseServiceImpl<StorageMapper, StorageDO
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateStatus(CommonStatusUpdateReq req, Long id) {
|
||||
StorageDO storage = super.getById(id);
|
||||
// 状态未改变
|
||||
DisEnableStatusEnum newStatus = req.getStatus();
|
||||
if (storage.getStatus().equals(newStatus)) {
|
||||
return;
|
||||
}
|
||||
// 修改状态
|
||||
baseMapper.lambdaUpdate().eq(StorageDO::getId, id).set(StorageDO::getStatus, newStatus).update();
|
||||
// 加载、卸载存储引擎
|
||||
StorageReq storageReq = BeanUtil.copyProperties(storage, StorageReq.class);
|
||||
switch (newStatus) {
|
||||
case ENABLE:
|
||||
this.load(storageReq);
|
||||
break;
|
||||
case DISABLE:
|
||||
CheckUtils.throwIfEqual(Boolean.TRUE, storage.getIsDefault(), "[{}] 是默认存储,不允许禁用", storage.getName());
|
||||
this.unload(storageReq);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void setDefault(Long id) {
|
||||
StorageDO storage = super.getById(id);
|
||||
if (Boolean.TRUE.equals(storage.getIsDefault())) {
|
||||
return;
|
||||
}
|
||||
baseMapper.lambdaUpdate().eq(StorageDO::getIsDefault, true).set(StorageDO::getIsDefault, false).update();
|
||||
baseMapper.lambdaUpdate().eq(StorageDO::getId, id).set(StorageDO::getIsDefault, true).update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageDO getDefaultStorage() {
|
||||
return baseMapper.lambdaQuery().eq(StorageDO::getIsDefault, true).one();
|
||||
@@ -138,8 +176,8 @@ public class StorageServiceImpl extends BaseServiceImpl<StorageMapper, StorageDO
|
||||
fileStorageList.addAll(FileStorageServiceBuilder.buildLocalPlusFileStorage(Collections
|
||||
.singletonList(config)));
|
||||
SpringWebUtils.registerResourceHandler(MapUtil.of(URLUtil.url(req.getDomain()).getPath(), bucketName));
|
||||
} else if (StorageTypeEnum.S3.equals(type)) {
|
||||
ValidationUtils.validate(req, ValidationGroup.Storage.S3.class);
|
||||
} else if (StorageTypeEnum.OSS.equals(type)) {
|
||||
ValidationUtils.validate(req, ValidationGroup.Storage.OSS.class);
|
||||
FileStorageProperties.AmazonS3Config config = new FileStorageProperties.AmazonS3Config();
|
||||
config.setPlatform(req.getCode());
|
||||
config.setAccessKey(req.getAccessKey());
|
||||
@@ -169,7 +207,7 @@ public class StorageServiceImpl extends BaseServiceImpl<StorageMapper, StorageDO
|
||||
* @param storage 存储信息
|
||||
*/
|
||||
private void decodeSecretKey(StorageReq req, StorageDO storage) {
|
||||
if (!StorageTypeEnum.S3.equals(req.getType())) {
|
||||
if (!StorageTypeEnum.OSS.equals(req.getType())) {
|
||||
return;
|
||||
}
|
||||
// 修改时,如果 SecretKey 不修改,需要手动修正
|
||||
|
@@ -37,9 +37,9 @@ public interface ValidationGroup extends Default {
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容S3协议存储
|
||||
* 对象存储
|
||||
*/
|
||||
interface S3 extends Storage {
|
||||
interface OSS extends Storage {
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user