feat(sync-history): 新增同步结果查询(每次自动/手动/立即同步的记录)
后端: - db.py SCHEMA 新增 sync_history 表(不可变,每次同步尝试一行) + 索引 - garmin.py: _log_sync_history() 在 sync_data 全部出口记录; trigger 区分 auto(调度器)/manual(同步页开始同步)/quick(设置页立即同步); start_sync 限流拦截分支同样留档; 写入失败只告警不影响同步 - scheduler.py 自动同步传 trigger="auto"; routes 新增 GET /garmin/sync-history 按时间倒序返回(默认 50 条,上限 200) 前端: - api.ts 增加 SyncHistoryItem 类型 + getSyncHistory() - 新页面 /sync-history/ 同步记录: 卡片列表, 状态(成功/失败/被限流)chip 配色, 时间本地化(今天/昨天/X月X日), 触发类型标签, 范围与耗时 - 同步页与设置页同步区块均加入口链接
This commit is contained in:
@@ -129,6 +129,26 @@ export interface SyncResult {
|
||||
lastSyncTime: string;
|
||||
}
|
||||
|
||||
/** One recorded sync attempt, for the 同步记录 screen. */
|
||||
export interface SyncHistoryItem {
|
||||
/** What started the run: the scheduler, the sync page, or 立即同步. */
|
||||
triggerKind: 'auto' | 'manual' | 'quick';
|
||||
status: 'success' | 'error' | 'rate_limited' | 'syncing';
|
||||
days: number;
|
||||
recordsSynced: number;
|
||||
activitiesSynced: number;
|
||||
badgesSynced: number;
|
||||
personalRecordsSynced: number;
|
||||
startedAt: string;
|
||||
finishedAt: string | null;
|
||||
durationSeconds: number;
|
||||
message: string | null;
|
||||
}
|
||||
|
||||
export interface SyncHistoryResponse {
|
||||
items: SyncHistoryItem[];
|
||||
}
|
||||
|
||||
export interface Recommendation {
|
||||
id: string;
|
||||
category: string;
|
||||
@@ -664,6 +684,14 @@ class ApiClient {
|
||||
return data;
|
||||
}
|
||||
|
||||
/** Every recorded sync attempt, newest first (auto / manual / 立即同步). */
|
||||
async getSyncHistory(limit = 50) {
|
||||
const { data } = await this.client.get<SyncHistoryResponse>('/garmin/sync-history', {
|
||||
params: { limit },
|
||||
});
|
||||
return data.items;
|
||||
}
|
||||
|
||||
// --- health ---
|
||||
private range(startDate?: string, endDate?: string) {
|
||||
return { params: { startDate, endDate } };
|
||||
|
||||
Reference in New Issue
Block a user