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

@@ -117,6 +117,7 @@ DevTask 没有"已完成"状态,"已提测"就是终态——开发交付完
**V2.1(当前):** 通用服务端文档表 `app_data`
- 后端:`apps/server/src/modules/data/` 提供 `GET/PUT /api/v1/data/:key`
- 数据库Prisma `AppData` 模型,表名 `app_data``key` 为主键,`value` 为 JSONB
- 一致性:`GET` 返回 `updatedAt` 派生的 `version`;前端保存时带上最近读取的 `version`,后端用 `key + updatedAt` 原子更新,版本不匹配返回 `409 APP_DATA_CONFLICT`
- 前端:各 Zustand store 保持现有数据形状,通过 `apps/web/lib/server-data.ts` 读写服务端
- 覆盖范围:产品/项目/版本树、需求池、调研/产品方案/UI 计划、开发任务、测试用例、Bug、成员/角色/部门、任务类型、任务工时日志、加班记录
- 浏览器仅保留登录会话(`ftb_auth_session` / `ftb_auth_persist`),不再作为业务数据主存储

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。

View File

@@ -6,6 +6,11 @@
### 已完成(按时间倒序)
**2026-07-02**
- `app_data` 读写增加乐观锁版本:`GET` 返回 `version`,前端保存携带最近版本,后端用 `key + updatedAt` 原子更新
- stale version / create race 返回 `409 APP_DATA_CONFLICT`,阻止多人同时编辑时的静默覆盖
- 新增后端 AppData 并发写入回归测试和前端 `server-data` 版本缓存测试
**2026-07-01**
- 补齐云服务器生产部署基线:`Dockerfile.web``Dockerfile.server``docker-compose.prod.yml`、Nginx 反代模板和 `.env.production.example`
- 补齐本地服务器/局域网部署基线:`docker-compose.local.yml``.env.local-server.example``deploy:local:*` 脚本

View File

@@ -135,6 +135,13 @@
- 涉及 UI 改动:`curl http://localhost:3000/<path>` 检查 200
- 不会自动跑 dev server假定它已经运行
## AppData 并发写入流程
- 前端通过 `loadServerData(key)` 读取业务文档时,必须缓存响应里的 `version`
- 前端通过 `saveServerData(key, value)` 保存已读取过的 key 时,必须把最近一次成功读取/保存得到的 `version` 一起提交。
- 后端只在 `key + updatedAt(version)` 匹配时更新;如果其他用户已经先保存,返回 `409 APP_DATA_CONFLICT`,响应包含当前服务端 `currentVersion``currentValue`
- 收到 `ServerDataConflictError` 时,不要自动重试覆盖。当前处理策略是阻止静默覆盖,后续 UI 冲突合并能力再单独补。
## 与我相关Workspace数据流
```