fix(definitions): F05 定义库保存原子化并引入 revision 冲突检测

- FormulaDefinition 增加 revision 字段(迁移 0005)
- 保存接口在单事务内锁定用户所属行序列化并发:配额(50/数据集)检查与写入原子化,
  不再可能产生第 51 条;唯一约束竞争返回 409
- 同名更新必须携带匹配的 revision,两个窗口不再相互静默覆盖;
  GET 列表返回当前 revision;保存增加 30/小时限流
- 审计与定义变更(保存/删除)同一事务提交,审计失败一并回滚
- 前端保存同名定义时携带已知 revision,并明示“同名覆盖”行为
- 测试:revision 边界(缺失/过期/正确/再次过期)、配额拒绝与释放、
  审计失败回滚;PostgreSQL 并发配额用例独立门禁(本机 SQLite 跳过)
This commit is contained in:
Jayway Liu
2026-09-26 01:37:55 +08:00
parent 9a5ab26014
commit 0aaa87e31a
6 changed files with 208 additions and 18 deletions
+6 -1
View File
@@ -168,16 +168,21 @@ def test_formula_definition_library_is_private_validated_and_audited(workspace):
saved = client.post(url, data={"definition": definition}, content_type="application/json")
assert saved.status_code == 201
definition_id = saved.json()["id"]
assert saved.json()["revision"] == 1
assert client.get(url).json()["items"][0]["definition"] == definition
updated = client.post(
url,
data={"definition": {**definition, "name": " 收入成本差 ", "meaning": "更新说明"}},
data={
"definition": {**definition, "name": " 收入成本差 ", "meaning": "更新说明"},
"revision": 1,
},
content_type="application/json",
)
assert updated.status_code == 200
assert FormulaDefinition.objects.filter(dataset=ds, user=ds.created_by).count() == 1
assert updated.json()["definition"]["meaning"] == "更新说明"
assert updated.json()["revision"] == 2
other = Client()
other_user = ds.created_by.__class__.objects.create_user(