## 1. 数据模型与核心序列化 - [x] 1.1 定义备份 JSON 数据接口:在 `viewmodel/` 下新建 `PlaylistBackup.ets`,定义 `PlaylistBackupData`、`PlaylistBackupItem`、`PlaylistSongBackupItem` 接口,包含 `version`、`exportTime`、`appVersion`、`playlists` 字段 - [x] 1.2 在 `PlaylistTable` 中新增 `exportAllPlaylists()` 方法:查询所有歌单及其关联歌曲,返回 `PlaylistBackupData` 对象。超过 100 个歌单时分批查询 - [x] 1.3 在 `PlaylistTable` 中新增 `importPlaylists()` 方法:接受 `PlaylistBackupData`,逐一创建歌单和歌曲关联,返回导入结果统计 ## 2. PlaylistBackupManager 核心类 - [x] 2.1 新建 `common/util/PlaylistBackupManager.ets`,实现单例模式和基本结构 - [x] 2.2 实现 `exportToJson()` 方法:调用 `PlaylistTable.exportAllPlaylists()`,序列化为 JSON 字符串 - [x] 2.3 实现 `validateBackupJson()` 方法:解析 JSON 并校验 `version`、必要字段完整性,返回校验结果 - [x] 2.4 实现 `importFromJson()` 方法:解析并校验 JSON,调用 `PlaylistTable.importPlaylists()` 写入数据 ## 3. 本地文件导出导入 - [x] 3.1 实现 `saveToLocal()` 方法:使用 `DocumentViewPicker` 保存 JSON 文件,文件名格式 `playlist_backup_YYYYMMDD_HHmmss.json` - [x] 3.2 实现 `loadFromLocal()` 方法:使用 `DocumentViewPicker` 选择 `.json` 文件,读取并返回文件内容 - [x] 3.3 导出失败和取消处理:用户取消文件选择器或写入失败时显示错误 toast,确保无残留文件 ## 4. 导入冲突解决 - [x] 4.1 新建 `dialog/ImportConflictDialog.ets`:冲突解决对话框,显示冲突歌单名称,提供「覆盖」「跳过」「重命名」三个选项和「应用到全部」复选框 - [x] 4.2 在 `importFromJson()` 中集成冲突检测:导入前按歌单名称查询现有歌单,发现冲突时弹出对话框 - [x] 4.3 实现覆盖逻辑:删除现有歌单及其歌曲关联,重新创建 - [x] 4.4 实现重命名逻辑:自动生成后缀名称(如 "我的歌单 (2)"),检查后缀名也不冲突 - [x] 4.5 实现「应用到全部」:记录用户选择,后续冲突自动应用同一策略 ## 5. WebDAV 备份上传 - [x] 5.1 实现 `uploadToWebDav()` 方法:接受 WebDAV 账户参数,生成备份 JSON,通过 `RemoteDriveManager` 的 `rcpSocket` 上传到 `/TTMusic/backups/` - [x] 5.2 实现目录自动创建:上传前检查并创建 `/TTMusic/backups/` 目录(使用 `createWebDavFolder` 或 `createDirectory`) - [x] 5.3 新建 WebDAV 账户选择器对话框:过滤仅显示 `RemoteDriveType.WebDav` 类型的账户,无账户时提示用户添加(集成在 BackupManageView 弹层中) - [x] 5.4 错误处理:网络错误和认证失败时显示具体错误信息 ## 6. WebDAV 备份恢复 - [x] 6.1 实现 `listWebDavBackups()` 方法:列出 WebDAV 服务器 `/TTMusic/backups/` 下的所有 `.json` 文件 - [x] 6.2 实现 `downloadFromWebDav()` 方法:下载选中的备份文件内容 - [x] 6.3 新建备份文件选择列表 UI:显示 WebDAV 上的备份文件列表(文件名 + 时间),用户点击后下载并触发导入流程(集成在 BackupManageView 弹层中) - [x] 6.4 目录不存在或为空时显示「暂无备份」提示 ## 7. 服务器自动备份(VIP) - [x] 7.1 实现 `uploadToServer()` 方法:通过 `POST /backup/playlist/upload` 上传备份 JSON,请求携带 `userToken` - [x] 7.2 实现 `downloadFromServer()` 方法:通过 `GET /backup/playlist/download` 下载指定备份 - [x] 7.3 实现 `listServerBackups()` 方法:通过 `GET /backup/playlist/list` 获取备份历史列表 - [x] 7.4 实现 VIP 权限检查:在自动备份和服务器恢复入口检查 `hasActiveSubscription`,非 VIP 引导到 VipPage - [x] 7.5 实现 token 过期处理:服务器返回认证错误时提示用户重新登录,暂停自动备份 ## 8. 自动备份防抖机制 - [x] 8.1 实现 `scheduleAutoBackup()` 方法:使用 `setTimeout` 实现 5 分钟防抖,窗口内多次调用只触发一次上传 - [x] 8.2 在 `PlaylistTable` 的增删改方法中添加自动备份触发点:`createPlaylist`、`deletePlaylist`、`updatePlaylist`、`addSongToPlaylist`、`removeSongFromPlaylist` 操作完成后调用 `scheduleAutoBackup()` - [x] 8.3 实现自动备份偏好持久化:使用 `PreferencesUtil` 保存自动备份开关状态 - [x] 8.4 实现 VIP 过期自动关闭:检测到 VIP 状态变化时关闭自动备份并通知用户 ## 9. 备份管理 UI - [x] 9.1 新建 `view/BackupManageView.ets`:备份管理视图组件,包含导出、导入、WebDAV 备份、WebDAV 恢复、服务器备份(VIP)、服务器恢复(VIP)功能入口 - [x] 9.2 在 `SettingPage.ets` 中添加「备份与恢复」菜单项,通过 Sheet 弹层打开 BackupManageView - [x] 9.3 实现备份历史列表 UI:显示最近备份记录(类型图标、时间、歌单数量),无记录时显示引导提示 - [x] 9.4 实现自动备份开关 UI(VIP 标识):开关控件带 VIP 标签,非 VIP 点击引导到会员页面 - [x] 9.5 实现操作进度展示:导出/导入/上传/下载过程中显示 loading 状态和进度提示 ## 10. 备份历史记录持久化 - [x] 10.1 定义备份记录数据模型:包含备份类型(local/webdav/server)、时间戳、歌单数量、文件路径/标识 - [x] 10.2 使用 `PreferencesUtil` 存储最近备份记录列表(JSON 数组,最多保留 20 条) - [x] 10.3 在每次备份/恢复操作完成后写入历史记录 ## 11. 页面路由注册 - [x] 11.1 备份管理以 Sheet 弹层方式集成在 SettingPage 中,无需单独路由注册 - [x] 11.2 从 SettingPage 点击「备份与恢复」菜单项即可打开备份管理界面