Files
kefu/2026-08-19-18-27-34/.workbuddy/memory/2026-08-20.md
T
2026-08-27 14:04:28 +08:00

121 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-08-20
**MySQL SQL 导出升级(crm_import 链路)**:
- 用户确定表用 MySQL,建表 SQL 已提供(create_tables_mysql.sql),需求:导出时直接生成 SQL 数据文件方便导入
- 修改 `wxwork_export_db.py`:新增 `sql_escape`/`sql_num`(MySQL 字面量转义)、`MYSQL_MSG_COLS`/`MYSQL_USER_COLS`/`MYSQL_CONV_COLS`、`chunked_insert`(每 500 行一条 INSERT)、`build_mysql_data_sql`(建表+INSERT 一体,含 SET NAMES utf8mb4、FOREIGN_KEY_CHECKS=0、文件名按范围 `crm_import_mysql_YYYY-MM-DD.sql` / `_all.sql`)
- 注意:MySQL 版 chat_conversations 无 account 列(SQLite 版有),SQL 生成时该列写 NULL
- 验证:144 条(7 天)与 2794 条(全量)SQL 行数与 SQLite 完全一致;users 392 vs 390、convs 199 vs 151 差异为多账号重复主键,INSERT IGNORE 自动去重属预期
- `wxwork_gui.py` 集成:新增「同时生成 MySQL 导入 SQL」复选框(config `with_db`,默认开),导出流程改 4 步(解密/CSV/Excel/MySQL SQL),do_export 加 with_db 参数
- 已重新打包 dist/企业微信导出助手.exe(含 wxwork_export_db 模块)
- 导入方式:`mysql -u root -p --default-character-set=utf8mb4 < crm_import_mysql_xxx.sql`
**表名对齐 zyt_ 前缀**:
- 用户提供截图显示现有 MySQL 表名为 `zyt_wx_chat_messages` / `zyt_wx_chat_users` / `zyt_wx_chat_conversations`
- 已将 `wxwork_export_db.py` 中 SQLite/MySQL 表名、索引名、INSERT 语句全部替换为 `zyt_wx_*` 前缀
- 重新生成最近 7 天与全量 SQL,验证 INSERT 表名正确
- 已重新打包 `dist/企业微信导出助手.exe`
**一键打包脚本**:
- 新增 `build.py` + `build.bat`:一键打包(PyInstaller onefile/windowed/icon/add-data,打包后自动同步 wxwork_keys.json 到 dist)
- 手动命令:`C:\Users\pc\.workbuddy\binaries\python\envs\gui311\Scripts\python.exe -m PyInstaller --onefile --windowed --name "企业微信导出助手" --icon app_icon.png --add-data "app_icon.png;." wxwork_gui.py`
- 注意:沙箱拦截 shutil.rmtree,build.py 不做 build 缓存清理(PyInstaller 自动覆盖)
**图片/语音媒体导出可行性调查**:
- 媒体文件存储位置:`Documents/WXWork/{account}/Cache/{Image,Voice,Video,File}/按月/`
- **文件全部明文不加密**:图片=PNG/JPG(89 50 4E 47 / FF D8 FF)、语音=SILK_V3(#!SILK_V3)、视频=MP4(ftyp)、文件=原始格式
- 消息 content 中含 protobuf 编码的文件 UUID + CDN URL(wework.qpic.cn/wwpic3az/...)
- **UUID 匹配率极低(3/2758=0.1%)**:消息 content UUID 与 Cache 文件名不是同一套体系
- Cache/File 用原始文件名存储(如 `【全栈工程师...】.pdf`),可与消息 content 文件名匹配
- Cache 覆盖范围有限:仅存 2026-03~08(约 644M,图片553+语音21),更早的已清理
- message_table 列名:content_type(非 msg_type),实际 content_type 值与 MSG_TYPE_MAP 不同(14=图片,16=语音,23=视频,565=文件,31=链接,123=未知)
- forever_store.db 为空,无文件映射表
- 结论:可导出本地 Cache 媒体文件(无需解密),但无法保证全部历史媒体;图片/语音精确关联到消息需额外开发 protobuf 深度解析
**媒体导出功能开发完成**:
- 新增 `wxwork_export_media.py` 模块:extract_media_refs(UUID+CDN URL+文件名提取)、build_cache_index(Cache 扫描索引)、match_media(UUID/文件名/模糊匹配)、export_media(复制到输出目录)
- 集成到 `wxwork_export_db.py`:export_to_db 自动调用 export_media,消息表增加 `media_file_path` + `media_url` 两列(SQLite/MySQL/CSV/SQL 全部同步)
- GUI 集成:步骤 4/4 描述改为"MySQL SQL + 导出媒体文件",无需额外选项(with_db=True 时自动导出媒体)
- 验证:全量 2794 条消息中 292 条复制了本地文件(图片256+语音22+视频10+文件4,202MB),655 条有 CDN URL
- 关联方式:文件名匹配 254 条(主力)、UUID 3 条、模糊 35 条
- 已重新打包 `dist/企业微信导出助手.exe`(含 wxwork_export_media hidden-import)
- build.py 也加了 `--hidden-import wxwork_export_media`
**修复今天消息/图片"丢失"问题**:
- 根因 1: `decrypt_with_keys` 缓存只比较主库 `message.db` mtime, 未考虑 `-wal` 文件。企业微信运行时使用 WAL 模式, 新消息先写 `message.db-wal`, 退出后才 checkpoint 到主库。旧缓存 `crm_import/decrypted/.../message.db` (09:38) 比源库 (11:02) 旧, 但用户导出时 WAL 尚未 checkpoint, 导致今天消息缺失。
- 修复 1: `wxwork_export_final.py` 中 `decrypt_with_keys` 缓存判断改为 `src_mtime = max(db_mtime, wal_mtime)`; 若 WAL 比解密副本新则强制重新解密。
- 根因 2: `session.db` 的 `conversation_table` 对 S: 开头单聊未存会话名, 代码 fallback 为"群聊 S:...", 导致用户以为消息不在该会话。
- 修复 2: `wxwork_export_final.py` 与 `wxwork_export_db.py` 中对 `S:对方UID_本账号UID` 格式的 conversation_id, 从 `user_cache` 查对方 UID 的名字, 正确显示为"甄养堂(霍医生)"。
- 根因 3: `MSG_TYPE_MAP` 把 content_type=14 标为"语音/加密", 但实际为截图/图片; content_type=123 标为"文本", 实际也带截图文件。
- 修复 3: `MSG_TYPE_MAP[14]` 改为"图片"; `wxwork_export_db.py` 中若 `media_file_path` 指向图片/视频/语音/文件, 按扩展名覆盖 `msg_type_name`。
- 新增: `wxwork_gui.py` 导出前检测 `message.db-wal`, 若存在则提示"企业微信仍在运行, 最新消息可能未写入本地, 建议退出后重试"。
- 验证: 重新全量导出后 `crm_wecom.db` 今天 14 条消息、3 条图片类型、会话名正确、图片文件路径正确 (media_file_path)。
- 已重新打包 `dist/企业微信导出助手.exe`。
**只导出单聊(过滤群聊/应用消息)**:
- 用户需求:只导出用户之间的聊天记录,群组数据不导出
- 会话类型判定(基于 message.db 真实数据归纳):`M:xxx`=微信单聊、`S:UID_UID`=企微单聊、`R:xxx`=群聊、`Y:xxx`=应用消息、`O:xxx`=第三方应用/服务号(SCRM/加粉工具)、`APPROVAL`/`MAIL` 等=系统虚拟会话
- 新增 `is_personal_chat(conv_id)`(wxwork_export_final.py):白名单保留 `M:` 与 `S:数字_数字`,其余全过滤
- 三个导出入口全部加 `personal_only=True` 参数(默认单聊):`export_messages`(final)、`export_to_db`(db)、`export_media`(media,SQL 加 `conversation_id LIKE 'M:%' OR 'S:%'`);CLI 加 `--with-groups` 可恢复全量
- **S: 会话名解析修正**:S: 两个 UID 顺序不固定(对方_本账号 或 本账号_对方),改为取"不等于 user_dir 的那个 UID"作为对方,避免把本账号名(高兴亮)当对方名
- GUI 新增「仅导出单聊(不含群组/应用消息)」勾选框(config `personal_only`,默认 True),do_export 贯穿 4 步导出
- 验证:主账号 2772 条 → 单聊 1394 条(S:1275 + M:119),过滤 1378 条(R:540+Y:716+O:102+AP/MA20 完全对应);今天 12 条完整;会话表 64 个全为真实单聊(霍医生/史医生/甄养堂助理系列)
- 注意:`M:` 微信单聊(9 会话 119 条)名字本地库查不到(wx_friend/wechat_contactV1 均未命中),显示 UID 数字
- 已重新打包 `dist/企业微信导出助手.exe`(19.0 MB)
**按会话名称过滤官方/系统账号**:
- 用户反馈:截图里 "13102691405879689"(M:微信单聊,内容全是"X月X日汇报提醒")和 "企业微信团队"(S: 单聊,对方 user 表名即官方账号名)不想导出,选择"按会话名称过滤"
- 新增 `DEFAULT_BLOCKED_CONV_NAMES = ("企业微信团队","微信团队","微信支付","腾讯客服","腾讯新闻")` + `is_blocked_conv_name()`(wxwork_export_final.py,包含匹配,空名不命中)
- 过滤同时支持**会话名称**和**会话ID**(含 M:/S: 前缀的包含匹配):GUI 输入框可填官方账号名或数字会话ID(如 13102691405879689)
- 三个导出入口全部加 `blocked_conv_names=None` 参数:export_messages / export_to_db(先预扫描 conv_cache 算出 blocked_conv_ids 供媒体同步过滤)/ export_media(新增 blocked_conv_ids 参数,行级跳过)
- GUI:设置卡新增输入框「过滤的账号名称或会话ID(逗号分隔)」config `blocked_names`(默认含 5 个官方关键词),KeyRelease 自动保存;_export_worker 解析逗号为 tuple 传入 do_export
- 验证:全量导出 1394 → 1329 条(过滤官方账号 65 条),会话 64 → 63,官方关键词消息/会话残留均 0;今天消息 11 条完整;媒体"复制 0 个文件"属正常(dest 已存在跳过,非 bug)
- 打包注意:build.py 遇 safe-delete 拦截,需先 `rm -rf build/企业微信导出助手 build/*.toc build/*.pyz` 再删 `dist/企业微信导出助手.exe`,最后非沙箱跑 build.py
- 已重新打包 `dist/企业微信导出助手.exe`(19.0 MB)
**Excel 图片显示为编码串修复(媒体路径+超链接)**:
- 用户反馈:Excel 里图片消息内容列显示十六进制编码串,图片"没用显示"
- 修复 1: `wxwork_export_final.py` 的 `export_messages` 集成 `wxwork_export_media.export_media`,导出 CSV/Excel 时同步复制本地 Cache 媒体文件到 `out_dir/媒体文件/`
- 修复 2: CSV/Excel 新增「媒体文件路径」「媒体URL」两列;图片/截图/语音/视频/文件消息按 `server_id` 从 `media_map` 回填真实本地路径
- 修复 3: `wxwork_export_media.py` 把 `result[sid]` 的 key 统一为 `str(sid)`,避免 final/db 两端用 `server_id` 字符串匹配失败;并把对 `wxwork_export_final` 的顶层导入改为 `export_media` 函数内部延迟导入,打破循环引用
- 修复 4: `wxwork_export_db.py` 中 `media_map.get(str(msg.get("server_id")))` 同步修正,保证 crm_wecom.db 的 `media_file_path` 字段正确
- 修复 5: `MSG_TYPE_MAP[123]` 从"文本"改为"截图"(企业微信截图实际 content_type=123)
- 修复 6: `wxwork_gui.py` 的 `csv_to_xlsx` 把「媒体文件路径」单元格设为 `file:///` 超链接(点击直接打开图片),并把图片/截图等媒体行「内容」列的原始编码串替换为 `[图片]`/`[截图]` 等友好提示;列宽调整为 14 列
- 验证:今天 3 条图片/截图消息在 Excel 中均显示 `[图片]`/`[截图]` + 可点击的 `媒体文件\图片\...` 路径;数据库 `crm_wecom.db` 190 条消息带 `media_file_path`
- 已重新打包 `dist/企业微信导出助手.exe`(19.0 MB)
**企业微信数据目录可配置(非默认安装)**:
- 用户需求:企微非默认安装时(如数据在 `C:\Users\Administrator\Documents\WXWork`)默认路径 `~\Documents\WXWork` 找不到数据库导致提取密钥失败,需要可设置的地方
- `wxwork_export_final.py` 新增 `detect_wxwork_dir(candidates=None)`:依次尝试 ①用户配置目录 ②当前用户 Documents\WXWork ③注册表 Shell Folders 的 Personal 位置下 WXWork ④扫描 C:\Users\*\Documents\WXWork;目录有效性=含账号子目录 Data\message.db
- `wxwork_find_key.py` `extract_wxwork_key()` 新增 `db_base` 参数替代硬编码路径
- `wxwork_gui.py`:设置卡新增「企业微信数据目录」输入框+浏览+自动检测按钮(config `db_dir`,留空回退默认路径);`extract_all_keys(log, db_dir)` 与 `do_export(..., db_dir)` 参数化;启动时若默认目录不存在自动 detect 并回填;提取/导出日志均显示实际数据目录
- 验证:detect_wxwork_dir 跳过无效候选、正确回退默认目录;py_compile 全通过
- 已重新打包 `dist/企业微信导出助手.exe`(19.0 MB)
**企业微信数据目录默认路径显示优化**:
- 用户反馈:GUI 启动时「企业微信数据目录」输入框为空,希望显示默认值
- 修复:`wxwork_gui.py` 中 `db_dir_var` 初始值改为 `self.config.get('db_dir', DEFAULT_DB_BASE) or DEFAULT_DB_BASE`,启动时始终默认填入 `~\Documents\WXWork`
- 启动检测逻辑:若默认路径有效显示绿色 ✓;若默认路径无效则尝试自动检测,找到则替换,未找到保留默认路径并提示"默认位置未检测到"
- 用户手动留空时 `get_db_dir()` 仍回退到默认路径,行为与旧版一致
- 已重新打包 `dist/企业微信导出助手.exe`(19.0 MB)
**语音转文字功能(读缓存 + 本地ASR兜底)**:
- 用户需求:录音能否直接转成文字导出
- 重大发现:解密后 message.db 有 `msg_voice2text` 表(企微客户端点过"转文字"的语音才有缓存),关联键 `msg_voice2text.message_id = message_table.server_id`(注意不是 message_id 字段)
- 主账号 39 条语音消息 39 条全部有转写文本(纯文本质量高);另一账号 `msg_voice2text` 空表说明从未点过转文字
- 新增 `wxwork_voice2text.py`:`load_voice2text(decrypted_dbs)` 读缓存返回 `{(user_dir, str(server_id)): text}`;`decode_silk_to_wav`(pilk);`asr_missing`(pilk+faster-whisper 本地识别,懒加载缺依赖时优雅跳过)
- `wxwork_export_final.py` / `wxwork_export_db.py`:`export_messages`/`export_to_db` 新增 `voice2text_map=None, voice_asr=False` 参数;CSV/SQLite/MySQL 三处新增「语音转文字」列(字段 voice_text);语音消息(.silk/.amr)若有转写文本,内容列直接显示转写文本
- `wxwork_gui.py`:新增「无缓存语音用本地 AI 识别」勾选框(config `voice_asr` 默认关,勾选后 do_export 预读缓存→asr_missing 合并,CSV 与 DB 共享避免重复识别);`csv_to_xlsx` 语音列自动换行、有转写文本时不替换为 `[语音]`
- 验证:全量导出 23 条语音消息带转写文本(CSV/SQLite 均正确);MySQL SQL 建表与 INSERT 均含 voice_text
- build.py 新增 `--hidden-import wxwork_voice2text`(pilk/faster-whisper 不打包,懒加载)
- 已重新打包 `dist/企业微信导出助手.exe`
**ASR 兜底识别验证与打包决策**:
- pilk 0.2.4 API 变化:`decode(silk, pcm, pcm_rate=)` 只输出 PCM,需用 `pilk.silk_to_wav(silk, wav, rate=)` 便捷函数;已兼容两种 API
- faster-whisper 1.2.1 + ctranslate2 4.8.1 + onnxruntime + av + numpy 安装到 gui311 venv(约 155MB 依赖)
- **huggingface_hub 1.28 与 hf-mirror 不兼容**(FileMetadataError: Distant resource does not seem to be on huggingface.co),必须直连官方源;已移除 HF_ENDPOINT 镜像设置,仅保留 HF_HUB_DISABLE_XET=1(xet 后端也不稳)
- Windows 沙箱环境模型缓存符号链接失效 → 快照文件 0 字节,需手动把 blobs 复制到 snapshots/ 目录;tree json 可查文件→blob 映射(tokenizer.json 2203239B vs vocabulary.txt 459861B,勿搞反)
- ASR 链路验证:base 模型加载 1.8s、7 秒语音识别 1.2s,结果「主要是我就找不到这个人的名字我们这边看不到」质量高;asr_missing 合并 {('',sid):text} 正常
- 默认模型定为 base(~145MB,与承诺的 150MB 一致),GUI 文案已更新
- 决策:ASR 依赖打包进 exe(否则勾选框在 exe 中无效),build.py 加 pilk/faster_whisper/ctranslate2/tokenizers/av/onnxruntime hidden-import
- 最终打包成功:`dist/企业微信导出助手.exe` 96.1 MB(含 ASR 依赖;GUI 两个开关:读缓存默认开、本地 AI 识别默认关)