feat: 七牛云存储支持(本地缓存/备份)

- StorageService 抽象层(store/read/exists)
- QiniuStorageService: 七牛云主存储(@ConditionalOnProperty)
- LocalStorageService: 本地文件存储(默认/@ConditionalOnMissingBean)
- CachedStorageService: 七牛主+本地缓存(写:七牛→本地, 读:本地→七牛)
- 配置: PAR_STORAGE_TYPE=qiniu + QINIU_AK/SK/BUCKET/REGION
- 不配七牛时自动降级到纯本地存储
This commit is contained in:
mediabot-pt
2026-06-29 22:58:32 +08:00
parent 98e040c06b
commit f351d71d18
8 changed files with 271 additions and 61 deletions

View File

@@ -4,19 +4,13 @@ import com.par.core.dto.ApiResponse;
import com.par.core.dto.SiteConfigDTO;
import com.par.core.entity.SiteConfig;
import com.par.core.service.ConfigService;
import com.par.core.storage.StorageService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
@@ -27,13 +21,15 @@ import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/v1/configs")
@RequiredArgsConstructor
public class ConfigController {
private final ConfigService configService;
private final StorageService storageService;
@Value("${par.static.path:./static}")
private String staticPath;
public ConfigController(ConfigService configService, StorageService storageService) {
this.configService = configService;
this.storageService = storageService;
}
/**
* 列出所有待审核配置
@@ -76,19 +72,18 @@ public class ConfigController {
.build();
}
// 读取文件内容
Path filePath = Paths.get(staticPath, config.getStoragePath());
// 读取文件内容(通过存储抽象:本地缓存或七牛云)
String content;
try {
content = Files.readString(filePath, StandardCharsets.UTF_8);
} catch (IOException e) {
log.error("Failed to read config file: {}", filePath, e);
content = storageService.read(config.getStoragePath());
} catch (Exception e) {
log.error("Failed to read config: siteId={}, version={}", siteId, version, e);
return ResponseEntity.status(500).body("Failed to read config");
}
return ResponseEntity.ok()
.eTag(config.getConfigHash())
.header(HttpHeaders.CONTENT_TYPE, "application/json")
.header(org.springframework.http.HttpHeaders.CONTENT_TYPE, "application/json")
.body(content);
}

View File

@@ -46,6 +46,13 @@ mybatis-plus:
par:
static:
path: ${PAR_STATIC_PATH:./data/static}
storage:
type: ${PAR_STORAGE_TYPE:local} # qiniu / local
qiniu:
access-key: ${QINIU_AK:}
secret-key: ${QINIU_SK:}
bucket: ${QINIU_BUCKET:par-configs}
region: ${QINIU_REGION:z2}
mediabot:
psk: ${PAR_MEDIABOT_PSK:}
admin: