修复新订单广播绕过等级可见延迟导致抢单提示不可拼单
This commit is contained in:
@@ -311,6 +311,8 @@ async function publishWorkOrderSnapshots(input: {
|
|||||||
? {
|
? {
|
||||||
...mapWorkOrderForWorker(workOrder, { depositFreeAmount: 0 }),
|
...mapWorkOrderForWorker(workOrder, { depositFreeAmount: 0 }),
|
||||||
requiredDepositAmount: Number(workOrder.required_deposit_amount || 0),
|
requiredDepositAmount: Number(workOrder.required_deposit_amount || 0),
|
||||||
|
// 广播无法按打手等级过滤可见延迟,携带入厅时间由客户端按各自延迟推迟展示。
|
||||||
|
hallQueuedAt: workOrder.hall_queued_at || null,
|
||||||
sharingProgress: { joinedQuantity, pendingSubmissionCount },
|
sharingProgress: { joinedQuantity, pendingSubmissionCount },
|
||||||
}
|
}
|
||||||
: { workOrderId },
|
: { workOrderId },
|
||||||
|
|||||||
@@ -228,6 +228,12 @@ function patchWorkerMyOrderCaches(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 新订单广播最多推迟入缓存的时长;超过则不占定时器,交给列表刷新自然带出。 */
|
||||||
|
const HALL_BROADCAST_MAX_SCHEDULE_MS = 60_000
|
||||||
|
|
||||||
|
/** 等待可见延迟到期的挂起插入;期间收到下架事件需取消,避免把已下架订单插回缓存。 */
|
||||||
|
const pendingHallUpsertTimers = new Map<number, ReturnType<typeof setTimeout>>()
|
||||||
|
|
||||||
function patchWorkerHallCaches(
|
function patchWorkerHallCaches(
|
||||||
queryClient: ReturnType<typeof useQueryClient>,
|
queryClient: ReturnType<typeof useQueryClient>,
|
||||||
event: RealtimeEvent,
|
event: RealtimeEvent,
|
||||||
@@ -237,6 +243,35 @@ function patchWorkerHallCaches(
|
|||||||
void queryClient.invalidateQueries({ queryKey: ['worker-hall-orders'] })
|
void queryClient.invalidateQueries({ queryKey: ['worker-hall-orders'] })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const workOrderId = Number(event.entityId || 0)
|
||||||
|
const pendingTimer = pendingHallUpsertTimers.get(workOrderId)
|
||||||
|
if (pendingTimer) {
|
||||||
|
clearTimeout(pendingTimer)
|
||||||
|
pendingHallUpsertTimers.delete(workOrderId)
|
||||||
|
}
|
||||||
|
if (event.operation === 'remove') {
|
||||||
|
applyWorkerHallCachePatch(queryClient, event)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 服务端广播无法按打手等级过滤可见延迟;未到解锁时间的订单推迟入缓存,与列表接口和抢单校验对齐。
|
||||||
|
const delayMs = resolveHallVisibilityDelayMs(queryClient, event.data, event.occurredAt)
|
||||||
|
if (delayMs > 0) {
|
||||||
|
if (delayMs <= HALL_BROADCAST_MAX_SCHEDULE_MS) {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
pendingHallUpsertTimers.delete(workOrderId)
|
||||||
|
applyWorkerHallCachePatch(queryClient, event)
|
||||||
|
}, delayMs)
|
||||||
|
pendingHallUpsertTimers.set(workOrderId, timer)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
applyWorkerHallCachePatch(queryClient, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyWorkerHallCachePatch(
|
||||||
|
queryClient: ReturnType<typeof useQueryClient>,
|
||||||
|
event: RealtimeEvent,
|
||||||
|
) {
|
||||||
const workerSnapshot = applyWorkerDepositRule(queryClient, event.data)
|
const workerSnapshot = applyWorkerDepositRule(queryClient, event.data)
|
||||||
const item = workerSnapshot as {
|
const item = workerSnapshot as {
|
||||||
status?: unknown
|
status?: unknown
|
||||||
@@ -267,6 +302,41 @@ function patchWorkerHallCaches(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 订单入厅时间 + 当前打手等级的可见延迟尚未到期时,返回需要推迟的毫秒数。 */
|
||||||
|
function resolveHallVisibilityDelayMs(
|
||||||
|
queryClient: ReturnType<typeof useQueryClient>,
|
||||||
|
data: unknown,
|
||||||
|
occurredAt: string | undefined,
|
||||||
|
): number {
|
||||||
|
if (!isRecord(data)) return 0
|
||||||
|
const queuedAtMs = Date.parse(String(data.hallQueuedAt || ''))
|
||||||
|
if (!Number.isFinite(queuedAtMs)) return 0
|
||||||
|
const visibleDelaySeconds = readWorkerHallVisibleDelaySeconds(queryClient)
|
||||||
|
if (visibleDelaySeconds <= 0) return 0
|
||||||
|
const unlockAtMs = queuedAtMs + visibleDelaySeconds * 1000
|
||||||
|
// 入厅时间是服务器时钟,用事件的服务器时间戳校正浏览器钟差:快钟提前展示会重现“看得见抢不了”。
|
||||||
|
const occurredAtMs = occurredAt ? Date.parse(occurredAt) : Number.NaN
|
||||||
|
const serverNowMs = Number.isFinite(occurredAtMs) ? occurredAtMs : Date.now()
|
||||||
|
const clockSkewMs = Date.now() - serverNowMs
|
||||||
|
return unlockAtMs - (Date.now() - clockSkewMs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 大厅列表响应自带当前打手的 visibleDelaySeconds,从已缓存分页读取,无需额外接口。 */
|
||||||
|
function readWorkerHallVisibleDelaySeconds(queryClient: ReturnType<typeof useQueryClient>): number {
|
||||||
|
for (const [, cached] of queryClient.getQueriesData({ queryKey: ['worker-hall-orders'] })) {
|
||||||
|
const pages = (cached as { pages?: unknown } | undefined)?.pages
|
||||||
|
if (!Array.isArray(pages)) continue
|
||||||
|
for (const page of pages) {
|
||||||
|
const delaySeconds = Number(
|
||||||
|
(page as { data?: { visibleDelaySeconds?: unknown } } | null)?.data?.visibleDelaySeconds ||
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if (delaySeconds > 0) return delaySeconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
function applyWorkerDepositRule(queryClient: ReturnType<typeof useQueryClient>, data: unknown) {
|
function applyWorkerDepositRule(queryClient: ReturnType<typeof useQueryClient>, data: unknown) {
|
||||||
if (!isRecord(data)) return data
|
if (!isRecord(data)) return data
|
||||||
const profile = queryClient.getQueryData(['worker-profile'])
|
const profile = queryClient.getQueryData(['worker-profile'])
|
||||||
|
|||||||
@@ -501,6 +501,8 @@ export type WorkOrder = {
|
|||||||
status: WorkOrderStatus
|
status: WorkOrderStatus
|
||||||
rawStatus?: WorkOrderStatus
|
rawStatus?: WorkOrderStatus
|
||||||
hallAvailable?: boolean
|
hallAvailable?: boolean
|
||||||
|
/** 订单进入抢单大厅的时间;实时广播携带,客户端按打手等级可见延迟推迟展示。 */
|
||||||
|
hallQueuedAt?: string | null
|
||||||
/** 验收模式:standard 标准验收 / friend_gift 好友赠送 */
|
/** 验收模式:standard 标准验收 / friend_gift 好友赠送 */
|
||||||
acceptanceMode?: 'standard' | 'friend_gift' | string
|
acceptanceMode?: 'standard' | 'friend_gift' | string
|
||||||
/** 好友赠送模式进度;仅 acceptanceMode = friend_gift 时返回 */
|
/** 好友赠送模式进度;仅 acceptanceMode = friend_gift 时返回 */
|
||||||
|
|||||||
Reference in New Issue
Block a user