编号:C-04 严重级:high 工作线:消息核心(internal/app/message、serve 的 messageLoops) 来源:审查 M-06、I-02 依赖:无 被依赖:U-02 (#40)(同改 group 包,本条先合)、C-07 (#38) 第 4 点
两份审查结论一致,采用统一终态函数方案,由消息线实现并改完所有调用方:
RejectPendingTx(tx, seq, 编号, reason, nowMs)
TryFinalizeTx(tx, seq, nowMs, retentionDays)
internal/app/group/void.go
internal/app/identity/lifecycle.go
internal/app/message/dispatch.go(导出函数)、recover.go(兜底);internal/app/group/void.go;internal/app/identity/lifecycle.go(作废与收尾函数)。
internal/app/message/dispatch.go
recover.go
只改 void.go 与 lifecycle.go 中的作废、收尾函数:B-04 只删 lifecycle.go 第 131–138 行的断开 goroutine;U-03 只在删除端路径加"清除锁定";U-02 改 group/app.go 的校验逻辑,互不重叠。group 包按 C-04 → U-02 顺序合入。
以下是本次复审各区审查报告的原文段落。A、M、I、P、S 开头的是原始发现编号(A 管理后台与网页、M 消息核心、I 身份认证群在线、P 传输平台部署、S SDK)。解决方案以本 issue 上方的"结论与统一方案"为准;原文里的方案与之不一致时,按上方执行。
voidMemberDeliveriesTx
voidGroupAllTx
dispatched
max_pending_per_sender
quota_exceeded
completed
for _, r := range list { if _, execErr := tx.Exec(` UPDATE deliveries SET state = 'rejected', reason = ?, updated_at = ? WHERE seq = ? AND endpoint_id = ? AND state = 'pending'`, reason, nowMs, r.seq, endpointID); execErr != nil { return execErr } if r.pushed.Valid && revokes != nil { *revokes = append(*revokes, revokeItem{ endpointID: endpointID, msgID: r.msgID, fromID: r.senderID, reason: reason, }) } } return nil
group/void.go:93-105
group/app.go:252-258
289-295
380-389
RejectPendingTx(tx, seq, 编号, reason, nowMs, lim)
finalizeMessageTx
group.leave
group.remove
group.dissolve
message_bodies
record_retention_days=0
tryFinalizeTx
internal/app/group/void.go:20-61
93-105
internal/app/group/app.go:252-258
internal/app/message/dispatch.go:217-245
message/submit.go:460-475
message/recover.go:81-89
identity/lifecycle.go:601-624
group/group_test.go:234-246
RejectPendingTx(tx, seq, ep, reason, nowMs)
group/void.go
group.Config
RecordRetentionDays
group/app.go
identity/lifecycle.go
message/dispatch.go
cmd/nixmsg/serve.go
pushReceipts
left_group
group_dissolved
复审基线:main 4059a15(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。
4059a15
已合入 origin/main 0c9b459。落地提交 f139b9e fix: 作废投递走统一终态函数并写回执收尾 (#35)。
0c9b459
f139b9e
No dependencies set.
The note is not visible to the blocked user.
编号:C-04 严重级:high 工作线:消息核心(internal/app/message、serve 的 messageLoops) 来源:审查 M-06、I-02
依赖:无 被依赖:U-02 (#40)(同改 group 包,本条先合)、C-07 (#38) 第 4 点
结论与统一方案
两份审查结论一致,采用统一终态函数方案,由消息线实现并改完所有调用方:
RejectPendingTx(tx, seq, 编号, reason, nowMs):条件更新;消息要求回执且发送方存在时写回执;返回是否已推送,供调用方发 revoked。TryFinalizeTx(tx, seq, nowMs, retentionDays):没有 pending 投递时 completed、删正文;保留 0 天时同一事务删行。internal/app/group/void.go的两个作废函数,以及internal/app/identity/lifecycle.go的作废与收尾函数,改为调用它们,删除各自复制的 SQL。group 引入 message 不会形成循环依赖。改动文件
internal/app/message/dispatch.go(导出函数)、recover.go(兜底);internal/app/group/void.go;internal/app/identity/lifecycle.go(作废与收尾函数)。与其他问题的交互 / 冲突说明
只改 void.go 与 lifecycle.go 中的作废、收尾函数:B-04 只删 lifecycle.go 第 131–138 行的断开 goroutine;U-03 只在删除端路径加"清除锁定";U-02 改 group/app.go 的校验逻辑,互不重叠。group 包按 C-04 → U-02 顺序合入。
验收与测试
问题明细(各区审查原文,证据含文件与行号)
[M-06] 退群、踢人、解散作废投递时不写回执、不收尾:消息永远停在 dispatched,正文永不删除(跨模块:身份线 group)
voidMemberDeliveriesTx和voidGroupAllTx只把 pending 投递改成 rejected。dispatched,正文永不删除,违背 F18。max_pending_per_sender配额,最终导致这个发送方持续quota_exceeded。completed的消息,这些消息永远不会被清掉。group/void.go:93-105:解散时的 pending 投递同样处理。group/app.go:252-258、289-295、380-389:踢人、退群、解散调用上面两个函数后没有收尾。RejectPendingTx(tx, seq, 编号, reason, nowMs, lim):条件更新、写回执(消息要求回执且发送方存在)、调用finalizeMessageTx(包括保留 0 天的处理),返回是否已推送,供调用方发 revoked。group 和 identity 改为调用它,去掉各自复制的 SQL。internal/app/message/dispatch.go(导出函数)、recover.go(兜底);internal/app/group/void.go、internal/app/identity/lifecycle.go(身份线)。[I-02] 退群、踢人、解散作废投递时不写回执也不收尾,正文永远不删,还占发送方配额
group.leave、group.remove、group.dissolve(包括后台踢人和解散)把 pending 投递改成 rejected 之后:message_bodies也不删。后果有三个:max_pending_per_sender,时间长了发送方会莫名收到quota_exceeded。record_retention_days=0也不生效。tryFinalizeTx也没有处理保留 0 天的情况:停用、删除收尾后,消息和投递行仍然保留。internal/app/group/void.go:20-61和93-105只做 UPDATE、收集 revoked 列表。internal/app/group/app.go:252-258(踢人)、289-295(退群)、380-389(解散)。internal/app/message/dispatch.go:217-245。message/submit.go:460-475;清理只删 completed:message/recover.go:81-89。identity/lifecycle.go:601-624。group/group_test.go:234-246。RejectPendingTx(tx, seq, ep, reason, nowMs):条件更新,并在消息要回执、发送方仍存在时写回执。TryFinalizeTx(tx, seq, nowMs, retentionDays):没有 pending 时收尾,保留 0 天时删行。group/void.go里补上回执写入和收尾,group.Config增加RecordRetentionDays,由 serve 注入。group/void.go、group/app.go、identity/lifecycle.go、message/dispatch.go、cmd/nixmsg/serve.go。pushReceipts每秒推出,不需要额外唤醒。left_group和group_dissolved。复审基线:main
4059a15(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。已合入 origin/main
0c9b459。落地提交f139b9efix: 作废投递走统一终态函数并写回执收尾 (#35)。