feat(工作台): 三列表(账号/视频/AI写脚本)批量操作——左下角图标按钮+勾选批量删除
- 后端: dsh-data.js batchDelete(kind,ids)——accounts 软删(del_flag=1)+连坐清 persona/analysis/videos/source/analysis/rewrite_log; videos|rewrites 删视频行+关联(video_id 与 aweme_id 双键,兼容 video_id=null 历史行); server.js POST /api/dsh/batch-delete(kind 校验+空 ids 幂等) - 前端: data-pages.js batchBarHtml/bindBatchBar + 三列表勾选列+左下角批量操作条; 图标版——app.js 新增 check-square/trash 图标, 批量模式图标右侧出 trash/x+全选+计数; style.css 批量样式+--danger - 修复 P0: 账号连坐删除漏删 account_videos 行(孤儿残留), 已补并复验 - 验证: API 实测(删测试账号/视频连坐全清+非法kind+空ids) + CDP UI 33项PASS + 端到端删除 PASS(cdp-verify-batch.mjs / cdp-verify-batch-e2e.mjs)
This commit is contained in:
1 parent
6bf0eca90e
commit
2e048459ea
10 files changed
+390
-9
No files matched your search
@@ -42,6 +42,51 @@ function setFavorite(accountId, fav) {
|
||||
const num = (v) => { const n = Number(v); return Number.isFinite(n) ? n : 0; };
|
||||
const fmt = (v) => (v == null || v === '') ? null : v;
|
||||
|
||||
// 批量删除:kind=accounts(软删账号+连坐清理)/ videos | rewrites(删视频行+关联)
|
||||
// ids: 主键 id 数组;返回 { deleted: 实际删除条数 }
|
||||
// 连坐范围:账号 → persona/analysis/videos(含 source/analysis/rewrite_log);视频 → source/analysis/rewrite_log
|
||||
// 注意 source/analysis 存在 video_id=null 的行(俊希等历史数据),须同时按 aweme_id 双键删
|
||||
function batchDelete(kind, ids) {
|
||||
if (!fs.existsSync(DSH_DB)) throw new Error('数据库不可用');
|
||||
const clean = (Array.isArray(ids) ? ids : []).map(Number).filter((n) => Number.isFinite(n) && n > 0);
|
||||
if (clean.length === 0) return { deleted: 0 };
|
||||
const q = clean.map(() => '?').join(',');
|
||||
const w = new DatabaseSync(DSH_DB);
|
||||
try {
|
||||
if (kind === 'accounts') {
|
||||
w.prepare(`DELETE FROM account_persona WHERE account_id IN (${q})`).run(...clean);
|
||||
w.prepare(`DELETE FROM account_analysis WHERE account_id IN (${q})`).run(...clean);
|
||||
deleteVideosDeep(w, w.prepare(`SELECT id, aweme_id FROM account_videos WHERE account_id IN (${q})`).all(...clean));
|
||||
w.prepare(`DELETE FROM account_videos WHERE account_id IN (${q})`).run(...clean);
|
||||
w.prepare(`DELETE FROM rewrite_log WHERE account_id IN (${q})`).run(...clean);
|
||||
const r = w.prepare(`UPDATE hot_accounts SET del_flag=1 WHERE id IN (${q}) AND del_flag=0`).run(...clean);
|
||||
return { deleted: Number(r.changes) };
|
||||
}
|
||||
// videos | rewrites:删视频行 + 关联解析/脚本(按 video_id 与 aweme_id 双键)
|
||||
deleteVideosDeep(w, w.prepare(`SELECT id, aweme_id FROM account_videos WHERE id IN (${q})`).all(...clean));
|
||||
const r = w.prepare(`DELETE FROM account_videos WHERE id IN (${q})`).run(...clean);
|
||||
return { deleted: Number(r.changes) };
|
||||
} finally {
|
||||
w.close();
|
||||
}
|
||||
}
|
||||
|
||||
function deleteVideosDeep(w, vids) {
|
||||
if (!vids.length) return;
|
||||
const ids = vids.map((v) => v.id);
|
||||
const awemes = vids.map((v) => v.aweme_id).filter(Boolean);
|
||||
const qi = ids.map(() => '?').join(',');
|
||||
w.prepare(`DELETE FROM account_video_source WHERE video_id IN (${qi})`).run(...ids);
|
||||
w.prepare(`DELETE FROM account_video_analysis WHERE video_id IN (${qi})`).run(...ids);
|
||||
w.prepare(`DELETE FROM rewrite_log WHERE video_id IN (${qi})`).run(...ids);
|
||||
if (awemes.length) {
|
||||
const qa = awemes.map(() => '?').join(',');
|
||||
w.prepare(`DELETE FROM account_video_source WHERE aweme_id IN (${qa})`).run(...awemes);
|
||||
w.prepare(`DELETE FROM account_video_analysis WHERE aweme_id IN (${qa})`).run(...awemes);
|
||||
w.prepare(`DELETE FROM rewrite_log WHERE aweme_id IN (${qa})`).run(...awemes);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================= 账号 ================= */
|
||||
|
||||
function listTracks() {
|
||||
@@ -327,7 +372,7 @@ function getRanking({ date = '', category = '', sortBy = 'rank', order = '', pag
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
dbAvailable, setFavorite, listTracks, listAccounts, listAccountVideos, getPersona, getAccountAnalysis,
|
||||
dbAvailable, setFavorite, batchDelete, listTracks, listAccounts, listAccountVideos, getPersona, getAccountAnalysis,
|
||||
listVideos, getVideoDetail, getVideoScript, listRewrites, listStoryboards, listRecentScripts,
|
||||
getReviewDetail, getReviewFull, getStats, rankingMeta, getRanking, DSH_DB, RANKING_DIR,
|
||||
};
|
||||
Reference in new issue
Block a user