fix(data): 清理旧数据并保护服务端写入

This commit is contained in:
Script Generator
2026-07-02 09:30:50 +08:00
parent 28e0c6de19
commit 916bd17a48
38 changed files with 1346 additions and 194 deletions

View File

@@ -476,3 +476,17 @@
- HTTPS 先交给云负载均衡、CDN、宿主机证书工具或外层 Nginx 终止Compose 内置 Nginx 保持 HTTP 反代基线。
**理由**Docker Compose 足够覆盖当前单机云服务器和本地服务器形态,部署成本低、可读性强,也符合现阶段 2 核 4G 云主机目标。同域反代能减少 CORS 和公网端口暴露面。本地服务器默认 8080避免占用 80 端口或要求管理员权限;云服务器继续使用 80 作为外层入口。HTTPS 证书自动续期和域名接入在不同云环境差异较大,先作为外层能力处理,避免把生产部署模板绑死在某一种证书方案上。
## 39. AppData 文档写入采用乐观锁,先阻止静默覆盖
**问题**V2.1 阶段业务数据仍按模块存成 `app_data.value` 整份 JSON 文档。多人同时打开同一模块后,如果 A 和 B 都基于旧副本编辑,原来的无条件 `upsert` 会让后保存的人覆盖先保存的人尤其是任务、Bug、成员等高频写入数据。
**决策**
- `GET /api/v1/data/:key` 返回 `version`,由 `AppData.updatedAt.toISOString()` 派生;空文档返回 `version: null`
- 前端 `server-data.ts` 在读取和保存成功后缓存每个 key 的最新 `version`
- 前端保存已读取过的 key 时提交 `{ value, version }`;尚未读取过的兼容路径仍可提交 `{ value }` 走旧式 upsert。
- 后端收到字符串 `version` 时使用 `updateMany({ where: { key, updatedAt }, data: { value } })` 原子比较并更新;`count !== 1` 返回 `409 APP_DATA_CONFLICT`
- 后端收到 `version: null` 时只允许创建不存在的行;如果其他客户端已经创建,返回同样的 `409 APP_DATA_CONFLICT`
- 冲突响应返回 `currentVersion``currentValue`,但当前前端不自动合并、不自动重试,避免把旧本地副本用新版本号再次覆盖服务端数据。
**理由**:这是 AppData 阶段成本最低、收益最高的一致性补强。它不能提供字段级协同编辑,但能阻止最危险的“静默最后写入覆盖”。后续拆成关系表和领域 API 后,再在具体实体上做更细粒度的事务、唯一约束、审计日志和冲突合并 UI。