fix: 修复导出日期、语音和联系人名字问题

- 导出的 HTML:点月份先看日期,选中日期后才跳到对应消息。
- 语音转文字:不同账号、不同聊天里的语音不会再串到一起。
- 联系人名字:正常昵称显示不变;昵称异常时使用可见的备注或账号,导出文件名也能正常使用。
This commit is contained in:
Wxw-Gu
2026-09-13 11:15:30 +08:00
parent ab3b3731b7
commit add16885fe
19 changed files with 702 additions and 150 deletions
+62
View File
@@ -51,4 +51,66 @@ describe('VoiceService batch lookup', () => {
expect(resolveVoice).toHaveBeenNthCalledWith(1, 'session', 10, 100, '1000')
expect(resolveVoice).toHaveBeenNthCalledWith(2, 'session', 11, 101, '1001')
})
it('keeps same local ids isolated by session and server identity, then reuses the exact cache entries', async () => {
const getVoiceDataBatch = vi
.fn()
.mockResolvedValue([
{ success: true, hex: '0102' },
{ success: true, hex: '0304' },
{ success: true, hex: '0506' }
])
const service = new VoiceService({ getVoiceDataBatch } as never, '/accounts/fixture-a')
const decoder = (service as unknown as {
decoderRegistry: { decode: ReturnType<typeof vi.fn> }
}).decoderRegistry
vi.spyOn(decoder, 'decode').mockImplementation(async (source: { data: Buffer }) => ({
pcm: Buffer.from([source.data[0]]),
sampleRate: 16_000,
channels: 1
}))
const references = [
{ sessionId: 'session-a', localId: 7, createTime: 100, svrId: 'same-second-a' },
{ sessionId: 'session-b', localId: 7, createTime: 100, svrId: 'same-second-a' },
{ sessionId: 'session-a', localId: 7, createTime: 100, svrId: 'same-second-b' }
]
const first = await service.resolveVoices(references)
expect(first.every((item) => item.success)).toBe(true)
expect(new Set(first.map((item) => item.data)).size).toBe(3)
expect(getVoiceDataBatch).toHaveBeenCalledOnce()
const second = await service.resolveVoices(references)
expect(second).toEqual(first)
expect(getVoiceDataBatch).toHaveBeenCalledOnce()
})
it('versions the in-memory namespace per account so a restarted or switched account cannot reuse old audio', async () => {
const client = {
getVoiceDataBatch: vi.fn().mockResolvedValue([{ success: true, hex: '0102' }]),
getAccountRoot: () => '/accounts/fixture-a'
}
const first = new VoiceService(client as never)
const second = new VoiceService(
{ ...client, getAccountRoot: () => '/accounts/fixture-b' } as never
)
const decode = (service: VoiceService, value: number): void => {
const decoder = (service as unknown as {
decoderRegistry: { decode: ReturnType<typeof vi.fn> }
}).decoderRegistry
vi.spyOn(decoder, 'decode').mockResolvedValue({
pcm: Buffer.from([value]),
sampleRate: 16_000,
channels: 1
})
}
decode(first, 1)
decode(second, 2)
const firstCache = (first as unknown as { voiceCache: Map<string, string> }).voiceCache
const secondCache = (second as unknown as { voiceCache: Map<string, string> }).voiceCache
await expect(first.resolveVoices([{ sessionId: 'session', localId: 1, createTime: 2 }])).resolves.toHaveLength(1)
await expect(second.resolveVoices([{ sessionId: 'session', localId: 1, createTime: 2 }])).resolves.toHaveLength(1)
expect([...firstCache.keys()][0]).not.toBe([...secondCache.keys()][0])
expect(client.getVoiceDataBatch).toHaveBeenCalledTimes(2)
})
})