mirror of
https://gitee.com/czh-dev/upload-hub
synced 2026-05-06 20:32:48 +08:00
feat:新增存储配置修改功能
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
package cn.czh.advice;
|
||||
|
||||
import cn.czh.base.BusinessException;
|
||||
import cn.czh.base.Result;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public Result<?> handleValidationException(MethodArgumentNotValidException ex) {
|
||||
FieldError fieldError = ex.getBindingResult().getFieldErrors().get(0);
|
||||
String errorMessage = fieldError.getDefaultMessage();
|
||||
return Result.error(400, errorMessage);
|
||||
}
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Result<?> handleBusinessException(BusinessException e) {
|
||||
return Result.error(500, e.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,21 @@ public class Result<T> implements Serializable {
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* 操作失败,无返回值
|
||||
* 操作失败,无返回值(自定义状态码)
|
||||
*/
|
||||
public static <T> Result<T> error(String msg) {
|
||||
public static <T> Result<T> error(int code, String msg) {
|
||||
Result<T> responseWrapper = new Result<>();
|
||||
responseWrapper.setCode(code);
|
||||
responseWrapper.setMsg(msg);
|
||||
responseWrapper.setCode(500);
|
||||
return responseWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作失败,无返回值(默认 500)
|
||||
*/
|
||||
public static <T> Result<T> error(String msg) {
|
||||
return error(500, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作成功,无返回值
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package cn.czh.context;
|
||||
|
||||
import cn.czh.entity.StorageConfig;
|
||||
import lombok.Getter;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
@Getter
|
||||
public class StorageConfigUpdateEvent extends ApplicationEvent {
|
||||
|
||||
private final StorageConfig newConfig;
|
||||
|
||||
public StorageConfigUpdateEvent(Object source, StorageConfig newConfig) {
|
||||
super(source);
|
||||
this.newConfig = newConfig;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cn.czh.controller;
|
||||
|
||||
import cn.czh.base.BusinessException;
|
||||
import cn.czh.base.Result;
|
||||
import cn.czh.entity.StorageConfig;
|
||||
import cn.czh.service.IStorageConfigService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/config")
|
||||
public class ConfigController {
|
||||
|
||||
@Resource
|
||||
private IStorageConfigService storageConfigService;
|
||||
|
||||
@GetMapping
|
||||
public Result<?> getConfig(String type) {
|
||||
return Result.success(storageConfigService.getStorageConfigByType(type));
|
||||
}
|
||||
|
||||
@PatchMapping
|
||||
public Result<?> updateConfig(@Valid @RequestBody StorageConfig config) {
|
||||
storageConfigService.updateStorageConfig(config);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@@ -32,6 +34,8 @@ public class StorageConfig implements Serializable {
|
||||
/**
|
||||
* 存储类型:'local','minio','oss','obs'
|
||||
*/
|
||||
@NotNull(message = "存储类型不能为空")
|
||||
@Pattern(regexp = "local|minio|oss|obs", message = "存储类型必须是 'local', 'minio', 'oss', 'obs' 之一")
|
||||
private String type;
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,4 +9,9 @@ public interface IStorageConfigService {
|
||||
*/
|
||||
StorageConfig getStorageConfigByType(String type);
|
||||
|
||||
/**
|
||||
* 更新存储配置
|
||||
*/
|
||||
void updateStorageConfig(StorageConfig config);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.czh.service.impl;
|
||||
|
||||
import cn.czh.base.BusinessException;
|
||||
import cn.czh.context.StorageConfigUpdateEvent;
|
||||
import cn.czh.dto.FileRecordDTO;
|
||||
import cn.czh.dto.MyPartSummary;
|
||||
import cn.czh.dto.TaskInfoDTO;
|
||||
@@ -16,12 +17,13 @@ import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
@@ -50,17 +52,28 @@ public class LocalStorageService implements IStorageService {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
this.refreshConfig();
|
||||
}
|
||||
|
||||
private void refreshConfig() {
|
||||
this.storageConfig = storageConfigService.getStorageConfigByType(StorageConfig.LOCAL);
|
||||
// Ensure the root directory exists
|
||||
File rootDir = new File(storageConfig.getBucket());
|
||||
if (!rootDir.exists()) {
|
||||
boolean mkdirs = rootDir.mkdirs();
|
||||
if (!mkdirs) {
|
||||
if (!rootDir.mkdirs()) {
|
||||
throw new BusinessException("无法创建根目录");
|
||||
}
|
||||
}
|
||||
log.info("LocalStorageService refreshed successfully");
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void handleConfigUpdate(StorageConfigUpdateEvent event) {
|
||||
if (StorageConfig.LOCAL.equals(event.getNewConfig().getType())) {
|
||||
refreshConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public FileRecordDTO uploadFile(MultipartFile file, String md5, String objectName) {
|
||||
UploadFile uploadFile = getByIdentifier(md5);
|
||||
@@ -129,6 +142,7 @@ public class LocalStorageService implements IStorageService {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public TaskInfoDTO createMultipartUpload(CreateMultipartUpload param) {
|
||||
String identifier = param.getIdentifier();
|
||||
@@ -162,6 +176,7 @@ public class LocalStorageService implements IStorageService {
|
||||
.setPath(generateWebUrl(objectKey));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public UploadFile merge(String identifier) {
|
||||
UploadFile uploadFile = getByIdentifier(identifier);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.czh.service.impl;
|
||||
|
||||
import cn.czh.base.BusinessException;
|
||||
import cn.czh.context.StorageConfigUpdateEvent;
|
||||
import cn.czh.dto.FileRecordDTO;
|
||||
import cn.czh.dto.MyPartSummary;
|
||||
import cn.czh.dto.TaskInfoDTO;
|
||||
@@ -33,10 +34,11 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import io.minio.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.MediaTypeFactory;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
@@ -79,14 +81,25 @@ public class MinioStorageService implements IStorageService {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
this.refreshConfig();
|
||||
}
|
||||
|
||||
private void refreshConfig() {
|
||||
this.storageConfig = storageConfigService.getStorageConfigByType(StorageConfig.MINIO);
|
||||
this.minioClient = MinioClient.builder()
|
||||
.endpoint(storageConfig.getEndpoint())
|
||||
.credentials(storageConfig.getAccessKey(), storageConfig.getSecretKey())
|
||||
.build();
|
||||
this.amazonS3 = minioAmazonS3Client(storageConfig);
|
||||
log.info("MinioStorageService refreshed successfully");
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void handleConfigUpdate(StorageConfigUpdateEvent event) {
|
||||
if (StorageConfig.MINIO.equals(event.getNewConfig().getType())) {
|
||||
refreshConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private AmazonS3 minioAmazonS3Client(StorageConfig storageConfig) {
|
||||
// 设置连接时的参数
|
||||
@@ -107,7 +120,7 @@ public class MinioStorageService implements IStorageService {
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public FileRecordDTO uploadFile(MultipartFile file, String md5, String objectName) {
|
||||
|
||||
@@ -179,6 +192,7 @@ public class MinioStorageService implements IStorageService {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public TaskInfoDTO createMultipartUpload(CreateMultipartUpload param) {
|
||||
Date currentDate = new Date();
|
||||
@@ -203,7 +217,7 @@ public class MinioStorageService implements IStorageService {
|
||||
return new TaskInfoDTO().setFinished(false).setTaskRecord(TaskRecordDTO.convertFromEntity(task)).setPath(getPath(key));
|
||||
}
|
||||
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public UploadFile merge(String identifier) {
|
||||
UploadFile uploadFile = getByIdentifier(identifier);
|
||||
@@ -273,19 +287,6 @@ public class MinioStorageService implements IStorageService {
|
||||
}
|
||||
|
||||
|
||||
@Scheduled(fixedRate = 60000) // 每分钟检查一次
|
||||
public void refreshStorageConfig() {
|
||||
StorageConfig newConfig = storageConfigService.getStorageConfigByType(StorageConfig.MINIO);
|
||||
if (!newConfig.equals(this.storageConfig)) {
|
||||
this.storageConfig = newConfig;
|
||||
this.minioClient = MinioClient.builder()
|
||||
.endpoint(newConfig.getEndpoint())
|
||||
.credentials(newConfig.getAccessKey(), newConfig.getSecretKey())
|
||||
.build();
|
||||
log.info("StorageConfig refreshed successfully");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件访问URL
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.czh.service.impl;
|
||||
|
||||
import cn.czh.base.BusinessException;
|
||||
import cn.czh.context.StorageConfigUpdateEvent;
|
||||
import cn.czh.dto.FileRecordDTO;
|
||||
import cn.czh.dto.MyPartSummary;
|
||||
import cn.czh.dto.TaskInfoDTO;
|
||||
@@ -22,7 +23,9 @@ import com.aliyun.oss.model.*;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
@@ -55,19 +58,30 @@ public class OssStorageService implements IStorageService {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
// 获取 OSS 配置
|
||||
this.refreshConfig();
|
||||
}
|
||||
|
||||
private void refreshConfig() {
|
||||
this.storageConfig = storageConfigService.getStorageConfigByType(StorageConfig.OSS);
|
||||
// 初始化 OSS 客户端
|
||||
this.ossClient = new OSSClientBuilder().build(
|
||||
storageConfig.getEndpoint(),
|
||||
storageConfig.getAccessKey(),
|
||||
storageConfig.getSecretKey()
|
||||
);
|
||||
log.info("OssStorageService refreshed successfully");
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void handleConfigUpdate(StorageConfigUpdateEvent event) {
|
||||
if (StorageConfig.OSS.equals(event.getNewConfig().getType())) {
|
||||
refreshConfig();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传单个文件
|
||||
*/
|
||||
@Transactional
|
||||
@Override
|
||||
public FileRecordDTO uploadFile(MultipartFile file, String md5, String objectName) {
|
||||
UploadFile uploadFile = getByIdentifier(md5);
|
||||
@@ -155,6 +169,7 @@ public class OssStorageService implements IStorageService {
|
||||
/**
|
||||
* 创建分片上传任务
|
||||
*/
|
||||
@Transactional
|
||||
@Override
|
||||
public TaskInfoDTO createMultipartUpload(CreateMultipartUpload param) {
|
||||
String identifier = param.getIdentifier();
|
||||
@@ -194,6 +209,7 @@ public class OssStorageService implements IStorageService {
|
||||
/**
|
||||
* 合并分片
|
||||
*/
|
||||
@Transactional
|
||||
@Override
|
||||
public UploadFile merge(String identifier) {
|
||||
UploadFile uploadFile = getByIdentifier(identifier);
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
package cn.czh.service.impl;
|
||||
|
||||
import cn.czh.base.BusinessException;
|
||||
import cn.czh.context.StorageConfigUpdateEvent;
|
||||
import cn.czh.entity.StorageConfig;
|
||||
import cn.czh.mapper.StorageConfigMapper;
|
||||
import cn.czh.mapper.UploadFileMapper;
|
||||
import cn.czh.service.IStorageConfigService;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
|
||||
@Service
|
||||
public class StorageConfigServiceImpl extends ServiceImpl<StorageConfigMapper, StorageConfig> implements IStorageConfigService {
|
||||
|
||||
@Resource
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@Override
|
||||
public StorageConfig getStorageConfigByType(String type) {
|
||||
LambdaQueryWrapper<StorageConfig> queryWrapper = new LambdaQueryWrapper<>();
|
||||
@@ -27,4 +37,12 @@ public class StorageConfigServiceImpl extends ServiceImpl<StorageConfigMapper, S
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void updateStorageConfig(StorageConfig config) {
|
||||
super.updateById(config);
|
||||
eventPublisher.publishEvent(new StorageConfigUpdateEvent(this, config)); // 发布事件
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user