fix: 修复日报成员名称与图片识别兼容性

This commit is contained in:
Wxw-Gu
2026-07-15 21:17:54 +08:00
parent 1abaa57a0c
commit 1275c512c4
4 changed files with 20 additions and 11 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ macOS / Windows 微信聊天记录查看,AI 一键生成群聊总结。
在微信 4.0 数据库解析、解密思路上,项目参考了 [WeFlow](https://github.com/hicccc77/WeFlow) 等开源项目的实现方式;此项目围绕我自己的使用场景做的定制化工具,重点放在本地聊天记录查看、群聊总结和个人工作流集成上。 在微信 4.0 数据库解析、解密思路上,项目参考了 [WeFlow](https://github.com/hicccc77/WeFlow) 等开源项目的实现方式;此项目围绕我自己的使用场景做的定制化工具,重点放在本地聊天记录查看、群聊总结和个人工作流集成上。
> 当前版本:`v2.1.0`。macOS 支持相对稳定;Windows 已初步支持微信 4.0 数据库连接、自动获取密钥和聊天记录查看,仍在持续兼容不同微信版本与本地目录结构。 > 当前版本:`v2.1.2`。macOS 支持相对稳定;Windows 已初步支持微信 4.0 数据库连接、自动获取密钥和聊天记录查看,仍在持续兼容不同微信版本与本地目录结构。
## ✨ 功能特性 ## ✨ 功能特性
+3 -3
View File
@@ -556,9 +556,9 @@ app.whenReady().then(async () => {
if (!provider || !model) { if (!provider || !model) {
return { success: false, error: '当前 AI 模型不存在' } return { success: false, error: '当前 AI 模型不存在' }
} }
if (!model.capabilities.vision) { // Capability metadata is stored per machine. A model verified on macOS
return { success: false, error: '当前模型不支持图片理解' } // may still be unmarked on Windows, so do not reject before making the
} // real multimodal request. The provider response remains authoritative.
// request 来自 renderer,imageHash 是 md5(优先)或 sha256(...),dataUrl 在内部算出 // request 来自 renderer,imageHash 是 md5(优先)或 sha256(...),dataUrl 在内部算出
// 这里直接调 service,dataUrl 由 renderer 通过 window.api.getImage 拿到再传进来 // 这里直接调 service,dataUrl 由 renderer 通过 window.api.getImage 拿到再传进来
return imageInsightService.analyze(request) return imageInsightService.analyze(request)
+3
View File
@@ -195,6 +195,9 @@ export class AIProviderService {
const imageError = validateVisionImage(imagePart.dataUrl) const imageError = validateVisionImage(imagePart.dataUrl)
if (imageError) throw new Error(imageError) if (imageError) throw new Error(imageError)
const result = await this.request(messages as AIMessage[], options) const result = await this.request(messages as AIMessage[], options)
if (options?.providerId && options.modelId) {
this.markCapabilities(options.providerId, options.modelId, { vision: true, ocr: true })
}
return { success: true, ...result } return { success: true, ...result }
} catch (error) { } catch (error) {
return { success: false, error: safeAIError(error) } return { success: false, error: safeAIError(error) }
@@ -175,13 +175,18 @@ const applyGroupMemberNames = async (
const member = memberMap.get(senderId) const member = memberMap.get(senderId)
if (!member) return message if (!member) return message
const preferredNames: Record<ReportMemberNamePreference, string[]> = { const preferredNames: Record<ReportMemberNamePreference, string[]> = {
groupNickname: [member.groupNickname, member.wechatNickname, member.remark, member.nickname], // Keep the three modes semantically distinct. `member.nickname` may
wechatNickname: [member.wechatNickname, member.groupNickname, member.remark, member.nickname], // already be a contact remark, so it must not leak into the first two.
groupNickname: [member.groupNickname, member.wechatNickname],
wechatNickname: [member.wechatNickname, member.groupNickname],
remark: [member.remark, member.groupNickname, member.wechatNickname, member.nickname] remark: [member.remark, member.groupNickname, member.wechatNickname, member.nickname]
} }
const name = preferredNames[preference].find((value) => value && !isInternalName(value)) const name = preferredNames[preference].find((value) => value && !isInternalName(value))
if (!name) return message return {
return { ...message, name, img: message.img || member.avatar } ...message,
name: name || (preference === 'remark' ? message.name : ''),
img: message.img || member.avatar
}
}) })
} }
@@ -222,11 +227,12 @@ export function useGroupReportGeneration({
const [generatedImage, setGeneratedImage] = useState<string | null>(null) const [generatedImage, setGeneratedImage] = useState<string | null>(null)
const [reportPaths, setReportPaths] = useState<ReportPaths | null>(null) const [reportPaths, setReportPaths] = useState<ReportPaths | null>(null)
const [templateId, setTemplateId] = useState<ReportTemplateId>('v1') const [templateId, setTemplateId] = useState<ReportTemplateId>('v1')
const [memberNamePreference, setMemberNamePreferenceState] = const [memberNamePreference, setMemberNamePreferenceState] = useState<ReportMemberNamePreference>(
useState<ReportMemberNamePreference>(() => { () => {
const saved = localStorage.getItem('group_report_member_name_preference') const saved = localStorage.getItem('group_report_member_name_preference')
return saved === 'wechatNickname' || saved === 'remark' ? saved : 'groupNickname' return saved === 'wechatNickname' || saved === 'remark' ? saved : 'groupNickname'
}) }
)
const [reportTimeoutSeconds, setReportTimeoutSecondsState] = useState<number>(() => { const [reportTimeoutSeconds, setReportTimeoutSecondsState] = useState<number>(() => {
const saved = Number(localStorage.getItem('group_report_timeout_seconds')) const saved = Number(localStorage.getItem('group_report_timeout_seconds'))
return Number.isFinite(saved) && saved >= 30 ? saved : 300 return Number.isFinite(saved) && saved >= 30 ? saved : 300