mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-10-31 22:57:17 +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