From 6505f621d15b660f4f939f65b546afe44c90f39e Mon Sep 17 00:00:00 2001 From: mediabot-pt <295750538+mediabot-pt@users.noreply.github.com> Date: Tue, 7 Jul 2026 13:12:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BC=96=E8=BE=91=E5=99=A8=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E3=80=8C=E9=87=8D=E7=BD=AE=E3=80=8D=E5=92=8C=E3=80=8C?= =?UTF-8?q?=E4=B8=A2=E5=BC=83=E9=A2=84=E8=A7=88=E7=89=88=E3=80=8D=E6=8C=89?= =?UTF-8?q?=E9=92=AE=EF=BC=8C=E6=94=AF=E6=8C=81=E5=9B=9E=E9=80=80=E9=A2=84?= =?UTF-8?q?=E8=A7=88=E7=89=88=E5=BF=AB=E7=85=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../par/api/controller/AdminController.java | 9 +++++++++ .../par/core/mapper/SiteSnapshotMapper.java | 4 ++++ .../par/core/service/SiteSnapshotService.java | 6 ++++++ .../service/impl/SiteSnapshotServiceImpl.java | 13 ++++++++++++ web/src/views/AccessResultCheck.vue | 20 ++++++++++++++++++- web/src/views/RiskRules.vue | 20 ++++++++++++++++++- web/src/views/SitePages.vue | 20 ++++++++++++++++++- web/src/views/Sites.vue | 20 ++++++++++++++++++- 8 files changed, 108 insertions(+), 4 deletions(-) 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 2723cd9..f51b249 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 @@ -462,6 +462,15 @@ public class AdminController { return ApiResponse.success(result); } + /** 丢弃预览版快照(删除所有 draft),站点配置回退到正式版 */ + @PostMapping("/sites/{siteId}/discard-draft") + public ApiResponse> discardDraft(@PathVariable("siteId") String siteId, + HttpServletRequest request) { + checkAdmin(request); + int deleted = snapshotService.discardDraft(siteId); + return ApiResponse.success(Map.of("siteId", siteId, "deleted", deleted)); + } + @GetMapping("/sites") public ApiResponse> listAllSites(HttpServletRequest request) { checkAdmin(request); diff --git a/par-core/src/main/java/com/par/core/mapper/SiteSnapshotMapper.java b/par-core/src/main/java/com/par/core/mapper/SiteSnapshotMapper.java index 883e434..2a9706c 100644 --- a/par-core/src/main/java/com/par/core/mapper/SiteSnapshotMapper.java +++ b/par-core/src/main/java/com/par/core/mapper/SiteSnapshotMapper.java @@ -23,4 +23,8 @@ public interface SiteSnapshotMapper extends BaseMapper { /** 查询站点最新快照:draft 优先,无 draft 则取 published */ @Select("SELECT * FROM site_snapshots WHERE site_id = #{siteId} AND status IN ('draft','published') ORDER BY CASE WHEN status = 'draft' THEN 0 ELSE 1 END, version DESC LIMIT 1") SiteSnapshot selectLatestWithDraft(@Param("siteId") String siteId); + + /** 删除站点指定状态的快照 */ + @org.apache.ibatis.annotations.Delete("DELETE FROM site_snapshots WHERE site_id = #{siteId} AND status = #{status}") + int deleteBySiteIdAndStatus(@Param("siteId") String siteId, @Param("status") String status); } 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 6707404..0884de9 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 @@ -71,4 +71,10 @@ public interface SiteSnapshotService { * @return 成功发布的站点数 */ int publishChanged(); + + /** + * 丢弃预览版快照(删除所有 draft 记录),回退到正式版 + * @return 删除的 draft 记录数 + */ + int discardDraft(String siteId); } 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 9288232..0596067 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 @@ -333,6 +333,19 @@ public class SiteSnapshotServiceImpl implements SiteSnapshotService { return count; } + @Override + @Transactional + public int discardDraft(String siteId) { + int deleted = snapshotMapper.deleteBySiteIdAndStatus(siteId, "draft"); + if (deleted > 0) { + jsonCache.invalidate(draftKey(siteId)); + log.info("预览版已丢弃: siteId={}, 删除 {} 条记录", siteId, deleted); + } else { + log.info("站点 {} 无预览版可丢弃", siteId); + } + return deleted; + } + /** 将快照转为 mediabot 可用的 JSON 字符串 */ private String toBundleJson(SiteSnapshot snapshot) { try { diff --git a/web/src/views/AccessResultCheck.vue b/web/src/views/AccessResultCheck.vue index 785d3ca..fd65775 100644 --- a/web/src/views/AccessResultCheck.vue +++ b/web/src/views/AccessResultCheck.vue @@ -48,6 +48,7 @@ 回滚 + 重置 保存 删除 保存 @@ -161,7 +163,7 @@ const siteId = route.params.siteId const loading = ref(false), parseLoading = ref(false), scriptExpanded = ref(true), historyVersion = ref(null), urlFilter = ref(''), deleting = ref(false) const selectedConfigId = ref(null), selectedConfig = ref(null) const scriptContent = ref(''), originalScript = ref(''), saving = ref(false), approving = ref(false), rejecting = ref(false) -const savingPreview = ref(false), publishing = ref(false) +const savingPreview = ref(false), publishing = ref(false), discardingDraft = ref(false) const snapshotStatus = ref(null) const allConfigVersions = ref([]), allResults = ref([]) const pageParsed = ref({}), reparsingUrls = reactive(new Set()), expanded = reactive(new Set()) @@ -254,6 +256,22 @@ const publishSnapshot = async () => { finally { publishing.value = false } } +const resetScript = () => { + scriptContent.value = originalScript.value + ElMessage.success('已重置到上次保存的状态') +} + +const discardDraftSnapshot = async () => { + try { await ElMessageBox.confirm('确认丢弃预览版?将回退到正式版。', '丢弃预览版', {confirmButtonText:'确认丢弃',type:'warning'}) } catch { return } + discardingDraft.value = true + try { + await api.raw.post('/admin/sites/' + siteId + '/discard-draft') + ElMessage.success('预览版已丢弃,当前为正式版') + fetchSnapshotStatus() + } catch { ElMessage.error('丢弃预览版失败') } + finally { discardingDraft.value = false } +} + watch(selectedConfig, (v) => { if (v) loadScript() }) const loadHistoryScript = async (configId) => { diff --git a/web/src/views/RiskRules.vue b/web/src/views/RiskRules.vue index d16eced..2a646be 100644 --- a/web/src/views/RiskRules.vue +++ b/web/src/views/RiskRules.vue @@ -41,9 +41,11 @@
保存 + 重置 上传 保存预览版 发布正式版 + 丢弃预览版 返回列表
@@ -279,7 +281,7 @@ const presets = [ ] // ============ 状态 ============ -const loading = ref(false), saving = ref(false), uploading = ref(false), savingPreview = ref(false), publishing = ref(false) +const loading = ref(false), saving = ref(false), uploading = ref(false), savingPreview = ref(false), publishing = ref(false), discardingDraft = ref(false) const sites = ref([]), rulesMap = ref({}) const editType = ref(''), editorJson = ref('[]'), originalJson = ref('') const snapshotStatus = ref(null) @@ -403,6 +405,22 @@ const publishSnapshot = async () => { finally { publishing.value = false } } +const resetEditor = () => { + editorJson.value = originalJson.value + ElMessage.success('已重置到上次保存的状态') +} + +const discardDraft = async () => { + try { await ElMessageBox.confirm('确认丢弃预览版?将回退到正式版。', '丢弃预览版', {confirmButtonText:'确认丢弃',type:'warning'}) } catch { return } + discardingDraft.value = true + try { + await api.raw.post('/admin/sites/' + editType.value + '/discard-draft') + ElMessage.success('预览版已丢弃,当前为正式版') + fetchSnapshotStatus() + } catch { ElMessage.error('丢弃预览版失败') } + finally { discardingDraft.value = false } +} + const sync = () => { editorJson.value = JSON.stringify(parsed.value, null, 2) } const confirmRemove = async (idx) => { diff --git a/web/src/views/SitePages.vue b/web/src/views/SitePages.vue index 3d3c5c3..eda20f8 100644 --- a/web/src/views/SitePages.vue +++ b/web/src/views/SitePages.vue @@ -53,9 +53,11 @@
保存 + 重置 上传 保存预览版 发布正式版 + 丢弃预览版 删除 返回列表
@@ -122,7 +124,7 @@