From d1492007abd1920b507935cfe72097cffdf863f7 Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 1 Dec 2025 08:43:05 +0800 Subject: [PATCH] fix(store): filter inbox notifications by message type at database level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add MessageType filter to FindInbox to exclude legacy VERSION_UPDATE notifications from inbox queries. This resolves the issue where users saw notification counts but no items displayed, as VERSION_UPDATE entries cannot be rendered in the new UserNotification API. Changes: - Add MessageType field to FindInbox struct for database-level filtering - Implement JSON extraction filters in SQLite, MySQL, and PostgreSQL drivers - Update ListUserNotifications to filter MEMO_COMMENT type at store level This approach improves performance by filtering at the database rather than in application code, reducing unnecessary data transfer for users with many legacy inbox entries. Fixes #5278 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- server/router/api/v1/user_service.go | 5 ++++- store/db/mysql/inbox.go | 5 +++++ store/db/postgres/inbox.go | 5 +++++ store/db/sqlite/inbox.go | 5 +++++ store/inbox.go | 9 +++++---- 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/server/router/api/v1/user_service.go b/server/router/api/v1/user_service.go index 8ca617734..053713307 100644 --- a/server/router/api/v1/user_service.go +++ b/server/router/api/v1/user_service.go @@ -1583,8 +1583,11 @@ func (s *APIV1Service) ListUserNotifications(ctx context.Context, request *v1pb. } // Fetch inbox items from storage + // Filter at database level to only include MEMO_COMMENT notifications (ignore legacy VERSION_UPDATE entries) + memoCommentType := storepb.InboxMessage_MEMO_COMMENT inboxes, err := s.Store.ListInboxes(ctx, &store.FindInbox{ - ReceiverID: &userID, + ReceiverID: &userID, + MessageType: &memoCommentType, }) if err != nil { return nil, status.Errorf(codes.Internal, "failed to list inboxes: %v", err) diff --git a/store/db/mysql/inbox.go b/store/db/mysql/inbox.go index a18fb51f1..ec20a8ebe 100644 --- a/store/db/mysql/inbox.go +++ b/store/db/mysql/inbox.go @@ -60,6 +60,11 @@ func (d *DB) ListInboxes(ctx context.Context, find *store.FindInbox) ([]*store.I if find.Status != nil { where, args = append(where, "`status` = ?"), append(args, *find.Status) } + if find.MessageType != nil { + // Filter by message type using JSON extraction + // Note: The type field in JSON is stored as string representation of the enum name + where, args = append(where, "JSON_EXTRACT(`message`, '$.type') = ?"), append(args, find.MessageType.String()) + } query := "SELECT `id`, UNIX_TIMESTAMP(`created_ts`), `sender_id`, `receiver_id`, `status`, `message` FROM `inbox` WHERE " + strings.Join(where, " AND ") + " ORDER BY `created_ts` DESC" if find.Limit != nil { diff --git a/store/db/postgres/inbox.go b/store/db/postgres/inbox.go index 1777d801d..7df32e287 100644 --- a/store/db/postgres/inbox.go +++ b/store/db/postgres/inbox.go @@ -50,6 +50,11 @@ func (d *DB) ListInboxes(ctx context.Context, find *store.FindInbox) ([]*store.I if find.Status != nil { where, args = append(where, "status = "+placeholder(len(args)+1)), append(args, *find.Status) } + if find.MessageType != nil { + // Filter by message type using PostgreSQL JSON extraction + // Note: The type field in JSON is stored as string representation of the enum name + where, args = append(where, "message->>'type' = "+placeholder(len(args)+1)), append(args, find.MessageType.String()) + } query := "SELECT id, created_ts, sender_id, receiver_id, status, message FROM inbox WHERE " + strings.Join(where, " AND ") + " ORDER BY created_ts DESC" if find.Limit != nil { diff --git a/store/db/sqlite/inbox.go b/store/db/sqlite/inbox.go index 776ed7d0b..2ab8e68d0 100644 --- a/store/db/sqlite/inbox.go +++ b/store/db/sqlite/inbox.go @@ -52,6 +52,11 @@ func (d *DB) ListInboxes(ctx context.Context, find *store.FindInbox) ([]*store.I if find.Status != nil { where, args = append(where, "`status` = ?"), append(args, *find.Status) } + if find.MessageType != nil { + // Filter by message type using JSON extraction + // Note: The type field in JSON is stored as string representation of the enum name + where, args = append(where, "JSON_EXTRACT(`message`, '$.type') = ?"), append(args, find.MessageType.String()) + } query := "SELECT `id`, `created_ts`, `sender_id`, `receiver_id`, `status`, `message` FROM `inbox` WHERE " + strings.Join(where, " AND ") + " ORDER BY `created_ts` DESC" if find.Limit != nil { diff --git a/store/inbox.go b/store/inbox.go index 2df3c19d1..417ea9f86 100644 --- a/store/inbox.go +++ b/store/inbox.go @@ -39,10 +39,11 @@ type UpdateInbox struct { // FindInbox specifies filter criteria for querying inbox items. type FindInbox struct { - ID *int32 - SenderID *int32 - ReceiverID *int32 - Status *InboxStatus + ID *int32 + SenderID *int32 + ReceiverID *int32 + Status *InboxStatus + MessageType *storepb.InboxMessage_Type // Pagination Limit *int