From a578acebf70b567fad5bf4dfe34ed42e0e98cd63 Mon Sep 17 00:00:00 2001 From: richjohnrambo Date: Sun, 23 Aug 2026 23:08:56 +0800 Subject: [PATCH] fix: handle nullable recalled_by in mysql messages --- server/db/mysql/infrastructure_schema_test.go | 12 ++++-- server/db/mysql/tenant_adapter.go | 43 ++++++++++++------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/server/db/mysql/infrastructure_schema_test.go b/server/db/mysql/infrastructure_schema_test.go index 656d307..783911f 100644 --- a/server/db/mysql/infrastructure_schema_test.go +++ b/server/db/mysql/infrastructure_schema_test.go @@ -12,11 +12,17 @@ func TestEnsureIMHTTPInfrastructureTablesUsesSelectedTenantDatabase(t *testing.T if err := ensureIMHTTPInfrastructureTables(exec); err != nil { t.Fatal(err) } - if len(exec.statements) != 2 { - t.Fatalf("created %d infrastructure tables, want 2", len(exec.statements)) + if len(exec.statements) != 5 { + t.Fatalf("created %d infrastructure tables, want 5", len(exec.statements)) } joined := strings.ToLower(strings.Join(exec.statements, "\n")) - for _, table := range []string{"im_http_idempotency", "im_business_events_outbox"} { + for _, table := range []string{ + "im_http_idempotency", + "im_business_events_outbox", + "im_realname_appeals", + "im_conversation_settings", + "im_topic_drafts", + } { if !strings.Contains(joined, table) { t.Fatalf("missing table %s", table) } diff --git a/server/db/mysql/tenant_adapter.go b/server/db/mysql/tenant_adapter.go index 9c2d504..3b19795 100644 --- a/server/db/mysql/tenant_adapter.go +++ b/server/db/mysql/tenant_adapter.go @@ -82,6 +82,16 @@ func stampTenantMessage(id t.TenantID, msg *t.Message) *t.Message { return msg } +func finalizeTenantMessage(id t.TenantID, msg t.Message, recalledBy sql.NullInt64) t.Message { + msg.TenantID = id + msg.From = common.EncodeUidString(msg.From).String() + if recalledBy.Valid { + msg.RecalledBy = common.EncodeUidString(strconv.FormatInt(recalledBy.Int64, 10)).String() + } + msg.Content = common.FromJSON(msg.Content) + return msg +} + func stampTenantFile(id t.TenantID, file *t.FileDef) *t.FileDef { if file != nil { file.TenantID = id @@ -1659,20 +1669,21 @@ func (a *adapter) TenantMessageGetByClientID(id t.TenantID, from t.Uid, clientMs if cancel != nil { defer cancel() } - var msg t.Message + row := struct { + t.Message + RecalledBy sql.NullInt64 `db:"recalled_by"` + }{} err := a.db.QueryRowxContext(ctx, "SELECT id,createdat,updatedat,deletedat,delid,seqid,topic,`from`,client_msg_id,version,recalled_at,recalled_by,head,content "+ "FROM im_messages WHERE `from`=? AND client_msg_id=?", - decodeUID(from), clientMsgID).StructScan(&msg) + decodeUID(from), clientMsgID).StructScan(&row) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, err } - msg.From = common.EncodeUidString(msg.From).String() - msg.Content = common.FromJSON(msg.Content) - msg.TenantID = id + msg := finalizeTenantMessage(id, row.Message, row.RecalledBy) return &msg, nil } @@ -1865,13 +1876,14 @@ func (a *adapter) TenantMessageGetAll(id t.TenantID, topic string, uid t.Uid, op defer rows.Close() msgs := make([]t.Message, 0, limit) for rows.Next() { - var msg t.Message - if err = rows.StructScan(&msg); err != nil { + row := struct { + t.Message + RecalledBy sql.NullInt64 `db:"recalled_by"` + }{} + if err = rows.StructScan(&row); err != nil { return nil, err } - msg.From = common.EncodeUidString(msg.From).String() - msg.Content = common.FromJSON(msg.Content) - msgs = append(msgs, msg) + msgs = append(msgs, finalizeTenantMessage(id, row.Message, row.RecalledBy)) } return msgs, rows.Err() } @@ -1929,14 +1941,15 @@ func (a *adapter) TenantMessageSearch(id t.TenantID, topic string, uid t.Uid, qu msgs := make([]t.Message, 0, limit) nextCursor := "" for rows.Next() { - var msg t.Message - if err = rows.StructScan(&msg); err != nil { + row := struct { + t.Message + RecalledBy sql.NullInt64 `db:"recalled_by"` + }{} + if err = rows.StructScan(&row); err != nil { return nil, "", err } - msg.From = common.EncodeUidString(msg.From).String() + msg := finalizeTenantMessage(id, row.Message, row.RecalledBy) msg.Head = kvMapFromJSON(msg.Head) - msg.Content = common.FromJSON(msg.Content) - msg.TenantID = id if len(msgs) == limit { nextCursor = strconv.Itoa(msg.SeqId) continue