mirror of
https://github.com/LifeArchiveProject/WeChatDataAnalysis.git
synced 2026-06-18 15:54:08 +08:00
Add detailed chat search click tracing
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -128,7 +128,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="search-sidebar-close"
|
class="search-sidebar-close"
|
||||||
@click="closeMessageSearch"
|
@click="closeMessageSearch('close-button')"
|
||||||
title="关闭搜索 (Esc)"
|
title="关闭搜索 (Esc)"
|
||||||
>
|
>
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -175,9 +175,9 @@
|
|||||||
:class="{ 'privacy-blur': privacyMode }"
|
:class="{ 'privacy-blur': privacyMode }"
|
||||||
@focus="searchInputFocused = true"
|
@focus="searchInputFocused = true"
|
||||||
@blur="searchInputFocused = false"
|
@blur="searchInputFocused = false"
|
||||||
@keydown.enter.exact.prevent="runMessageSearch({ reset: true })"
|
@keydown.enter.exact.prevent="runMessageSearch({ reset: true, source: 'input-enter' })"
|
||||||
@keydown.enter.shift.prevent="onSearchPrev"
|
@keydown.enter.shift.prevent="onSearchPrev"
|
||||||
@keydown.escape="closeMessageSearch"
|
@keydown.escape="closeMessageSearch('input-escape')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 清除按钮 -->
|
<!-- 清除按钮 -->
|
||||||
@@ -185,7 +185,7 @@
|
|||||||
v-if="messageSearchQuery"
|
v-if="messageSearchQuery"
|
||||||
type="button"
|
type="button"
|
||||||
class="search-clear-inline"
|
class="search-clear-inline"
|
||||||
@click="messageSearchQuery = ''; runMessageSearch({ reset: true })"
|
@click="messageSearchQuery = ''; runMessageSearch({ reset: true, source: 'clear-button' })"
|
||||||
>
|
>
|
||||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
||||||
@@ -197,7 +197,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
class="search-btn-inline"
|
class="search-btn-inline"
|
||||||
:disabled="messageSearchLoading"
|
:disabled="messageSearchLoading"
|
||||||
@click="runMessageSearch({ reset: true })"
|
@click="runMessageSearch({ reset: true, source: 'search-button' })"
|
||||||
>
|
>
|
||||||
<svg v-if="messageSearchLoading" class="animate-spin w-4 h-4" fill="none" viewBox="0 0 24 24">
|
<svg v-if="messageSearchLoading" class="animate-spin w-4 h-4" fill="none" viewBox="0 0 24 24">
|
||||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||||
@@ -428,6 +428,8 @@
|
|||||||
:key="hit.id + ':' + idx"
|
:key="hit.id + ':' + idx"
|
||||||
class="sidebar-result-card"
|
class="sidebar-result-card"
|
||||||
:class="{ 'sidebar-result-card-selected': idx === messageSearchSelectedIndex }"
|
:class="{ 'sidebar-result-card-selected': idx === messageSearchSelectedIndex }"
|
||||||
|
@pointerdown="onSearchHitPointerDown(hit, idx, $event)"
|
||||||
|
@click.capture="onSearchHitClickCapture(hit, idx, $event)"
|
||||||
@click="onSearchHitClick(hit, idx)"
|
@click="onSearchHitClick(hit, idx)"
|
||||||
>
|
>
|
||||||
<div class="sidebar-result-row">
|
<div class="sidebar-result-row">
|
||||||
|
|||||||
@@ -65,6 +65,52 @@ if (isDesktopRenderer()) {
|
|||||||
console.info(`[chat-search] ${phase}`, payload)
|
console.info(`[chat-search] ${phase}`, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const describeEventTarget = (target) => {
|
||||||
|
if (!target || typeof target !== 'object') return ''
|
||||||
|
const nodeName = String(target.nodeName || '').trim().toLowerCase()
|
||||||
|
let cls = ''
|
||||||
|
try {
|
||||||
|
cls = typeof target.className === 'string'
|
||||||
|
? target.className
|
||||||
|
: String(target.className?.baseVal || '')
|
||||||
|
} catch {
|
||||||
|
cls = ''
|
||||||
|
}
|
||||||
|
cls = String(cls || '').trim().replace(/\s+/g, '.')
|
||||||
|
if (!nodeName) return cls.slice(0, 120)
|
||||||
|
if (!cls) return nodeName
|
||||||
|
return `${nodeName}.${cls}`.slice(0, 120)
|
||||||
|
}
|
||||||
|
|
||||||
|
const summarizeHitForLog = (hit, idx) => ({
|
||||||
|
index: Number(idx ?? -1),
|
||||||
|
hitId: String(hit?.id || '').trim(),
|
||||||
|
hitUsername: String(hit?.username || '').trim(),
|
||||||
|
conversationName: String(hit?.conversationName || '').trim(),
|
||||||
|
senderUsername: String(hit?.senderUsername || '').trim(),
|
||||||
|
renderType: String(hit?.renderType || '').trim(),
|
||||||
|
createTime: Number(hit?.createTime || 0),
|
||||||
|
isSent: !!hit?.isSent
|
||||||
|
})
|
||||||
|
|
||||||
|
const buildRunSearchLogDetails = ({ source = 'unknown', reset = false } = {}) => {
|
||||||
|
const q = String(messageSearchQuery.value || '').trim()
|
||||||
|
return {
|
||||||
|
source: String(source || 'unknown'),
|
||||||
|
reset: !!reset,
|
||||||
|
scope: String(messageSearchScope.value || 'conversation'),
|
||||||
|
queryLength: q.length,
|
||||||
|
selectedContactUsername: String(selectedContact.value?.username || '').trim(),
|
||||||
|
sender: String(messageSearchSender.value || '').trim(),
|
||||||
|
sessionType: String(messageSearchSessionType.value || '').trim(),
|
||||||
|
rangeDays: String(messageSearchRangeDays.value || '').trim(),
|
||||||
|
startDate: String(messageSearchStartDate.value || '').trim(),
|
||||||
|
endDate: String(messageSearchEndDate.value || '').trim(),
|
||||||
|
offset: Number(messageSearchOffset.value || 0),
|
||||||
|
resultCount: Number(messageSearchResults.value?.length || 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const messageSearchOpen = ref(false)
|
const messageSearchOpen = ref(false)
|
||||||
const messageSearchQuery = ref('')
|
const messageSearchQuery = ref('')
|
||||||
const messageSearchScope = ref('global') // conversation | global
|
const messageSearchScope = ref('global') // conversation | global
|
||||||
@@ -151,7 +197,7 @@ try {
|
|||||||
// 应用搜索历史
|
// 应用搜索历史
|
||||||
const applySearchHistory = async (query) => {
|
const applySearchHistory = async (query) => {
|
||||||
messageSearchQuery.value = query
|
messageSearchQuery.value = query
|
||||||
await runMessageSearch({ reset: true })
|
await runMessageSearch({ reset: true, source: 'history-apply' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageSearchIndexExists = computed(() => !!messageSearchIndexInfo.value?.exists)
|
const messageSearchIndexExists = computed(() => !!messageSearchIndexInfo.value?.exists)
|
||||||
@@ -296,11 +342,22 @@ closeMessageSearchSenderDropdown()
|
|||||||
|
|
||||||
const fetchMessageSearchIndexStatus = async () => {
|
const fetchMessageSearchIndexStatus = async () => {
|
||||||
if (!selectedAccount.value) return null
|
if (!selectedAccount.value) return null
|
||||||
|
logSearchPhase('search-index-status:start', {
|
||||||
|
queryLength: String(messageSearchQuery.value || '').trim().length
|
||||||
|
})
|
||||||
try {
|
try {
|
||||||
const resp = await api.getChatSearchIndexStatus({ account: selectedAccount.value })
|
const resp = await api.getChatSearchIndexStatus({ account: selectedAccount.value })
|
||||||
messageSearchIndexInfo.value = resp?.index || null
|
messageSearchIndexInfo.value = resp?.index || null
|
||||||
|
logSearchPhase('search-index-status:end', {
|
||||||
|
exists: !!messageSearchIndexInfo.value?.exists,
|
||||||
|
ready: !!messageSearchIndexInfo.value?.ready,
|
||||||
|
buildStatus: String(messageSearchIndexInfo.value?.build?.status || '').trim()
|
||||||
|
})
|
||||||
return messageSearchIndexInfo.value
|
return messageSearchIndexInfo.value
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
logSearchPhase('search-index-status:error', {
|
||||||
|
error: String(e?.message || e || '')
|
||||||
|
})
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,6 +367,7 @@ messageSearchSenderError.value = ''
|
|||||||
if (!selectedAccount.value) {
|
if (!selectedAccount.value) {
|
||||||
messageSearchSenderOptions.value = []
|
messageSearchSenderOptions.value = []
|
||||||
messageSearchSenderOptionsKey.value = ''
|
messageSearchSenderOptionsKey.value = ''
|
||||||
|
logSearchPhase('search-senders:skip:no-account')
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,6 +383,9 @@ if (scope === 'conversation') {
|
|||||||
if (!selectedContact.value?.username) {
|
if (!selectedContact.value?.username) {
|
||||||
messageSearchSenderOptions.value = []
|
messageSearchSenderOptions.value = []
|
||||||
messageSearchSenderOptionsKey.value = ''
|
messageSearchSenderOptionsKey.value = ''
|
||||||
|
logSearchPhase('search-senders:skip:no-selected-contact', {
|
||||||
|
scope
|
||||||
|
})
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
params.username = selectedContact.value.username
|
params.username = selectedContact.value.username
|
||||||
@@ -332,6 +393,10 @@ if (scope === 'conversation') {
|
|||||||
if (msgQ.length < 2) {
|
if (msgQ.length < 2) {
|
||||||
messageSearchSenderOptions.value = []
|
messageSearchSenderOptions.value = []
|
||||||
messageSearchSenderOptionsKey.value = ''
|
messageSearchSenderOptionsKey.value = ''
|
||||||
|
logSearchPhase('search-senders:skip:query-too-short', {
|
||||||
|
scope,
|
||||||
|
queryLength: msgQ.length
|
||||||
|
})
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -370,10 +435,21 @@ if (scope === 'global') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
messageSearchSenderLoading.value = true
|
messageSearchSenderLoading.value = true
|
||||||
|
logSearchPhase('search-senders:start', {
|
||||||
|
scope,
|
||||||
|
queryLength: msgQ.length,
|
||||||
|
username: String(params.username || '').trim()
|
||||||
|
})
|
||||||
try {
|
try {
|
||||||
const resp = await api.listChatSearchSenders(params)
|
const resp = await api.listChatSearchSenders(params)
|
||||||
const status = String(resp?.status || 'success')
|
const status = String(resp?.status || 'success')
|
||||||
if (status !== 'success') {
|
if (status !== 'success') {
|
||||||
|
logSearchPhase('search-senders:non-success', {
|
||||||
|
scope,
|
||||||
|
status,
|
||||||
|
queryLength: msgQ.length,
|
||||||
|
message: String(resp?.message || '')
|
||||||
|
})
|
||||||
if (status !== 'index_building') {
|
if (status !== 'index_building') {
|
||||||
messageSearchSenderError.value = String(resp?.message || '加载发送者失败')
|
messageSearchSenderError.value = String(resp?.message || '加载发送者失败')
|
||||||
}
|
}
|
||||||
@@ -388,11 +464,22 @@ try {
|
|||||||
if (cur && !list.some((s) => String(s?.username || '').trim() === cur)) {
|
if (cur && !list.some((s) => String(s?.username || '').trim() === cur)) {
|
||||||
messageSearchSender.value = ''
|
messageSearchSender.value = ''
|
||||||
}
|
}
|
||||||
|
logSearchPhase('search-senders:end', {
|
||||||
|
scope,
|
||||||
|
queryLength: msgQ.length,
|
||||||
|
username: String(params.username || '').trim(),
|
||||||
|
optionCount: list.length
|
||||||
|
})
|
||||||
return list
|
return list
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
messageSearchSenderError.value = e?.message || '加载发送者失败'
|
messageSearchSenderError.value = e?.message || '加载发送者失败'
|
||||||
messageSearchSenderOptions.value = []
|
messageSearchSenderOptions.value = []
|
||||||
messageSearchSenderOptionsKey.value = ''
|
messageSearchSenderOptionsKey.value = ''
|
||||||
|
logSearchPhase('search-senders:error', {
|
||||||
|
scope,
|
||||||
|
queryLength: msgQ.length,
|
||||||
|
error: String(e?.message || e || '')
|
||||||
|
})
|
||||||
return []
|
return []
|
||||||
} finally {
|
} finally {
|
||||||
messageSearchSenderLoading.value = false
|
messageSearchSenderLoading.value = false
|
||||||
@@ -423,7 +510,7 @@ messageSearchIndexPollTimer = setInterval(async () => {
|
|||||||
await fetchMessageSearchSenders()
|
await fetchMessageSearchSenders()
|
||||||
}
|
}
|
||||||
if (String(messageSearchQuery.value || '').trim()) {
|
if (String(messageSearchQuery.value || '').trim()) {
|
||||||
await runMessageSearch({ reset: true })
|
await runMessageSearch({ reset: true, source: 'index-poll-ready' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 1200)
|
}, 1200)
|
||||||
@@ -645,7 +732,13 @@ for (let i = 0; i < 42; i++) {
|
|||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
})
|
})
|
||||||
const closeMessageSearch = () => {
|
const closeMessageSearch = (reason = 'manual') => {
|
||||||
|
logSearchPhase('message-search:close', {
|
||||||
|
reason: String(reason || 'manual'),
|
||||||
|
queryLength: String(messageSearchQuery.value || '').trim().length,
|
||||||
|
resultCount: Number(messageSearchResults.value?.length || 0),
|
||||||
|
selectedIndex: Number(messageSearchSelectedIndex.value ?? -1)
|
||||||
|
})
|
||||||
messageSearchOpen.value = false
|
messageSearchOpen.value = false
|
||||||
closeMessageSearchSenderDropdown()
|
closeMessageSearchSenderDropdown()
|
||||||
messageSearchError.value = ''
|
messageSearchError.value = ''
|
||||||
@@ -820,29 +913,47 @@ if (messageSearchScope.value === 'conversation' && !selectedContact.value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const toggleMessageSearch = async () => {
|
const toggleMessageSearch = async () => {
|
||||||
messageSearchOpen.value = !messageSearchOpen.value
|
const nextOpen = !messageSearchOpen.value
|
||||||
|
logSearchPhase('message-search:toggle', {
|
||||||
|
nextOpen,
|
||||||
|
queryLength: String(messageSearchQuery.value || '').trim().length,
|
||||||
|
resultCount: Number(messageSearchResults.value?.length || 0)
|
||||||
|
})
|
||||||
|
messageSearchOpen.value = nextOpen
|
||||||
ensureMessageSearchScopeValid()
|
ensureMessageSearchScopeValid()
|
||||||
if (!messageSearchOpen.value) return
|
if (!messageSearchOpen.value) {
|
||||||
|
closeMessageSearch('toggle-close')
|
||||||
|
return
|
||||||
|
}
|
||||||
closeTimeSidebar()
|
closeTimeSidebar()
|
||||||
await nextTick()
|
await nextTick()
|
||||||
try {
|
try {
|
||||||
messageSearchInputRef.value?.focus?.()
|
messageSearchInputRef.value?.focus?.()
|
||||||
} catch {}
|
} catch {}
|
||||||
|
logSearchPhase('message-search:open:ready', {
|
||||||
|
scope: String(messageSearchScope.value || 'conversation'),
|
||||||
|
selectedContactUsername: String(selectedContact.value?.username || '').trim()
|
||||||
|
})
|
||||||
await fetchMessageSearchIndexStatus()
|
await fetchMessageSearchIndexStatus()
|
||||||
await fetchMessageSearchSenders()
|
await fetchMessageSearchSenders()
|
||||||
if (String(messageSearchQuery.value || '').trim()) {
|
if (String(messageSearchQuery.value || '').trim()) {
|
||||||
await runMessageSearch({ reset: true })
|
await runMessageSearch({ reset: true, source: 'toggle-open-with-query' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let messageSearchReqId = 0
|
let messageSearchReqId = 0
|
||||||
|
|
||||||
const runMessageSearch = async ({ reset } = {}) => {
|
const runMessageSearch = async ({ reset, source = 'unknown' } = {}) => {
|
||||||
if (!selectedAccount.value) return
|
if (!selectedAccount.value) {
|
||||||
|
logSearchPhase('runMessageSearch:skip:no-account', buildRunSearchLogDetails({ source, reset }))
|
||||||
|
return
|
||||||
|
}
|
||||||
ensureMessageSearchScopeValid()
|
ensureMessageSearchScopeValid()
|
||||||
|
|
||||||
const q = String(messageSearchQuery.value || '').trim()
|
const q = String(messageSearchQuery.value || '').trim()
|
||||||
|
logSearchPhase('runMessageSearch:start', buildRunSearchLogDetails({ source, reset }))
|
||||||
if (!q) {
|
if (!q) {
|
||||||
|
logSearchPhase('runMessageSearch:skip:empty-query', buildRunSearchLogDetails({ source, reset }))
|
||||||
messageSearchResults.value = []
|
messageSearchResults.value = []
|
||||||
messageSearchHasMore.value = false
|
messageSearchHasMore.value = false
|
||||||
messageSearchError.value = ''
|
messageSearchError.value = ''
|
||||||
@@ -863,6 +974,10 @@ const reqId = ++messageSearchReqId
|
|||||||
messageSearchLoading.value = true
|
messageSearchLoading.value = true
|
||||||
messageSearchError.value = ''
|
messageSearchError.value = ''
|
||||||
messageSearchBackendStatus.value = ''
|
messageSearchBackendStatus.value = ''
|
||||||
|
logSearchPhase('runMessageSearch:request:start', {
|
||||||
|
...buildRunSearchLogDetails({ source, reset }),
|
||||||
|
reqId
|
||||||
|
})
|
||||||
|
|
||||||
const scope = String(messageSearchScope.value || 'conversation')
|
const scope = String(messageSearchScope.value || 'conversation')
|
||||||
|
|
||||||
@@ -907,6 +1022,7 @@ if (String(messageSearchSender.value || '').trim()) {
|
|||||||
|
|
||||||
if (scope === 'conversation') {
|
if (scope === 'conversation') {
|
||||||
if (!selectedContact.value?.username) {
|
if (!selectedContact.value?.username) {
|
||||||
|
logSearchPhase('runMessageSearch:skip:no-selected-contact', buildRunSearchLogDetails({ source, reset }))
|
||||||
messageSearchLoading.value = false
|
messageSearchLoading.value = false
|
||||||
messageSearchError.value = '请选择一个会话再搜索'
|
messageSearchError.value = '请选择一个会话再搜索'
|
||||||
return
|
return
|
||||||
@@ -916,7 +1032,15 @@ if (scope === 'conversation') {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await api.searchChatMessages(params)
|
const resp = await api.searchChatMessages(params)
|
||||||
if (reqId !== messageSearchReqId) return
|
if (reqId !== messageSearchReqId) {
|
||||||
|
logSearchPhase('runMessageSearch:response:stale', {
|
||||||
|
...buildRunSearchLogDetails({ source, reset }),
|
||||||
|
reqId,
|
||||||
|
activeReqId: Number(messageSearchReqId || 0),
|
||||||
|
status: String(resp?.status || '').trim()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (resp?.index) {
|
if (resp?.index) {
|
||||||
messageSearchIndexInfo.value = resp.index
|
messageSearchIndexInfo.value = resp.index
|
||||||
@@ -926,6 +1050,11 @@ try {
|
|||||||
messageSearchBackendStatus.value = status
|
messageSearchBackendStatus.value = status
|
||||||
|
|
||||||
if (status === 'index_building') {
|
if (status === 'index_building') {
|
||||||
|
logSearchPhase('runMessageSearch:response:index-building', {
|
||||||
|
...buildRunSearchLogDetails({ source, reset }),
|
||||||
|
reqId,
|
||||||
|
status
|
||||||
|
})
|
||||||
if (reset) {
|
if (reset) {
|
||||||
messageSearchResults.value = []
|
messageSearchResults.value = []
|
||||||
messageSearchSelectedIndex.value = -1
|
messageSearchSelectedIndex.value = -1
|
||||||
@@ -937,6 +1066,12 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (status === 'index_error') {
|
if (status === 'index_error') {
|
||||||
|
logSearchPhase('runMessageSearch:response:index-error', {
|
||||||
|
...buildRunSearchLogDetails({ source, reset }),
|
||||||
|
reqId,
|
||||||
|
status,
|
||||||
|
message: String(resp?.message || '')
|
||||||
|
})
|
||||||
if (reset) {
|
if (reset) {
|
||||||
messageSearchResults.value = []
|
messageSearchResults.value = []
|
||||||
messageSearchSelectedIndex.value = -1
|
messageSearchSelectedIndex.value = -1
|
||||||
@@ -949,6 +1084,12 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (status !== 'success') {
|
if (status !== 'success') {
|
||||||
|
logSearchPhase('runMessageSearch:response:non-success', {
|
||||||
|
...buildRunSearchLogDetails({ source, reset }),
|
||||||
|
reqId,
|
||||||
|
status,
|
||||||
|
message: String(resp?.message || '')
|
||||||
|
})
|
||||||
if (reset) {
|
if (reset) {
|
||||||
messageSearchResults.value = []
|
messageSearchResults.value = []
|
||||||
messageSearchSelectedIndex.value = -1
|
messageSearchSelectedIndex.value = -1
|
||||||
@@ -974,6 +1115,17 @@ try {
|
|||||||
messageSearchSelectedIndex.value = 0
|
messageSearchSelectedIndex.value = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logSearchPhase('runMessageSearch:response:success', {
|
||||||
|
...buildRunSearchLogDetails({ source, reset }),
|
||||||
|
reqId,
|
||||||
|
status,
|
||||||
|
hitsCount: hits.length,
|
||||||
|
total: Number(resp?.total ?? resp?.totalInScan ?? 0),
|
||||||
|
hasMore: !!resp?.hasMore,
|
||||||
|
backendScope: String(resp?.scope || '').trim(),
|
||||||
|
backendUsername: String(resp?.username || '').trim()
|
||||||
|
})
|
||||||
|
|
||||||
// 保存搜索历史(仅在有结果时保存)
|
// 保存搜索历史(仅在有结果时保存)
|
||||||
if (!privacyMode.value && reset && hits.length > 0) {
|
if (!privacyMode.value && reset && hits.length > 0) {
|
||||||
saveSearchHistory(q)
|
saveSearchHistory(q)
|
||||||
@@ -981,9 +1133,20 @@ try {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (reqId !== messageSearchReqId) return
|
if (reqId !== messageSearchReqId) return
|
||||||
messageSearchError.value = e?.message || '搜索失败'
|
messageSearchError.value = e?.message || '搜索失败'
|
||||||
|
logSearchPhase('runMessageSearch:error', {
|
||||||
|
...buildRunSearchLogDetails({ source, reset }),
|
||||||
|
reqId,
|
||||||
|
error: String(e?.message || e || '')
|
||||||
|
})
|
||||||
} finally {
|
} finally {
|
||||||
if (reqId === messageSearchReqId) {
|
if (reqId === messageSearchReqId) {
|
||||||
messageSearchLoading.value = false
|
messageSearchLoading.value = false
|
||||||
|
logSearchPhase('runMessageSearch:finalize', {
|
||||||
|
...buildRunSearchLogDetails({ source, reset }),
|
||||||
|
reqId,
|
||||||
|
loading: !!messageSearchLoading.value,
|
||||||
|
error: String(messageSearchError.value || '')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -992,7 +1155,7 @@ const loadMoreSearchResults = async () => {
|
|||||||
if (!messageSearchHasMore.value) return
|
if (!messageSearchHasMore.value) return
|
||||||
if (messageSearchLoading.value) return
|
if (messageSearchLoading.value) return
|
||||||
messageSearchOffset.value = Number(messageSearchOffset.value || 0) + messageSearchLimit
|
messageSearchOffset.value = Number(messageSearchOffset.value || 0) + messageSearchLimit
|
||||||
await runMessageSearch({ reset: false })
|
await runMessageSearch({ reset: false, source: 'load-more' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const exitSearchContext = async () => {
|
const exitSearchContext = async () => {
|
||||||
@@ -1075,6 +1238,10 @@ logSearchPhase('locateSearchHit:target-resolved', {
|
|||||||
: 'none'
|
: 'none'
|
||||||
})
|
})
|
||||||
if (targetContact && selectedContact.value?.username !== targetUsername) {
|
if (targetContact && selectedContact.value?.username !== targetUsername) {
|
||||||
|
logSearchPhase('locateSearchHit:selectContact:start', {
|
||||||
|
hitId: String(hit?.id || '').trim(),
|
||||||
|
targetUsername
|
||||||
|
})
|
||||||
await selectContact(targetContact, { skipLoadMessages: true })
|
await selectContact(targetContact, { skipLoadMessages: true })
|
||||||
logSearchPhase('locateSearchHit:selectContact:done', {
|
logSearchPhase('locateSearchHit:selectContact:done', {
|
||||||
hitId: String(hit?.id || '').trim(),
|
hitId: String(hit?.id || '').trim(),
|
||||||
@@ -1114,6 +1281,12 @@ if (!searchContext.value?.active) {
|
|||||||
searchContext.value.loadingBefore = false
|
searchContext.value.loadingBefore = false
|
||||||
searchContext.value.loadingAfter = false
|
searchContext.value.loadingAfter = false
|
||||||
}
|
}
|
||||||
|
logSearchPhase('locateSearchHit:search-context:ready', {
|
||||||
|
hitId: String(hit?.id || '').trim(),
|
||||||
|
targetUsername,
|
||||||
|
contextActive: !!searchContext.value?.active,
|
||||||
|
savedMessagesCount: Number(searchContext.value?.savedMessages?.length || 0)
|
||||||
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logSearchPhase('locateSearchHit:messagesAround:start', {
|
logSearchPhase('locateSearchHit:messagesAround:start', {
|
||||||
@@ -1145,6 +1318,12 @@ try {
|
|||||||
searchContext.value.anchorId = String(resp?.anchorId || hit.id)
|
searchContext.value.anchorId = String(resp?.anchorId || hit.id)
|
||||||
searchContext.value.anchorIndex = Number(resp?.anchorIndex ?? -1)
|
searchContext.value.anchorIndex = Number(resp?.anchorIndex ?? -1)
|
||||||
|
|
||||||
|
logSearchPhase('locateSearchHit:scroll:start', {
|
||||||
|
hitId: String(hit?.id || '').trim(),
|
||||||
|
targetUsername,
|
||||||
|
anchorId: String(searchContext.value.anchorId || '').trim(),
|
||||||
|
messageCount: mapped.length
|
||||||
|
})
|
||||||
const ok = await scrollToMessageId(searchContext.value.anchorId)
|
const ok = await scrollToMessageId(searchContext.value.anchorId)
|
||||||
logSearchPhase('locateSearchHit:scroll:end', {
|
logSearchPhase('locateSearchHit:scroll:end', {
|
||||||
hitId: String(hit?.id || '').trim(),
|
hitId: String(hit?.id || '').trim(),
|
||||||
@@ -1441,14 +1620,37 @@ try {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onSearchHitPointerDown = (hit, idx, event) => {
|
||||||
|
logSearchPhase('search-result:pointerdown', {
|
||||||
|
...summarizeHitForLog(hit, idx),
|
||||||
|
button: Number(event?.button ?? -1),
|
||||||
|
detail: Number(event?.detail ?? 0),
|
||||||
|
target: describeEventTarget(event?.target),
|
||||||
|
currentTarget: describeEventTarget(event?.currentTarget)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSearchHitClickCapture = (hit, idx, event) => {
|
||||||
|
logSearchPhase('search-result:click-capture', {
|
||||||
|
...summarizeHitForLog(hit, idx),
|
||||||
|
button: Number(event?.button ?? -1),
|
||||||
|
detail: Number(event?.detail ?? 0),
|
||||||
|
target: describeEventTarget(event?.target),
|
||||||
|
currentTarget: describeEventTarget(event?.currentTarget)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const onSearchHitClick = async (hit, idx) => {
|
const onSearchHitClick = async (hit, idx) => {
|
||||||
messageSearchSelectedIndex.value = Number(idx || 0)
|
messageSearchSelectedIndex.value = Number(idx || 0)
|
||||||
logSearchPhase('onSearchHitClick', {
|
logSearchPhase('onSearchHitClick', {
|
||||||
index: Number(idx || 0),
|
...summarizeHitForLog(hit, idx),
|
||||||
hitId: String(hit?.id || '').trim(),
|
selectedIndex: Number(messageSearchSelectedIndex.value || 0)
|
||||||
hitUsername: String(hit?.username || '').trim()
|
|
||||||
})
|
})
|
||||||
await locateSearchHit(hit)
|
await locateSearchHit(hit)
|
||||||
|
logSearchPhase('onSearchHitClick:done', {
|
||||||
|
...summarizeHitForLog(hit, idx),
|
||||||
|
selectedIndex: Number(messageSearchSelectedIndex.value || 0)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSearchNext = async () => {
|
const onSearchNext = async () => {
|
||||||
@@ -1456,7 +1658,7 @@ const q = String(messageSearchQuery.value || '').trim()
|
|||||||
if (!q) return
|
if (!q) return
|
||||||
|
|
||||||
if (!messageSearchResults.value.length && !messageSearchLoading.value) {
|
if (!messageSearchResults.value.length && !messageSearchLoading.value) {
|
||||||
await runMessageSearch({ reset: true })
|
await runMessageSearch({ reset: true, source: 'search-next-bootstrap' })
|
||||||
}
|
}
|
||||||
if (!messageSearchResults.value.length) return
|
if (!messageSearchResults.value.length) return
|
||||||
|
|
||||||
@@ -1471,7 +1673,7 @@ const q = String(messageSearchQuery.value || '').trim()
|
|||||||
if (!q) return
|
if (!q) return
|
||||||
|
|
||||||
if (!messageSearchResults.value.length && !messageSearchLoading.value) {
|
if (!messageSearchResults.value.length && !messageSearchLoading.value) {
|
||||||
await runMessageSearch({ reset: true })
|
await runMessageSearch({ reset: true, source: 'search-prev-bootstrap' })
|
||||||
}
|
}
|
||||||
if (!messageSearchResults.value.length) return
|
if (!messageSearchResults.value.length) return
|
||||||
|
|
||||||
@@ -1484,13 +1686,27 @@ const openMessageSearch = async () => {
|
|||||||
closeTimeSidebar()
|
closeTimeSidebar()
|
||||||
messageSearchOpen.value = true
|
messageSearchOpen.value = true
|
||||||
ensureMessageSearchScopeValid()
|
ensureMessageSearchScopeValid()
|
||||||
|
logSearchPhase('message-search:open:start', {
|
||||||
|
scope: String(messageSearchScope.value || 'conversation'),
|
||||||
|
queryLength: String(messageSearchQuery.value || '').trim().length
|
||||||
|
})
|
||||||
await nextTick()
|
await nextTick()
|
||||||
try {
|
try {
|
||||||
messageSearchInputRef.value?.focus?.()
|
messageSearchInputRef.value?.focus?.()
|
||||||
} catch {}
|
} catch {}
|
||||||
await fetchMessageSearchIndexStatus()
|
await fetchMessageSearchIndexStatus()
|
||||||
|
logSearchPhase('message-search:open:end', {
|
||||||
|
scope: String(messageSearchScope.value || 'conversation'),
|
||||||
|
queryLength: String(messageSearchQuery.value || '').trim().length
|
||||||
|
})
|
||||||
}
|
}
|
||||||
watch(messageSearchScope, async () => {
|
watch(messageSearchScope, async (next, prev) => {
|
||||||
|
logSearchPhase('message-search-scope:change', {
|
||||||
|
previous: String(prev || '').trim(),
|
||||||
|
next: String(next || '').trim(),
|
||||||
|
open: !!messageSearchOpen.value,
|
||||||
|
queryLength: String(messageSearchQuery.value || '').trim().length
|
||||||
|
})
|
||||||
if (!messageSearchOpen.value) return
|
if (!messageSearchOpen.value) return
|
||||||
ensureMessageSearchScopeValid()
|
ensureMessageSearchScopeValid()
|
||||||
closeMessageSearchSenderDropdown()
|
closeMessageSearchSenderDropdown()
|
||||||
@@ -1502,22 +1718,34 @@ messageSearchOffset.value = 0
|
|||||||
messageSearchResults.value = []
|
messageSearchResults.value = []
|
||||||
messageSearchSelectedIndex.value = -1
|
messageSearchSelectedIndex.value = -1
|
||||||
if (String(messageSearchQuery.value || '').trim()) {
|
if (String(messageSearchQuery.value || '').trim()) {
|
||||||
await runMessageSearch({ reset: true })
|
await runMessageSearch({ reset: true, source: 'scope-change' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(messageSearchRangeDays, async () => {
|
watch(messageSearchRangeDays, async (next, prev) => {
|
||||||
|
logSearchPhase('message-search-range:change', {
|
||||||
|
previous: String(prev || '').trim(),
|
||||||
|
next: String(next || '').trim(),
|
||||||
|
open: !!messageSearchOpen.value,
|
||||||
|
queryLength: String(messageSearchQuery.value || '').trim().length
|
||||||
|
})
|
||||||
if (!messageSearchOpen.value) return
|
if (!messageSearchOpen.value) return
|
||||||
closeMessageSearchSenderDropdown()
|
closeMessageSearchSenderDropdown()
|
||||||
messageSearchOffset.value = 0
|
messageSearchOffset.value = 0
|
||||||
messageSearchResults.value = []
|
messageSearchResults.value = []
|
||||||
messageSearchSelectedIndex.value = -1
|
messageSearchSelectedIndex.value = -1
|
||||||
if (String(messageSearchQuery.value || '').trim()) {
|
if (String(messageSearchQuery.value || '').trim()) {
|
||||||
await runMessageSearch({ reset: true })
|
await runMessageSearch({ reset: true, source: 'range-change' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(messageSearchSessionType, async () => {
|
watch(messageSearchSessionType, async (next, prev) => {
|
||||||
|
logSearchPhase('message-search-session-type:change', {
|
||||||
|
previous: String(prev || '').trim(),
|
||||||
|
next: String(next || '').trim(),
|
||||||
|
open: !!messageSearchOpen.value,
|
||||||
|
queryLength: String(messageSearchQuery.value || '').trim().length
|
||||||
|
})
|
||||||
if (!messageSearchOpen.value) return
|
if (!messageSearchOpen.value) return
|
||||||
if (String(messageSearchScope.value || '') !== 'global') return
|
if (String(messageSearchScope.value || '') !== 'global') return
|
||||||
closeMessageSearchSenderDropdown()
|
closeMessageSearchSenderDropdown()
|
||||||
@@ -1529,11 +1757,18 @@ messageSearchOffset.value = 0
|
|||||||
messageSearchResults.value = []
|
messageSearchResults.value = []
|
||||||
messageSearchSelectedIndex.value = -1
|
messageSearchSelectedIndex.value = -1
|
||||||
if (String(messageSearchQuery.value || '').trim()) {
|
if (String(messageSearchQuery.value || '').trim()) {
|
||||||
await runMessageSearch({ reset: true })
|
await runMessageSearch({ reset: true, source: 'session-type-change' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
watch([messageSearchStartDate, messageSearchEndDate], async () => {
|
watch([messageSearchStartDate, messageSearchEndDate], async ([nextStart, nextEnd], [prevStart, prevEnd]) => {
|
||||||
|
logSearchPhase('message-search-custom-range:change', {
|
||||||
|
previousStart: String(prevStart || '').trim(),
|
||||||
|
previousEnd: String(prevEnd || '').trim(),
|
||||||
|
nextStart: String(nextStart || '').trim(),
|
||||||
|
nextEnd: String(nextEnd || '').trim(),
|
||||||
|
open: !!messageSearchOpen.value
|
||||||
|
})
|
||||||
if (!messageSearchOpen.value) return
|
if (!messageSearchOpen.value) return
|
||||||
if (String(messageSearchRangeDays.value || '') !== 'custom') return
|
if (String(messageSearchRangeDays.value || '') !== 'custom') return
|
||||||
closeMessageSearchSenderDropdown()
|
closeMessageSearchSenderDropdown()
|
||||||
@@ -1541,34 +1776,62 @@ messageSearchOffset.value = 0
|
|||||||
messageSearchResults.value = []
|
messageSearchResults.value = []
|
||||||
messageSearchSelectedIndex.value = -1
|
messageSearchSelectedIndex.value = -1
|
||||||
if (String(messageSearchQuery.value || '').trim()) {
|
if (String(messageSearchQuery.value || '').trim()) {
|
||||||
await runMessageSearch({ reset: true })
|
await runMessageSearch({ reset: true, source: 'custom-range-change' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(messageSearchSender, async () => {
|
watch(messageSearchSender, async (next, prev) => {
|
||||||
|
logSearchPhase('message-search-sender:change', {
|
||||||
|
previous: String(prev || '').trim(),
|
||||||
|
next: String(next || '').trim(),
|
||||||
|
open: !!messageSearchOpen.value,
|
||||||
|
queryLength: String(messageSearchQuery.value || '').trim().length
|
||||||
|
})
|
||||||
if (!messageSearchOpen.value) return
|
if (!messageSearchOpen.value) return
|
||||||
messageSearchOffset.value = 0
|
messageSearchOffset.value = 0
|
||||||
messageSearchResults.value = []
|
messageSearchResults.value = []
|
||||||
messageSearchSelectedIndex.value = -1
|
messageSearchSelectedIndex.value = -1
|
||||||
if (String(messageSearchQuery.value || '').trim()) {
|
if (String(messageSearchQuery.value || '').trim()) {
|
||||||
await runMessageSearch({ reset: true })
|
await runMessageSearch({ reset: true, source: 'sender-change' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(messageSearchQuery, () => {
|
watch(messageSearchQuery, (next, prev) => {
|
||||||
|
logSearchPhase('message-search-query:change', {
|
||||||
|
previousLength: String(prev || '').trim().length,
|
||||||
|
nextLength: String(next || '').trim().length,
|
||||||
|
open: !!messageSearchOpen.value
|
||||||
|
})
|
||||||
if (!messageSearchOpen.value) return
|
if (!messageSearchOpen.value) return
|
||||||
if (messageSearchDebounceTimer) clearTimeout(messageSearchDebounceTimer)
|
if (messageSearchDebounceTimer) clearTimeout(messageSearchDebounceTimer)
|
||||||
messageSearchDebounceTimer = null
|
messageSearchDebounceTimer = null
|
||||||
const q = String(messageSearchQuery.value || '').trim()
|
const q = String(messageSearchQuery.value || '').trim()
|
||||||
if (q.length < 2) return
|
if (q.length < 2) {
|
||||||
|
logSearchPhase('message-search-query:debounce:skip-short', {
|
||||||
|
queryLength: q.length
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logSearchPhase('message-search-query:debounce:scheduled', {
|
||||||
|
queryLength: q.length,
|
||||||
|
delayMs: 280
|
||||||
|
})
|
||||||
messageSearchDebounceTimer = setTimeout(() => {
|
messageSearchDebounceTimer = setTimeout(() => {
|
||||||
runMessageSearch({ reset: true })
|
logSearchPhase('message-search-query:debounce:fire', {
|
||||||
|
queryLength: String(messageSearchQuery.value || '').trim().length
|
||||||
|
})
|
||||||
|
runMessageSearch({ reset: true, source: 'query-debounce' })
|
||||||
}, 280)
|
}, 280)
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => selectedContact.value?.username,
|
() => selectedContact.value?.username,
|
||||||
async () => {
|
async () => {
|
||||||
|
logSearchPhase('message-search-selected-contact:change', {
|
||||||
|
open: !!messageSearchOpen.value,
|
||||||
|
scope: String(messageSearchScope.value || '').trim(),
|
||||||
|
selectedContactUsername: String(selectedContact.value?.username || '').trim()
|
||||||
|
})
|
||||||
if (!messageSearchOpen.value) return
|
if (!messageSearchOpen.value) return
|
||||||
if (String(messageSearchScope.value || '') !== 'conversation') return
|
if (String(messageSearchScope.value || '') !== 'conversation') return
|
||||||
closeMessageSearchSenderDropdown()
|
closeMessageSearchSenderDropdown()
|
||||||
@@ -1577,11 +1840,31 @@ async () => {
|
|||||||
messageSearchSenderOptionsKey.value = ''
|
messageSearchSenderOptionsKey.value = ''
|
||||||
await fetchMessageSearchSenders()
|
await fetchMessageSearchSenders()
|
||||||
if (String(messageSearchQuery.value || '').trim()) {
|
if (String(messageSearchQuery.value || '').trim()) {
|
||||||
await runMessageSearch({ reset: true })
|
await runMessageSearch({ reset: true, source: 'selected-contact-change' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch(messageSearchOpen, (next, prev) => {
|
||||||
|
logSearchPhase('message-search-open:change', {
|
||||||
|
previous: !!prev,
|
||||||
|
next: !!next,
|
||||||
|
queryLength: String(messageSearchQuery.value || '').trim().length,
|
||||||
|
resultCount: Number(messageSearchResults.value?.length || 0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(messageSearchResults, (next, prev) => {
|
||||||
|
const nextList = Array.isArray(next) ? next : []
|
||||||
|
const prevList = Array.isArray(prev) ? prev : []
|
||||||
|
logSearchPhase('message-search-results:change', {
|
||||||
|
previousCount: prevList.length,
|
||||||
|
nextCount: nextList.length,
|
||||||
|
firstHitId: String(nextList[0]?.id || '').trim(),
|
||||||
|
selectedIndex: Number(messageSearchSelectedIndex.value ?? -1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
const autoLoadReady = ref(true)
|
const autoLoadReady = ref(true)
|
||||||
|
|
||||||
let timeSidebarScrollSyncRaf = null
|
let timeSidebarScrollSyncRaf = null
|
||||||
@@ -1793,6 +2076,8 @@ if (c.scrollTop <= 60 && autoLoadReady.value && hasMoreMessages.value && !isLoad
|
|||||||
onTimeSidebarDayClick,
|
onTimeSidebarDayClick,
|
||||||
loadMoreSearchContextAfter,
|
loadMoreSearchContextAfter,
|
||||||
loadMoreSearchContextBefore,
|
loadMoreSearchContextBefore,
|
||||||
|
onSearchHitPointerDown,
|
||||||
|
onSearchHitClickCapture,
|
||||||
onSearchHitClick,
|
onSearchHitClick,
|
||||||
onSearchNext,
|
onSearchNext,
|
||||||
onSearchPrev,
|
onSearchPrev,
|
||||||
|
|||||||
@@ -6190,6 +6190,24 @@ async def _search_chat_messages_via_fts(
|
|||||||
sender = None
|
sender = None
|
||||||
|
|
||||||
session_type_norm = _normalize_session_type(session_type)
|
session_type_norm = _normalize_session_type(session_type)
|
||||||
|
trace_id = f"msg-search-{int(time.time() * 1000)}-{threading.get_ident()}"
|
||||||
|
logger.info(
|
||||||
|
"[%s] chat search start account=%s scope=%s username=%s sender=%s q_len=%s token_count=%s limit=%s offset=%s start_time=%s end_time=%s render_types=%s include_hidden=%s include_official=%s",
|
||||||
|
trace_id,
|
||||||
|
str(account or "").strip(),
|
||||||
|
"conversation" if username else "global",
|
||||||
|
str(username or "").strip(),
|
||||||
|
str(sender or "").strip(),
|
||||||
|
len(str(q or "")),
|
||||||
|
len(tokens),
|
||||||
|
int(limit),
|
||||||
|
int(offset),
|
||||||
|
"" if start_ts is None else int(start_ts),
|
||||||
|
"" if end_ts is None else int(end_ts),
|
||||||
|
str(render_types or "").strip(),
|
||||||
|
bool(include_hidden),
|
||||||
|
bool(include_official),
|
||||||
|
)
|
||||||
|
|
||||||
account_dir = _resolve_account_dir(account)
|
account_dir = _resolve_account_dir(account)
|
||||||
contact_db_path = account_dir / "contact.db"
|
contact_db_path = account_dir / "contact.db"
|
||||||
@@ -6214,6 +6232,14 @@ async def _search_chat_messages_via_fts(
|
|||||||
index_ready = bool(index.get("ready"))
|
index_ready = bool(index.get("ready"))
|
||||||
|
|
||||||
if build_status == "error":
|
if build_status == "error":
|
||||||
|
logger.warning(
|
||||||
|
"[%s] chat search index_error account=%s scope=%s username=%s message=%s",
|
||||||
|
trace_id,
|
||||||
|
account_dir.name,
|
||||||
|
"conversation" if username else "global",
|
||||||
|
str(username or "").strip(),
|
||||||
|
str(build.get("error") or "Search index build failed."),
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
"status": "index_error",
|
"status": "index_error",
|
||||||
"account": account_dir.name,
|
"account": account_dir.name,
|
||||||
@@ -6232,6 +6258,14 @@ async def _search_chat_messages_via_fts(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if not index_ready:
|
if not index_ready:
|
||||||
|
logger.info(
|
||||||
|
"[%s] chat search index_building account=%s scope=%s username=%s build_status=%s",
|
||||||
|
trace_id,
|
||||||
|
account_dir.name,
|
||||||
|
"conversation" if username else "global",
|
||||||
|
str(username or "").strip(),
|
||||||
|
build_status,
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
"status": "index_building",
|
"status": "index_building",
|
||||||
"account": account_dir.name,
|
"account": account_dir.name,
|
||||||
@@ -6315,7 +6349,13 @@ async def _search_chat_messages_via_fts(
|
|||||||
params + [int(limit), int(offset)],
|
params + [int(limit), int(offset)],
|
||||||
).fetchall()
|
).fetchall()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception("Chat search index query failed")
|
logger.exception(
|
||||||
|
"[%s] chat search index query failed account=%s scope=%s username=%s",
|
||||||
|
trace_id,
|
||||||
|
account_dir.name,
|
||||||
|
"conversation" if username else "global",
|
||||||
|
str(username or "").strip(),
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
"status": "index_error",
|
"status": "index_error",
|
||||||
"account": account_dir.name,
|
"account": account_dir.name,
|
||||||
@@ -6623,7 +6663,7 @@ async def _search_chat_messages_via_fts(
|
|||||||
wcdb_display_names=wcdb_display_names,
|
wcdb_display_names=wcdb_display_names,
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
response = {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"account": account_dir.name,
|
"account": account_dir.name,
|
||||||
"scope": scope,
|
"scope": scope,
|
||||||
@@ -6638,6 +6678,19 @@ async def _search_chat_messages_via_fts(
|
|||||||
"index": index,
|
"index": index,
|
||||||
"hits": hits,
|
"hits": hits,
|
||||||
}
|
}
|
||||||
|
logger.info(
|
||||||
|
"[%s] chat search done account=%s scope=%s username=%s sender=%s total=%s hits=%s has_more=%s rows=%s",
|
||||||
|
trace_id,
|
||||||
|
account_dir.name,
|
||||||
|
scope,
|
||||||
|
str(username or "").strip(),
|
||||||
|
str(sender or "").strip(),
|
||||||
|
int(total),
|
||||||
|
len(hits),
|
||||||
|
bool(response["hasMore"]),
|
||||||
|
len(rows),
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/chat/search", summary="搜索聊天记录(消息)")
|
@router.get("/api/chat/search", summary="搜索聊天记录(消息)")
|
||||||
|
|||||||
Reference in New Issue
Block a user