diff --git a/docs/superpowers/specs/2026-04-24-user-data-backup-design.md b/docs/superpowers/specs/2026-04-24-user-data-backup-design.md new file mode 100644 index 0000000..d6b120b --- /dev/null +++ b/docs/superpowers/specs/2026-04-24-user-data-backup-design.md @@ -0,0 +1,198 @@ +# 用户数据备份设计(以导出/迁移为核心) + +## 1. 背景与目标 + +本设计面向 `person-panel` 的“用户数据可携带性”场景,核心目标是让用户可以导出自己的文件与业务数据,并可用于后续迁移或恢复到兼容系统。 + +已确认目标与约束: + +- 目标类型:用户可导出/迁移数据(非灾备优先) +- 交付形态:目录结构 + 清单 JSON +- 范围:用户全量业务数据(排除敏感凭据) +- 敏感策略:导出时可选原始/脱敏,默认脱敏 + +## 2. 设计原则 + +- 可迁移优先:导出格式稳定、可版本化、可校验 +- 文件与数据一体:业务 JSON 与媒体文件同时导出 +- 安全默认:默认脱敏,且永不导出高敏字段 +- 一致性可解释:导出基于固定时间截点,避免数据漂移 +- 演进兼容:通过 `schemaVersion` 管理未来格式升级 + +## 3. 总体架构 + +采用“异步导出任务 + 标准导出包”模式。 + +### 3.1 API 入口 + +- `POST /api/me/export/request` + - 创建导出任务 + - 参数:`maskPolicy`(`masked` | `raw`,默认 `masked`) +- `GET /api/me/export/tasks` + - 查询当前用户导出任务列表与状态 +- `GET /api/me/export/tasks/:id/download` + - 下载导出结果(目录打包或直接目录挂载下载,按部署实现) + +### 3.2 任务状态机 + +- `queued`:任务已创建,待执行 +- `running`:导出中 +- `succeeded`:导出完成,可下载 +- `failed`:导出失败,包含错误信息 +- `expired`:产物过期被清理 + +### 3.3 一致性模型 + +任务进入 `running` 时写入 `exportCutoffAt`。所有 SQL 查询统一加上时间边界(可按表字段映射),保证导出结果在逻辑上对应同一时间点快照。 + +## 4. 导出包规范(v1) + +### 4.1 目录结构 + +```text +export--/ + manifest.json + data/ + user.json + profile.json + timeline.json + posts.json + comments.json + media-assets.json + media-refs.json + ...(其他用户归属资源) + files/ + +``` + +### 4.2 manifest.json(关键字段) + +- `schemaVersion`: `1` +- `exportedAt`: 导出完成时间(ISO8601 UTC) +- `exportCutoffAt`: 截点时间(ISO8601 UTC) +- `userId`: 用户 ID +- `maskPolicy`: `masked` 或 `raw` +- `stats`: 各资源条数、文件数、总字节数 +- `checksums`: + - `data`: 每个 JSON 文件的 SHA256 + - `files`: 每个文件的 SHA256 + 相对路径 + +### 4.3 编码与类型规范 + +- JSON 使用 UTF-8 +- 时间统一 ISO8601(UTC) +- 枚举保持稳定字符串值 +- 布尔/数值不做字符串化 + +## 5. 数据边界与脱敏规则 + +### 5.1 导出范围(全量用户业务) + +按“该用户可归属”原则导出: + +- 用户基础信息(非敏感) +- 个人资料与配置 +- 时间线、文章、评论等内容 +- 媒体元数据(`mediaAssets`)与引用关系(`mediaRefs`) +- 其他用户域业务资源(按服务注册表补齐) + +### 5.2 永不导出字段 + +无论 `maskPolicy` 取值,以下字段均不导出: + +- 密码哈希与密码重置类 token +- 登录会话 token / 刷新 token +- API 密钥、签名密钥、内部凭据 +- 不可归属该用户的他人敏感数据 + +### 5.3 可配置脱敏字段 + +导出时支持策略切换: + +- `masked`(默认): + - 邮箱:保留前后缀,隐藏中间 + - 手机号:保留前 3 后 2 + - IP:保留网段,隐藏主机位 + - 设备标识:部分掩码 +- `raw`: + - 输出原值,但仍遵循“永不导出字段”排除规则 + +## 6. 文件导出策略(重点) + +### 6.1 文件来源 + +从用户关联的 `mediaAssets` 读取 `storageKey`,在媒体目录(当前项目已有公开上传目录)中查找实体文件,复制到导出包 `files/` 下。 + +### 6.2 文件-数据关联 + +- `data/media-assets.json` 记录资产元数据与 `storageKey` +- `data/media-refs.json` 记录 `assetId` 与业务对象关联 +- 导入方据此可重建资源引用关系 + +### 6.3 完整性校验 + +每个导出文件生成 SHA256 写入 `manifest.checksums.files`。导入前可进行全量核验,防止传输损坏或篡改。 + +## 7. 执行流程与失败恢复 + +### 7.1 执行步骤 + +1. 创建任务(`queued`) +2. Worker 抢占任务并置 `running` +3. 记录 `exportCutoffAt` +4. 分页导出各资源 JSON 到 `data/` +5. 收集并复制媒体文件到 `files/` +6. 计算校验并生成 `manifest.json` +7. 置 `succeeded` 并返回下载能力 + +### 7.2 失败策略 + +- 任一步异常置 `failed`,记录 `errorCode/errorMessage` +- 任务目录按 `taskId` 隔离,失败不会污染其他任务 +- 用户可重试,创建新任务而非复用失败产物 + +## 8. 安全与性能控制 + +### 8.1 安全控制 + +- 仅允许用户导出/下载自己的任务 +- 下载链接短时有效(建议 10 分钟) +- 导出产物过期清理(建议 24~72 小时) +- 保留审计日志(发起人、时间、策略、结果) + +### 8.2 性能控制 + +- 大表分页读取,避免内存峰值过高 +- 文件复制并发数限流,避免 IO 打满 +- 单用户同一时刻仅允许 1 个导出任务 + +## 9. 测试与验收标准 + +### 9.1 测试建议 + +- 单元测试: + - 字段白/黑名单过滤 + - `masked`/`raw` 脱敏策略 + - `manifest` 校验信息生成 +- 集成测试: + - 用户全量导出流程(含媒体文件) + - 导出期间数据变动下的一致性行为 + - 权限边界(越权访问导出任务) + +### 9.2 验收标准 + +- 导出包包含该用户全量业务数据与关联文件 +- 永不导出字段在任何策略下都不存在 +- `masked` 与 `raw` 行为符合预期 +- `manifest` 校验通过,引用关系可重建 +- 中等数据量用户导出耗时在可接受范围 + +## 10. 分阶段落地计划(建议) + +- Phase 1:后端导出任务 + 导出包生成 + 基础下载 +- Phase 2:前端“数据导出中心”页面(发起、进度、下载、失败重试) +- Phase 3:导入校验工具(离线校验 `manifest` 与文件完整性) + +--- + +该设计聚焦“用户文件和数据可迁移”,优先保证格式稳定、数据完整与安全边界,便于后续演进到导入能力与跨环境迁移能力。