Show sync freshness status
This commit is contained in:
+53
-8
@@ -41,7 +41,8 @@
|
||||
.status { color: var(--muted); text-align: right; }
|
||||
.status[data-state="error"] { color: var(--error-text); }
|
||||
.status[data-state="empty"] { color: var(--muted); }
|
||||
.alert { margin: 0 0 18px; padding: 11px 14px; border: 1px solid var(--warning-border); border-radius: 10px; background: var(--warning-bg); color: var(--warning-text); }
|
||||
.alert { margin: 0 0 18px; padding: 11px 14px; border: 1px solid var(--warning-border); border-radius: 10px; background: var(--warning-bg); color: var(--warning-text); white-space: pre-line; }
|
||||
.alert[data-stale="true"] { border-color: var(--error-border); background: var(--error-bg); color: var(--error-text); }
|
||||
.alert[hidden] { display: none; }
|
||||
.filters { display: grid; gap: 12px; margin: 0 0 20px; padding: 16px 18px; border: 1px solid var(--border); border-radius: 14px; background: var(--surface); box-shadow: var(--shadow); }
|
||||
.filter-row { display: flex; flex-wrap: wrap; align-items: end; gap: 14px 20px; }
|
||||
@@ -283,24 +284,68 @@
|
||||
charts = [];
|
||||
}
|
||||
|
||||
function formatShanghaiTime(value) {
|
||||
if (!value) return null;
|
||||
var timestamp = Date.parse(value);
|
||||
if (Number.isNaN(timestamp)) return null;
|
||||
var parts = new Intl.DateTimeFormat('en-US', {
|
||||
timeZone: 'Asia/Shanghai',
|
||||
year: 'numeric', month: '2-digit', day: '2-digit',
|
||||
hour: '2-digit', minute: '2-digit', second: '2-digit',
|
||||
hourCycle: 'h23'
|
||||
}).formatToParts(new Date(timestamp));
|
||||
var values = {};
|
||||
parts.forEach(function (part) { values[part.type] = part.value; });
|
||||
return values.year + '-' + values.month + '-' + values.day + ' ' + values.hour + ':' + values.minute + ':' + values.second;
|
||||
}
|
||||
|
||||
function isOlderThan(value, milliseconds) {
|
||||
var timestamp = Date.parse(value);
|
||||
return !Number.isNaN(timestamp) && Date.now() - timestamp > milliseconds;
|
||||
}
|
||||
|
||||
function updateNotice() {
|
||||
var messages = [];
|
||||
if (state.syncLog) messages.push('最后成功运行:' + state.syncLog.displayTime);
|
||||
var log = state.syncLog;
|
||||
var lastSuccessAt = log && log.lastSuccessAt ? formatShanghaiTime(log.lastSuccessAt) : null;
|
||||
var lastDataUpdateAt = log && log.lastDataUpdateAt ? formatShanghaiTime(log.lastDataUpdateAt) : null;
|
||||
var dataIsStale = false;
|
||||
messages.push('最后成功运行:' + (lastSuccessAt || '暂无记录'));
|
||||
messages.push('数据最后更新:' + (lastDataUpdateAt || '暂无记录'));
|
||||
if (state.syncLogUnavailable) messages.push('运行日志暂不可用。');
|
||||
if (log && log.lastSuccessAt && isOlderThan(log.lastSuccessAt, 24 * 60 * 60 * 1000)) {
|
||||
messages.push('最后成功运行已超过24小时,请刷新页面或检查自动任务。');
|
||||
}
|
||||
if (log && log.lastDataUpdateAt) {
|
||||
dataIsStale = isOlderThan(log.lastDataUpdateAt, 48 * 60 * 60 * 1000);
|
||||
if (dataIsStale) messages.push('数据已超过48小时未更新,请检查数据同步。');
|
||||
} else {
|
||||
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。');
|
||||
elements.warning.textContent = messages.join(' ');
|
||||
elements.warning.textContent = messages.join('\n');
|
||||
elements.warning.dataset.stale = dataIsStale ? 'true' : 'false';
|
||||
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。');
|
||||
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
||||
throw new Error('运行日志格式无效。');
|
||||
}
|
||||
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 parseTimestamp(field) {
|
||||
var value = payload[field];
|
||||
if (value === null || value === undefined || value === '') return null;
|
||||
if (typeof value !== 'string' || Number.isNaN(Date.parse(value))) {
|
||||
throw new Error('运行日志缺少有效的 ' + field + '。');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return {
|
||||
lastSuccessAt: parseTimestamp('last_success_at'),
|
||||
lastDataUpdateAt: parseTimestamp('last_data_update_at')
|
||||
};
|
||||
}
|
||||
|
||||
function setRateStatus(message) { elements.rateStatus.textContent = message; }
|
||||
|
||||
Reference in New Issue
Block a user