64 lines
1.6 KiB
Markdown
64 lines
1.6 KiB
Markdown
# 文件编辑锁契约 v1
|
||
|
||
## 公共接口
|
||
|
||
```text
|
||
POST /api/v1/files/{storage_object_id}/lock
|
||
POST /api/v1/file-locks/{edit_session_id}/heartbeat
|
||
DELETE /api/v1/file-locks/{edit_session_id}
|
||
```
|
||
|
||
三个接口均要求用户身份和 Workspace 身份。当前开发基线使用
|
||
`X-User-ID`、`X-Workspace-ID`,后续接入 JWT 时保持路径和业务 DTO 不变。
|
||
|
||
加锁成功返回一次性原始 `lock_token`。心跳和释放请求体均为:
|
||
|
||
```json
|
||
{"lock_token": "raw-token-returned-by-acquire"}
|
||
```
|
||
|
||
原始 token 只由客户端持有,禁止写入 MySQL 和日志。
|
||
|
||
## Redis 数据
|
||
|
||
```text
|
||
Key: lock:file:{workspace_id}:{storage_object_id}
|
||
TTL: 45000 ms
|
||
```
|
||
|
||
Value:
|
||
|
||
```json
|
||
{
|
||
"edit_session_id": "01J...",
|
||
"user_id": "01J...",
|
||
"display_name": "张三",
|
||
"token_hash": "sha256-hex",
|
||
"acquired_at": "UTC timestamp"
|
||
}
|
||
```
|
||
|
||
加锁必须使用:
|
||
|
||
```text
|
||
SET key value NX PX 45000
|
||
```
|
||
|
||
心跳和释放必须执行 Lua 原子操作,并同时比较
|
||
`edit_session_id + token_hash`:
|
||
|
||
```text
|
||
heartbeat: compare owner -> PEXPIRE 45000
|
||
release: compare owner -> DEL
|
||
```
|
||
|
||
禁止使用 `GET` 后单独 `DEL`,也禁止在 MySQL 文件表增加 `is_locked`。
|
||
|
||
## 状态与错误
|
||
|
||
- Redis 是实时锁唯一权威。
|
||
- `edit_sessions` 记录 `active/closed/expired`,仅用于审计。
|
||
- 冲突返回 HTTP 409、错误码 `FILE_LOCK_CONFLICT`,并包含当前编辑者及租约到期时间。
|
||
- 错误 token 返回 HTTP 403,且不得续期或释放现有锁。
|
||
- 浏览器建议每 15 秒心跳;关闭、断线或心跳超时后由主动释放或 TTL 释放。
|