# 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 识别默认关)