最近在整理老项目库的时候,我碰到了一个挺典型的问题:一大堆 workbuddy 导出的记录文件,散着十几年的任务、备注、标签和附件,要统一挪进 dsh 的归档体系里管理。刚开始我图省事,直接把文件往 dsh 里拖,结果字段对不上、标签全丢、附件路径全部失效,折腾了一天一地鸡毛。后来花时间把 workbuddy-to-dsh 整个流程理顺,才算是把这套迁移真正跑通了。
workbuddy-to-dsh 这个名字看着像个小工具,其实是解决大问题的:它能把 workbuddy 导出的 JSON 数据结构化地转换成 dsh 能识别的归档格式,包括字段映射、标签合并、附件路径重写、元数据补齐这些脏活累活全干完。这个教程适合谁呢?一类是手里攒了大量 workbuddy 老数据、想迁到 dsh 管理的个人用户;另一类是团队里要把多个成员的工作记录统一归档到 dsh 插件体系里的管理员。这两种情况我都实际跑过,下面把从准备到校验的完整过程拆开讲。
1. 为什么要做这个转换:两个工具的数据逻辑差在哪
1.1 workbuddy 的记录方式与它的“自由”从哪来
workbuddy 这类工具的设计思路其实非常朴素,核心就是“记录当下”:一条待办、一段随记、一个项目切片,随手存进去,字段命名也比较随性。比如同一个“状态”字段,有的人写state,有的人写status,还有人直接写成done;附件路径有人用绝对路径,有人用相对路径,甚至有人直接把内容嵌在 JSON 里。
这种自由度在日常记录的时候很舒服,但一旦要归档就麻烦了。我迁移过一个大概有 1800 个条目、80 多个附件的老库,光字段名就统计出来十几种变体:title、name、headline、subject全都有。如果靠人工写脚本去猜字段含义,改到最后自己都记不清哪个脚本对应哪一套规则。workbuddy-to-dsh 的价值在于,它提供了一个固定的转换框架,把这种“自由散漫”的数据结构,统一收束成 dsh 认可的规范格式。
1.2 dsh 的归档模型:更严格,但也更适合长期管理
dsh 本身是一个带插件体系的归档管理平台,它要求每一条记录都有明确的归档结构:条目 ID、标题、摘要、标签集合、时间戳、附件索引。dsh 插件市场里有很多扩展,比如归档管理插件,可以批量导入导出、做全文检索、管理版本变更。这种设计对长期存续的数据是好事——字段严格,检索就准确;结构统一,后续插件才好处理。
但严格意味着“门槛”:dsh 导入接口认的是它自己的格式,不像 workbuddy 那样什么都往里塞。如果直接把 workbuddy 导出的结构喂给 dsh,往往会在导入阶段就被拦下来,报字段缺失或类型不匹配的错误。即便勉强导入了,后面的检索、筛选、统计也可能因为标签分散、层级错乱而完全不可用。我最早一次硬导数据,导完之后用 dsh 归档管理插件做标签统计,结果“工作”和“工作/日常”这两种标签被当成了完全不同的两个分类,得重新批量改标签——那感觉真的酸爽。
1.3 没有转换工具会怎样
有些人会问,反正都是 JSON,自己用 Python 写个脚本转换行不行?当然可以,但实际场景远没那么简单。附件复制、ID 冲突处理、父子层级重建、空字段补默认值、编码问题、路径分隔符兼容……这些细节堆在一起,一个临时脚本大概率要磨好几天。
我见过一个同事手工转换,脚本写到第五版还因为 Windows 路径里的反斜杠问题挂掉。而 workbuddy-to-dsh 这样的专用工具,把这些边界情况都提前处理好了,你要做的只是提供映射规则,剩下的事情它来做。这也是为什么我最终还是选了这个工具:把时间花在确认业务规则上,而不是反复打磨脚本。
2. 迁移前准备:先搞定三件事,后面才不返工
2.1 确认 workbuddy 的导出格式
开始之前,先搞清楚你的 workbuddy 数据到底长什么样。workbuddy 通常支持导出 JSON 文件,但不同版本导出的结构不完全一样:有的是一个数组,里面每个元素是一条记录;有的是带items字段的嵌套对象;还有的会把附件元数据和正文内容拆开放在两个字段里。
我建议拿一个小样本先看结构。比如:
head -c 2000 ./export/workbuddy-sample.json看几个关键点:第一,最外层是数组还是对象;第二,记录里用的是id还是其他的唯一标识;第三,附件是直接存文件的绝对路径,还是有一个独立的attachments数组;第四,文本字段里中文是不是正常显示(有些老工具导出的是 UTF-8 BOM,有些是 GBK,这个问题后面专门说)。
这一步看似简单,但能省下后面大量的排查时间。我实际操作时,第一次转换就发现导出的 JSON 带了 BOM 头,导致转换工具在读取第一个字段名时多了一个不可见字符,所有记录的id_field全部没匹配上。如果没有预先检查,这个问题会藏在几百条数据里,很难一眼发现。
2.2 安装 workbuddy-to-dsh:不用纠结,两种方式都很快
workbuddy-to-dsh 的安装不复杂,常用的有两条路。
第一种是直接下载发布包。去工具发布页找到对应系统平台的二进制文件,Windows 就下载.exe,macOS 就下载.dmg或者直接用命令行工具,Linux 下载.tar.gz解压。这种方式的好处是开箱即用,不依赖额外环境,适合只做一次性迁移的场景。
第二种是自己编译。如果你的机器上有 Go 环境,直接执行:
git clone https://github.com/your-repo/workbuddy-to-dsh.git cd workbuddy-to-dsh go build -o workbuddy-to-dsh .编译安装的好处是之后可以通过源码里的example/目录快速查看配置模板。我自己是用的编译安装,因为需要配合内部 dsh 插件市场做一些格式验证,改了源码里的模板才跑通的。
装好之后,建议先跑一遍版本确认:
workbuddy-to-dsh --version能正常输出版本信息,说明工具没问题。
2.3 准备 dsh 侧的插件环境
这一步经常被跳过,但我不建议省。dsh 本身和数据迁移工具是独立的两套东西,你得先在 dsh 里把归档管理插件装好,准备好接收导入内容的插槽,后面才谈得上“导入”。
具体来说,在 dsh 插件市场里搜索并下载归档管理插件,安装后确认插件版本和你使用的 dsh 主版本兼容。我遇到过的情况是:插件还没升级,直接用新版 dsh 的导入接口,结果导入后插件列表里看不到任何归档数据。排查半天,才发现是插件版本太老,接口返回的数据格式和插件预期的不一致。后来解决的方案是先把 dsh 升级到和插件匹配的版本,再继续导入流程。
另外,建议在 dsh 里新建一个测试分类,比如“迁移验证明”,等转换完成之后,先用这个小分类做一次试导入,确认结构没问题后再批量导入正式数据。
3. 映射配置与关键参数:这一步决定转换质量
3.1 字段映射:从 workbuddy 结构到 dsh 结构
workbuddy-to-dsh 的核心是映射配置文件。整体思路很简单:告诉工具“workbuddy 里的哪个字段,对应 dsh 里的哪个字段”。下面是一个我实际使用的配置模板,你可以直接参考修改:
source: input_dir: ./export/workbuddy format: json encoding: utf-8 target: output_dir: ./import/for-dsh format: dsh mapping: record: id_field: guid title_field: title summary_field: note content_field: body status_field: state tags: source_field: labels delimiter: "," mode: merge attachment: base_dir: ./files copy_mode: hardlink hierarchy: parent_field: parent_id preserve: true archive: code_prefix: WB include_timestamps: true language: zh-CN几个字段的含义我解释一下。
id_field告诉工具 workbuddy 里哪个字段是唯一标识,这很重要,dsh 的归档条目要求 ID 全局唯一,如果两条记录的 ID 一样,后面的导入会被判定为冲突。title_field是归档条目标题,summary_field是摘要。content_field是可选的,有些数据有正文、有些没有,不传也没关系,工具会自动补空值。status_field用来映射状态,workbuddy 里可能是done/pending这样的值,转换工具会按规则映射成 dsh 能识别的枚举值。
配置里需要注意的是,source.encoding和target.language最好明确写出来,不要用默认值。第一次转换我漏写了encoding: utf-8,工具按系统默认编码读取,结果所有中文标题全部变成了乱码,导出的文件看起来是好的,但一进 dsh 就完全没法看。这个坑我后面细说。
3.2 标签与分类的处理策略
标签是 workbuddy 数据里最灵活、也最容易被搞砸的部分。workbuddy 的标签通常保存在labels字段里,有时候是数组["工作","项目-A"],有时候是逗号分隔的字符串"工作,项目-A"。workbuddy-to-dsh 提供了一个delimiter参数来指定分隔符:如果你的labels是数组,这个参数就可以忽略;如果是字符串,就必须写对。
更关键的是mode参数,我实际用过三种模式:replace、merge、split。
replace是直接用转换后的标签替换原标签,适合你希望在导入 dsh 后重新整理标签的情况;merge是把 workbuddy 原有的标签合并到 dsh 已有的标签体系里,适合原标签本身有一定分类价值的情况;split则是把带层级结构的标签(比如工作/项目-A)拆成 dsh 的子标签结构,方便后续按层级筛选。
我推荐大部分场景使用merge。因为它保留了原有标签信息,同时 dsh 的归档管理插件能基于已有的全局标签结构做统计。如果你选择replace,原标签信息就丢掉了,后面想追溯历史分类会很麻烦。
3.3 附件与关联文件的组织方式
附件是所有迁移里最容易被忽略、也最容易出问题的一块。workbuddy 里的附件通常是文件路径,但路径写法五花八门:绝对路径、相对路径、带盘符的路径、带网络共享名的路径都有。workbuddy-to-dsh 处理附件时有几个选项。
base_dir用来指定附件的基础目录,工具会把相对于这个目录的路径转换成 dsh 能理解的相对路径。copy_mode有三种:copy是复制文件到目标目录,move是移动文件(源文件会被清理),hardlink是创建硬链接。
我个人建议全程用hardlink。原因很简单:复制大附件耗时且占双倍空间,移动又有风险(万一转换失败源数据没了)。硬链接只在同一文件系统下有效,但绝大多数场景都满足这个条件,它的好处是文件本身只存一份,两边都能访问,转换过程几乎是秒级的。
我在一次迁移里处理了 80 个附件,总共 2GB 多,用copy模式跑了 4 分多钟,换成hardlink之后十几秒就完成了,而且磁盘占用没增加。如果你的源文件系统支持硬链接,强烈建议优先考虑这种模式。
4. 实操:从导出到导入 dsh 的完整转换流程
4.1 第一步:生成前期检查报告
workbuddy-to-dsh 有一个很实用的功能:dry-run 预检模式。它不会真正生成转换结果,而是扫描源数据,输出一份报告,告诉你:有多少条记录、多少个附件、哪些字段缺失、哪些 ID 重复、哪些标签格式不一致。
我建议所有迁移都先跑一遍 dry-run,即使数据量很小也不要跳。实际操作命令:
workbuddy-to-dsh \ --config ./workbuddy-to-dsh.yml \ --dry-run \ --report ./preflight-report.json跑完之后,打开报告看看:
jq '.summary' ./preflight-report.json输出类似这样:
{ "total_records": 1800, "missing_title": 12, "duplicate_ids": 3, "attachment_count": 82, "tag_format_mismatch": 15 }干这行的都知道,数据迁移最怕的不是数据少,而是问题潜伏在数据里。有了预检报告,你能在真正执行转换之前知道哪里有坑,比如missing_title有 12 条,你就要决定:是人工补标题,还是让工具生成默认标题(比如用 ID 作为备选)。具体策略看你的业务要求,但一定要在预检阶段决策,而不是等导入 dsh 之后再返工。
4.2 第二步:执行转换并看懂日志
预检没问题之后,执行正式转换:
workbuddy-to-dsh \ --config ./workbuddy-to-dsh.yml \ --verbose过程中的日志信息要看懂,而不是扫一眼就过去。正常的日志长这样:
[info] 读取源数据: export/workbuddy 目录 [info] 共发现 1800 条记录, 82 个附件 [info] 开始转换... [info] 已转换 500 条记录, 处理 21 个附件 [info] 已转换 1000 条记录, 处理 43 个附件 [info] 已转换 1500 条记录, 处理 62 个附件 [info] 完成: 1800 条记录, 82 个附件如果日志里出现了[warn]或[error],不要忽略。最常见的警告是“字段缺失,使用默认值”,这意味着源数据里某些字段没有值,工具帮你补了默认值。看起来没什么大碍,但如果你要求每条记录都必须有摘要,而工具默认补了空字符串,就会导致导入后大量记录的摘要栏是空的。
还有一种警告是“附件文件不存在,跳过”,这种情况通常发生在附件路径是绝对路径、且源文件已经被移动过的情况下。处理方式是在转换前把附件文件夹恢复到base_dir下的对应位置,重新跑一遍。
转换完成后,输出目录里会有一个import-report.json,里面记录了转换的统计信息。顺手可以检查这个文件,和预检报告对比一下,确认没有出现新的问题。
4.3 第三步:校验输出并导入 dsh
转换完成后不要急着导入。先看看生成的数据结构是否符合 dsh 的预期,我习惯用 jq 做一次快速检查:
jq '.records[0]' ./import/for-dsh/records.json确认结构类似于下面的样子:
{ "id": "WB-0001", "title": "整理项目资料", "summary": "归档项目的背景与关键结论", "tags": ["工作", "归档"], "attachments": [{"path": "files/附件说明.pdf"}], "created_at": "2024-05-11T10:30:00Z", "updated_at": "2024-05-15T08:12:00Z" }结构没问题后,打开 dsh,进入归档管理插件,选择导入功能,指定生成的records.json和附件目录files/,按指引完成导入.导入过程中 dsh 会做自己的校验,如果有错误会提示具体条目,这时候对照import-report.json就能快速定位。
4.4 第四步:导入后的核验清单
导入只是一半,核验同样重要。我总结了一个核验清单,每次迁移都照着过一遍:
- 总数核对:dsh 分类里的条目数量,是否与转换工具报告的
total_records一致。不一致,优先检查是否有 ID 冲突被跳过。 - 抽样内容:随机抽 3 到 5 条原始 workbuddy 记录,到 dsh 里找到对应条目,逐字段对比,看标题、摘要、状态是否一致。
- 标签统计:用归档管理插件跑一次标签统计,看标签总量和主要分类是否符合预期。如果标签数量异常多,说明标签映射有问题,可能把包含逗号的文本错误拆分成了多个标签。
- 附件抽查:随机抽几个附件,点击预览或下载,确认文件能正常打开,路径没有失效。
- 时间戳核验:看几条记录的创建时间和更新时间,确认时区转换是否合理。workbuddy 导出的时间戳有时不含时区信息,工具会按本地时区处理,如果和 dsh 的默认时区不一致,展示出来的时间会偏移。
这些检查做完,基本就能确认迁移是稳妥的。
5. 常见问题排查与避坑记录
5.1 问题速查表
我把实际踩过、以及和同事交流时遇到过的问题整理成了一张速查表:
| 现象 | 原因 | 解决办法 |
|---|---|---|
| 导入 dsh 后中文全是乱码 | 源 JSON 不是 UTF-8 编码,或带 BOM 头 | 转换前用统一脚本转成 UTF-8 无 BOM;在配置里显式指定encoding: utf-8 |
| 大量条目标题缺失 | workbuddy 里有些记录的标题字段为空 | 设置title_fallback: id,用 ID 作为备选标题;或人工补全 |
| 转换后附件无法打开 | 附件路径是绝对路径,或源文件位置变动 | 迁移前把附件文件统一放到base_dir目录内,用相对路径 |
| 导入时提示 ID 冲突 | workbuddy 导出的数据中存在重复 ID | 在配置里打开id_deduplicate: true,自动加后缀 |
| 标签数量暴增 | labels字符串里包含逗号,被错误拆分 | 检查delimiter配置是否正确,或改用数组解析 |
| 嵌套的父子关系丢失 | 源数据有parent_id关系,但配置没开启层级 | 设置hierarchy.preserve: true,确认parent_field指向正确字段 |
| 导入后时间显示错误 | 时间戳不含时区信息,工具按本地时区处理 | 在配置里显式指定timezone: Asia/Shanghai,确保和 dsh 默认时区一致 |
| 大文件转换时内存暴涨 | 一次性加载全部数据导致内存耗尽 | 启用batch_size: 500,分批处理 |
| 插件市场里找不到更新版本 | dsh 版本和插件版本不兼容,搜索被过滤 | 先升级 dsh 主程序,再刷新插件市场,重新下载归档管理插件 |
| 转换工具提示“未知字段” | workbuddy 导出格式与工具预期不完全一致 | 查看schema.example.yaml里的示例字段,调整映射配置 |
5.2 两个印象最深的坑
第一个坑是 BOM 编码问题。有个客户的 workbuddy 数据是老版本 Windows 导出的,文件头带了 BOM,转换工具读第一个字段名时,名字前面多了一个不可见字符。结果所有记录的id都匹配不上,工具自动按生成规则改名,导入 dsh 后大量条目变成了无关联的孤儿记录。后来我在转换前加了一步强制转码:先读取文件,用utf-8-sig解析,再重新输出为无 BOM 的 UTF-8。这一步放在脚本里固定执行之后,再也没有出过类似问题。
第二个坑是 wbx 标签分隔符混乱。这批数据里,有人用逗号分隔标签,有人用竖线,还有人直接在标签文本里写了含逗号的句子。转换之后,dsh 里多出来上百个奇怪的“标签”,每个其实是半句话。排查的时候发现,问题的根源是源数据没有统一的标签规范,而不是转换工具本身的问题。这件事给我留下的教训是:转换前一定要先做标签格式统计,凡是不符合预设分隔符格式的标签,要么先手工清洗,要么用mode: replace统一重写。不要指望一个分隔符配置能处理所有脏数据。
5.3 关于大库迁移的一点补充
如果你的 workbuddy 数据特别大,比如几万条记录加几十 GB 附件,还有一个实用经验:分批转换。workbuddy-to-dsh 的配置里有batch_size参数,可以在内存和效率之间平衡。
我实际测过,默认不设batch_size时,处理 1800 条记录没问题,但数据量到 2 万条以上,内存占用可能会到 1GB 甚至更多。设置batch_size: 500之后,转换器会分批次读取和写入,内存占用能控制在 200MB 内,虽然转换时间会略长,但至少不会跑到一半进程被系统杀掉。
另外,分批转换时要注意附件的copy_mode。如果设置成move,第一批转换完可能会把部分源附件移动到目标目录,第二批再执行时就会报“附件文件不存在,跳过”。所以在大库迁移时,建议一开始就用copy或hardlink,等所有批次都完成,确认没问题再手动清理源目录。
最后说个我自己的习惯。每次迁移前,不管数据量大小,我都会先跑一次 dry-run 预检,然后把预检报告打开看一下,重点关注duplicate_ids、tag_format_mismatch、missing_title这三项。只要这三项没有异常,后面的转换基本稳了。如果报告里有警告,我会先停机处理,而不是直接往下走。数据迁移这件事,慢一点,稳一点,比什么都强。