fix: 作废投递走统一终态函数并写回执收尾

This commit is contained in:
Nixevol
2026-09-30 14:58:58 +08:00
parent 4059a1576b
commit ac90495137
10 changed files with 365 additions and 166 deletions
+37 -122
View File
@@ -7,6 +7,7 @@ import (
"errors"
"time"
"git.asio.asia/nixevol/NixMsg/internal/app/message"
"git.asio.asia/nixevol/NixMsg/internal/app/port"
"git.asio.asia/nixevol/NixMsg/internal/protocol"
)
@@ -140,9 +141,10 @@ WHERE id = ?`, endpointID); e != nil {
}
func voidEndpointMessagesTx(tx *sql.Tx, endpointID, recvReason, sendReason string, nowMs int64, revokes *[]revokeItem) error {
days := message.DefaultVoidRetentionDays
// 发给 X 的 pending → rejected
rows, err := tx.Query(`
SELECT d.seq, d.pushed_at, m.id, m.sender_id, m.receipt
SELECT d.seq, m.id, m.sender_id
FROM deliveries d
JOIN messages m ON m.seq = d.seq
WHERE d.endpoint_id = ? AND d.state = 'pending'`, endpointID)
@@ -151,15 +153,13 @@ WHERE d.endpoint_id = ? AND d.state = 'pending'`, endpointID)
}
type pendRow struct {
seq int64
pushed sql.NullInt64
msgID string
senderID string
receipt int
}
var pending []pendRow
for rows.Next() {
var r pendRow
if scanErr := rows.Scan(&r.seq, &r.pushed, &r.msgID, &r.senderID, &r.receipt); scanErr != nil {
if scanErr := rows.Scan(&r.seq, &r.msgID, &r.senderID); scanErr != nil {
_ = rows.Close()
return scanErr
}
@@ -171,18 +171,11 @@ WHERE d.endpoint_id = ? AND d.state = 'pending'`, endpointID)
}
finalSeqs := map[int64]struct{}{}
for _, r := range pending {
if _, execErr := tx.Exec(`
UPDATE deliveries SET state = 'rejected', reason = ?, updated_at = ?
WHERE seq = ? AND endpoint_id = ? AND state = 'pending'`,
recvReason, nowMs, r.seq, endpointID); execErr != nil {
pushed, execErr := message.RejectPendingTx(tx, r.seq, endpointID, recvReason, nowMs)
if execErr != nil {
return execErr
}
if r.receipt != 0 {
if e := insertReceiptIfWantedTx(tx, r.senderID, r.msgID, endpointID, "rejected", recvReason, nowMs, true); e != nil {
return e
}
}
if r.pushed.Valid && revokes != nil {
if pushed && revokes != nil {
*revokes = append(*revokes, revokeItem{
endpointID: endpointID, msgID: r.msgID, fromID: r.senderID, reason: recvReason,
})
@@ -192,21 +185,20 @@ WHERE seq = ? AND endpoint_id = ? AND state = 'pending'`,
// 发给 X 的 scheduled 单聊 → completed,要回执则写
srows, err := tx.Query(`
SELECT seq, id, sender_id, receipt FROM messages
SELECT seq, sender_id, receipt FROM messages
WHERE dest_kind = 'endpoint' AND dest_id = ? AND state = 'scheduled'`, endpointID)
if err != nil {
return err
}
type schedRow struct {
seq int64
msgID string
senderID string
receipt int
}
var scheduledTo []schedRow
for srows.Next() {
var r schedRow
if scanErr := srows.Scan(&r.seq, &r.msgID, &r.senderID, &r.receipt); scanErr != nil {
if scanErr := srows.Scan(&r.seq, &r.senderID, &r.receipt); scanErr != nil {
_ = srows.Close()
return scanErr
}
@@ -217,19 +209,8 @@ WHERE dest_kind = 'endpoint' AND dest_id = ? AND state = 'scheduled'`, endpointI
return err
}
for _, r := range scheduledTo {
if _, execErr := tx.Exec(`
UPDATE messages SET state = 'completed', reason = ? WHERE seq = ? AND state = 'scheduled'`,
recvReason, r.seq); execErr != nil {
return execErr
}
if _, execErr := tx.Exec(`DELETE FROM message_bodies WHERE seq = ?`, r.seq); execErr != nil {
return execErr
}
if r.receipt != 0 {
// 消息级作废:endpoint_id 空,state=rejected(DEVELOPMENT 6.4)
if e := insertReceiptIfWantedTx(tx, r.senderID, r.msgID, "", "rejected", recvReason, nowMs, true); e != nil {
return e
}
if e := message.FinalizeMessageTx(tx, r.seq, r.receipt != 0, r.senderID, "", recvReason, nowMs, days); e != nil {
return e
}
}
@@ -252,19 +233,14 @@ UPDATE messages SET state = 'completed', reason = ? WHERE seq = ? AND state = 's
return err
}
for _, seq := range outSeqs {
if _, execErr := tx.Exec(`
UPDATE messages SET state = 'completed', reason = ? WHERE seq = ? AND state = 'scheduled'`,
sendReason, seq); execErr != nil {
return execErr
}
if _, execErr := tx.Exec(`DELETE FROM message_bodies WHERE seq = ?`, seq); execErr != nil {
return execErr
if e := message.FinalizeMessageTx(tx, seq, false, endpointID, "", sendReason, nowMs, days); e != nil {
return e
}
}
// X 发出的消息的 pending 投递 → rejected(sender_*),不写回执
drows, err := tx.Query(`
SELECT d.seq, d.endpoint_id, d.pushed_at, m.id, m.sender_id
SELECT d.seq, d.endpoint_id, m.id, m.sender_id
FROM deliveries d
JOIN messages m ON m.seq = d.seq
WHERE m.sender_id = ? AND d.state = 'pending'`, endpointID)
@@ -274,14 +250,13 @@ WHERE m.sender_id = ? AND d.state = 'pending'`, endpointID)
type outPend struct {
seq int64
endpointID string
pushed sql.NullInt64
msgID string
senderID string
}
var outPending []outPend
for drows.Next() {
var r outPend
if scanErr := drows.Scan(&r.seq, &r.endpointID, &r.pushed, &r.msgID, &r.senderID); scanErr != nil {
if scanErr := drows.Scan(&r.seq, &r.endpointID, &r.msgID, &r.senderID); scanErr != nil {
_ = drows.Close()
return scanErr
}
@@ -292,13 +267,11 @@ WHERE m.sender_id = ? AND d.state = 'pending'`, endpointID)
return err
}
for _, r := range outPending {
if _, execErr := tx.Exec(`
UPDATE deliveries SET state = 'rejected', reason = ?, updated_at = ?
WHERE seq = ? AND endpoint_id = ? AND state = 'pending'`,
sendReason, nowMs, r.seq, r.endpointID); execErr != nil {
pushed, execErr := message.RejectPendingTx(tx, r.seq, r.endpointID, sendReason, nowMs)
if execErr != nil {
return execErr
}
if r.pushed.Valid && revokes != nil {
if pushed && revokes != nil {
*revokes = append(*revokes, revokeItem{
endpointID: r.endpointID, msgID: r.msgID, fromID: r.senderID, reason: sendReason,
})
@@ -307,7 +280,7 @@ WHERE seq = ? AND endpoint_id = ? AND state = 'pending'`,
}
for seq := range finalSeqs {
if e := tryFinalizeTx(tx, seq); e != nil {
if e := message.TryFinalizeTx(tx, seq, nowMs, days); e != nil {
return e
}
}
@@ -445,7 +418,7 @@ func withoutMember(ids []string, drop string) []string {
// voidMemberDeliveriesTx 与 group 包同语义:退群成员的 pending 群投递改 rejected。
func voidMemberDeliveriesTx(tx *sql.Tx, groupID, endpointID, reason string, nowMs int64, revokes *[]revokeItem) error {
rows, err := tx.Query(`
SELECT d.seq, d.pushed_at, m.id, m.sender_id
SELECT d.seq, m.id, m.sender_id
FROM deliveries d
JOIN messages m ON m.seq = d.seq
WHERE d.endpoint_id = ? AND d.state = 'pending'
@@ -455,14 +428,13 @@ WHERE d.endpoint_id = ? AND d.state = 'pending'
}
type row struct {
seq int64
pushed sql.NullInt64
msgID string
senderID string
}
var list []row
for rows.Next() {
var r row
if scanErr := rows.Scan(&r.seq, &r.pushed, &r.msgID, &r.senderID); scanErr != nil {
if scanErr := rows.Scan(&r.seq, &r.msgID, &r.senderID); scanErr != nil {
_ = rows.Close()
return scanErr
}
@@ -472,19 +444,18 @@ WHERE d.endpoint_id = ? AND d.state = 'pending'
if err = rows.Err(); err != nil {
return err
}
days := message.DefaultVoidRetentionDays
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 {
pushed, execErr := message.RejectPendingTx(tx, r.seq, endpointID, reason, nowMs)
if execErr != nil {
return execErr
}
if r.pushed.Valid && revokes != nil {
if pushed && revokes != nil {
*revokes = append(*revokes, revokeItem{
endpointID: endpointID, msgID: r.msgID, fromID: r.senderID, reason: reason,
})
}
if e := tryFinalizeTx(tx, r.seq); e != nil {
if e := message.TryFinalizeTx(tx, r.seq, nowMs, days); e != nil {
return e
}
}
@@ -492,8 +463,9 @@ WHERE seq = ? AND endpoint_id = ? AND state = 'pending'`,
}
func voidGroupAllTx(tx *sql.Tx, groupID string, nowMs int64, revokes *[]revokeItem) error {
days := message.DefaultVoidRetentionDays
rows, err := tx.Query(`
SELECT d.seq, d.endpoint_id, d.pushed_at, m.id, m.sender_id
SELECT d.seq, d.endpoint_id, m.id, m.sender_id
FROM deliveries d
JOIN messages m ON m.seq = d.seq
WHERE d.state = 'pending' AND m.dest_kind = 'group' AND m.dest_id = ?`, groupID)
@@ -503,14 +475,13 @@ WHERE d.state = 'pending' AND m.dest_kind = 'group' AND m.dest_id = ?`, groupID)
type drow struct {
seq int64
endpointID string
pushed sql.NullInt64
msgID string
senderID string
}
var dlist []drow
for rows.Next() {
var r drow
if scanErr := rows.Scan(&r.seq, &r.endpointID, &r.pushed, &r.msgID, &r.senderID); scanErr != nil {
if scanErr := rows.Scan(&r.seq, &r.endpointID, &r.msgID, &r.senderID); scanErr != nil {
_ = rows.Close()
return scanErr
}
@@ -521,38 +492,35 @@ WHERE d.state = 'pending' AND m.dest_kind = 'group' AND m.dest_id = ?`, groupID)
return err
}
for _, r := range dlist {
if _, execErr := tx.Exec(`
UPDATE deliveries SET state = 'rejected', reason = ?, updated_at = ?
WHERE seq = ? AND endpoint_id = ? AND state = 'pending'`,
reasonGroupDissolved, nowMs, r.seq, r.endpointID); execErr != nil {
pushed, execErr := message.RejectPendingTx(tx, r.seq, r.endpointID, reasonGroupDissolved, nowMs)
if execErr != nil {
return execErr
}
if r.pushed.Valid && revokes != nil {
if pushed && revokes != nil {
*revokes = append(*revokes, revokeItem{
endpointID: r.endpointID, msgID: r.msgID, fromID: r.senderID, reason: reasonGroupDissolved,
})
}
if e := tryFinalizeTx(tx, r.seq); e != nil {
if e := message.TryFinalizeTx(tx, r.seq, nowMs, days); e != nil {
return e
}
}
srows, err := tx.Query(`
SELECT seq, id, sender_id, receipt FROM messages
SELECT seq, sender_id, receipt FROM messages
WHERE dest_kind = 'group' AND dest_id = ? AND state = 'scheduled'`, groupID)
if err != nil {
return err
}
type srow struct {
seq int64
msgID string
senderID string
receipt int
}
var slist []srow
for srows.Next() {
var r srow
if scanErr := srows.Scan(&r.seq, &r.msgID, &r.senderID, &r.receipt); scanErr != nil {
if scanErr := srows.Scan(&r.seq, &r.senderID, &r.receipt); scanErr != nil {
_ = srows.Close()
return scanErr
}
@@ -563,66 +531,13 @@ WHERE dest_kind = 'group' AND dest_id = ? AND state = 'scheduled'`, groupID)
return err
}
for _, r := range slist {
if _, execErr := tx.Exec(`
UPDATE messages SET state = 'completed', reason = ? WHERE seq = ? AND state = 'scheduled'`,
reasonGroupDissolved, r.seq); execErr != nil {
return execErr
}
if _, execErr := tx.Exec(`DELETE FROM message_bodies WHERE seq = ?`, r.seq); execErr != nil {
return execErr
}
if r.receipt != 0 {
if e := insertReceiptIfWantedTx(tx, r.senderID, r.msgID, "", "rejected", reasonGroupDissolved, nowMs, true); e != nil {
return e
}
if e := message.FinalizeMessageTx(tx, r.seq, r.receipt != 0, r.senderID, "", reasonGroupDissolved, nowMs, days); e != nil {
return e
}
}
return nil
}
func insertReceiptIfWantedTx(tx *sql.Tx, senderID, msgID, endpointID, state, reason string, nowMs int64, alreadyWanted bool) error {
if !alreadyWanted {
return nil
}
var one int
err := tx.QueryRow(`SELECT 1 FROM endpoints WHERE id = ?`, senderID).Scan(&one)
if errors.Is(err, sql.ErrNoRows) {
return nil
}
if err != nil {
return err
}
_, err = tx.Exec(`
INSERT INTO receipts(sender_id, msg_id, endpoint_id, state, reason, created_at, acked)
VALUES(?,?,?,?,?,?,0)`, senderID, msgID, endpointID, state, reason, nowMs)
return err
}
func tryFinalizeTx(tx *sql.Tx, seq int64) error {
var n int
if err := tx.QueryRow(`SELECT COUNT(*) FROM deliveries WHERE seq = ? AND state = 'pending'`, seq).Scan(&n); err != nil {
return err
}
if n > 0 {
return nil
}
var state string
if err := tx.QueryRow(`SELECT state FROM messages WHERE seq = ?`, seq).Scan(&state); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil
}
return err
}
if state == "completed" {
return nil
}
if _, err := tx.Exec(`UPDATE messages SET state = 'completed' WHERE seq = ?`, seq); err != nil {
return err
}
_, err := tx.Exec(`DELETE FROM message_bodies WHERE seq = ?`, seq)
return err
}
func (a *App) publishRevokes(ctx context.Context, items []revokeItem) {
if a.down == nil || len(items) == 0 {
return