fix: 群写操作在事务内复核并去重建群成员

This commit is contained in:
Nixevol
2026-09-30 16:22:38 +08:00
parent 7c926a0904
commit ad4f13193c
4 changed files with 648 additions and 159 deletions
+30
View File
@@ -716,6 +716,36 @@
- 备选方案:仅按 joined_at。
- 影响:同毫秒加入时编号小者优先。
### 复审修复 U-02
1. **群写操作在同一写事务内复核**
- 原条款:PRD F16 群主同时是成员、停用端不能加入、成员上限、新群收不到旧群消息;issue #40。
- 实际做法:加人/踢人/退群/转让/改名/解散在 `Queue.Do` 内重读群主、成员关系和成员数;加人再复核目标端 `enabled`。对话密码(argon2)仍在事务外,事务里只做廉价 SQL。`INSERT OR IGNORE` 改为先复核再 `INSERT`;外键失败按 `not_found`。不改 `emit`,不改 message `RejectPendingTx`。
- 原因:读后写会在解散后留下孤儿成员、并发加人超过上限、转让后群主不在成员里。
- 备选方案:只靠外键、事务外校验(否决,无法给出原错误码)。
- 影响:加人与解散并发时整次加人返回 `not_found`,不写孤儿行。
2. **建群/加人先去重再截断,单请求成员数设上限**
- 原条款:部分失败仍建群;成员上限。
- 实际做法:先去掉自己和重复编号,再按剩余名额截断,超出记 `group_full`,然后才做密码校验。整表请求成员数超过 `2*max_group_members`(至少 256)回 `bad_request`。原先「校验通过人数加群主超上限则整次建群失败」改为截断后仍建群。
- 原因:重复编号会校验两次并在插入时主键冲突,客户端按 `busy` 一直重试;一个请求可带上万个成员打满哈希池。
- 备选方案:协议层去重(禁止改 protocol)。
- 影响:带重复成员的建群会成功且只留一条;超上限的多余成员在 `failed` 里而不是整次失败。
3. **后台建群校验群主并补推 `member_added`**
- 原条款:群主必须是已启用的端。
- 实际做法:`createAdmin` 校验群主编号格式、存在且 `enabled`;成员去重;建成后按与客户端建群相同方式 `emit` `member_added`。群主不存在 `invalid_target`,已停用 `endpoint_disabled`,格式非法 `bad_request`。
- 原因:原先可不存在/已停用的编号当群主,成员也不去重,也不推事件。
- 备选方案:由 admin HTTP 层预校验(仍会与写路径竞态)。
- 影响:后台建群失败码与加人目标错误码对齐。
4. **可选迁移 `0003_group_members_fk.sql`**
- 原条款:TASKS 4.2 改表加新文件,rebase 时取当时最大号加一;issue 写「排在 C-03 的 0003 之后」。
- 实际做法:本分支基于 C-04,当时最大号 0002,按 TASKS 4.2 用 0003:重建 `group_members` 并 `REFERENCES groups(id) ON DELETE CASCADE`。C-03 尚未合入。
- 原因:无外键时同编号新建群会继承旧孤儿成员。
- 备选方案:等 C-03 占用 0003 后再用 0004(rebase 时改号)。
- 影响:若 C-03 先合入并占用 0003,本文件 rebase 时改号。
## 后台接口 A
### A1 2026-09-30