Docker Build and Push / build-image (push) Successful in 1m25s
- 标题上限收紧至 30 字 (服务端校验+详情编辑框); 快速添加输入框 放宽到 200 字: 超长时在 30 字内分隔符处截断为标题, 完整原文进描述 - Task.markDate (DateTime, 迁移 20261025120000): 详情页 DatePicker 选择/清除, 列表行与看板卡显示 MM-dd 小标签 - 快速添加时间词前缀: 今天/明天/后天/本周/下周/本月/下月 开头 (后跟分隔符) 自动设为时间标记并移出标题; 本周=本周日, 本月=月末 - 需求/功能/数据库设计文档同步更新 T-TK-14~16 与 Task 表字段
128 lines
10 KiB
Markdown
128 lines
10 KiB
Markdown
# 基于 PowerSync 的离线编辑可行性研究报告
|
||
|
||
| 项 | 内容 |
|
||
| --- | --- |
|
||
| 版本 | V0.2 |
|
||
| 日期 | 2026-10-25 |
|
||
| 评估对象 | PowerSync(powersync.com,开源 self-hosted + 云服务) |
|
||
| 评估目标 | 项目离线操作(本地编辑)+ 联网后自动同步 |
|
||
| 结论(TL;DR) | **技术上可行,但落地成本高、风险中高。不建议全量接入;若确需离线,建议仅对「打卡 / 清单 / 任务」等用户编辑域做局部接入,且需要先解决 MariaDB 兼容与主键策略两个前置问题。** |
|
||
|
||
---
|
||
|
||
## 1. 项目现状约束(评估输入)
|
||
|
||
结合代码库(`package.json`、`prisma/schema.prisma`、`server/index.js`)梳理:
|
||
|
||
1. **前端**:React 18 SPA(antd-mobile),通过 axios 调 `/api/*` REST 接口;`src/api`、`src/hooks`、`src/pages` 均以"请求 → state → 渲染"模式组织,无任何本地持久化数据层。
|
||
2. **桌面/移动端**:Tauri 2(Windows WebView2 桌面 + Android System WebView),意味着前端运行在 WebView 内,浏览器类 SDK 是候选。
|
||
3. **后端**:Express + Prisma,数据库为 **MariaDB / MySQL**(`provider = "mysql"`)。
|
||
4. **认证**:自签 JWT(jsonwebtoken),登录接口在 `/api/auth`。
|
||
5. **写路径**:全部业务写操作走 REST(打卡、清单、任务模块);同时存在大量**服务端定时写入**(cron:新闻、指标、监控、米家采样等,见 `server/cron.js`)。
|
||
6. **数据模型**:约 25 张表,主键全部为 `Int @id @default(autoincrement())` 自增整数,多表带复合唯一约束(如 `CheckIn @@unique([habitId, date, seq])`)。
|
||
7. **数据体量特征**:两类数据并存——
|
||
- **用户编辑域**(Habit / CheckIn / CheckList* / 任务模块 10 张表):冲突敏感、离线价值高;
|
||
- **服务端只读时序域**(News / Indicator / ServerNodeHistory / MihomeEnv* 等):append-only、量大、离线价值中等(缓存即可满足)。
|
||
|
||
---
|
||
|
||
## 2. PowerSync 是什么、怎么工作
|
||
|
||
- 架构:客户端本地 **SQLite**(Web 端为 WASM SQLite,存 OPFS/IndexedDB)↔ **PowerSync Service**(self-hosted 开源组件或云服务)↔ 源数据库。服务端通过 **逻辑复制/变更流** 捕获源库写入,按 **Sync Rules** 定义的下推子集同步到每个客户端的本地 SQLite。
|
||
- 写路径(官方推荐模式):客户端离线写本地 SQLite → 进入 **upload queue** → 联网后由前端把变更批量 POST 到**现有后端 API** → 后端用原有方式(本项目即 Prisma)写库 → 变更流捕获 → 广播回各端。
|
||
- 认证:客户端持后端签发的 JWT 连接同步服务;PowerSync 只做校验,不签发(可复用现有 jsonwebtoken 体系)。
|
||
- 服务端直写的表(cron 产生)也能被变更流捕获并下推,不需要改造写入方。
|
||
|
||
参考:[PowerSync Service(GitHub)](https://github.com/powersync-ja/powersync-service)、[源数据库支持](https://docs.powersync.com/configuration/source-db/setup)。
|
||
|
||
---
|
||
|
||
## 3. 逐项可行性分析
|
||
|
||
### 3.1 数据库兼容性 —— ⚠️ 主要风险点
|
||
|
||
- PowerSync 对源库支持:**PostgreSQL(GA)**、MongoDB、SQL Server;**MySQL 仍处于 Beta**(基于 binlog 复制,见 [MySQL Beta 公告](https://releases.powersync.com/announcements/mysql-backend-database-support-has-reached-beta)、[Alpha 公告](https://releases.powersync.com/announcements/announcing-mysql-as-a-source-database-alpha))。
|
||
- 本项目用的是 **MariaDB**。MariaDB 的 binlog 格式与 MySQL 8 有差异(GTID、行事件细节),PowerSync MySQL 插件(@powersync/mysql-zongji 系)对 MariaDB **没有官方兼容承诺,需要实测验证**,很可能踩坑。
|
||
- **结论**:要么实测 MariaDB + MySQL-beta 通路(风险高),要么把数据库迁到 PostgreSQL(PowerSync 最成熟路径,Prisma 迁移成本可控但涉及全部表和线上数据)。这是"能不能用"的第一道门槛。
|
||
|
||
### 3.2 数据模型适配 —— 改造成本大
|
||
|
||
- PowerSync Sync Rules 要求同步表有稳定的行标识,**强烈建议 UUID 文本主键**;而本项目全部是自增整数主键,且客户端离线创建记录时无法预知自增 id。
|
||
- 离线创建的通用解法:主键改为客户端生成的 UUID(`char(36)`),或新增 `uuid` 列 + 应用层幂等 upsert。对用户编辑域的 10 余张表意味着**一次不小的 schema 迁移 + 全部相关路由改造**(迁移原则"只增不改"仍可遵守,但外键列类型变更工作量大)。
|
||
- `DateTime`、`Json` 类型可映射(客户端以 ISO 文本/JSON 文本存储),但 `BigInt`(监控表)在 JS 端需按文本处理,注意精度。
|
||
- 复合唯一约束(`CheckIn(habitId,date,seq)`、`BoardColumn(projectId,name)` 等)在离线多端并发下会冲突,需在 upload 处理端路由里做服务端仲裁(如 seq 重排、重名加后缀),现有路由大多不具备幂等/仲裁语义。
|
||
|
||
### 3.3 写路径(离线编辑 + 上行同步)—— 模式契合,但需逐路由改造
|
||
|
||
- PowerSync 的 upload-queue → 后端 API 模式与现有 Express 架构**天然契合**:不需要后端暴露直接写库的通口,各业务路由补成"接收客户端 UUID + 幂等 upsert"即可。
|
||
- 打卡/清单/任务的路由数量约 3 个 router 文件(checkin / checklists / projects / tasks),逐个改造为幂等是可控的。
|
||
- 服务端 cron 写入的表(新闻/指标/监控/米家)无需改造写入方,只需在 Sync Rules 里定义是否下推。
|
||
|
||
### 3.4 读路径与前端数据层 —— 全面前重写
|
||
|
||
这是**工作量最大**的部分:
|
||
|
||
- 现有 `src/api` + axios + useEffect 模式必须替换为 PowerSync 本地 SQLite 查询 + `@powersync/react` 的响应式 hooks(useQuery 等),否则离线读不到数据。
|
||
- 涉及 `src/pages` 下全部用户编辑域页面(checkin、task),以及若要离线浏览还需覆盖 news/macro/monitor 的只读缓存页。
|
||
- 工作量量级:相当于把前端数据层重构一遍;可与"离线只读缓存(HTTP 缓存)"分层实施以降低风险。
|
||
|
||
### 3.5 客户端运行环境 —— 大体可行,Android 需验证
|
||
|
||
- **浏览器 / Tauri 桌面(WebView2)**:PowerSync Web SDK(WASM SQLite + OPFS)在 Chromium 系 WebView 已被官方支持,可行。
|
||
- **Tauri Android(System WebView)**:WASM SQLite 性能与 OPFS 在部分国产/旧版 WebView 上存在兼容性风险,需真机验证;备选方案是封装原生 SQLite(官方 Capacitor 插件思路可借鉴,但 Tauri 无官方插件,需自研 Rust 命令桥接,成本额外增加)。
|
||
- 时序大表(ServerNodeHistory 每分钟一条、MihomeEnv*)不建议纳入同步——Sync Rules 需做时间窗过滤(如仅近 7 天),否则客户端存储和同步流量都会失控。
|
||
|
||
### 3.6 认证与部署 —— 可行
|
||
|
||
- JWT 可复用:新增一个 `/api/powersync/token` 端点为已登录用户签发 PowerSync 接受的 token,改造小。
|
||
- 部署:self-hosted 需在现有 Express 之外多跑一个 powersync-service 进程(含 Mongo 存储元数据或云实例),并暴露 WebSocket 端口;或使用 PowerSync Cloud。运维面增加一个组件。
|
||
|
||
### 3.7 冲突解决
|
||
|
||
- PowerSync 本身不做自动合并,只提供上传队列与冲突回调;对"最后写入胜出"友好的字段(清单勾选、任务标题/状态)默认策略够用;对 `CheckIn.seq`、`BoardColumn.sortOrder` 这类"位置/序号型"数据需要自定义仲裁逻辑(上传端或同步流中处理),设计上可解,但要专门做。
|
||
|
||
---
|
||
|
||
## 4. 风险与成本汇总
|
||
|
||
| # | 风险/成本项 | 等级 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| 1 | MariaDB 兼容性未验证,MySQL 支持仅 Beta | 高 | 很可能需要迁移 PostgreSQL |
|
||
| 2 | 前端数据层全面重构(axios → 本地 SQLite 查询) | 高 | 涉及所有相关页面 |
|
||
| 3 | 用户编辑域主键整数自增 → UUID 迁移 | 中高 | schema + 路由幂等改造 |
|
||
| 4 | 复合唯一约束的离线冲突仲裁 | 中 | CheckIn.seq、列名、排序等 |
|
||
| 5 | Android WebView WASM SQLite/OPFS 兼容 | 中 | 需真机验证 |
|
||
| 6 | 新增 self-hosted 组件的运维 | 中 | 多一个常驻服务 |
|
||
| 7 | 时序大表同步范围控制 | 低 | Sync Rules 时间窗过滤即可 |
|
||
|
||
对照收益:真正需要离线**编辑**的只有打卡 / 清单 / 任务三个域(数据量小、单用户为主、冲突场景有限);其余域离线**只读缓存**即可满足。
|
||
|
||
---
|
||
|
||
## 5. 结论与建议
|
||
|
||
### 5.1 结论
|
||
|
||
**可行,但不建议以"全项目接入 PowerSync"的方式落地。** PowerSync 的同步模型与项目的 Express API 写路径契合、认证可复用、桌面端可行;但 MariaDB/MySQL-beta 兼容、整数自增主键、前端数据层全面重构三座大山使总成本明显高于当前阶段的收益。
|
||
|
||
### 5.2 建议(按优先序)
|
||
|
||
1. **首选(低成本方案)**:不引入 PowerSync,为用户编辑域实现轻量离线队列——前端把写请求落到 localStorage/IndexedDB 待发队列(含客户端生成的 UUID),联网后重放到现有 REST API(路由补幂等 upsert);只读页用 HTTP 缓存 + SW。可覆盖约 80% 的离线诉求,改动局限在 `src/api` 层与少量路由。
|
||
2. **若决定采用 PowerSync**:
|
||
- 前置 1:源库迁移 PostgreSQL(避开 MySQL Beta + MariaDB 兼容雷区);
|
||
- 前置 2:仅将用户编辑域(Habit/CheckIn/CheckList*/任务模块)纳入 Sync Rules,时序与监控表排除或做时间窗过滤;
|
||
- 前置 3:这些表的主键/唯一标识改造为客户端可生成的 UUID;
|
||
- 分阶段:先桌面端(WebView2)验证全链路,再攻 Android WebView 兼容性;
|
||
- 冲突策略:字段级 LWW 为主,seq/sortOrder 类服务端仲裁。
|
||
3. **不建议**:为离线只读域(新闻/指标/监控)上 PowerSync——用简单缓存即可,同步引擎属于过度设计。
|
||
|
||
---
|
||
|
||
## 6. 参考资料
|
||
|
||
- [PowerSync Service(GitHub,self-hosted 开源)](https://github.com/powersync-ja/powersync-service)
|
||
- [PowerSync 源数据库配置文档](https://docs.powersync.com/configuration/source-db/setup)
|
||
- [MySQL 后端支持 Beta 公告](https://releases.powersync.com/announcements/mysql-backend-database-support-has-reached-beta)
|
||
- [MySQL 后端支持 Alpha 公告](https://releases.powersync.com/announcements/announcing-mysql-as-a-source-database-alpha)
|
||
- 本项目:`prisma/schema.prisma`、`server/index.js`、`docs/V0.2/数据库设计文档.md`、`docs/V0.2/任务模块/任务模块数据库设计文档.md`
|