diff --git a/backend/database.js b/backend/database.js index 2eb78da..29ecdda 100644 --- a/backend/database.js +++ b/backend/database.js @@ -2284,8 +2284,12 @@ const DownloadTrafficReservationDB = { }; }, - cleanupFinalizedHistory(keepDays = 7) { - const days = Math.min(365, Math.max(1, Math.floor(Number(keepDays) || 7))); + cleanupFinalizedHistory(keepDays = 0) { + // keepDays=0 表示立即清理全部已完成历史(confirmed/expired/cancelled) + const normalized = Number(keepDays); + const days = Number.isFinite(normalized) + ? Math.min(365, Math.max(0, Math.floor(normalized))) + : 0; return db.prepare(` DELETE FROM user_download_traffic_reservations WHERE status IN ('confirmed', 'expired', 'cancelled') diff --git a/backend/server.js b/backend/server.js index d13366c..51cb474 100644 --- a/backend/server.js +++ b/backend/server.js @@ -8843,7 +8843,10 @@ app.post('/api/admin/download-reservations/:id/cancel', authMiddleware, adminMid // 下载流量预扣运维面板:批量清理(过期 pending + 历史 finalized) app.post('/api/admin/download-reservations/cleanup', authMiddleware, adminMiddleware, (req, res) => { try { - const keepDays = Math.min(365, Math.max(1, parseInt(req.body?.keep_days, 10) || 7)); + // 默认 keep_days=0:立即清理全部已完成历史记录,避免“清理无效果”的感知 + const rawKeepDays = req.body?.keep_days; + const parsedKeepDays = Number.isFinite(Number(rawKeepDays)) ? parseInt(rawKeepDays, 10) : 0; + const keepDays = Math.min(365, Math.max(0, Number.isFinite(parsedKeepDays) ? parsedKeepDays : 0)); const expireResult = DownloadTrafficReservationDB.expirePendingReservations(); const cleanupResult = DownloadTrafficReservationDB.cleanupFinalizedHistory(keepDays); @@ -8860,7 +8863,7 @@ app.post('/api/admin/download-reservations/cleanup', authMiddleware, adminMiddle res.json({ success: true, - message: '预扣清理完成', + message: `预扣清理完成(过期待确认 ${Number(expireResult?.changes || 0)} 条,删除历史 ${Number(cleanupResult?.changes || 0)} 条)`, result: { keep_days: keepDays, expired_pending: Number(expireResult?.changes || 0), diff --git a/frontend/app.js b/frontend/app.js index b6249d3..3d10379 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -3181,11 +3181,25 @@ handleDragLeave(e) { openShare(url) { if (!url) return; - const newWindow = window.open(url, '_blank', 'noopener'); - if (!newWindow || newWindow.closed || typeof newWindow.closed === 'undefined') { - // 弹窗被拦截时提示用户手动打开,避免当前页跳转 - this.showToast('info', '提示', '浏览器阻止了新标签页,请允许弹窗或手动打开链接'); + // 仅在真正被拦截(返回 null/undefined)时提示,避免已打开仍误报 + let newWindow = null; + try { + newWindow = window.open(url, '_blank'); + } catch (error) { + newWindow = null; } + + if (newWindow) { + try { + // 防止新窗口通过 opener 反向控制当前页面 + newWindow.opener = null; + } catch (e) { + // 忽略跨域写 opener 失败 + } + return; + } + + this.showToast('info', '提示', '浏览器阻止了新标签页,请允许弹窗或手动打开链接'); }, copyTextToClipboard(text, successMessage = '已复制到剪贴板') { @@ -4559,15 +4573,17 @@ handleDragLeave(e) { async cleanupReservations() { if (this.reservationMonitor.cleaning) return; - if (!confirm('确认清理过期/历史预扣记录吗?')) return; + if (!confirm('确认清理预扣历史吗?将立即删除已完成/已过期/已取消记录。')) return; this.reservationMonitor.cleaning = true; try { const response = await axios.post(`${this.apiBase}/api/admin/download-reservations/cleanup`, { - keep_days: 7 + keep_days: 0 }); if (response.data?.success) { - this.showToast('success', '成功', response.data.message || '预扣清理完成'); + const result = response.data?.result || {}; + const msg = `已清理历史 ${Number(result.deleted_finalized || 0)} 条,过期待确认 ${Number(result.expired_pending || 0)} 条`; + this.showToast('success', '成功', msg); await this.loadDownloadReservationMonitor(1); } } catch (error) {