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
@@ -149,7 +149,8 @@ type MonitorExecutionPhase =
const heartbeatIntervalMs = 15_000
const pullIntervalMs = 60_000
const pumpWatchdogIntervalMs = 15_000
const publishPullIntervalMs = 30_000
const pumpWatchdogIntervalMs = 30_000
const leaseExtendIntervalMs = 60_000
const defaultPublishTaskTimeoutMs = 5 * 60_000
const publishTaskStaleProgressMs = 2 * 60_000
@@ -816,11 +817,11 @@ function schedulePullLoop(): void {
clearInterval(state.pullTimer)
}
setSchedulerNextPull(Date.now() + pullIntervalMs)
setSchedulerNextPull(Date.now() + publishPullIntervalMs)
state.pullTimer = setInterval(() => {
setSchedulerNextPull(Date.now() + pullIntervalMs)
setSchedulerNextPull(Date.now() + publishPullIntervalMs)
pumpExecutionLoop()
}, pullIntervalMs)
}, publishPullIntervalMs)
}
function schedulePumpWatchdogLoop(): void {
@@ -859,6 +860,7 @@ function connectDispatchWs(): void {
setTransportDispatchState('streaming')
setSchedulerError(null)
recordActivity('success', '任务分发通道已连接', '统一 WebSocket 分发通道已就绪。')
pumpExecutionLoop()
void pullNextTask('monitor')
if (hasConnectedOnce) {
void resumeLeasedMonitoringTasks('reconnect')
@@ -872,6 +874,7 @@ function connectDispatchWs(): void {
if (isConnectedEvent(payload)) {
state.lastServerTime = payload.server_time
}
pumpExecutionLoop()
})
const dispatchTaskHandler = (payload: unknown) => {
@@ -1162,6 +1165,7 @@ async function sendHeartbeat(source: 'startup' | 'timer'): Promise<void> {
if (source === 'startup' || previousStatus === 'failed') {
recordActivity('success', '心跳连接正常', '客户端心跳已回写到服务端。')
}
pumpExecutionLoop()
} catch (error) {
state.lastHeartbeatAt = Date.now()
state.lastHeartbeatStatus = 'failed'
@@ -1911,7 +1915,7 @@ async function pullNextTask(kind: 'publish' | 'monitor'): Promise<void> {
noteSchedulerPull()
if (!leased.task) {
state.publishFallbackBackoffUntil = Date.now() + pullIntervalMs
state.publishFallbackBackoffUntil = Date.now() + publishPullIntervalMs
return
}
@@ -1923,7 +1927,7 @@ async function pullNextTask(kind: 'publish' | 'monitor'): Promise<void> {
state.lastPullAt = Date.now()
state.lastPullStatus = 'failed'
state.lastError = errorMessage(error)
state.publishFallbackBackoffUntil = Date.now() + pullIntervalMs
state.publishFallbackBackoffUntil = Date.now() + publishPullIntervalMs
noteTransportPull(false)
setSchedulerError(state.lastError)
@@ -3505,7 +3509,6 @@ function canStartAnotherPublishExecution(): boolean {
function shouldPullPublishFallback(): boolean {
return (
!state.dispatchWsConnected &&
Date.now() >= state.publishFallbackBackoffUntil &&
!isLeaseInFlight('publish') &&
!hasPublishBacklog() &&