Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions DEVELOPMENT_LOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Development Log

## 2026-08-26 发送中心 AI 文案操作分层

- 明确区分“同步遗漏切片”和“AI 重写文案”:前者继续固定使用 `use_ai=false`,只为遗漏成片补建发布草稿与默认封面;后者只修改已存在草稿的标题、话题和简介。
- 原页面顶栏“补充缺失任务”移入内容准备的维护区并改名为“同步遗漏切片”,维护区明确标注“不调用 AI、不修改已有文案”;账号管理继续留在页面顶栏。
- 单条 AI 操作移动到发布文案字段上方并命名为“AI 重写本条文案”;勾选后的批量入口改为“AI 重写已选文案”,切换到排期计划或执行记录后隐藏。
- 移除 `/publish` 页面加载时静默调用旧草稿全局 AI 升级的行为;后端兼容接口继续保留,但 AI 文案只在用户明确点击单条或已选批量入口后执行。
- 发布任务、调度器、Publisher、账号、排期及执行记录状态机均未修改。
- 定向发送中心回归 `52 passed`、完整回归 `794 passed`;Python Compileall、JavaScript 语法、Ruff 和 `git diff --check` 均通过,测试使用隔离数据与 Mock,未调用真实 AI、封面生成、排期或平台发送。

## 2026-08-26 片段审核累计统计精简

- `/clips` 顶部统计从“待 AI 分析、待检查、可生成切片、已完成、异常任务”五项过程指标,精简为“累计审核任务、已通过视频、已完成任务”三项累计结果。
Expand Down
8 changes: 8 additions & 0 deletions NEXT_STEPS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Next Steps

## 2026-08-26 发送中心 AI 文案操作检查

1. 打开 `http://127.0.0.1:8001/publish`:页面顶栏只保留“账号管理”,“同步遗漏切片”和“补齐抖音缺失封面”位于内容准备的维护区。
2. “同步遗漏切片”只为尚未进入发送中心的成片补建草稿和默认封面,不调用 AI,也不会改写已有文案;平时自动同步正常时无需点击。
3. 需要改一条文案时,使用内容卡上方的“AI 重写本条文案”;需要改多条时先勾选,再使用底部“AI 重写已选文案”。AI 结果会立即保存到对应草稿。
4. 排期计划只管理时间,执行记录只核对发送结果,这两个区域不再提供 AI 文案按钮。刷新页面不会自动重写旧草稿。
5. 本轮验收不需要点击任何 AI、封面、排期或发送按钮,也不会触发真实抖音/B站投稿。

## 2026-08-26 片段审核累计统计检查

1. 打开 `http://127.0.0.1:8001/clips`,顶部应只显示“累计审核任务、已通过视频、已完成任务”三张卡片,不再出现待 AI、待检查、可生成切片和异常任务统计。
Expand Down
47 changes: 46 additions & 1 deletion app/static/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -5309,7 +5309,29 @@ td a,
flex-wrap: wrap;
}

.publish-content-header [data-backfill-covers][data-loading="true"] {
.publish-content-maintenance {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
margin-bottom: 16px;
padding: 12px 14px;
border: 1px solid rgba(126, 151, 187, 0.18);
border-radius: 14px;
background: rgba(247, 250, 255, 0.78);
}

.publish-content-maintenance > div:first-child {
display: grid;
gap: 3px;
}

.publish-content-maintenance small,
.publish-copy-toolbar small {
color: var(--muted);
}

.publish-content-maintenance [data-backfill-covers][data-loading="true"] {
cursor: wait;
opacity: 0.72;
}
Expand Down Expand Up @@ -5414,6 +5436,23 @@ td a,
flex-wrap: wrap;
gap: 10px;
}

.publish-copy-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
margin-bottom: 12px;
padding: 10px 12px;
border-radius: 12px;
background: rgba(238, 245, 255, 0.7);
}

.publish-copy-toolbar > div {
display: grid;
gap: 2px;
}

.publish-schedule-badge {
display: inline-flex;
align-items: center;
Expand Down Expand Up @@ -5805,6 +5844,12 @@ td a,
}

@media (max-width: 760px) {
.publish-content-maintenance,
.publish-copy-toolbar {
align-items: stretch;
flex-direction: column;
}
.publish-copy-toolbar .secondary-button { width: 100%; }
.publish-content-header-actions {
width: 100%;
justify-content: stretch;
Expand Down
33 changes: 8 additions & 25 deletions app/static/js/publish-center.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ if (publishCenterRoot) {
backfillCoversButton.disabled = loading || count === 0;
backfillCoversButton.textContent = loading
? `正在补充${platformLabel()} ${count} 条封面…`
: `一键补充${platformLabel()}缺失封面${count ? `(${count})` : ""}`;
: `补齐${platformLabel()}缺失封面${count ? `(${count})` : ""}`;
}

function beijingDatetimeValue(timestamp) {
Expand Down Expand Up @@ -1128,6 +1128,8 @@ if (publishCenterRoot) {
panel.hidden = !active;
panel.classList.toggle("active", active);
});
const batchAiButton = document.querySelector("[data-batch-ai]");
if (batchAiButton) batchAiButton.hidden = tab !== "content";
if (tab === "history") void refreshHistory({ calendar: true, records: true });
}

Expand Down Expand Up @@ -1400,24 +1402,6 @@ if (publishCenterRoot) {
}
}

async function upgradePendingDouyinMetadata() {
try {
const data = await window.apiFetch("/api/publish/jobs/metadata/upgrade-pending-douyin", { method: "POST" });
(data.jobs || []).forEach((job) => {
const row = document.querySelector(
`[data-publish-row][data-section="content"][data-job-id="${CSS.escape(job.id || "")}"]`,
);
applyGeneratedMetadataToForm(row, job);
updateRowFromJob(job);
});
if (Number(data.upgraded_count || 0) > 0 || Number(data.failed_count || 0) > 0) {
showMessage(data.message || "抖音旧草稿文案升级完成。", data.failed_count ? "error" : "success");
}
} catch (error) {
showMessage(`旧草稿文案升级已中止:${error.message}`, "error");
}
}

async function refreshAccounts() {
try {
const data = await window.apiFetch("/api/publish/accounts");
Expand Down Expand Up @@ -1866,8 +1850,8 @@ if (publishCenterRoot) {
const data = await window.apiFetch(`/api/publish/jobs/${jobId}/metadata?use_ai=true`, { method: "POST" });
applyGeneratedMetadataToForm(row, data.job);
updateRowFromJob(data.job);
showMessage("AI 文案已重写并同步到最终发送字段。", "success");
} catch (error) { showMessage(`AI 重写失败:${error.message}`, "error"); }
showMessage("本条 AI 文案已重写并保存到最终发送字段。", "success");
} catch (error) { showMessage(`本条 AI 重写失败:${error.message}`, "error"); }
finally { metadataButton.disabled = false; }
return;
}
Expand Down Expand Up @@ -2052,7 +2036,7 @@ if (publishCenterRoot) {
}
}
showMessage(
`批量 AI 重写完成:成功 ${succeeded} 条,失败 ${failed} 条;失败项已保留原文。`,
`已选文案 AI 重写完成:成功 ${succeeded} 条,失败 ${failed} 条;失败项已保留原文。`,
failed ? "error" : "success",
);
});
Expand Down Expand Up @@ -2200,8 +2184,8 @@ if (publishCenterRoot) {
const button = event.currentTarget; button.disabled = true;
try {
const data = await window.apiFetch(`/api/publish/queue/refresh?use_ai=false&platform=${encodeURIComponent(activePlatform)}`, { method: "POST" });
showMessage(data.message || "缺失任务已补充,请稍后查看内容准备区。", "success");
} catch (error) { showMessage(`补充任务失败:${error.message}`, "error"); }
showMessage(data.message || "遗漏切片已同步,请在内容准备区核对。", "success");
} catch (error) { showMessage(`同步遗漏切片失败:${error.message}`, "error"); }
finally { button.disabled = false; }
});

Expand All @@ -2216,7 +2200,6 @@ if (publishCenterRoot) {
if (scheduleForm?.elements.start_at_local) scheduleForm.elements.start_at_local.value = beijingDatetimeValue(Date.now() + 10 * 60 * 1000);
document.querySelectorAll('[data-publish-row][data-section="schedule"], [data-publish-row][data-section="history"]').forEach((row) => applyRowReadiness(row));
updateSelectionUi(); applyHistoryFilter(); refreshScheduleViews(); updateBackfillCoversButton();
void upgradePendingDouyinMetadata();
if (focus?.dataset.taskId) {
const group = document.querySelector(
`[data-publish-task-group][data-task-id="${CSS.escape(focus.dataset.taskId)}"]`,
Expand Down
31 changes: 25 additions & 6 deletions app/templates/publish.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@
<button type="button" class="text-button danger" data-dismiss-job>移出内容准备</button>
</div>
</div>
<div class="publish-copy-toolbar">
<div>
<strong>发布文案</strong>
<small>AI 重写会立即生成并保存本条标题、话题和简介</small>
</div>
<button type="button" class="secondary-button" data-generate-metadata>AI 重写本条文案</button>
</div>
<div class="publish-content-grid">
<label>
<span class="publish-copy-label"><span>标题</span><small data-copy-count="title">{{ job.title|length }}/30</small></span>
Expand All @@ -59,7 +66,6 @@
</div>
<div class="button-row">
<button type="submit" class="primary-button">保存草稿</button>
<button type="button" class="secondary-button" data-generate-metadata>AI 重写文案</button>
<button type="button" class="secondary-button" data-generate-cover data-task-id="{{ job.task_id }}" data-clip-id="{{ job.output_clip_id }}" data-video-source="{{ job.video_source or 'original' }}">生成 / 更换封面帧</button>
<button type="button" class="secondary-button" data-add-to-plan>{{ "调整排期" if job.status == "SCHEDULED" else "加入发布计划" }}</button>
<span class="form-hint" data-editor-result></span>
Expand Down Expand Up @@ -145,7 +151,7 @@
></div>
<section class="page-heading send-heading">
<div><p class="eyebrow">Publish Center</p><h1>发送中心</h1><p>先准备内容,再按北京时间排期,最后在执行记录中确认平台结果。</p></div>
<div class="button-row"><button type="button" class="secondary-button" data-open-account-drawer>账号管理</button><button type="button" class="secondary-button" data-supplement-publish-jobs>补充缺失任务</button></div>
<div class="button-row"><button type="button" class="secondary-button" data-open-account-drawer>账号管理</button></div>
</section>

<section id="send-center-message" class="inline-alert tone-blue" hidden></section>
Expand Down Expand Up @@ -183,17 +189,30 @@
{% set initial_platform = "douyin" %}
{% set initial_missing_cover_count = missing_cover_counts.get(initial_platform, 0) %}
<div class="panel-header publish-content-header">
<div><h2>内容准备</h2><p>这里不展示历史错误;内容、平台和账号完整后再加入排期。</p></div>
<div><h2>内容准备</h2><p>逐条编辑文案,或勾选多条后批量重写;内容、平台和账号完整后再加入排期。</p></div>
</div>
<aside class="publish-content-maintenance" aria-label="内容同步与封面维护">
<div>
<p class="eyebrow">Content Maintenance</p>
<strong>内容同步与封面维护</strong>
<small>只补建遗漏草稿和默认封面,不调用 AI,也不修改已有文案。</small>
</div>
<div class="publish-content-header-actions">
<button
type="button"
class="secondary-button"
data-supplement-publish-jobs
title="扫描已完成切片,仅补建尚未进入发送中心的发布草稿"
>同步遗漏切片</button>
<button
type="button"
class="secondary-button"
data-backfill-covers
data-missing-count="{{ initial_missing_cover_count }}"
{% if not initial_missing_cover_count %}disabled{% endif %}
>一键补充抖音缺失封面{% if initial_missing_cover_count %}({{ initial_missing_cover_count }}){% endif %}</button>
>补齐抖音缺失封面{% if initial_missing_cover_count %}({{ initial_missing_cover_count }}){% endif %}</button>
</div>
</div>
</aside>
<div class="publish-content-list">
{% for group in publish_task_groups %}
<section class="publish-task-group" data-publish-task-group data-task-id="{{ group.task_id }}" data-expanded="{{ 'true' if loop.first else 'false' }}">
Expand Down Expand Up @@ -318,7 +337,7 @@ <h3 data-history-list-title>全部执行记录</h3>
<span class="status-pill tone-blue">仅限 <b data-selection-platform>抖音</b></span>
<select data-batch-account aria-label="批量账号">{{ account_options({}) }}</select>
<button type="button" class="secondary-button" data-apply-batch-target>批量设置当前平台账号</button>
<button type="button" class="secondary-button" data-batch-ai>批量 AI 重写文案</button>
<button type="button" class="secondary-button" data-batch-ai title="立即重写并保存当前勾选内容的标题、话题和简介">AI 重写已选文案</button>
<button type="button" class="primary-button" data-open-schedule-drawer>设置排期</button>
<button type="button" class="text-button" data-clear-selection>取消选择</button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion docs/TASK_FLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ output_clip 生成成功
└─ opencli_publish → 显式兼容开关 → PUBLISHED / FAILED / NEED_REVIEW
```

`platform` 只能是 `douyin` / `bilibili`;`publish_mode` 只能表示执行方式,禁止互相混用。发送中心的“补充缺失任务”只补缺,不覆盖已有任务的执行方式
`platform` 只能是 `douyin` / `bilibili`;`publish_mode` 只能表示执行方式,禁止互相混用。发送中心内容准备区的“同步遗漏切片”固定使用 `use_ai=false`,只补建尚未进入发送中心的草稿与默认封面,不覆盖已有任务的执行方式或文案。AI 文案仅由内容卡“AI 重写本条文案”或明确勾选后的“AI 重写已选文案”触发;页面加载、排期计划和执行记录不会调用 AI 重写

### 发送任务状态(publish_jobs.status)

Expand Down
13 changes: 11 additions & 2 deletions docs/UI_REFERENCE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# UI 参考说明

## 2026-08-26 更新:发送中心 AI 文案操作分层

- 页面顶栏只保留账号管理;原“补充缺失任务”改名为“同步遗漏切片”,与“补齐抖音缺失封面”共同放入内容准备维护区。
- 维护区使用独立浅色条明确说明:同步动作只补建遗漏草稿和默认封面,不调用 AI、不修改已有文案。
- 每条内容卡的 AI 按钮紧贴发布文案字段,命名为“AI 重写本条文案”;勾选内容后的固定操作栏使用“AI 重写已选文案”,范围一眼可见。
- 批量 AI 仅在内容准备标签页显示;排期计划只处理时间,执行记录只处理结果和人工复核。
- 页面打开和刷新不再静默执行旧草稿全局 AI 升级,AI 文案必须由用户明确点击触发。后端兼容接口保留,不改变历史数据或发布状态机。
- 窄屏下维护区与单条文案工具栏改为纵向排列,按钮使用整行宽度。

## 2026-08-26 更新:片段审核累计统计

- 片段审核工作台顶部只保留三项结果指标:“累计审核任务、已通过视频、已完成任务”,移除待 AI、待检查、可生成切片和异常任务等过程型统计。
Expand Down Expand Up @@ -80,8 +89,8 @@
- 发送中心全局平台卡只保留抖音;`platform=bilibili` 深链参数会被忽略。B站历史记录、API 和 Publisher 继续保留在后端,但当前前台不展示 B站任务或发送入口。
- 内容卡账号状态直接复用统一发送预检:唯一正常抖音账号自动选中并显示“内容完整”,多账号显示“缺少:发布账号”,登录失效显示“账号需登录”。
- 每个原始处理任务组头增加“全选本任务 / 取消全选”复选框;只作用于本组当前可操作的抖音视频,支持部分选择状态,任务组折叠时仍可使用。
- 标题输入限制 30 字并显示实时计数;标签提示 4~6 个、每个 2~3 字;简介限制 15~35 字并显示实时计数。单条和批量按钮统一命名为“AI 重写文案”,成功后立即刷新三个输入框。
- 页面首次打开会异步调用幂等旧草稿升级;成功项原地更新,失败项提示保留原文。页面仍保持 Apple 风格浅色卡片、玻璃感和响应式布局,不引入前端框架。
- 标题输入限制 30 字并显示实时计数;标签提示 4~6 个、每个 2~3 字;简介限制 15~35 字并显示实时计数。单条与批量入口分别命名为“AI 重写本条文案”和“AI 重写已选文案”,成功后立即刷新三个输入框。
- 旧草稿升级接口继续保留后台兼容,但页面不再自动触发;AI 文案只通过单条或明确勾选后的批量入口执行。页面仍保持 Apple 风格浅色卡片、玻璃感和响应式布局,不引入前端框架。

## v2.1 界面总览

Expand Down
Loading