notify.ts
3.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { defineStore } from 'pinia'
import { ref } from 'vue'
import {
getUnreadNotifications,
acknowledgeNotification,
acknowledgeAllNotifications,
streamNotifications,
getNotificationsPage,
type NotificationDTO
} from '@/api/notify'
export const useNotifyStore = defineStore('notify', () => {
const unreadNotifications = ref<NotificationDTO[]>([])
const unreadCount = ref(0)
const isConnected = ref(false)
let closeStream: (() => void) | null = null
let reconnectTimer: any = null
let reconnectCount = 0
const fetchUnread = async () => {
try {
// 优化方案:调用分页接口获取真实的未读总数 (total)
const { data: pageData } = await getNotificationsPage({
read: false,
page: 0,
size: 1
})
unreadCount.value = pageData.total || 0
// 获取前 50 条消息用于界面列表展示
const { data: listData } = await getUnreadNotifications(50)
unreadNotifications.value = listData || []
} catch (error) {
console.error('Failed to fetch unread notifications:', error)
}
}
const startListening = () => {
if (closeStream) return
const connect = () => {
const { close } = streamNotifications({
onOpen: () => {
isConnected.value = true
reconnectCount = 0
fetchUnread()
console.log('Notification stream connected')
},
onMessage: () => {
fetchUnread()
},
onError: (err) => {
console.error('Notification stream error:', err)
isConnected.value = false
// 指数退避重连逻辑
if (reconnectTimer) clearTimeout(reconnectTimer)
const delay = Math.min(30000, Math.pow(2, reconnectCount) * 1000)
console.log(`Attempting to reconnect in ${delay}ms...`)
reconnectTimer = setTimeout(() => {
reconnectCount++
startListening()
}, delay)
},
onEnd: () => {
isConnected.value = false
closeStream = null
console.log('Notification stream ended')
}
})
closeStream = close
}
connect()
}
const stopListening = () => {
if (reconnectTimer) {
clearTimeout(reconnectTimer)
reconnectTimer = null
}
if (closeStream) {
closeStream()
closeStream = null
}
}
const markAsRead = async (id: number) => {
try {
await acknowledgeNotification(id)
unreadNotifications.value = unreadNotifications.value.filter(n => n.id !== id)
unreadCount.value = Math.max(0, unreadCount.value - 1)
} catch (error) {
console.error('Failed to mark notification as read:', error)
}
}
const markAllAsRead = async () => {
try {
await acknowledgeAllNotifications()
unreadNotifications.value = []
unreadCount.value = 0
} catch (error) {
console.error('Failed to mark all notifications as read:', error)
}
}
return {
unreadNotifications,
unreadCount,
isConnected,
fetchUnread,
startListening,
stopListening,
markAsRead,
markAllAsRead
}
})