优化导出

This commit is contained in:
xuncha
2026-01-28 20:24:48 +08:00
parent b6c9f2b32b
commit 9969c073e5
6 changed files with 140 additions and 27 deletions

View File

@@ -26,6 +26,7 @@ interface ConfigSchema {
whisperDownloadSource: string
autoTranscribeVoice: boolean
transcribeLanguages: string[]
exportDefaultConcurrency: number
}
export class ConfigService {
@@ -54,7 +55,8 @@ export class ConfigService {
whisperModelDir: '',
whisperDownloadSource: 'tsinghua',
autoTranscribeVoice: false,
transcribeLanguages: ['zh']
transcribeLanguages: ['zh'],
exportDefaultConcurrency: 2
}
})
}

View File

@@ -78,6 +78,7 @@ export interface ExportOptions {
txtColumns?: string[]
sessionLayout?: 'shared' | 'per-session'
displayNamePreference?: 'group-nickname' | 'remark' | 'nickname'
exportConcurrency?: number
}
const TXT_COLUMN_DEFINITIONS: Array<{ id: string; label: string }> = [
@@ -1288,6 +1289,7 @@ class ExportService {
): Promise<{ rows: any[]; memberSet: Map<string, { member: ChatLabMember; avatarUrl?: string }>; firstTime: number | null; lastTime: number | null }> {
const rows: any[] = []
const memberSet = new Map<string, { member: ChatLabMember; avatarUrl?: string }>()
const senderSet = new Set<string>()
let firstTime: number | null = null
let lastTime: number | null = null
@@ -1321,16 +1323,7 @@ class ExportService {
const localId = parseInt(row.local_id || row.localId || '0', 10)
const actualSender = isSend ? cleanedMyWxid : (senderUsername || sessionId)
const memberInfo = await this.getContactInfo(actualSender)
if (!memberSet.has(actualSender)) {
memberSet.set(actualSender, {
member: {
platformId: actualSender,
accountName: memberInfo.displayName
},
avatarUrl: memberInfo.avatarUrl
})
}
senderSet.add(actualSender)
// 提取媒体相关字段
let imageMd5: string | undefined
@@ -1375,6 +1368,30 @@ class ExportService {
await wcdbService.closeMessageCursor(cursor.cursor)
}
if (senderSet.size > 0) {
const usernames = Array.from(senderSet)
const [nameResult, avatarResult] = await Promise.all([
wcdbService.getDisplayNames(usernames),
wcdbService.getAvatarUrls(usernames)
])
const nameMap = nameResult.success && nameResult.map ? nameResult.map : {}
const avatarMap = avatarResult.success && avatarResult.map ? avatarResult.map : {}
for (const username of usernames) {
const displayName = nameMap[username] || username
const avatarUrl = avatarMap[username]
memberSet.set(username, {
member: {
platformId: username,
accountName: displayName
},
avatarUrl
})
this.contactCache.set(username, { displayName, avatarUrl })
}
}
return { rows, memberSet, firstTime, lastTime }
}
@@ -1856,6 +1873,16 @@ class ExportService {
const sessionInfo = await this.getContactInfo(sessionId)
const myInfo = await this.getContactInfo(cleanedMyWxid)
const contactCache = new Map<string, { success: boolean; contact?: any; error?: string }>()
const getContactCached = async (username: string) => {
if (contactCache.has(username)) {
return contactCache.get(username)!
}
const result = await wcdbService.getContact(username)
contactCache.set(username, result)
return result
}
onProgress?.({
current: 0,
total: 100,
@@ -1934,6 +1961,16 @@ class ExportService {
? await this.getGroupNicknamesForRoom(sessionId)
: new Map<string, string>()
const contactCache = new Map<string, { success: boolean; contact?: any; error?: string }>()
const getContactCached = async (username: string) => {
if (contactCache.has(username)) {
return contactCache.get(username)!
}
const result = await wcdbService.getContact(username)
contactCache.set(username, result)
return result
}
// ========== 阶段3构建消息列表 ==========
onProgress?.({
current: 55,
@@ -1962,7 +1999,7 @@ class ExportService {
// 获取发送者信息用于名称显示
const senderWxid = msg.senderUsername
const contact = await wcdbService.getContact(senderWxid)
const contact = await getContactCached(senderWxid)
const senderNickname = contact.success && contact.contact?.nickName
? contact.contact.nickName
: (senderInfo.displayName || senderWxid)
@@ -2005,7 +2042,7 @@ class ExportService {
const { chatlab, meta } = this.getExportMeta(sessionId, sessionInfo, isGroup)
// 获取会话的昵称和备注信息
const sessionContact = await wcdbService.getContact(sessionId)
const sessionContact = await getContactCached(sessionId)
const sessionNickname = sessionContact.success && sessionContact.contact?.nickName
? sessionContact.contact.nickName
: sessionInfo.displayName
@@ -2098,8 +2135,18 @@ class ExportService {
const sessionInfo = await this.getContactInfo(sessionId)
const myInfo = await this.getContactInfo(cleanedMyWxid)
const contactCache = new Map<string, { success: boolean; contact?: any; error?: string }>()
const getContactCached = async (username: string) => {
if (contactCache.has(username)) {
return contactCache.get(username)!
}
const result = await wcdbService.getContact(username)
contactCache.set(username, result)
return result
}
// 获取会话的备注信息
const sessionContact = await wcdbService.getContact(sessionId)
const sessionContact = await getContactCached(sessionId)
const sessionRemark = sessionContact.success && sessionContact.contact?.remark ? sessionContact.contact.remark : ''
const sessionNickname = sessionContact.success && sessionContact.contact?.nickName ? sessionContact.contact.nickName : sessionId
@@ -2328,7 +2375,7 @@ class ExportService {
senderWxid = msg.senderUsername
// 用 getContact 获取联系人详情,分别取昵称和备注
const contactDetail = await wcdbService.getContact(msg.senderUsername)
const contactDetail = await getContactCached(msg.senderUsername)
if (contactDetail.success && contactDetail.contact) {
// nickName 才是真正的昵称
senderNickname = contactDetail.contact.nickName || msg.senderUsername
@@ -2343,7 +2390,7 @@ class ExportService {
} else {
// 单聊对方消息 - 用 getContact 获取联系人详情
senderWxid = sessionId
const contactDetail = await wcdbService.getContact(sessionId)
const contactDetail = await getContactCached(sessionId)
if (contactDetail.success && contactDetail.contact) {
senderNickname = contactDetail.contact.nickName || sessionId
senderRemark = contactDetail.contact.remark || ''
@@ -2567,7 +2614,7 @@ class ExportService {
senderNickname = myInfo.displayName || cleanedMyWxid
} else if (isGroup && msg.senderUsername) {
senderWxid = msg.senderUsername
const contactDetail = await wcdbService.getContact(msg.senderUsername)
const contactDetail = await getContactCached(msg.senderUsername)
if (contactDetail.success && contactDetail.contact) {
senderNickname = contactDetail.contact.nickName || msg.senderUsername
senderRemark = contactDetail.contact.remark || ''
@@ -2578,7 +2625,7 @@ class ExportService {
}
} else {
senderWxid = sessionId
const contactDetail = await wcdbService.getContact(sessionId)
const contactDetail = await getContactCached(sessionId)
if (contactDetail.success && contactDetail.contact) {
senderNickname = contactDetail.contact.nickName || sessionId
senderRemark = contactDetail.contact.remark || ''
@@ -3005,13 +3052,20 @@ class ExportService {
const sessionLayout = exportMediaEnabled
? (options.sessionLayout ?? 'per-session')
: 'shared'
let completedCount = 0
const rawConcurrency = typeof options.exportConcurrency === 'number'
? Math.floor(options.exportConcurrency)
: 2
const clampedConcurrency = Math.max(1, Math.min(rawConcurrency, 6))
const sessionConcurrency = (exportMediaEnabled && sessionLayout === 'shared')
? 1
: clampedConcurrency
for (let i = 0; i < sessionIds.length; i++) {
const sessionId = sessionIds[i]
await parallelLimit(sessionIds, sessionConcurrency, async (sessionId) => {
const sessionInfo = await this.getContactInfo(sessionId)
onProgress?.({
current: i + 1,
current: completedCount,
total: sessionIds.length,
currentSession: sessionInfo.displayName,
phase: 'exporting'
@@ -3053,7 +3107,15 @@ class ExportService {
failCount++
console.error(`导出 ${sessionId} 失败:`, result.error)
}
}
completedCount++
onProgress?.({
current: completedCount,
total: sessionIds.length,
currentSession: sessionInfo.displayName,
phase: 'exporting'
})
})
onProgress?.({
current: sessionIds.length,