feat(client): track digital sync retry history

This commit is contained in:
baiyanyun
2026-06-07 01:42:39 +08:00
parent 646901b5fa
commit cef3fe4a43
10 changed files with 375 additions and 133 deletions

View File

@@ -1698,6 +1698,59 @@
font-size: 13px;
line-height: 1.55;
}
.de-sync-attempt-history {
display: grid;
gap: 8px;
padding: 14px;
border: 1px solid rgba(126,163,194,0.2);
border-radius: 14px;
background: rgba(247,251,255,0.82);
}
.de-sync-attempt-history > span {
color: #6f8196;
font-size: 12px;
font-weight: 800;
}
.de-sync-attempt-list {
display: grid;
gap: 8px;
}
.de-sync-attempt-row {
display: grid;
gap: 5px;
padding: 9px 10px;
border-radius: 12px;
background: rgba(255,255,255,0.72);
}
.de-sync-attempt-row > div {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.de-sync-attempt-row strong {
color: #153754;
font-size: 12px;
}
.de-sync-attempt-row em {
font-size: 12px;
font-style: normal;
font-weight: 800;
}
.de-sync-attempt-row span,
.de-sync-attempt-row small {
color: #667b90;
font-size: 12px;
}
.de-sync-attempt-row small {
line-height: 1.45;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.de-sync-attempt-row--success em { color: #23805d; }
.de-sync-attempt-row--danger em { color: #bb3b3b; }
.de-sync-attempt-row--info em { color: #2e6fa5; }
.de-event-modal-actions {
display: flex;
justify-content: flex-end;

View File

@@ -64,6 +64,15 @@ interface QimingclawSyncStatus {
recentFailures?: QimingclawSyncFailure[];
}
interface QimingclawSyncAttempt {
attemptNo: number;
status: string;
error: string | null;
endpoint?: string | null;
startedAt: string;
finishedAt: string;
}
interface QimingclawSyncFailure {
id: string;
entityType: string;
@@ -74,6 +83,7 @@ interface QimingclawSyncFailure {
nextRetryAt?: string | null;
dueForRetry?: boolean;
managementRecordUrl?: string | null;
attemptHistory?: QimingclawSyncAttempt[];
error: string | null;
createdAt: string;
updatedAt: string;

View File

@@ -64,6 +64,15 @@ interface ManagementSyncStatus {
recentFailures?: ManagementSyncFailure[];
}
interface ManagementSyncAttempt {
attemptNo: number;
status: string;
error: string | null;
endpoint?: string | null;
startedAt: string;
finishedAt: string;
}
interface ManagementSyncFailure {
id: string;
entityType: string;
@@ -74,6 +83,7 @@ interface ManagementSyncFailure {
nextRetryAt?: string | null;
dueForRetry?: boolean;
managementRecordUrl?: string | null;
attemptHistory?: ManagementSyncAttempt[];
error: string | null;
createdAt: string;
updatedAt: string;
@@ -495,6 +505,18 @@ function syncFailureRetryTime(failure: ManagementSyncFailure): string {
return formatBeijingDateTime(failure.nextRetryAt) || '待计算';
}
function syncAttemptStatusLabel(status: string): string {
if (status === 'synced') return '已同步';
if (status === 'failed') return '失败';
return status || '未知';
}
function syncAttemptTone(status: string): 'success' | 'danger' | 'info' {
if (status === 'synced') return 'success';
if (status === 'failed') return 'danger';
return 'info';
}
async function readManagementSyncStatus(): Promise<ManagementSyncStatus | null> {
const bridge = window.QimingClawBridge?.digital;
if (!bridge?.getSyncStatus) return null;
@@ -1837,6 +1859,31 @@ export default function CommandDeck({ onNavigate }: { onNavigate: (target: Digit
<span></span>
<p>{selectedSyncFailure.error || '暂无错误详情。'}</p>
</div>
{(selectedSyncFailure.attemptHistory?.length ?? 0) > 0 && (
<div className="de-sync-attempt-history">
<span></span>
<div className="de-sync-attempt-list">
{selectedSyncFailure.attemptHistory?.map((attempt) => (
<div
key={`${attempt.attemptNo}-${attempt.finishedAt}`}
className={`de-sync-attempt-row de-sync-attempt-row--${syncAttemptTone(
attempt.status,
)}`}
>
<div>
<strong> {attempt.attemptNo} </strong>
<em>{syncAttemptStatusLabel(attempt.status)}</em>
<span>
{formatBeijingDateTime(attempt.finishedAt) ||
attempt.finishedAt}
</span>
</div>
{attempt.error && <small>{attempt.error}</small>}
</div>
))}
</div>
</div>
)}
<div className="de-event-modal-actions">
<button
type="button"