feat(desktop&tenant): Enhance desktop account and task consumer presence management
Desktop Client Build / Resolve Build Metadata (push) Successful in 1m13s
Desktop Client Build / Build Desktop Client (push) Has been cancelled
Desktop Client Build / Publish Client Artifacts to NAS (push) Has been cancelled
Backend CI / Backend (push) Failing after 13m49s
Frontend CI / Frontend (push) Successful in 6m38s

- Added new fields to DesktopAccountInfo for tracking account session and task consumer online status, along with queued publish task count and oldest queued publish task timestamp.
- Updated DesktopDispatchEvent to include server time for better synchronization.
- Implemented separate presence management for task consumers, including marking presence and loading presence state.
- Enhanced DesktopAccountService to apply publish queue summaries and manage task consumer presence.
- Introduced new methods for replaying queued publish tasks in DesktopDispatchHandler.
- Updated tests to cover new presence management logic and ensure correct behavior of account session and task consumer online status.
This commit is contained in:
2026-06-07 19:15:20 +08:00
parent 88c37e50b2
commit 67be43319e
15 changed files with 636 additions and 129 deletions
+25 -6
View File
@@ -60,8 +60,12 @@ interface MediaAccountCard {
verifiedAt: string | null
clientId: string | null
clientOnline: boolean | null
accountSessionOnline: boolean | null
taskConsumerOnline: boolean | null
clientDeviceName: string | null
clientLastSeenAt: string | null
queuedPublishTaskCount: number
oldestQueuedPublishTaskAt: string | null
publishState: PublishState
publishHint: string
deleteRequestedAt: string | null
@@ -251,8 +255,12 @@ const mediaAccounts = computed<MediaAccountCard[]>(() => {
verifiedAt: resolveAccountCheckedAt(account),
clientId: account.client_id,
clientOnline: account.client_online,
accountSessionOnline: account.account_session_online ?? null,
taskConsumerOnline: account.task_consumer_online ?? null,
clientDeviceName: account.client_device_name,
clientLastSeenAt: account.client_last_seen_at,
queuedPublishTaskCount: account.queued_publish_task_count ?? 0,
oldestQueuedPublishTaskAt: account.oldest_queued_publish_task_at ?? null,
publishState,
publishHint: resolvePublishHint(account, publishState),
deleteRequestedAt: account.delete_requested_at,
@@ -322,7 +330,7 @@ function resolvePublishState(account: DesktopAccountInfo): PublishState {
if (!account.client_id) {
return 'unavailable'
}
return account.client_online === true ? 'immediate' : 'queued'
return account.task_consumer_online === true ? 'immediate' : 'queued'
}
function resolvePublishHint(account: DesktopAccountInfo, publishState: PublishState): string {
@@ -330,7 +338,12 @@ function resolvePublishHint(account: DesktopAccountInfo, publishState: PublishSt
return ''
}
if (publishState === 'queued') {
return '客户端当前离线,任务会先进入发布队列,等桌面端上线后自动继续。'
const count = account.queued_publish_task_count ?? 0
const prefix = count > 0 ? `当前已有 ${count} 个发布任务排队。` : ''
if (account.client_online === true) {
return `${prefix}客户端心跳在线,但任务消费通道未就绪;任务会进入发布队列,通道恢复后自动继续。`
}
return `${prefix}客户端当前离线,任务会先进入发布队列,等桌面端上线后自动继续。`
}
if (resolveAccountHealth(account) !== 'live') {
return '授权状态异常,先在桌面端重新校验后再发布。'
@@ -402,14 +415,20 @@ function clientStatusLabel(account: MediaAccountCard): string {
if (!account.clientId) {
return '未绑定'
}
return account.clientOnline ? '在线' : '离线'
if (account.taskConsumerOnline) {
return '任务通道在线'
}
return account.clientOnline ? '心跳在线,任务通道离线' : '离线'
}
function clientStatusColor(account: MediaAccountCard): string {
if (!account.clientId) {
return 'default'
}
return account.clientOnline ? 'green' : 'gold'
if (account.taskConsumerOnline) {
return 'green'
}
return account.clientOnline ? 'orange' : 'gold'
}
function clientStatusText(account: MediaAccountCard): string {
@@ -426,7 +445,7 @@ function publishStateLabel(state: PublishState): string {
case 'immediate':
return '可立即发布'
case 'queued':
return '离线排队'
return '排队等待'
default:
return '不可发布'
}
@@ -715,7 +734,7 @@ function releaseSize(value?: number | null): string {
</svg>
</div>
<div class="chip-copy">
<span>离线排队</span>
<span>排队等待</span>
<strong>{{ overview.queued }}</strong>
</div>
</div>