feat: 快照改为预览版/正式版两阶段发布 - 新增 saveDraft 接口手动保存草稿快照 - 新增 getDraftBundleJson 方法读取预览版(Caffeine缓存) - SnapshotController 支持 ?preview=true 参数读取 draft - 前端编辑页改为'保存草稿'+'发布正式版'两个按钮 - AdminController 新增单站点 save-draft/publish 接口 - 复用 populateSnapshotFields 消除重复代码
This commit is contained in:
@@ -385,6 +385,44 @@ public class AdminController {
|
||||
} catch (Exception e) { return ApiResponse.error(500, "发布失败: " + e.getMessage()); }
|
||||
}
|
||||
|
||||
/** 保存站点配置草稿(预览版,不上传 CDN) */
|
||||
@PostMapping("/sites/{siteId}/save-draft")
|
||||
public ApiResponse<Map<String, Object>> saveDraft(@PathVariable("siteId") String siteId,
|
||||
HttpServletRequest request) {
|
||||
checkAdmin(request);
|
||||
try {
|
||||
var draft = snapshotService.saveDraft(siteId);
|
||||
if (draft == null) {
|
||||
return ApiResponse.error(400, "该站点无有效配置,无法保存草稿");
|
||||
}
|
||||
return ApiResponse.success(Map.of(
|
||||
"siteId", draft.getSiteId(),
|
||||
"version", draft.getVersion(),
|
||||
"checksum", draft.getChecksum().substring(0, 8)));
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.error(500, "保存草稿失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** 发布指定站点快照(预览版 → 正式版,上传 CDN) */
|
||||
@PostMapping("/sites/{siteId}/publish")
|
||||
public ApiResponse<Map<String, Object>> publishSite(@PathVariable("siteId") String siteId,
|
||||
HttpServletRequest request) {
|
||||
checkAdmin(request);
|
||||
try {
|
||||
var published = snapshotService.publish(siteId);
|
||||
if (published == null) {
|
||||
return ApiResponse.error(400, "该站点无 draft 快照可发布");
|
||||
}
|
||||
return ApiResponse.success(Map.of(
|
||||
"siteId", published.getSiteId(),
|
||||
"version", published.getVersion(),
|
||||
"checksum", published.getChecksum().substring(0, 8)));
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.error(500, "发布失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/sites")
|
||||
public ApiResponse<List<com.par.core.dto.SiteDTO>> listAllSites(HttpServletRequest request) {
|
||||
checkAdmin(request);
|
||||
|
||||
@@ -34,22 +34,28 @@ public class SnapshotController {
|
||||
* 获取站点当前生效配置快照(供 mediabot 使用)
|
||||
* 支持 ETag (If-None-Match) 实现增量更新
|
||||
* 优先从 Caffeine 缓存读取,缓存未命中查数据库并回退七牛云 CDN
|
||||
* @param preview true=读取最新 draft(预览版),false/不传=读取最新 published(正式版)
|
||||
*/
|
||||
@GetMapping("/{siteId}")
|
||||
public ResponseEntity<?> getSnapshot(
|
||||
@PathVariable("siteId") String siteId,
|
||||
@RequestHeader(value = "If-None-Match", required = false) String ifNoneMatch) {
|
||||
@RequestHeader(value = "If-None-Match", required = false) String ifNoneMatch,
|
||||
@RequestParam(value = "preview", required = false) boolean preview) {
|
||||
|
||||
try {
|
||||
String bundleJson = snapshotService.getActiveBundleJson(siteId);
|
||||
String bundleJson = preview
|
||||
? snapshotService.getDraftBundleJson(siteId)
|
||||
: snapshotService.getActiveBundleJson(siteId);
|
||||
|
||||
if (bundleJson == null || bundleJson.isEmpty()) {
|
||||
// 尝试从云存储 CDN 回退
|
||||
String cached = storageService.read("snapshots/" + siteId + ".json");
|
||||
if (cached != null && !cached.isBlank()) {
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(cached);
|
||||
// 尝试从云存储 CDN 回退(仅正式版有 CDN 备份)
|
||||
if (!preview) {
|
||||
String cached = storageService.read("snapshots/" + siteId + ".json");
|
||||
if (cached != null && !cached.isBlank()) {
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(cached);
|
||||
}
|
||||
}
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
@@ -68,16 +74,18 @@ public class SnapshotController {
|
||||
.body(bundleJson);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取快照失败 siteId={}", siteId, e);
|
||||
// 数据库异常时回退到云存储
|
||||
try {
|
||||
String cached = storageService.read("snapshots/" + siteId + ".json");
|
||||
if (cached != null && !cached.isBlank()) {
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(cached);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
log.error("获取快照失败 siteId={} preview={}", siteId, preview, e);
|
||||
// 数据库异常时回退到云存储(仅正式版)
|
||||
if (!preview) {
|
||||
try {
|
||||
String cached = storageService.read("snapshots/" + siteId + ".json");
|
||||
if (cached != null && !cached.isBlank()) {
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(cached);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
return ResponseEntity.status(500).body("{\"error\":\"internal error\"}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user