Skip to content

Commit 9ffdd26

Browse files
committed
fix(biz): 优化服务号页实时刷新逻辑并减少无效同步
服务号页接入实时模式变更监听。 支持 only_official 与 backfill_limit 参数透传。 切换服务号时直接重载本地消息,避免重复触发整页全量同步。
1 parent 67d67a5 commit 9ffdd26

2 files changed

Lines changed: 151 additions & 25 deletions

File tree

frontend/components/BizMessages.vue

Lines changed: 149 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div class="biz-page h-screen flex overflow-hidden" style="background-color: var(--app-shell-bg)">
2+
<div class="biz-page h-full min-h-0 flex overflow-hidden" style="background-color: var(--app-shell-bg)">
33

44
<div :class="['w-[300px] lg:w-[320px] border-r flex flex-col flex-shrink-0 z-10', isDark ? 'bg-[#1e1e1e] border-[#333]' : 'bg-white border-gray-200']">
55
<div class="p-3 border-b" :class="isDark ? 'border-[#333]' : 'border-gray-200'" style="background-color: var(--app-surface-muted)">
@@ -17,15 +17,15 @@
1717
<div v-if="loadingAccounts" class="flex justify-center py-4">
1818
<span class="text-sm" :class="isDark ? 'text-gray-500' : 'text-gray-400'">加载中...</span>
1919
</div>
20-
<div v-else>
20+
<div v-else class="pb-4">
2121
<div
2222
v-for="item in filteredAccounts"
2323
:key="item.username"
2424
@click="selectAccount(item)"
2525
class="flex items-center gap-3 px-4 py-3 cursor-pointer transition-colors border-b"
2626
:class="[
2727
isDark ? 'border-[#333]' : 'border-gray-50',
28-
selectedAccount?.username === item.username
28+
selectedBizAccount?.username === item.username
2929
? (isDark ? 'bg-[#333]' : 'bg-[#E5E5E5]') // 选中状态
3030
: item.username === 'gh_3dfda90e39d6'
3131
? (isDark ? 'bg-[#2a2a2a] hover:bg-[#333]' : 'bg-[#F2F2F2] hover:bg-[#EAEAEA]') // 微信支付专门的底色
@@ -63,19 +63,20 @@
6363
</div>
6464

6565
<div class="flex-1 flex flex-col min-h-0 min-w-0" :class="isDark ? 'bg-[#121212]' : 'bg-[#F5F5F5]'">
66-
<div v-if="selectedAccount" class="flex-1 flex flex-col min-h-0 relative">
66+
<div v-if="selectedBizAccount" class="flex-1 flex flex-col min-h-0 relative">
6767
<div class="h-14 border-b flex items-center px-5 shrink-0 z-10" :class="isDark ? 'bg-[#121212] border-[#333]' : 'bg-[#F5F5F5] border-gray-200'">
68-
<h2 class="text-base" :class="isDark ? 'text-gray-100' : 'text-gray-900'">{{ selectedAccount.name }}</h2>
68+
<h2 class="text-base" :class="isDark ? 'text-gray-100' : 'text-gray-900'">{{ selectedBizAccount.name }}</h2>
6969
</div>
7070

7171
<div class="flex-1 overflow-y-auto px-4 py-6 flex flex-col-reverse" @scroll="handleScroll" ref="messageListRef">
72+
<div class="h-4 shrink-0" aria-hidden="true"></div>
7273
<div v-if="!hasMore" class="text-center text-xs py-4 w-full" :class="isDark ? 'text-gray-500' : 'text-gray-400'">没有更多消息了</div>
7374
<div v-if="loadingMessages" class="text-center text-xs py-4 w-full" :class="isDark ? 'text-gray-500' : 'text-gray-400'">正在加载...</div>
7475

7576
<div class="w-full max-w-[400px] mx-auto flex flex-col-reverse gap-6">
7677
<div v-for="msg in messages" :key="msg.local_id" class="w-full">
7778

78-
<div v-if="selectedAccount.username === 'gh_3dfda90e39d6'" class="rounded-xl shadow-sm p-5 border" :class="isDark ? 'bg-[#1e1e1e] border-[#333]' : 'bg-white border-gray-100'">
79+
<div v-if="selectedBizAccount.username === 'gh_3dfda90e39d6'" class="rounded-xl shadow-sm p-5 border" :class="isDark ? 'bg-[#1e1e1e] border-[#333]' : 'bg-white border-gray-100'">
7980
<div class="flex items-center text-sm mb-5" :class="isDark ? 'text-gray-400' : 'text-gray-500'">
8081
<img v-if="msg.merchant_icon" :src="api.getBizProxyImageUrl(msg.merchant_icon)" class="w-6 h-6 rounded-full mr-2 object-cover" alt=""/>
8182
<div v-else class="w-6 h-6 rounded-full mr-2 flex items-center justify-center" :class="isDark ? 'bg-green-900/40 text-green-400' : 'bg-green-100 text-green-600'">¥</div>
@@ -143,41 +144,69 @@
143144
</template>
144145

145146
<script setup>
146-
import { ref, computed, onMounted } from 'vue'
147+
import { ref, computed, onMounted, watch } from 'vue'
147148
148149
import { useApi } from '~/composables/useApi'
149150
const api = useApi()
150151
151152
import { storeToRefs } from 'pinia'
152153
import { useThemeStore } from '~/stores/theme'
154+
import { useChatAccountsStore } from '~/stores/chatAccounts'
155+
import { useChatRealtimeStore } from '~/stores/chatRealtime'
153156
154157
const accounts = ref([])
155158
const loadingAccounts = ref(false)
156159
const searchQuery = ref('')
157-
const selectedAccount = ref(null)
160+
const selectedBizAccount = ref(null)
158161
159162
const themeStore = useThemeStore()
163+
const chatAccountsStore = useChatAccountsStore()
164+
const realtimeStore = useChatRealtimeStore()
160165
const { isDark } = storeToRefs(themeStore)
166+
const { selectedAccount: selectedDbAccount } = storeToRefs(chatAccountsStore)
167+
const { enabled: realtimeEnabled, changeSeq } = storeToRefs(realtimeStore)
168+
161169
const messages = ref([])
162170
const loadingMessages = ref(false)
163171
const offset = ref(0)
164172
const limit = 20
165173
const hasMore = ref(true)
166174
167175
const messageListRef = ref(null)
176+
let realtimeRefreshFuture = null
177+
let realtimeRefreshQueued = false
168178
169179
// 默认占位图
170180
// const defaultAvatar = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48cmVjdCB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIGZpbGw9IiNlNWU3ZWIiLz48L3N2Zz4='
171181
const defaultImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MDAiIGhlaWdodD0iMTgwIj48cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjE4MCIgZmlsbD0iI2Y1ZjVmNSIvPjwvc3ZnPg=='
172182
173-
const fetchAccounts = async () => {
183+
const getCurrentAccountParam = () => {
184+
const account = String(selectedDbAccount.value || '').trim()
185+
return account || undefined
186+
}
187+
188+
const resetMessagesState = () => {
189+
messages.value = []
190+
offset.value = 0
191+
hasMore.value = true
192+
}
193+
194+
const fetchAccounts = async ({ preserveSelection = true } = {}) => {
174195
loadingAccounts.value = true
196+
const previousUsername = preserveSelection ? String(selectedBizAccount.value?.username || '').trim() : ''
175197
try {
176-
const res = await api.listBizAccounts()
177-
if (res && res.data) {
178-
accounts.value = res.data
198+
const res = await api.listBizAccounts({ account: getCurrentAccountParam() })
199+
const nextAccounts = Array.isArray(res?.data) ? res.data : []
200+
accounts.value = nextAccounts
201+
202+
if (previousUsername) {
203+
selectedBizAccount.value = nextAccounts.find(item => item.username === previousUsername) || null
204+
} else if (!selectedBizAccount.value?.username) {
205+
selectedBizAccount.value = null
179206
}
180207
} catch (err) {
208+
accounts.value = []
209+
selectedBizAccount.value = null
181210
console.error('获取服务号失败:', err)
182211
} finally {
183212
loadingAccounts.value = false
@@ -195,26 +224,29 @@ const filteredAccounts = computed(() => {
195224
})
196225
197226
// 点击选择服务号
198-
const selectAccount = (account) => {
199-
if (selectedAccount.value?.username === account.username) return
200-
selectedAccount.value = account
227+
const selectAccount = async (account) => {
228+
if (selectedBizAccount.value?.username === account.username) return
229+
selectedBizAccount.value = account
201230
202231
// 重置消息状态
203-
messages.value = []
204-
offset.value = 0
205-
hasMore.value = true
232+
resetMessagesState()
206233
207-
loadMessages()
234+
await loadMessages()
208235
}
209236
210237
// 加载消息
211238
const loadMessages = async () => {
212-
if (loadingMessages.value || !hasMore.value || !selectedAccount.value) return
239+
if (loadingMessages.value || !hasMore.value || !selectedBizAccount.value) return
213240
214241
loadingMessages.value = true
215242
try {
216-
const username = selectedAccount.value.username
217-
const params = { username, offset: offset.value, limit }
243+
const username = selectedBizAccount.value.username
244+
const params = {
245+
account: getCurrentAccountParam(),
246+
username,
247+
offset: offset.value,
248+
limit,
249+
}
218250
219251
let res
220252
if (username === 'gh_3dfda90e39d6') {
@@ -238,6 +270,66 @@ const loadMessages = async () => {
238270
}
239271
}
240272
273+
const reloadSelectedMessages = async () => {
274+
if (!selectedBizAccount.value) return
275+
resetMessagesState()
276+
await loadMessages()
277+
}
278+
279+
const syncAllBizRealtime = async ({ forceReload = false } = {}) => {
280+
const priorityUsername = String(selectedBizAccount.value?.username || '').trim()
281+
if (!realtimeEnabled.value) {
282+
if (forceReload) {
283+
await reloadSelectedMessages()
284+
}
285+
return
286+
}
287+
288+
try {
289+
const result = await api.syncChatRealtimeAll({
290+
account: getCurrentAccountParam(),
291+
max_scan: 200,
292+
priority_username: priorityUsername,
293+
priority_max_scan: 400,
294+
include_hidden: true,
295+
include_official: true,
296+
only_official: true,
297+
backfill_limit: 0,
298+
})
299+
const hasDelta = Number(result?.insertedTotal || 0) > 0 || Number(result?.sessionsUpdated || 0) > 0
300+
await fetchAccounts({ preserveSelection: true })
301+
if (selectedBizAccount.value?.username) {
302+
if (hasDelta || forceReload) {
303+
await reloadSelectedMessages()
304+
}
305+
} else if (forceReload) {
306+
resetMessagesState()
307+
}
308+
} catch (err) {
309+
console.error('实时同步服务号失败:', err)
310+
if (forceReload) {
311+
await fetchAccounts({ preserveSelection: true })
312+
await reloadSelectedMessages()
313+
}
314+
}
315+
}
316+
317+
const queueRealtimeBizRefresh = () => {
318+
if (!realtimeEnabled.value) return
319+
if (realtimeRefreshFuture) {
320+
realtimeRefreshQueued = true
321+
return
322+
}
323+
324+
realtimeRefreshFuture = syncAllBizRealtime().finally(() => {
325+
realtimeRefreshFuture = null
326+
if (realtimeRefreshQueued) {
327+
realtimeRefreshQueued = false
328+
queueRealtimeBizRefresh()
329+
}
330+
})
331+
}
332+
241333
// 向上滚动加载逻辑
242334
// 因为容器设置了 flex-col-reverse,所以 scrollTop 越靠近负值(或0取决于浏览器)越是到了历史消息端
243335
// 但比较通用兼容的做法是监听 scroll,距离顶部或底部小于阈值时触发
@@ -250,8 +342,40 @@ const handleScroll = (e) => {
250342
}
251343
}
252344
253-
onMounted(() => {
254-
fetchAccounts()
345+
watch(selectedDbAccount, async (next, prev) => {
346+
if (String(next || '').trim() === String(prev || '').trim()) return
347+
selectedBizAccount.value = null
348+
resetMessagesState()
349+
searchQuery.value = ''
350+
351+
if (!String(next || '').trim()) {
352+
accounts.value = []
353+
return
354+
}
355+
await fetchAccounts({ preserveSelection: false })
356+
if (realtimeEnabled.value) {
357+
await syncAllBizRealtime({ forceReload: true })
358+
}
359+
})
360+
361+
watch(changeSeq, (next, prev) => {
362+
if (!realtimeEnabled.value) return
363+
if (next === prev) return
364+
queueRealtimeBizRefresh()
365+
})
366+
367+
watch(realtimeEnabled, async (enabled, wasEnabled) => {
368+
if (enabled && !wasEnabled) {
369+
await syncAllBizRealtime({ forceReload: true })
370+
}
371+
})
372+
373+
onMounted(async () => {
374+
await chatAccountsStore.ensureLoaded()
375+
await fetchAccounts({ preserveSelection: false })
376+
if (realtimeEnabled.value) {
377+
await syncAllBizRealtime({ forceReload: true })
378+
}
255379
})
256380
</script>
257381
@@ -267,4 +391,4 @@ onMounted(() => {
267391
background-color: rgba(0,0,0,0.1);
268392
border-radius: 10px;
269393
}
270-
</style>
394+
</style>

frontend/composables/useApi.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ export const useApi = () => {
205205
if (params && params.priority_max_scan != null) query.set('priority_max_scan', String(params.priority_max_scan))
206206
if (params && params.include_hidden != null) query.set('include_hidden', String(!!params.include_hidden))
207207
if (params && params.include_official != null) query.set('include_official', String(!!params.include_official))
208+
if (params && params.only_official != null) query.set('only_official', String(!!params.only_official))
209+
if (params && params.backfill_limit != null) query.set('backfill_limit', String(params.backfill_limit))
208210
const url = '/chat/realtime/sync_all' + (query.toString() ? `?${query.toString()}` : '')
209211
return await request(url, { method: 'POST' })
210212
}

0 commit comments

Comments
 (0)