feat:抢单大厅数据排列优化
This commit is contained in:
@@ -21,6 +21,13 @@ type UseRealtimeEventsOptions = {
|
||||
token: string
|
||||
onEvent: (event: RealtimeEvent) => void
|
||||
onConnected?: () => void
|
||||
/**
|
||||
* 服务端告知「补发窗口溢出,历史事件已不连续」。
|
||||
*
|
||||
* 此时不能再依赖逐条快照补丁(会把列表写回旧值),调用方应整体失效对应查询,
|
||||
* 让列表接口重新给出权威数据。
|
||||
*/
|
||||
onResync?: (info: { headEventId: number; reason: string }) => void
|
||||
/** 鉴权失效(401/403)时触发:连接停止重连,调用方应清理会话并跳转登录页。 */
|
||||
onAuthExpired?: () => void
|
||||
}
|
||||
@@ -33,13 +40,16 @@ export function useRealtimeEvents({
|
||||
token,
|
||||
onEvent,
|
||||
onConnected,
|
||||
onResync,
|
||||
onAuthExpired,
|
||||
}: UseRealtimeEventsOptions) {
|
||||
const onEventRef = useRef(onEvent)
|
||||
const onConnectedRef = useRef(onConnected)
|
||||
const onResyncRef = useRef(onResync)
|
||||
const onAuthExpiredRef = useRef(onAuthExpired)
|
||||
onEventRef.current = onEvent
|
||||
onConnectedRef.current = onConnected
|
||||
onResyncRef.current = onResync
|
||||
onAuthExpiredRef.current = onAuthExpired
|
||||
|
||||
useEffect(() => {
|
||||
@@ -79,6 +89,15 @@ export function useRealtimeEvents({
|
||||
}
|
||||
onConnectedRef.current?.()
|
||||
await readSseStream(response.body, (eventName, data) => {
|
||||
// 补发窗口溢出:历史事件不连续,不能逐条复位快照,交给调用方整体重拉。
|
||||
if (eventName === 'resync') {
|
||||
const notice = normalizeResyncNotice(data)
|
||||
if (notice) {
|
||||
lastEventId = Math.max(lastEventId, notice.headEventId)
|
||||
onResyncRef.current?.(notice)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (eventName !== 'realtime') return
|
||||
const event = normalizeRealtimeEvent(data)
|
||||
if (event) {
|
||||
@@ -197,3 +216,16 @@ function normalizeRealtimeEvent(value: string): RealtimeEvent | null {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/** 服务端 resync 通知:补发窗口溢出,客户端应把游标推进到 headEventId 并整体重拉。 */
|
||||
function normalizeResyncNotice(value: string): { headEventId: number; reason: string } | null {
|
||||
try {
|
||||
const parsed = JSON.parse(value) as { headEventId?: unknown; reason?: unknown }
|
||||
return {
|
||||
headEventId: Math.max(0, Number(parsed.headEventId) || 0),
|
||||
reason: String(parsed.reason || ''),
|
||||
}
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,16 @@ export default function AdminLayout() {
|
||||
url: '/api/v1/admin/realtime',
|
||||
token: role === 'finance' ? '' : getAdminToken(),
|
||||
onEvent: (event) => applyAdminRealtimeEvent(event, queryClient),
|
||||
// 补发窗口溢出(断线太久):历史事件已不连续,整体失效让接口重新给权威数据。
|
||||
onResync: () => {
|
||||
void queryClient.invalidateQueries({ queryKey: ['admin-worker-platform-orders'] })
|
||||
void queryClient.invalidateQueries({ queryKey: ['admin-worker-platform-after-sales-cases'] })
|
||||
void queryClient.invalidateQueries({ queryKey: ['admin-worker-platform-finance-requests'] })
|
||||
void queryClient.invalidateQueries({ queryKey: ['admin-notifications'] })
|
||||
void queryClient.invalidateQueries({ queryKey: ['admin-tasks'] })
|
||||
void queryClient.invalidateQueries({ queryKey: ['admin-orders'] })
|
||||
void queryClient.invalidateQueries({ queryKey: ['admin-dashboard-summary'] })
|
||||
},
|
||||
// 会话过期时清登录态回登录页,避免 SSE 带着失效 token 无限重连刷 401。
|
||||
onAuthExpired: () => {
|
||||
clearAdminSession()
|
||||
|
||||
@@ -138,6 +138,16 @@ export default function WorkerLayout() {
|
||||
url: '/api/v1/worker/realtime',
|
||||
token: getWorkerToken(),
|
||||
onEvent: (event) => applyWorkerRealtimeEvent(event, queryClient),
|
||||
// 补发窗口溢出(断线太久):历史事件已不连续,逐条套旧快照会把列表写回旧值,
|
||||
// 这里整体失效,让列表接口重新给权威数据。
|
||||
onResync: () => {
|
||||
void queryClient.invalidateQueries({ queryKey: ['worker-hall-orders'] })
|
||||
void queryClient.invalidateQueries({ queryKey: ['worker-my-orders'] })
|
||||
void queryClient.invalidateQueries({ queryKey: ['worker-my-orders-overview'] })
|
||||
void queryClient.invalidateQueries({ queryKey: ['worker-after-sales-cases'] })
|
||||
void queryClient.invalidateQueries({ queryKey: ['worker-finance-requests'] })
|
||||
void queryClient.invalidateQueries({ queryKey: ['worker-profile'] })
|
||||
},
|
||||
// 会话过期时清登录态回登录页,避免 SSE 带着失效 token 无限重连刷 401。
|
||||
onAuthExpired: () => {
|
||||
expireWorkerSession()
|
||||
|
||||
Reference in New Issue
Block a user