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

15 KiB
Raw Blame History

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