修改企业微信推送

This commit is contained in:
dekun
2026-05-26 10:45:27 +08:00
parent 10dc45198c
commit 74bd241579
3 changed files with 74 additions and 132 deletions
+49 -109
View File
@@ -13,7 +13,7 @@ const tableState = {
const PERIOD_LS_PREFIX = "ba_period_";
const PERIOD_TTL_MS = 4 * 60 * 60 * 1000;
let statsData = null;
let wecomPreviewData = null;
let currentView = "today";
const SORT_KEYS = {
rank: (r) => Number(r.rank) || 0,
@@ -262,58 +262,25 @@ function downloadCsv(name, header, rows, periodStart) {
a.click();
}
function renderStatsTable() {
const wrap = document.getElementById("stats-table-wrap");
if (!wrap || !statsData) return;
const items = statsData.items || [];
document.getElementById("stats-criteria").textContent = statsData.criteria || "";
document.getElementById("stats-desc").textContent = statsData.message || "";
const sum = statsData.summary;
document.getElementById("stats-summary").textContent = statsData.ok
? `符合条件 ${statsData.count} 个 · 三日交集 ${sum?.intersection ?? 0}`
: "数据未就绪";
if (!statsData.ok) {
wrap.innerHTML = `<p class="loading">${statsData.message || "请等待三个周期数据就绪"}</p>`;
return;
function updateStatsHeader(payload) {
const criteria = document.getElementById("stats-criteria");
const summary = document.getElementById("stats-summary");
const desc = document.getElementById("stats-desc");
if (!payload) return;
if (criteria) {
criteria.textContent = payload.ok
? payload.period_label || "—"
: "数据未就绪";
}
if (!items.length) {
wrap.innerHTML = '<p class="loading">暂无符合条件的合约</p>';
return;
if (summary) {
summary.textContent = payload.ok
? `${payload.count} 个币种` +
(payload.parts > 1 ? ` · 企微将分 ${payload.parts} 条发送` : "")
: "";
}
if (desc && payload.message && !payload.ok) {
desc.textContent = payload.message;
}
wrap.innerHTML = `
<table data-table="stats" class="stats-table">
<thead><tr>
<th>合约</th>
<th>今日排名</th><th>今日涨跌</th><th>今日成交额</th>
<th>昨日排名</th><th>昨日涨跌</th><th>昨日成交额</th>
<th>前日排名</th><th>前日涨跌</th><th>前日成交额</th>
<th>三日总成交额</th>
</tr></thead>
<tbody id="stats-body"></tbody>
</table>`;
const body = document.getElementById("stats-body");
body.innerHTML = items
.map((row) => {
const d = (p) => row[p] || {};
const cell = (p, f) => {
const x = d(p);
const pct = x.price_change_pct ?? 0;
return `<td class="${f === "pct" ? pctClass(pct) : ""}">${f === "pct" ? x.price_change_pct_fmt || "—" : f === "rank" ? x.rank ?? "—" : x.quote_volume_fmt || "—"}</td>`;
};
return `<tr class="row-highlight stats-row" data-symbol="${row.symbol}">
<td><strong>${row.symbol}</strong></td>
${cell("today", "rank")}${cell("today", "pct")}${cell("today", "vol")}
${cell("yesterday", "rank")}${cell("yesterday", "pct")}${cell("yesterday", "vol")}
${cell("daybefore", "rank")}${cell("daybefore", "pct")}${cell("daybefore", "vol")}
<td class="stats-total-vol">${formatVol(row.total_quote_volume)}</td>
</tr>`;
})
.join("");
}
function escapeHtml(s) {
@@ -324,59 +291,43 @@ function escapeHtml(s) {
.replace(/\n/g, "<br>");
}
function formatVol(v) {
if (v >= 1e8) return (v / 1e8).toFixed(2) + "亿";
if (v >= 1e4) return (v / 1e4).toFixed(2) + "万";
return String(Math.round(v));
}
function renderWecomDayRow(label, row) {
if (!row?.rank) {
return `<div class="wecom-day muted"><span class="wecom-day-label">${label}</span>—</div>`;
}
const pct = row.price_change_pct ?? 0;
return `<div class="wecom-day">
<span class="wecom-day-label">${label}</span>
<span class="wecom-rank-num">#${row.rank}</span>
<span class="wecom-vol">${row.quote_volume_fmt || row.quote_volume}</span>
<span class="${pctClass(pct)}">${row.price_change_pct_fmt || pct.toFixed(2) + "%"}</span>
<span class="wecom-fr">${row.funding_rate_fmt || "—"}</span>
</div>`;
}
function renderWecomPreview(payload) {
const panel = document.getElementById("wecom-preview-panel");
const cards = document.getElementById("wecom-preview-cards");
const meta = document.getElementById("wecom-preview-meta");
if (!panel || !cards) return;
panel.classList.remove("hidden");
if (!cards) return;
wecomPreviewData = payload;
updateStatsHeader(payload);
if (!payload?.ok) {
if (meta) meta.textContent = "未就绪";
if (meta) meta.textContent = "";
cards.innerHTML = `<p class="error">${escapeHtml(payload?.message || "无法生成预览")}</p>`;
return;
}
if (meta) {
const partsHint =
payload.parts > 1 ? ` · 企微分 ${payload.parts} 条发送` : "";
meta.textContent = `${payload.period_label || "—"} · ${payload.count} 个币种${partsHint}`;
meta.textContent =
`昨日周期 ${payload.period_label || "—"} · 昨/今/前 = 排名+涨跌幅` +
(payload.parts > 1 ? ` · 超长将分 ${payload.parts} 条企微消息` : "");
}
if (!payload.items?.length) {
cards.innerHTML = '<p class="loading">暂无三日交集币种</p>';
return;
}
cards.innerHTML = payload.items
.map(
(it) => `
.map((it) => {
const line = (label, row) => {
if (!row?.rank) return `${label}`;
const pct = row.price_change_pct ?? 0;
const pctStr = row.price_change_pct_fmt || `${pct.toFixed(2)}%`;
return `${label}<strong>#${row.rank}</strong><span class="${pctClass(pct)}">${pctStr}</span>`;
};
return `
<article class="wecom-card">
<header class="wecom-card-head">
<span class="wecom-seq">${it.rank}</span>
<strong class="wecom-symbol">${it.symbol}</strong>
</header>
${renderWecomDayRow("昨", it.yesterday)}
${renderWecomDayRow("今日", it.today)}
${renderWecomDayRow("前日", it.daybefore)}
</article>`
)
<p class="wecom-compact-line">${line("昨 ", it.yesterday)} ${line("今 ", it.today)} ${line("前 ", it.daybefore)}</p>
</article>`;
})
.join("");
}
@@ -417,35 +368,25 @@ async function testWecomPush() {
}
async function loadStats() {
document.getElementById("stats-table-wrap").innerHTML =
'<p class="loading">统计中…</p>';
try {
const res = await fetch("/api/stats/three-day");
statsData = await res.json();
renderStatsTable();
await loadWecomPreview();
} catch (e) {
document.getElementById("stats-table-wrap").innerHTML = `<p class="error">${e.message}</p>`;
}
await loadWecomPreview();
}
function exportStatsCsv() {
if (!statsData?.items?.length) return alert("暂无数据");
const items = wecomPreviewData?.items;
if (!items?.length) return alert("暂无数据");
const header = [
"合约",
"今日排名", "今日涨跌%", "今日成交额",
"昨日排名", "昨日涨跌%", "昨日成交额",
"前日排名", "前日涨跌%", "前日成交额",
"三日总成交额",
"今日排名", "今日涨跌%",
"昨日排名", "昨日涨跌%",
"前日排名", "前日涨跌%",
];
const rows = statsData.items.map((r) => [
const rows = items.map((r) => [
r.symbol,
r.today?.rank, r.today?.price_change_pct, r.today?.quote_volume,
r.yesterday?.rank, r.yesterday?.price_change_pct, r.yesterday?.quote_volume,
r.daybefore?.rank, r.daybefore?.price_change_pct, r.daybefore?.quote_volume,
r.total_quote_volume,
r.today?.rank, r.today?.price_change_pct,
r.yesterday?.rank, r.yesterday?.price_change_pct,
r.daybefore?.rank, r.daybefore?.price_change_pct,
]);
downloadCsv("binance-three-day-stats", header, rows, "stats");
downloadCsv("binance-wecom-push", header, rows, wecomPreviewData?.period_label);
}
function switchView(view) {
@@ -458,7 +399,7 @@ function switchView(view) {
});
if (view === "stats") {
if (!statsData) loadStats();
if (!wecomPreviewData) loadStats();
return;
}
@@ -492,11 +433,10 @@ document.getElementById("btn-refresh").addEventListener("click", async () => {
});
document.getElementById("btn-reload-stats")?.addEventListener("click", () => {
statsData = null;
wecomPreviewData = null;
loadStats();
});
document.getElementById("btn-export-stats")?.addEventListener("click", exportStatsCsv);
document.getElementById("btn-push-preview")?.addEventListener("click", loadWecomPreview);
document.getElementById("btn-push-test")?.addEventListener("click", testWecomPush);
loadPeriod("today");