diff --git a/par-api/src/main/java/com/par/api/controller/AdminController.java b/par-api/src/main/java/com/par/api/controller/AdminController.java index d094810..545eed1 100644 --- a/par-api/src/main/java/com/par/api/controller/AdminController.java +++ b/par-api/src/main/java/com/par/api/controller/AdminController.java @@ -380,22 +380,22 @@ public class AdminController { } catch (Exception e) { return ApiResponse.error(500, "发布失败: " + e.getMessage()); } } - /** 保存站点配置草稿(预览版,不上传 CDN) */ - @PostMapping("/sites/{siteId}/save-draft") - public ApiResponse> saveDraft(@PathVariable("siteId") String siteId, - HttpServletRequest request) { + /** 保存站点配置预览版(不上传 CDN) */ + @PostMapping("/sites/{siteId}/save-preview") + public ApiResponse> savePreview(@PathVariable("siteId") String siteId, + HttpServletRequest request) { checkAdmin(request); try { - var draft = snapshotService.saveDraft(siteId); - if (draft == null) { - return ApiResponse.error(400, "该站点无有效配置,无法保存草稿"); + var preview = snapshotService.savePreview(siteId); + if (preview == null) { + return ApiResponse.error(400, "该站点无有效配置,无法保存预览版"); } return ApiResponse.success(Map.of( - "siteId", draft.getSiteId(), - "version", draft.getVersion(), - "checksum", draft.getChecksum().substring(0, 8))); + "siteId", preview.getSiteId(), + "version", preview.getVersion(), + "checksum", preview.getChecksum().substring(0, 8))); } catch (Exception e) { - return ApiResponse.error(500, "保存草稿失败: " + e.getMessage()); + return ApiResponse.error(500, "保存预览版失败: " + e.getMessage()); } } @@ -407,7 +407,7 @@ public class AdminController { try { var published = snapshotService.publish(siteId); if (published == null) { - return ApiResponse.error(400, "该站点无 draft 快照可发布"); + return ApiResponse.error(400, "该站点无预览版快照可发布"); } return ApiResponse.success(Map.of( "siteId", published.getSiteId(), diff --git a/par-core/src/main/java/com/par/core/service/SiteSnapshotService.java b/par-core/src/main/java/com/par/core/service/SiteSnapshotService.java index 712022a..6707404 100644 --- a/par-core/src/main/java/com/par/core/service/SiteSnapshotService.java +++ b/par-core/src/main/java/com/par/core/service/SiteSnapshotService.java @@ -17,20 +17,20 @@ public interface SiteSnapshotService { Map buildSnapshot(String siteId); /** - * 更新站点的 draft 快照(任意配置变更后调用) + * 更新站点的预览版快照(任意配置变更后调用) * 内容未变化(checksum 相同)则不产生新版本 * @return 新快照或 null(内容未变) */ SiteSnapshot updateSnapshot(String siteId); /** - * 手动保存草稿快照(管理员触发,强制生成新 draft) + * 手动保存预览版快照(管理员触发) * @return 新快照或 null */ - SiteSnapshot saveDraft(String siteId); + SiteSnapshot savePreview(String siteId); /** - * 发布指定站点的快照(draft → published) + * 发布指定站点的快照(预览版 → 正式版) * 同时上传到七牛云 CDN 作为灾备 */ SiteSnapshot publish(String siteId); @@ -41,22 +41,21 @@ public interface SiteSnapshotService { SiteSnapshot getActive(String siteId); /** - * 获取站点当前生效快照的预序列化 JSON(带 Caffeine 缓存) + * 获取站点当前生效快照的预序列化 JSON(带缓存) * 供 SnapshotController 使用,避免重复序列化 * @return JSON 字符串,空串表示无快照 */ String getActiveBundleJson(String siteId); /** - * 获取站点最新 draft 快照的预序列化 JSON(预览版,带 Caffeine 缓存) + * 获取站点最新预览版快照的预序列化 JSON(带缓存) * @param siteId 站点标识 - * @return JSON 字符串,空串表示无 draft + * @return JSON 字符串,空串表示无预览版 */ String getDraftBundleJson(String siteId); /** - * 获取预览版快照 JSON(draft 优先,无 draft 时 fallback 到 published) - * 即:正式版 + 预览版 的并集 + * 获取预览快照 JSON(预览版优先,无预览版时 fallback 到正式版) * @param siteId 站点标识 * @return JSON 字符串,空串表示无任何快照 */ diff --git a/par-core/src/main/java/com/par/core/service/impl/SiteSnapshotServiceImpl.java b/par-core/src/main/java/com/par/core/service/impl/SiteSnapshotServiceImpl.java index 0eb6186..9288232 100644 --- a/par-core/src/main/java/com/par/core/service/impl/SiteSnapshotServiceImpl.java +++ b/par-core/src/main/java/com/par/core/service/impl/SiteSnapshotServiceImpl.java @@ -43,23 +43,14 @@ public class SiteSnapshotServiceImpl implements SiteSnapshotService { private final CachedStorageService storageService; private static final ObjectMapper om = new ObjectMapper(); - /** 已发布快照 JSON 缓存:key=siteId, value=预序列化的 bundle JSON */ - private final Cache bundleJsonCache = Caffeine.newBuilder() + /** 统一快照 JSON 缓存:key = siteId + ":published" | siteId + ":draft",value = 预序列化 bundle JSON */ + private final Cache jsonCache = Caffeine.newBuilder() .expireAfterWrite(Duration.ofMinutes(10)) - .maximumSize(500) + .maximumSize(1000) .build(); - /** draft 快照 JSON 缓存:key=siteId, value=预序列化的 bundle JSON */ - private final Cache draftJsonCache = Caffeine.newBuilder() - .expireAfterWrite(Duration.ofMinutes(5)) - .maximumSize(500) - .build(); - - /** preview 快照 JSON 缓存:draft 优先 fallback published */ - private final Cache previewJsonCache = Caffeine.newBuilder() - .expireAfterWrite(Duration.ofMinutes(5)) - .maximumSize(500) - .build(); + private static String pubKey(String siteId) { return siteId + ":published"; } + private static String draftKey(String siteId) { return siteId + ":draft"; } @Override public Map buildSnapshot(String siteId) { @@ -134,18 +125,15 @@ public class SiteSnapshotServiceImpl implements SiteSnapshotService { } try { - // 计算 checksum String contentJson = om.writeValueAsString(data); String checksum = HmacUtil.sha256(contentJson); - // 查询最新 draft SiteSnapshot latestDraft = snapshotMapper.selectLatestDraft(siteId); if (latestDraft != null && checksum.equals(latestDraft.getChecksum())) { log.debug("站点 {} 配置未变化,跳过快照生成", siteId); return null; } - // 确定新版本号 int newVersion = (latestDraft != null) ? latestDraft.getVersion() + 1 : 1; SiteSnapshot snapshot = new SiteSnapshot(); @@ -157,10 +145,9 @@ public class SiteSnapshotServiceImpl implements SiteSnapshotService { snapshotMapper.insert(snapshot); - // 刷新 draft 和 preview 缓存(审批通过后异步生成 draft,需立即可被预览查询到) + // 刷新缓存:审批通过后异步生成的预览版,需立即可被 ?preview=true 查询到 String bundleJson = toBundleJson(snapshot); - draftJsonCache.put(siteId, bundleJson); - previewJsonCache.put(siteId, bundleJson); + jsonCache.put(draftKey(siteId), bundleJson); log.info("快照已更新: siteId={}, version={}, checksum={}", siteId, newVersion, checksum.substring(0, 8)); return snapshot; @@ -172,10 +159,10 @@ public class SiteSnapshotServiceImpl implements SiteSnapshotService { @Override @Transactional - public SiteSnapshot saveDraft(String siteId) { + public SiteSnapshot savePreview(String siteId) { Map data = buildSnapshot(siteId); if (data == null) { - log.warn("站点 {} 无有效配置,无法保存草稿", siteId); + log.warn("站点 {} 无有效配置,无法保存预览版", siteId); return null; } @@ -186,7 +173,7 @@ public class SiteSnapshotServiceImpl implements SiteSnapshotService { // 查询最新 draft,内容相同则跳过 SiteSnapshot latestDraft = snapshotMapper.selectLatestDraft(siteId); if (latestDraft != null && checksum.equals(latestDraft.getChecksum())) { - log.info("站点 {} 草稿内容未变化,跳过保存", siteId); + log.info("站点 {} 预览版内容未变化,跳过保存", siteId); return latestDraft; } @@ -201,15 +188,14 @@ public class SiteSnapshotServiceImpl implements SiteSnapshotService { snapshotMapper.insert(snapshot); - // 刷新 draft 和 preview 缓存 + // 刷新缓存 String bundleJson = toBundleJson(snapshot); - draftJsonCache.put(siteId, bundleJson); - previewJsonCache.put(siteId, bundleJson); + jsonCache.put(draftKey(siteId), bundleJson); - log.info("草稿已保存: siteId={}, version={}, checksum={}", siteId, newVersion, checksum.substring(0, 8)); + log.info("预览版已保存: siteId={}, version={}, checksum={}", siteId, newVersion, checksum.substring(0, 8)); return snapshot; } catch (Exception e) { - log.error("保存草稿失败 siteId={}", siteId, e); + log.error("保存预览版失败 siteId={}", siteId, e); return null; } } @@ -237,7 +223,7 @@ public class SiteSnapshotServiceImpl implements SiteSnapshotService { public SiteSnapshot publish(String siteId) { SiteSnapshot draft = snapshotMapper.selectLatestDraft(siteId); if (draft == null) { - log.warn("站点 {} 无 draft 快照可发布", siteId); + log.warn("站点 {} 无预览版快照可发布", siteId); return null; } @@ -255,10 +241,9 @@ public class SiteSnapshotServiceImpl implements SiteSnapshotService { // 序列化并刷新缓存 String bundleJson = toBundleJson(draft); - bundleJsonCache.put(siteId, bundleJson); - // 发布后 draft/preview 缓存已过时 - draftJsonCache.invalidate(siteId); - previewJsonCache.invalidate(siteId); + jsonCache.put(pubKey(siteId), bundleJson); + // 发布后 draft 缓存已过时(该记录不再是 draft) + jsonCache.invalidate(draftKey(siteId)); // 上传到七牛云 CDN 作为灾备(复用已序列化的 JSON) try { @@ -285,42 +270,42 @@ public class SiteSnapshotServiceImpl implements SiteSnapshotService { @Override public String getActiveBundleJson(String siteId) { - return bundleJsonCache.get(siteId, key -> { - SiteSnapshot active = snapshotMapper.selectLatestPublished(key); + return jsonCache.get(pubKey(siteId), key -> { + SiteSnapshot active = snapshotMapper.selectLatestPublished(siteId); if (active == null) { - log.debug("站点 {} 无 published 快照", key); + log.debug("站点 {} 无 published 快照", siteId); return ""; // 空字符串表示无数据,Caffeine 会缓存 } - log.debug("缓存未命中,序列化 siteId={}", key); + log.debug("缓存未命中,序列化 siteId={}", siteId); return toBundleJson(active); }); } @Override public String getDraftBundleJson(String siteId) { - return draftJsonCache.get(siteId, key -> { - SiteSnapshot draft = snapshotMapper.selectLatestDraft(key); + return jsonCache.get(draftKey(siteId), key -> { + SiteSnapshot draft = snapshotMapper.selectLatestDraft(siteId); if (draft == null) { - log.debug("站点 {} 无 draft 快照", key); - return ""; // 空字符串表示无数据,Caffeine 会缓存 + log.debug("站点 {} 无预览版快照", siteId); + return ""; } - log.debug("draft 缓存未命中,序列化 siteId={}", key); + log.debug("预览版缓存未命中 siteId={}", siteId); return toBundleJson(draft); }); } @Override public String getPreviewBundleJson(String siteId) { - // draft 优先,无 draft 则取 published — 一次查询 - return previewJsonCache.get(siteId, key -> { - SiteSnapshot snapshot = snapshotMapper.selectLatestWithDraft(key); - if (snapshot == null) { - log.debug("站点 {} 无任何快照", key); - return ""; - } - log.debug("预览缓存未命中 siteId={} status={}", key, snapshot.getStatus()); - return toBundleJson(snapshot); - }); + // draft 优先,无 draft 则 fallback 到 published — 一次查询 + // preview 不使用独立缓存,因为它是 draft + published 的逻辑并集; + // 当 draft 或 published 任一变化时,需要同时感知,直接查 DB 更准确 + SiteSnapshot snapshot = snapshotMapper.selectLatestWithDraft(siteId); + if (snapshot == null) { + log.debug("站点 {} 无任何快照", siteId); + return ""; + } + log.debug("预览查询 siteId={} status={}", siteId, snapshot.getStatus()); + return toBundleJson(snapshot); } @Override diff --git a/web/src/views/Sites.vue b/web/src/views/Sites.vue index 7780fa9..c07bca8 100644 --- a/web/src/views/Sites.vue +++ b/web/src/views/Sites.vue @@ -57,7 +57,7 @@
返回列表 保存站点 - 保存草稿 + 保存预览版 发布正式版
@@ -108,7 +108,7 @@