返回博客

1. 概要
离线优先的桌面应用与实时在线的 Web 应用协同工作,通过 Supabase 实现数据同步。
桌面应用端(Electron + Vue3 + SQLite)
- 支持离线数据管理
- 本地 SQLite 数据库存储
- 用户认证(Supabase Auth)
- 手动触发数据同步
Web 应用端(直接连接 Supabase)
- 实时数据访问
- 多终端即时同步
- 基于 Supabase 行级安全(RLS)的权限控制
数据同步核心
- 双向数据合并(Merge)
- 冲突检测与解决
- 增量同步优化
2. 注意点
-
Supabase 服务限制
- 使用免费版,避免深度绑定
- 仅使用 Auth 认证和 Table 存储功能
- 不使用存储过程、函数等高级功能
-
数据模型一致性
- 所有终端必须保持数据模型版本一致
- 同步协议需向前兼容
-
安全约束
- 桌面端需安全存储 Supabase 密钥
- 行级安全策略(RLS)必须覆盖所有表
-
离线场景
- 桌面端需处理同步失败时的暂存数据
- 冲突解决策略需考虑离线编辑的复杂性
3. 架构示意图
系统架构图
同步流程图
4. UUID 改造方案
4.1 数据库改造
SQLite 变更:
-- 原结构
CREATE TABLE PF_SUBJECT (
id INTEGER PRIMARY KEY AUTOINCREMENT,
...
);
-- UUID 改造后
CREATE TABLE PF_SUBJECT (
id TEXT PRIMARY KEY, -- 存储 UUID
...
);
Supabase 变更:
CREATE TABLE "PF_SUBJECT" (
"id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
...
);
4.2 应用层改造
1. ID 生成策略
// uuid 库方案(采用)
// 使用开源库生成完全符合 RFC4122 标准的 UUIDv4
import { v4 as uuidv4 } from 'uuid';
const id = uuidv4(); // 例: '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'
// Web Crypto API 方案(备用)
// 现代浏览器和 Electron 内置的标准化实现
// 使用 crypto API 生成 UUIDv4
const generateUUID = () => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
};
2. 数据操作适配
// 原代码
const recordId = 123; // number 类型
// 改造后
const recordId = 'd3b07384-d113-4ec6-8d67-61e5a678d6e3'; // string 类型
4.3 同步协议调整
{
"device_id": "DESKTOP-001",
"user_id": "auth0|12345",
"sync_token": "20231005T083000",
"changes": {
"PF_SUBJECT": {
"upsert": [
{
"id": "uuid1",
"name": "数学",
"_operation": "create",
"_timestamp": 1696491000
}
],
"delete": ["uuid2"]
}
}
}
5. 关键设计决策
5.1 UUID 优势分析
优势 | 说明 |
---|---|
全局唯一 | 彻底解决多用户 ID 冲突 |
离线生成 | 桌面端可独立创建记录 |
简化同步 | 无需 ID 映射逻辑 |
迁移友好 | 避免复合主键复杂性 |
5.2 冲突解决策略
1. 时间戳优先
2. 业务特定规则
- 数据:暂无
5.3 性能优化
- 批量处理
- 每次同步最多 100 条记录
- 使用 bulk insert 操作
- 增量同步
-- 桌面端查询
SELECT * FROM PF_SUBJECT
WHERE updated_at > last_sync_time;
- 索引优化
CREATE INDEX idx_subject_updated ON PF_SUBJECT(updated_at);
6. 风险与应对
风险点 | 应对措施 |
---|---|
同步性能下降 | 增加本地缓存层 |
UUID 存储空间增大 | 使用压缩算法 |
旧数据迁移失败 | 双轨并行过渡期 |
客户端兼容问题 | 版本回退机制 |