mirror of
https://github.com/halfwaystudent/douyin-sparkflow.git
synced 2026-09-04 23:17:51 +08:00
299 lines
15 KiB
HTML
299 lines
15 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block nav_key %}send_console{% endblock %}
|
|
{% block page_key %}send-console{% endblock %}
|
|
{% block title %}发送控制台{% endblock %}
|
|
{% block page_title %}发送控制台{% endblock %}
|
|
{% block page_subtitle %}异常优先查看,健康账号默认收起{% endblock %}
|
|
|
|
{% block topbar_actions %}
|
|
{% set top_summary = ops.send_console.summary %}
|
|
<a class="button button-quiet" href="/">
|
|
<i data-lucide="arrow-left"></i><span>返回首页</span>
|
|
</a>
|
|
<form
|
|
method="post"
|
|
action="/ops/run-failed"
|
|
data-confirm-title="补发异常目标"
|
|
data-confirm="将补发 {{ top_summary.today_attention_targets }} 个待核验、失败或受账号异常影响的目标。确认继续吗?"
|
|
data-confirm-accept="开始补发"
|
|
data-confirm-tone="primary"
|
|
>
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button
|
|
class="button button-soft"
|
|
type="submit"
|
|
data-action-count-source="attention"
|
|
{% if ops.task_lock.running or top_summary.today_attention_targets == 0 %}disabled{% endif %}
|
|
>
|
|
<i data-lucide="refresh-cw"></i>
|
|
<span>补发异常 <b data-action-count>{{ top_summary.today_attention_targets }}</b></span>
|
|
</button>
|
|
</form>
|
|
<form
|
|
method="post"
|
|
action="/ops/run-unsent"
|
|
data-confirm-title="补发今日待发送目标"
|
|
data-confirm="将立即补发 {{ top_summary.today_pending_targets + top_summary.today_unprocessed_targets }} 个尚未强确认且仍可重试的目标。确认继续吗?"
|
|
data-confirm-accept="开始补发"
|
|
data-confirm-tone="primary"
|
|
>
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button
|
|
class="button button-primary"
|
|
type="submit"
|
|
data-action-count-source="pending"
|
|
{% if ops.task_lock.running or (top_summary.today_pending_targets + top_summary.today_unprocessed_targets) == 0 %}disabled{% endif %}
|
|
>
|
|
<i data-lucide="send"></i>
|
|
<span>补发待发送 <b data-action-count>{{ top_summary.today_pending_targets + top_summary.today_unprocessed_targets }}</b></span>
|
|
</button>
|
|
</form>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
{% set send_console = ops.send_console %}
|
|
{% set summary = send_console.summary %}
|
|
|
|
<div class="console-shell" data-overview-root>
|
|
<div
|
|
class="status-banner {% if ops.task_lock.running %}warning{% elif ops.task_lock.stale %}info{% else %}success{% endif %}"
|
|
data-task-banner
|
|
>
|
|
<strong>
|
|
<i data-lucide="{% if ops.task_lock.running %}loader-circle{% elif ops.task_lock.stale %}info{% else %}circle-check{% endif %}"></i>
|
|
<span data-task-text>
|
|
{% if ops.task_lock.running %}
|
|
发送任务运行中,已运行约 {{ ops.task_lock.ageSeconds }} 秒
|
|
{% elif ops.task_lock.stale %}
|
|
检测到过期任务锁,下次启动任务时会自动清理
|
|
{% else %}
|
|
当前没有发送任务运行
|
|
{% endif %}
|
|
</span>
|
|
</strong>
|
|
<span class="pill info" data-overview-live-state>实时</span>
|
|
</div>
|
|
|
|
<div class="status-banner info refresh-notice" data-refresh-notice>
|
|
<strong><i data-lucide="circle-check"></i><span>任务已结束,刷新后可查看最新目标级结果。</span></strong>
|
|
<button class="button button-quiet" type="button" data-refresh-page>
|
|
<i data-lucide="refresh-cw"></i><span>刷新详情</span>
|
|
</button>
|
|
</div>
|
|
|
|
<section class="metric-grid" aria-label="发送状态汇总">
|
|
<article class="metric-card info">
|
|
<div class="metric-label">总目标</div>
|
|
<div class="metric-value" data-overview-value="total">{{ summary.total_targets }}</div>
|
|
<div class="metric-help">{{ summary.enabled_accounts }} 个启用账号</div>
|
|
</article>
|
|
<article class="metric-card success">
|
|
<div class="metric-label">强确认成功</div>
|
|
<div class="metric-value" data-overview-value="confirmed">{{ summary.today_confirmed_targets }}</div>
|
|
<div class="metric-help">服务端回执或页面回显</div>
|
|
</article>
|
|
<article class="metric-card danger">
|
|
<div class="metric-label">需要处理</div>
|
|
<div class="metric-value" data-overview-value="attention">{{ summary.today_attention_targets }}</div>
|
|
<div class="metric-help">待核验、失败和账号阻断</div>
|
|
</article>
|
|
<article class="metric-card warning">
|
|
<div class="metric-label">待发送</div>
|
|
<div class="metric-value" data-overview-value="pending">
|
|
{{ summary.today_pending_targets + summary.today_unprocessed_targets }}
|
|
</div>
|
|
<div class="metric-help">等待调度或尚未处理</div>
|
|
</article>
|
|
</section>
|
|
|
|
<section class="dashboard-section">
|
|
<div class="section-header">
|
|
<div>
|
|
<h2 class="section-title">账号明细</h2>
|
|
<p class="muted compact">异常账号自动展开;成功、异常和待发送分开查看。</p>
|
|
</div>
|
|
<span class="pill">{{ send_console.nowDisplay }}</span>
|
|
</div>
|
|
|
|
<div class="account-console-list">
|
|
{% for account in send_console.accounts %}
|
|
{% set exception_count = account.unconfirmed_targets|length + account.failed_targets|length + account.account_blocked_targets|length %}
|
|
{% set success_count = account.confirmed_targets|length %}
|
|
{% set waiting_targets = account.pending_targets + account.unprocessed_targets %}
|
|
{% set default_segment = "exceptions" if exception_count else ("success" if success_count else "pending") %}
|
|
<details
|
|
class="account-console state-{{ account.state }}"
|
|
id="account-{{ account.unique_id }}"
|
|
data-segment-owner
|
|
data-account-overview="{{ account.unique_id }}"
|
|
{% if account.state in ["attention", "paused", "warning"] %}open{% endif %}
|
|
>
|
|
<summary>
|
|
<span class="account-ident">
|
|
<span class="account-avatar">{{ (account.username or "A")[:1] }}</span>
|
|
<span class="account-name">
|
|
<strong>{{ account.username }}</strong>
|
|
<small>{{ account.confirmed_targets|length }}/{{ account.total_targets }} 已强确认</small>
|
|
</span>
|
|
</span>
|
|
<span class="account-console-metrics">
|
|
<span class="status-chip success">已确认 <b data-account-confirmed>{{ success_count }}</b></span>
|
|
{% if exception_count %}<span class="status-chip danger">需处理 <b data-account-attention>{{ exception_count }}</b></span>{% endif %}
|
|
{% if waiting_targets|length %}<span class="status-chip warning">待发送 <b data-account-pending>{{ waiting_targets|length }}</b></span>{% endif %}
|
|
{% if account.account_paused %}<span class="status-chip danger">账号暂停</span>{% endif %}
|
|
</span>
|
|
<i class="summary-chevron" data-lucide="chevron-down"></i>
|
|
</summary>
|
|
|
|
<div class="account-console-body">
|
|
{% if account.warnings %}
|
|
<div class="warning-box">
|
|
{% for warning in account.warnings %}<div>{{ warning.message }}</div>{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if account.account_failure %}
|
|
<div class="danger-box">
|
|
<strong>账号级异常:{{ account.account_failure.category or "-" }}</strong>
|
|
<div>今日尝试 {{ account.account_failure.attemptCount or 0 }}/{{ account.account_failure_pause_after }} · {{ account.account_failure.reason or "-" }}</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="account-meta-strip">
|
|
<span>
|
|
好友索引:
|
|
{% if account.friend_index_meta.lastScanAt %}
|
|
{{ "完整" if account.friend_index_meta.lastScanComplete else "未完整" }},记录 {{ account.friend_index_count }} 个
|
|
{% else %}
|
|
尚未扫描
|
|
{% endif %}
|
|
</span>
|
|
<span>最近强确认:{{ account.last_confirmed_display or "-" }}</span>
|
|
</div>
|
|
|
|
<div class="segmented-control" data-segment-group role="tablist" aria-label="{{ account.username }} 发送状态">
|
|
<button class="segment-button {% if default_segment == 'exceptions' %}active{% endif %}" type="button" data-segment-target="exceptions">
|
|
<i data-lucide="triangle-alert"></i><span>异常 {{ exception_count }}</span>
|
|
</button>
|
|
<button class="segment-button {% if default_segment == 'success' %}active{% endif %}" type="button" data-segment-target="success">
|
|
<i data-lucide="circle-check"></i><span>成功 {{ success_count }}</span>
|
|
</button>
|
|
<button class="segment-button {% if default_segment == 'pending' %}active{% endif %}" type="button" data-segment-target="pending">
|
|
<i data-lucide="clock-3"></i><span>待发送 {{ waiting_targets|length }}</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div data-segment-panel="exceptions" {% if default_segment != "exceptions" %}hidden{% endif %}>
|
|
<div class="target-list">
|
|
{% for item in account.unconfirmed_targets %}
|
|
<article class="target-record">
|
|
<div class="target-record-main">
|
|
<strong>{{ item.target }}</strong>
|
|
<small>{{ item.categoryLabel }} · 已尝试 {{ item.attemptCount or 0 }} 次</small>
|
|
</div>
|
|
<div class="target-reason" title="{{ item.reason or item.confirmationDetail or '' }}">
|
|
{{ item.reason or item.confirmationDetail or "发送结果缺少强确认" }}
|
|
</div>
|
|
<span class="target-meta">{{ item.displayLastAttemptAt or item.displaySentAt or "-" }}</span>
|
|
<form method="post" action="/accounts/{{ account.unique_id }}/retry-target">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<input type="hidden" name="target" value="{{ item.target }}">
|
|
<button class="button button-soft" type="submit"><i data-lucide="refresh-cw"></i><span>重试</span></button>
|
|
</form>
|
|
</article>
|
|
{% endfor %}
|
|
|
|
{% for item in account.failed_targets %}
|
|
<article class="target-record">
|
|
<div class="target-record-main">
|
|
<strong>{{ item.target }}</strong>
|
|
<small>{{ item.categoryLabel }} · 已尝试 {{ item.attemptCount or 0 }} 次</small>
|
|
</div>
|
|
<div class="target-reason" title="{{ item.reason or '' }}">{{ item.reason or "未记录失败原因" }}</div>
|
|
<span class="target-meta">{{ item.displayLastAttemptAt or "-" }}</span>
|
|
<form method="post" action="/accounts/{{ account.unique_id }}/retry-target">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<input type="hidden" name="target" value="{{ item.target }}">
|
|
<button class="button button-soft" type="submit"><i data-lucide="refresh-cw"></i><span>重试</span></button>
|
|
</form>
|
|
</article>
|
|
{% endfor %}
|
|
|
|
{% for item in account.account_blocked_targets %}
|
|
<article class="target-record">
|
|
<div class="target-record-main">
|
|
<strong>{{ item.target }}</strong>
|
|
<small>受账号异常影响</small>
|
|
</div>
|
|
<div class="target-reason" title="{{ item.reason or '' }}">{{ item.reason or item.categoryLabel }}</div>
|
|
<span class="target-meta">{{ item.displayLastAttemptAt or "-" }}</span>
|
|
<form method="post" action="/accounts/{{ account.unique_id }}/retry-target">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<input type="hidden" name="target" value="{{ item.target }}">
|
|
<button class="button button-soft" type="submit"><i data-lucide="refresh-cw"></i><span>重试</span></button>
|
|
</form>
|
|
</article>
|
|
{% endfor %}
|
|
|
|
{% if exception_count == 0 %}
|
|
<div class="empty-state">该账号今天没有待核验、失败或受阻断的目标。</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div data-segment-panel="success" {% if default_segment != "success" %}hidden{% endif %}>
|
|
<div class="target-list">
|
|
{% for item in account.confirmed_targets %}
|
|
<article class="target-record">
|
|
<div class="target-record-main">
|
|
<strong>{{ item.target }}</strong>
|
|
<small>{{ item.confirmationLabel }}</small>
|
|
</div>
|
|
<div class="target-message" title="{{ item.message or '' }}">{{ item.message or "未保存消息正文" }}</div>
|
|
<span class="target-meta" title="{{ item.sentAt or '' }}">{{ item.displaySentAt or "-" }}</span>
|
|
<form
|
|
method="post"
|
|
action="/accounts/{{ account.unique_id }}/mark-target-unconfirmed"
|
|
data-confirm-title="标记为待核验"
|
|
data-confirm="将把“{{ item.target }}”的今日强确认记录改为待核验并加入补发队列。确认继续吗?"
|
|
data-confirm-accept="标记待核验"
|
|
>
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<input type="hidden" name="target" value="{{ item.target }}">
|
|
<button class="button button-quiet" type="submit"><i data-lucide="flag"></i><span>待核验</span></button>
|
|
</form>
|
|
</article>
|
|
{% else %}
|
|
<div class="empty-state">该账号今天还没有强确认记录。</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<div data-segment-panel="pending" {% if default_segment != "pending" %}hidden{% endif %}>
|
|
<div class="target-list">
|
|
{% for item in waiting_targets %}
|
|
<article class="target-record">
|
|
<div class="target-record-main">
|
|
<strong>{{ item.target }}</strong>
|
|
<small>{% if item.status == "pending" %}等待计划时间{% else %}尚未处理{% endif %}</small>
|
|
</div>
|
|
<div class="target-message">好友索引:{% if item.friendIndex.seen %}已记录{% else %}尚未记录{% endif %}</div>
|
|
<span class="target-meta">{{ item.displayScheduledAt or "-" }}</span>
|
|
<span class="status-chip warning">待发送</span>
|
|
</article>
|
|
{% else %}
|
|
<div class="empty-state">该账号今天没有待发送目标。</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
{% else %}
|
|
<div class="empty-state">当前没有启用账号。</div>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
</div>
|
|
{% endblock %}
|