Files
meijiaka-zy/tauri-app/docs/data-architecture.md
T
小鱼开发 04e467e433 feat(points): 积分系统收尾 + 充值弹窗改造 + 命名统一
后端:
- 微信回调 db.commit 失败仍返回 SUCCESS,避免无限重试
- recharge() 加 order_id 幂等保护,防重复充值
- time_expire 使用北京时间(UTC+8),修复时区 bug
- 充值档位后端配置化(points-config.yaml + /recharge-options API)
- 代码审查 20 项修复(认证加固、扣费顺序、错误响应、状态同步等)

前端:
- 充值弹窗:自动轮询 + 【我已支付】手动兜底
- 二维码倒计时显示,过期后遮罩 + 刷新按钮
- 充值档位从后端动态加载
- 去掉 select/qrcode 弹窗标题,金额红色突出显示
- 全项目命名统一(视频生成/压制成片/配音合成/声音复刻等)
- Modal 关闭按钮独立于 title 显示
2026-05-09 21:29:35 +08:00

9.6 KiB
Raw Blame History

美家卡智影 - 数据架构设计

1. 架构目标

  • 离线优先:无网络时也能正常使用
  • 云端同步:多设备间数据同步
  • 多媒体本地存储:大文件存本地目录,小数据存云端
  • 自动冲突解决:简化用户操作

2. 数据分层

┌─────────────────────────────────────────────────────────────────┐
│                        云端 (PostgreSQL)                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐              │
│  │   users     │  │  projects   │  │   avatars   │  ...          │
│  │  (用户账号)  │  │ (项目元数据) │  │ (形象元数据) │              │
│  └─────────────┘  └─────────────┘  └─────────────┘              │
│       小数据、结构数据、需要同步的数据                              │
└─────────────────────────────────────────────────────────────────┘
                              ↑↓ 同步
┌─────────────────────────────────────────────────────────────────┐
│                    本地 (Tauri + 浏览器)                          │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  SQLite / 文件系统 (~/Documents/Meijiaka/)               │    │
│  │  ├─ projects/           # 项目数据                        │    │
│  │  │   └─ {project_id}/                                  │    │
│  │  │       ├─ project.json     # 项目配置                 │    │
│  │  │       ├─ segments/        # 分镜数据                  │    │
│  │  │       ├─ videos/          # 生成的视频               │    │
│  │  │       └─ covers/          # 封面图片                 │    │
│  │  ├─ avatars/            # 克隆形象                       │    │
│  │  │   └─ {avatar_id}/                                    │    │
│  │  │       ├─ avatar.json     # 形象配置                  │    │
│  │  │       └─ video.mp4       # 形象视频                  │    │
│  │  └─ temp/               # 临时文件                       │    │
│  └─────────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  SWR Cache (内存)                                       │    │
│  │  - API 响应缓存                                          │    │
│  │  - 乐观更新队列                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────┘

3. 数据分类

3.1 云端优先数据(小数据,结构化)

数据类型 存储位置 同步策略 说明
用户信息 云端 实时 账号、设置偏好
项目列表 云端 实时 项目ID、标题、状态、更新时间
形象元数据 云端 实时 形象ID、名称、云端视频URL
成本统计 云端 实时 API 调用记录、费用
脚本内容 云端+本地 双向同步 分镜文案、场景描述

3.2 本地优先数据(大文件,多媒体)

数据类型 存储位置 同步策略 说明
形象视频 本地 按需上传 克隆形象的原视频文件
生成视频 本地 可选上传 AI生成的分镜视频
封面图片 本地 可选上传 项目封面
合成视频 本地 可选上传 最终成片
临时文件 本地 不同步 处理过程中的缓存

4. 同步策略

4.1 自动同步(实时)

// 云端优先数据 - 自动同步
- 用户修改脚本  立即保存到云端
- 切换形象  立即同步到云端
- 项目状态变更  实时更新

4.2 手动同步(用户触发)

// 大文件 - 手动同步
- 上传形象视频到云端备份
- 下载云端形象到本地
- 分享项目(打包上传)

4.3 离线队列

// 离线时操作进入队列
interface OfflineQueue {
  id: string;
  type: 'create' | 'update' | 'delete';
  entity: 'project' | 'segment' | 'avatar';
  data: any;
  timestamp: number;
  retryCount: number;
}

5. 冲突解决

5.1 简单策略:最后写入优先

// 基于 updatedAt 时间戳
const resolveConflict = (local: Data, remote: Data) => {
  return local.updatedAt > remote.updatedAt ? local : remote;
};

5.2 字段级合并(可选)

// 不同字段分别决定
const mergeConflict = (local: Project, remote: Project) => {
  return {
    ...remote,
    segments: local.updatedAt > remote.updatedAt 
      ? local.segments 
      : remote.segments,
    coverPath: local.coverUpdatedAt > remote.coverUpdatedAt
      ? local.coverPath
      : remote.coverPath,
  };
};

6. 存储实现

6.1 本地目录结构

~/Documents/Meijiaka/                    # 用户文档目录
├── config.json                          # 全局配置
├── cache/                               # 缓存目录
│   └── swr/                             # SWR 缓存
├── projects/                            # 项目数据
│   └── {project_id}/
│       ├── meta.json                    # 项目元数据
│       ├── segments.json                # 分镜数据
│       ├── assets/                      # 资源文件
│       │   ├── videos/                  # 生成视频
│       │   ├── covers/                  # 封面图片
│       │   └── temp/                    # 临时文件
│       └── exports/                     # 导出文件
├── avatars/                             # 克隆形象
│   └── {avatar_id}/
│       ├── meta.json                    # 形象配置
│       └── source.mp4                   # 原视频
└── voices/                              # 克隆音色
    └── {voice_id}/
        └── sample.mp3

6.2 数据存储接口

// 统一存储接口
interface StorageAdapter {
  // 项目数据
  saveProject(project: Project): Promise<void>;
  loadProject(projectId: string): Promise<Project | null>;
  deleteProject(projectId: string): Promise<void>;
  listProjects(): Promise<ProjectSummary[]>;
  
  // 形象数据
  saveAvatar(avatar: Avatar): Promise<void>;
  loadAvatar(avatarId: string): Promise<Avatar | null>;
  deleteAvatar(avatarId: string): Promise<void>;
  listAvatars(): Promise<AvatarSummary[]>;
  
  // 多媒体文件
  saveMedia(projectId: string, file: File, type: MediaType): Promise<string>;
  loadMedia(path: string): Promise<Blob>;
  deleteMedia(path: string): Promise<void>;
}

7. 状态管理

7.1 分层状态

// 1. 服务端状态 (SWR)
const { data: projects } = useSWR('/api/projects', fetcher);

// 2. 本地持久化状态 (Zustand + Storage)
const projectStore = useProjectStore(); // 自动持久化到本地文件

// 3. 临时 UI 状态 (React State)
const [selectedTab, setSelectedTab] = useState('script');

7.2 同步 hook

// 自动同步 hook
function useSyncProject(projectId: string) {
  const { data: remoteProject } = useSWR(`/api/projects/${projectId}`);
  const localProject = useProjectStore(state => state.project);
  const sync = useProjectStore(state => state.sync);
  
  useEffect(() => {
    if (remoteProject && localProject) {
      sync(remoteProject, localProject);
    }
  }, [remoteProject, localProject]);
}

8. 迁移路径

Phase 1: 本地文件存储(当前)

  • 项目数据存 Tauri 本地
  • 形象数据存 localStorage

Phase 2: 云端同步(下一步)

  • 后端新增 project/avatar 表
  • 实现双向同步逻辑
  • 离线队列

Phase 3: 多媒体管理

  • 本地目录结构
  • 文件导入/导出
  • 云端备份(可选)

9. 关键决策

Q1: 是否需要本地 SQLite

建议Phase 1 不需要,JSON 文件足够简单。Phase 2 如需复杂查询再引入。

Q2: 如何处理大文件同步?

建议:不上传大文件,只在本地存储。用户需要跨设备时,提供"导出/导入"功能。

Q3: 离线支持到什么程度?

建议

  • 脚本编辑:完全离线
  • 视频生成:需要联网(AI 服务)
  • 压制成片:可离线(本地 FFmpeg