Track successful sync runs
This commit is contained in:
+35
-8
@@ -132,7 +132,6 @@
|
||||
</section>
|
||||
|
||||
<section class="metrics" id="metrics" aria-label="数据概览" hidden>
|
||||
<div class="metric"><span class="metric-label">数据更新时间</span><strong class="metric-value" id="updated-at">—</strong></div>
|
||||
<div class="metric"><span class="metric-label">记录数</span><strong class="metric-value" id="record-count">—</strong></div>
|
||||
<div class="metric"><span class="metric-label">LinkName 数</span><strong class="metric-value" id="link-count">—</strong></div>
|
||||
<div class="metric"><span class="metric-label">Total Commission</span><strong class="metric-value" id="total-commission">—</strong></div>
|
||||
@@ -167,6 +166,7 @@
|
||||
'use strict';
|
||||
|
||||
var DATA_URL = 'data/merged.html';
|
||||
var SYNC_LOG_URL = 'data/sync-log.json';
|
||||
var RATES_URL = 'https://open.er-api.com/v6/latest/USD';
|
||||
var REQUIRED_FIELDS = ['Date', 'Commission', 'Order', 'GMV', 'Currency', 'LinkName'];
|
||||
var charts = [];
|
||||
@@ -190,7 +190,6 @@
|
||||
rangeButtons: Array.from(document.querySelectorAll('[data-range]')),
|
||||
rateStatus: document.getElementById('rate-status'),
|
||||
metrics: document.getElementById('metrics'),
|
||||
updatedAt: document.getElementById('updated-at'),
|
||||
recordCount: document.getElementById('record-count'),
|
||||
linkCount: document.getElementById('link-count'),
|
||||
totalCommission: document.getElementById('total-commission'),
|
||||
@@ -205,7 +204,16 @@
|
||||
linkCommission: document.getElementById('link-commission-chart'),
|
||||
linkGmv: document.getElementById('link-gmv-chart')
|
||||
};
|
||||
var state = { rawRecords: [], sourceCurrencies: [], selectedCurrency: '', latestDate: null, dateOnlyRows: 0, invalidCurrencyRows: 0 };
|
||||
var state = {
|
||||
rawRecords: [],
|
||||
sourceCurrencies: [],
|
||||
selectedCurrency: '',
|
||||
latestDate: null,
|
||||
dateOnlyRows: 0,
|
||||
invalidCurrencyRows: 0,
|
||||
syncLog: null,
|
||||
syncLogUnavailable: false
|
||||
};
|
||||
|
||||
function setStatus(message, status) {
|
||||
elements.status.textContent = message;
|
||||
@@ -277,6 +285,8 @@
|
||||
|
||||
function updateNotice() {
|
||||
var messages = [];
|
||||
if (state.syncLog) messages.push('最后成功运行:' + state.syncLog.displayTime);
|
||||
if (state.syncLogUnavailable) messages.push('运行日志暂不可用。');
|
||||
if (state.dateOnlyRows) messages.push('已过滤 ' + formatNumber(state.dateOnlyRows) + ' 条只有 Date、Commission 和 GMV 均为空的日期行。');
|
||||
if (state.invalidCurrencyRows) messages.push('已排除 ' + formatNumber(state.invalidCurrencyRows) + ' 条币种为空、Unknown 或格式无效且包含金额的记录,请检查原始数据。');
|
||||
if (state.sourceCurrencies.length > 1) messages.push('检测到多个已知源币种(' + state.sourceCurrencies.join('、') + '),已统一换算为 USD,不能切换到 IDR。');
|
||||
@@ -284,6 +294,15 @@
|
||||
elements.warning.hidden = messages.length === 0;
|
||||
}
|
||||
|
||||
function parseSyncLog(payload) {
|
||||
if (!payload || typeof payload.last_success_at !== 'string' || !payload.last_success_at.trim() || Number.isNaN(Date.parse(payload.last_success_at))) {
|
||||
throw new Error('运行日志缺少有效的 last_success_at。');
|
||||
}
|
||||
var shanghaiTime = typeof payload.last_success_at_shanghai === 'string' ? payload.last_success_at_shanghai.trim() : '';
|
||||
var displayTime = shanghaiTime && !Number.isNaN(Date.parse(shanghaiTime)) ? shanghaiTime : payload.last_success_at;
|
||||
return { displayTime: displayTime };
|
||||
}
|
||||
|
||||
function setRateStatus(message) { elements.rateStatus.textContent = message; }
|
||||
|
||||
function updateCurrencyOptions() {
|
||||
@@ -415,8 +434,7 @@
|
||||
return total + (record.gmv === null ? 0 : record.gmv);
|
||||
}, 0);
|
||||
elements.title.textContent = 'TTSHOP 销售看板 · ' + state.selectedCurrency;
|
||||
elements.subtitle.textContent = 'Commission 与 GMV 趋势(当前加载时间:' + dateFormatter.format(new Date()) + ')';
|
||||
elements.updatedAt.textContent = dateFormatter.format(new Date());
|
||||
elements.subtitle.textContent = 'Commission 与 GMV 趋势(数据来自最近一次成功同步)';
|
||||
elements.recordCount.textContent = formatNumber(records.length);
|
||||
elements.linkCount.textContent = formatNumber(links.size);
|
||||
elements.totalCommission.textContent = state.selectedCurrency + ' ' + formatNumber(totalCommission);
|
||||
@@ -551,11 +569,20 @@
|
||||
}
|
||||
|
||||
function loadData() {
|
||||
fetch(DATA_URL, { cache: 'no-store' }).then(function (response) {
|
||||
var dataPromise = fetch(DATA_URL, { cache: 'no-store' }).then(function (response) {
|
||||
if (!response.ok) throw new Error('无法读取 data/merged.html(HTTP ' + response.status + ')。');
|
||||
return response.text();
|
||||
}).then(function (htmlText) {
|
||||
var result = readTable(htmlText);
|
||||
});
|
||||
var syncLogPromise = fetch(SYNC_LOG_URL, { cache: 'no-store' }).then(function (response) {
|
||||
if (!response.ok) throw new Error('无法读取 data/sync-log.json(HTTP ' + response.status + ')。');
|
||||
return response.json();
|
||||
}).then(parseSyncLog).catch(function () {
|
||||
state.syncLogUnavailable = true;
|
||||
return null;
|
||||
});
|
||||
Promise.all([dataPromise, syncLogPromise]).then(function (results) {
|
||||
var result = readTable(results[0]);
|
||||
state.syncLog = results[1];
|
||||
state.rawRecords = result.records;
|
||||
state.dateOnlyRows = result.dateOnlyRows;
|
||||
state.invalidCurrencyRows = result.invalidCurrencyRows;
|
||||
|
||||
Reference in New Issue
Block a user