Sync remote sparkflow changes and add send console

This commit is contained in:
Rixuan Shao
2026-05-17 22:48:30 +08:00
parent de4f3aeb74
commit 9e3cc85215
7 changed files with 967 additions and 83 deletions
@@ -0,0 +1,185 @@
{% extends "base.html" %}
{% block title %}发送控制台{% endblock %}
{% block page_title %}发送控制台{% endblock %}
{% block content %}
{% set send_console = ops.send_console %}
<div class="stack">
<section class="panel">
<div class="section-title-row">
<div>
<h2>今日发送总览</h2>
<p class="muted compact">当前时间:{{ send_console.now }}。本页按账号展示今天已成功、失败待补发、待发送和未处理目标。</p>
</div>
<div class="button-row two" style="width: 320px;">
<a class="ghost-button" href="/">返回首页</a>
<form method="post" action="/ops/run-now">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<button type="submit">补发全部失败项</button>
</form>
</div>
</div>
<div class="stats-grid">
<article class="stat-card">
<div class="stat-meta">
<span class="stat-label">启用账号</span>
<strong class="stat-value">{{ send_console.summary.enabled_accounts }}</strong>
</div>
<div class="stat-icon blue">A</div>
</article>
<article class="stat-card">
<div class="stat-meta">
<span class="stat-label">今日成功目标</span>
<strong class="stat-value">{{ send_console.summary.today_sent_targets }}</strong>
</div>
<div class="stat-icon green">OK</div>
</article>
<article class="stat-card">
<div class="stat-meta">
<span class="stat-label">失败待补发</span>
<strong class="stat-value">{{ send_console.summary.today_failed_targets }}</strong>
</div>
<div class="stat-icon soft">!</div>
</article>
<article class="stat-card">
<div class="stat-meta">
<span class="stat-label">待发送 / 未处理</span>
<strong class="stat-value">{{ send_console.summary.today_pending_targets + send_console.summary.today_unprocessed_targets }}</strong>
</div>
<div class="stat-icon soft">...</div>
</article>
</div>
</section>
{% for account in send_console.accounts %}
<section class="panel">
<div class="section-title-row">
<div>
<h2>{{ account.username }}</h2>
<p class="muted compact">unique_id: {{ account.unique_id }}</p>
</div>
<div class="status-line">
<span class="pill soft">成功 {{ account.sent_targets|length }}</span>
<span class="pill {% if account.failed_targets %}danger{% else %}soft{% endif %}">失败 {{ account.failed_targets|length }}</span>
<span class="pill">待发送 {{ account.pending_targets|length }}</span>
<span class="pill warning">未处理 {{ account.unprocessed_targets|length }}</span>
</div>
</div>
<div class="layout-grid" style="grid-template-columns: repeat(2, minmax(0, 1fr));">
<section class="panel" style="box-shadow:none; margin:0; padding:14px;">
<h3>今日已成功</h3>
<div class="table-shell" style="margin-top: 12px;">
<table>
<thead>
<tr>
<th>目标</th>
<th>消息</th>
<th>sentAt</th>
</tr>
</thead>
<tbody>
{% for item in account.sent_targets %}
<tr>
<td>{{ item.target }}</td>
<td>{{ item.message or "-" }}</td>
<td>{{ item.sentAt or "-" }}</td>
</tr>
{% else %}
<tr><td colspan="3">暂无今日成功目标。</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
<section class="panel" style="box-shadow:none; margin:0; padding:14px;">
<h3>失败待补发</h3>
<div class="table-shell" style="margin-top: 12px;">
<table>
<thead>
<tr>
<th>目标</th>
<th>失败分类</th>
<th>失败原因</th>
<th>次数</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for item in account.failed_targets %}
<tr>
<td>{{ item.target }}</td>
<td>{{ item.category or "-" }}</td>
<td>{{ item.reason or "-" }}</td>
<td>{{ item.attemptCount or 0 }}</td>
<td>
<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 type="submit" class="ghost-button">重试此目标</button>
</form>
</td>
</tr>
{% else %}
<tr><td colspan="5">暂无失败待补发目标。</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
<section class="panel" style="box-shadow:none; margin:0; padding:14px;">
<h3>今日待发送</h3>
<div class="table-shell" style="margin-top: 12px;">
<table>
<thead>
<tr>
<th>目标</th>
<th>scheduledAt</th>
</tr>
</thead>
<tbody>
{% for item in account.pending_targets %}
<tr>
<td>{{ item.target }}</td>
<td>{{ item.scheduledAt or "-" }}</td>
</tr>
{% else %}
<tr><td colspan="2">暂无今日待发送目标。</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
<section class="panel" style="box-shadow:none; margin:0; padding:14px;">
<h3>今日未处理</h3>
<div class="table-shell" style="margin-top: 12px;">
<table>
<thead>
<tr>
<th>目标</th>
<th>scheduledAt</th>
</tr>
</thead>
<tbody>
{% for item in account.unprocessed_targets %}
<tr>
<td>{{ item.target }}</td>
<td>{{ item.scheduledAt or "-" }}</td>
</tr>
{% else %}
<tr><td colspan="2">暂无未处理目标。</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
</div>
</section>
{% endfor %}
</div>
{% endblock %}