feat: 完善日报图片与公众号消息展示

修复日报图片读取格式与模型不支持时的友好降级
恢复话题关键词回退,确保词云在缺少顶层关键词时仍可展示
支持公众号多文章消息在聊天、日报、检索和 HTML 导出中完整呈现
迁移账号身份缓存并优化首次连接的账号占位信息
补充图片、词云、账号缓存与多文章消息回归测试
This commit is contained in:
电摇小子
2026-08-07 00:00:52 +08:00
parent 0ec2e6a0be
commit 3af62783dd
18 changed files with 432 additions and 36 deletions
+37 -1
View File
@@ -1,4 +1,5 @@
import { mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'fs'
import { mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync, mkdirSync } from 'fs'
import { createHash } from 'crypto'
import { tmpdir } from 'os'
import { join } from 'path'
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
@@ -105,4 +106,39 @@ describe('bootstrap cache', () => {
}).nickname
).toBe('濑岛田井卫')
})
it('migrates the previous startup cache so account discovery can show identity before unlock', () => {
const legacyRoot = '/fixture/legacy-account'
const digest = createHash('sha1')
.update(`${process.platform}:${legacyRoot}`)
.digest('hex')
.slice(0, 16)
const legacyFile = join(userData, 'cache', 'bootstrap', `${process.platform}-${digest}.json`)
mkdirSync(join(userData, 'cache', 'bootstrap'), { recursive: true })
writeFileSync(
legacyFile,
JSON.stringify({
version: 1,
platform: process.platform,
accountRoot: legacyRoot,
updatedAt: Date.now(),
self: {
wxid: 'wxid_legacy',
nickname: '缓存昵称',
avatar: 'data:image/png;base64,fixture',
accountRoot: legacyRoot
},
contacts: []
}),
'utf8'
)
clearBootstrapCache()
expect(getBootstrapCache(legacyRoot)?.self).toMatchObject({
wxid: 'wxid_legacy',
nickname: '缓存昵称',
avatar: 'data:image/png;base64,fixture'
})
flushBootstrapCacheWritesSync()
})
})
+46
View File
@@ -0,0 +1,46 @@
import { describe, expect, it } from 'vitest'
import { parseGroupDailyReport } from '../../src/renderer/src/utils/group-report'
import type { GroupReportMetadata } from '../../src/shared/group-report'
const metadata: GroupReportMetadata = {
groupName: '测试群',
reportDate: '2026-08-06',
dateRange: '今日',
messageCount: 3,
activeUsers: 2,
timeSpan: '1 h',
generatedAt: '2026/8/6 12:00:00',
recordNote: 'fixture',
footerNote: '',
heroParticipants: [],
reportMode: 'full'
}
const media = {
gallery: [],
voiceHighlights: [],
funBadges: []
}
describe('group report parsing', () => {
it('falls back to topic keywords when the model omits top-level keywords', () => {
const report = parseGroupDailyReport(
JSON.stringify({
topics: [
{
title: '健身安排',
summary: '讨论训练时间和肌酸。',
keywords: ['肌酸', '训练']
}
]
}),
[],
'',
[],
metadata,
media
)
expect(report.keywords).toEqual(['肌酸', '训练', '健身安排'])
})
})
+17
View File
@@ -69,6 +69,23 @@ describe('message parser', () => {
expect(parsed).toMatchObject({ type: 'share', title: '普通分享', typeVal: '5' })
})
it('preserves every article in a public-account multi-article message', () => {
const parsed = parseMessageContent(
`<appmsg><type>5</type><appname>长江日报</appname><mmreader><category count="3"><item><title><![CDATA[女子吃酒席时意外发现]]></title><url><![CDATA[https://mp.weixin.qq.com/a]]></url><cover><![CDATA[https://img.test/a.jpg]]></cover></item><item><title>霍尔木兹海峡开放临时协议</title><digest>国际油价短期走势</digest><url>https://mp.weixin.qq.com/b</url></item><item><title>东野圭吾新作</title><url>https://mp.weixin.qq.com/c</url></item></category></mmreader></appmsg>`,
49
)
expect(parsed).toMatchObject({ type: 'share', appname: '长江日报' })
if (parsed.type === 'share') {
expect(parsed.articles).toHaveLength(3)
expect(parsed.articles?.map((article) => article.title)).toEqual([
'女子吃酒席时意外发现',
'霍尔木兹海峡开放临时协议',
'东野圭吾新作'
])
}
})
it('uses the quoted group member id instead of the chatroom id', () => {
const parsed = parseMessageContent(
'<appmsg><type>57</type><title>回复内容</title><refermsg><type>1</type><fromusr>123456789@chatroom</fromusr><chatusr>wxid_fixture_member</chatusr><content>被引用内容</content></refermsg></appmsg>',