数据迁移指南
爱生活 内置了从旧版 uni.storage 到新版 Dexie/SQLite 引擎的自动迁移机制。用户升级应用后首次启动时自动执行,无需手动干预。
1. 迁移背景
| 维度 | 旧版本 | 新版本 (v1.0.0+) |
|---|---|---|
| 存储引擎 | uni.storage(键值对) | Dexie (H5) / SQLite (App) |
| 加密方式 | 每表独立加密存储 | EncryptedStore 透明加密 |
| 存储格式 | lifeos_{tableName} 键 | 关系型数据表 |
| 数据容量 | 受 uni.storage 限制 | 更大容量(索引查询) |
2. 自动迁移流程
/src/database/migration.ts — App 启动时 db.init() 自动触发:
db.init()
↓
migrateFromLegacy(db)
↓
① 检查 MIGRATED_KEY === '1' ?
├─ 是 → 跳过迁移
└─ 否 → 继续
↓
② 检查是否存在旧格式数据?
├─ 无 → 标记已迁移,结束
└─ 有 → 继续
↓
③ getActiveKey() 存在?
├─ 是 → 使用当前密钥
└─ 否 → 弹出密码弹窗
├─ 取消 → 标记已迁移(跳过)
└─ 输入密码 → 设置 ActiveKey
↓
④ 遍历所有表(TABLE_NAMES)
↓
⑤ 读取 uni.storage('lifeos_{tableName}')
↓
⑥ 检测加密格式:{e, i, a, m}
↓
⑦ decrypt(key, e, i, algo, m) → JSON.parse
↓
⑧ bulkPut 写入新引擎
↓
⑨ markMigrated() → 写入 MIGRATED_KEY = '1'关键设计决策
- 不自动清除旧数据:迁移完成后,旧
uni.storage数据保留。用户可手动清理(安全性考虑,防止迁移异常导致数据丢失) - 迁移标记:
lifeos_migrated_v2 = '1'存储在uni.storage中,避免每次启动重复执行 - 密码弹窗:如果启动时尚未解锁,会弹出密码输入弹窗。用户取消则跳过本次迁移(下次启动再次尝试)
3. 旧数据格式
迁移仅处理以下格式的加密数据:
json
{
"e": "base64-encoded-ciphertext",
"i": "base64-encoded-iv",
"a": "gcm | cbc",
"m": "base64-encoded-hmac"
}| 字段 | 说明 |
|---|---|
e | 密文(Base64) |
i | 初始化向量 |
a | 算法标识:gcm 或 cbc |
m | HMAC 校验值(仅 CBC 模式) |
4. 密码遗忘场景
场景一:忘记密码 + 已迁移到新引擎
数据已通过 AES-256-GCM 加密存储在新引擎中。无法恢复。唯一途径:使用之前导出的备份文件 + 旧密码通过"合并旧数据"功能恢复。
场景二:忘记密码 + 旧数据仍在 uni.storage
若迁移被跳过(用户取消密码输入),旧数据在 uni.storage 中保留。用户可在设置中重置加密(清除旧数据后重新设置密码),此时旧数据无法恢复。
场景三:忘记密码 + 有旧备份文件
使用 数据管理 → 合并旧数据 功能:
- 输入旧密码解密备份
- 用当前密钥重新加密
- 导入当前数据库
5. 手动迁移
如自动迁移失败,可通过以下方法手动迁移:
导出旧数据
在旧版本中执行:
数据管理 → 导出备份 → 保存 JSON 文件导入新版本
在新版本中执行:
数据管理 → 导入恢复 → 选择备份文件或使用"合并旧数据":
数据管理 → 合并旧数据 → 输入旧密码 → 选择备份文件6. 跨设备迁移
使用云端同步
- 旧设备:配置 WebDAV / GitHub Gist → 上传
- 新设备:安装爱生活 → 设置相同加密密码 → 配置相同同步服务 → 下载
使用本地备份
- 旧设备:导出备份 → 将 JSON 文件传输到新设备
- 新设备:安装爱生活 → 设置相同加密密码 → 导入恢复
重要:新旧设备必须使用相同的加密密码,否则无法解密备份中的数据。
7. 迁移日志
迁移过程中的日志通过 console.log 输出,可在调试时查看:
[Migration] 检测到旧格式数据,开始迁移...
[Migration] 需要密码解密旧数据,弹出密码弹窗...
[Migration] task: 42 items
[Migration] event: 18 items
...
[Migration] 迁移完成,共导入 256 条记录若某张表迁移失败,日志会显示警告但不中断整体流程:
[Migration] 跳过表 unknown_table: Error: ...