feat(client): show digital sync failure details

This commit is contained in:
baiyanyun
2026-06-07 01:05:49 +08:00
parent ac1a6fd0e6
commit 0672e82fc7
10 changed files with 280 additions and 126 deletions

View File

@@ -61,6 +61,19 @@ interface QimingclawSyncStatus {
failed: number;
lastSyncAt: string | null;
lastSyncError: string | null;
recentFailures?: QimingclawSyncFailure[];
}
interface QimingclawSyncFailure {
id: string;
entityType: string;
entityId: string;
operation: string;
attempts: number;
lastAttemptAt: string | null;
error: string | null;
createdAt: string;
updatedAt: string;
}
interface QimingclawSnapshot {
@@ -372,6 +385,16 @@ function syncLine(snapshot: QimingclawSnapshot | null): string {
return sync.lastSyncAt ? `管理端同步:已同步,最近 ${sync.lastSyncAt}` : '管理端同步:等待本地数据';
}
function compactSyncText(value?: string | null): string {
return String(value || '').replace(/\s+/g, ' ').trim();
}
function syncFailureLine(failure: QimingclawSyncFailure): string {
const error = compactSyncText(failure.error);
const detail = error.length > 120 ? `${error.slice(0, 120)}...` : error;
return `${failure.entityType}/${failure.operation} ${failure.id} 重试 ${failure.attempts}${detail ? `${detail}` : ''}`;
}
function withRuntimePlan(plan: DebugPlan, snapshot: QimingclawSnapshot | null): DebugPlan {
return { ...plan, status: runtimeStatusForPlan(plan, snapshot) };
}
@@ -543,13 +566,21 @@ function buildEvents(duties: DigitalEmployeeDuty[], confirmed: boolean, snapshot
}));
const sync = snapshot?.sync;
if (sync && (sync.pending > 0 || sync.failed > 0 || sync.lastSyncAt)) {
const recentFailureText = (sync.recentFailures || [])
.slice(0, 3)
.map(syncFailureLine)
.join('');
events.push({
id: 'event-management-sync',
time: sync.syncing ? '实时' : '同步',
plan_title: '管理端联动',
task_title: '同步数字员工本地状态',
step_title: sync.failed > 0 ? '同步补偿' : 'outbox 推送',
detail: `${syncLine(snapshot)}${sync.lastSyncError ? `。错误:${sync.lastSyncError}` : ''}`,
detail: [
syncLine(snapshot),
sync.lastSyncError ? `错误:${sync.lastSyncError}` : '',
recentFailureText ? `最近失败:${recentFailureText}` : '',
].filter(Boolean).join('。'),
status: sync.failed > 0 ? 'failed' : sync.syncing || sync.pending > 0 ? 'running' : 'completed',
status_label: sync.failed > 0 ? '异常' : sync.syncing || sync.pending > 0 ? '同步中' : '已同步',
status_tone: sync.failed > 0 ? 'danger' : sync.syncing || sync.pending > 0 ? 'running' : 'success',