Skip to content

Commit b75b5ef

Browse files
committed
「我的站点」不再计入上游聚合统计 (v1.5.1)
自有站的余额是 root 账号额度,混进聚合会污染数字。标记 isOwn 的站点: - 总览五张统计卡(总剩余余额/今日总消耗/日均估算/低余额/查询异常)全部排除 - 总余额趋势图、日均消耗对比图排除;图表副标题注明「不含我的站点」 - 用量统计页「全部上游站点」聚合排除,下拉仍可单独选看(标注「我的站」) - 站点列表行保留展示,名称旁加「我的站」标签
1 parent a0beb23 commit b75b5ef

3 files changed

Lines changed: 25 additions & 19 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "relay-monitor",
3-
"version": "1.5.0",
3+
"version": "1.5.1",
44
"private": true,
55
"description": "监控 sub2api / new-api 中转站余额的网页面板",
66
"type": "module",

public/app.js

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ function stationRow(s) {
224224
<div class="st-row" data-id="${s.id}">
225225
<div class="st-plate">${PLATE[s.type] || "?"}</div>
226226
<div class="st-main" data-act="trend" title="查看余额趋势">
227-
<div class="st-name">${esc(s.name)}${s.demo ? '<span class="demo-tag">演示</span>' : ""} ${statusPill(st)}</div>
227+
<div class="st-name">${esc(s.name)}${s.isOwn ? '<span class="demo-tag">我的站</span>' : ""}${s.demo ? '<span class="demo-tag">演示</span>' : ""} ${statusPill(st)}</div>
228228
<div class="st-meta">${meta}</div>
229229
${bar}
230230
${predict}
@@ -247,21 +247,23 @@ const HDR_BTNS = `
247247

248248
function renderDashboard() {
249249
const list = state.stations;
250-
const okList = list.filter((s) => s.balance?.ok);
251-
const anyRate = list.some((s) => rateOf(s) !== 1);
250+
// 聚合统计只算上游:标记「我的站点」的余额是自家 root 账号额度,混进来会污染数字
251+
const ups = list.filter((s) => !s.isOwn);
252+
const okList = ups.filter((s) => s.balance?.ok);
253+
const anyRate = ups.some((s) => rateOf(s) !== 1);
252254
const totalRemaining = okList.reduce((a, s) => a + s.balance.remaining, 0);
253255
const totalRemainingCny = okList.reduce((a, s) => a + s.balance.remaining * rateOf(s), 0);
254256
const totalUsedCny = okList.reduce((a, s) => a + s.balance.used * rateOf(s), 0);
255-
const totalBurnCny = list.reduce((a, s) => a + (s.prediction?.burnPerDay || 0) * rateOf(s), 0);
256-
const todayTotalCny = list.reduce((a, s) => a + (s.todayUsed || 0) * rateOf(s), 0);
257+
const totalBurnCny = ups.reduce((a, s) => a + (s.prediction?.burnPerDay || 0) * rateOf(s), 0);
258+
const todayTotalCny = ups.reduce((a, s) => a + (s.todayUsed || 0) * rateOf(s), 0);
257259
// 任一站点的今日消耗是历史推算值时,合计也只能算约数
258-
const todayApprox = list.some((s) => (s.todayUsed || 0) > 0 && s.todayIsEstimate);
259-
const lowCount = list.filter((s) => ["warn", "danger"].includes(statusOf(s))).length;
260-
const errCount = list.filter((s) => statusOf(s) === "error").length;
260+
const todayApprox = ups.some((s) => (s.todayUsed || 0) > 0 && s.todayIsEstimate);
261+
const lowCount = ups.filter((s) => ["warn", "danger"].includes(statusOf(s))).length;
262+
const errCount = ups.filter((s) => statusOf(s) === "error").length;
261263

262264
// 今日 tokens / 请求数:只有 sub2api 站点能提供,有数据才显示
263-
const tokList = list.filter((s) => s.todayTokens != null);
264-
const reqList = list.filter((s) => s.todayRequests != null);
265+
const tokList = ups.filter((s) => s.todayTokens != null);
266+
const reqList = ups.filter((s) => s.todayRequests != null);
265267
const subBits = [];
266268
if (tokList.length) subBits.push(`${fmtTokens(tokList.reduce((a, s) => a + s.todayTokens, 0))} tokens`);
267269
if (reqList.length) subBits.push(`${reqList.reduce((a, s) => a + s.todayRequests, 0).toLocaleString("en-US")} 次请求`);
@@ -283,7 +285,7 @@ function renderDashboard() {
283285
<div class="charts-grid">
284286
<div class="panel chart-card">
285287
<div class="chart-card-head">
286-
<div><h3>总余额趋势</h3><div class="chart-sub">全部中转站剩余余额合计(按充值汇率折算 ¥)</div></div>
288+
<div><h3>总余额趋势</h3><div class="chart-sub">上游站点剩余余额合计(按充值汇率折算 ¥,不含我的站点)</div></div>
287289
<div class="seg" id="ovRange">${RANGES.map(([h, l]) =>
288290
`<button data-hours="${h}" class="${state.trendHours === h ? "active" : ""}">${l}</button>`).join("")}</div>
289291
</div>
@@ -302,7 +304,7 @@ function renderDashboard() {
302304
: emptyState();
303305
$("#content").innerHTML = stats + charts + body;
304306
if (list.length) {
305-
drawBurnBars($("#burnChart"), list);
307+
drawBurnBars($("#burnChart"), ups);
306308
mountOverviewChart();
307309
}
308310
}
@@ -326,11 +328,12 @@ async function mountOverviewChart() {
326328
}
327329
}
328330

329-
// 聚合全部站点:时间并集 + 各站前向填充求和(按各站充值汇率折算成 ¥)
331+
// 聚合上游站点:时间并集 + 各站前向填充求和(按各站充值汇率折算成 ¥,不含我的站点
330332
function drawTotalChart(wrap, ov) {
331333
const rateMap = new Map(state.stations.map((s) => [s.id, rateOf(s)]));
334+
const ownIds = new Set(state.stations.filter((s) => s.isOwn).map((s) => s.id));
332335
const seriesList = ov.series
333-
.filter((s) => s.points && s.points.length)
336+
.filter((s) => s.points && s.points.length && !ownIds.has(s.id))
334337
.map((s) => ({ ...s, rate: rateMap.get(s.id) ?? 1 }));
335338
const times = [];
336339
for (const s of seriesList) for (const p of s.points) times.push(p[0]);
@@ -592,8 +595,8 @@ function renderUsage() {
592595
<div class="seg" id="usageRange">${USAGE_RANGES.map(([v, l]) =>
593596
`<button data-range="${v}" class="${state.usageRange === v ? "active" : ""}">${l}</button>`).join("")}</div>
594597
<select class="select usage-station" id="usageStation">
595-
<option value="all">全部中转站</option>
596-
${state.stations.map((s) => `<option value="${s.id}"${state.usageStation === s.id ? " selected" : ""}>${esc(s.name)}</option>`).join("")}
598+
<option value="all">全部上游站点</option>
599+
${state.stations.map((s) => `<option value="${s.id}"${state.usageStation === s.id ? " selected" : ""}>${esc(s.name)}${s.isOwn ? "(我的站)" : ""}</option>`).join("")}
597600
</select>
598601
</div>
599602
<div id="usageBody"><div class="chart-empty">加载中…</div></div>`;
@@ -623,7 +626,10 @@ function renderUsageBody() {
623626
const el = $("#usageBody");
624627
if (!el || !state.usageData) return;
625628
const data = state.usageData;
626-
const sts = state.usageStation === "all" ? data.stations : data.stations.filter((s) => s.id === state.usageStation);
629+
// 「全部」只聚合上游;我的站点仍可在下拉里单独选看
630+
const sts = state.usageStation === "all"
631+
? data.stations.filter((s) => !s.isOwn)
632+
: data.stations.filter((s) => s.id === state.usageStation);
627633
const okSts = sts.filter((s) => s.ok);
628634
const errSts = sts.filter((s) => !s.ok);
629635

server.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ app.get("/api/usage", async (req, res) => {
444444
const endMs = now;
445445

446446
const stations = await Promise.all(store.list().map(async (s) => {
447-
const meta = { id: s.id, name: s.name, type: s.type, cnyPerUsd: s.cnyPerUsd ?? null };
447+
const meta = { id: s.id, name: s.name, type: s.type, cnyPerUsd: s.cnyPerUsd ?? null, isOwn: !!s.isOwn };
448448
try {
449449
const u = await queryStationUsage(s, {
450450
startMs, endMs, granularity, tz,

0 commit comments

Comments
 (0)