feat: 成员Tab中支持设置Owner视角

This commit is contained in:
digua
2025-12-24 23:45:06 +08:00
parent c82334b1ad
commit 8175d175b5
9 changed files with 174 additions and 25 deletions
@@ -357,19 +357,41 @@ async function* parseEchotrace(options: ParseOptions): AsyncGenerator<ParseEvent
// 提取群头像(从 avatars 中获取群ID对应的头像)
const groupAvatar = groupId ? avatarsMap.get(groupId) : undefined
// 发送 meta
// 快速扫描获取 ownerId(通过 isSend === 1 推断)
let ownerId: string | undefined
try {
await new Promise<void>((resolve) => {
const scanStream = fs.createReadStream(filePath, { encoding: 'utf-8' })
const scanPipeline = chain([scanStream, parser(), pick({ filter: /^messages\.\d+$/ }), streamValues()])
scanPipeline.on('data', ({ value }: { value: EchotraceMessage }) => {
if (value.isSend === 1 && value.senderUsername && !value.senderUsername.endsWith('@chatroom')) {
ownerId = value.senderUsername
scanStream.destroy() // 找到后立即停止扫描
}
})
scanStream.on('close', () => resolve())
scanPipeline.on('end', () => resolve())
scanPipeline.on('error', () => resolve())
})
} catch {
// 扫描失败,ownerId 保持 undefined
}
// 发送 meta(包含推断的 ownerId
const meta: ParsedMeta = {
name: chatName,
platform: KNOWN_PLATFORMS.WECHAT,
type: chatType,
groupId,
groupAvatar,
ownerId,
}
yield { type: 'meta', data: meta }
// 收集成员和消息
const memberMap = new Map<string, MemberInfo>()
let messageBatch: ParsedMessage[] = []
// 流式解析
await new Promise<void>((resolve, reject) => {
@@ -518,4 +540,3 @@ const module_: FormatModule = {
}
export default module_
+1
View File
@@ -16,6 +16,7 @@ export interface ParsedMeta {
type: ChatType
groupId?: string // 群ID(群聊类型有值)
groupAvatar?: string // 群头像(base64 Data URL
ownerId?: string // 所有者/导出者的 platformId
}
/**
+21 -16
View File
@@ -18,15 +18,7 @@ import {
type ParsedMessage,
} from '../../parser'
import { getDbDir } from '../core'
import {
initPerfLog,
logPerf,
logPerfDetail,
resetPerfLog,
logInfo,
logError,
logSummary,
} from '../core'
import { initPerfLog, logPerf, logPerfDetail, resetPerfLog, logInfo, logError, logSummary } from '../core'
/** 流式导入结果 */
export interface StreamImportResult {
@@ -75,7 +67,9 @@ function createTempDatabase(dbPath: string): Database.Database {
platform TEXT NOT NULL,
type TEXT NOT NULL,
group_id TEXT,
group_avatar TEXT
group_avatar TEXT,
owner_id TEXT,
schema_version INTEGER DEFAULT 1
);
CREATE TABLE IF NOT EXISTS member (
@@ -154,7 +148,9 @@ function createDatabaseWithoutIndexes(sessionId: string): Database.Database {
type TEXT NOT NULL,
imported_at INTEGER NOT NULL,
group_id TEXT,
group_avatar TEXT
group_avatar TEXT,
owner_id TEXT,
schema_version INTEGER DEFAULT 1
);
CREATE TABLE IF NOT EXISTS member (
@@ -264,7 +260,7 @@ export async function streamImport(filePath: string, requestId: string): Promise
// 准备语句
const insertMeta = db.prepare(`
INSERT INTO meta (name, platform, type, imported_at, group_id, group_avatar) VALUES (?, ?, ?, ?, ?, ?)
INSERT INTO meta (name, platform, type, imported_at, group_id, group_avatar, owner_id) VALUES (?, ?, ?, ?, ?, ?, ?)
`)
const insertMember = db.prepare(`
INSERT OR IGNORE INTO member (platform_id, account_name, group_nickname, avatar) VALUES (?, ?, ?, ?)
@@ -386,7 +382,8 @@ export async function streamImport(filePath: string, requestId: string): Promise
meta.type,
Math.floor(Date.now() / 1000),
meta.groupId || null,
meta.groupAvatar || null
meta.groupAvatar || null,
meta.ownerId || null
)
metaInserted = true
}
@@ -394,7 +391,12 @@ export async function streamImport(filePath: string, requestId: string): Promise
onMembers: (members: ParsedMember[]) => {
for (const member of members) {
insertMember.run(member.platformId, member.accountName || null, member.groupNickname || null, member.avatar || null)
insertMember.run(
member.platformId,
member.accountName || null,
member.groupNickname || null,
member.avatar || null
)
const row = getMemberId.get(member.platformId) as { id: number } | undefined
if (row) {
memberIdMap.set(member.platformId, row.id)
@@ -730,7 +732,9 @@ export async function streamParseFileInfo(filePath: string, requestId: string):
const db = createTempDatabase(tempDbPath)
// 准备语句
const insertMeta = db.prepare('INSERT INTO meta (name, platform, type, group_id, group_avatar) VALUES (?, ?, ?, ?, ?)')
const insertMeta = db.prepare(
'INSERT INTO meta (name, platform, type, group_id, group_avatar, owner_id) VALUES (?, ?, ?, ?, ?, ?)'
)
const insertMember = db.prepare(
'INSERT OR IGNORE INTO member (platform_id, account_name, group_nickname, avatar) VALUES (?, ?, ?, ?)'
)
@@ -764,7 +768,8 @@ export async function streamParseFileInfo(filePath: string, requestId: string):
parsedMeta.platform,
parsedMeta.type,
parsedMeta.groupId || null,
parsedMeta.groupAvatar || null
parsedMeta.groupAvatar || null,
parsedMeta.ownerId || null
)
metaInserted = true
}