From b666a5ad996de99c3fc377efc3813916d1770af4 Mon Sep 17 00:00:00 2001 From: zhx Date: Sat, 4 Jul 2026 22:23:59 +0800 Subject: [PATCH] fix room owner list scan columns --- .../internal/storage/mysql/room_list.go | 11 +- .../internal/storage/mysql/room_list_test.go | 125 ++++++++++++++++++ 2 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 services/room-service/internal/storage/mysql/room_list_test.go diff --git a/services/room-service/internal/storage/mysql/room_list.go b/services/room-service/internal/storage/mysql/room_list.go index cb1c9f24..e57ee680 100644 --- a/services/room-service/internal/storage/mysql/room_list.go +++ b/services/room-service/internal/storage/mysql/room_list.go @@ -559,6 +559,13 @@ const roomListSelectColumns = ` heat, online_count, seat_count, occupied_seat_count, sort_score, created_at_ms, updated_at_ms FROM room_list_entries` +// owner 定向列表复用公共列表扫描器,但不参与公共发现页置顶排序;这里补零值列保持 scan 契约稳定。 +const roomListOwnerSelectColumns = ` + SELECT app_code, room_id, room_short_id, visible_region_id, owner_country_code, owner_user_id, title, cover_url, mode, status, locked, + heat, online_count, seat_count, occupied_seat_count, sort_score, created_at_ms, updated_at_ms, + 0 AS is_pinned, 0 AS pin_list_rank, 0 AS pin_weight, 0 AS pinned_until_ms + FROM room_list_entries` + const roomUserFeedSelectColumns = ` SELECT r.app_code, r.room_id, r.room_short_id, r.visible_region_id, r.owner_country_code, r.owner_user_id, r.title, r.cover_url, r.mode, r.status, r.locked, r.heat, r.online_count, r.seat_count, r.occupied_seat_count, r.sort_score, r.created_at_ms, f.updated_at_ms @@ -700,7 +707,7 @@ func buildRoomListByOwnersQuerySQL(query roomservice.RoomListQuery) (string, []a args = append(args, query.CursorCreatedAtMS, query.CursorCreatedAtMS, query.CursorRoomID) } args = append(args, query.Limit) - return roomListSelectColumns + "\n\tWHERE " + strings.Join(where, " AND ") + "\n\tORDER BY created_at_ms DESC, room_id ASC\n\tLIMIT ?", args + return roomListOwnerSelectColumns + "\n\tWHERE " + strings.Join(where, " AND ") + "\n\tORDER BY created_at_ms DESC, room_id ASC\n\tLIMIT ?", args } if query.CursorRoomID != "" { @@ -708,7 +715,7 @@ func buildRoomListByOwnersQuerySQL(query roomservice.RoomListQuery) (string, []a args = append(args, query.CursorSortScore, query.CursorSortScore, query.CursorRoomID) } args = append(args, query.Limit) - return roomListSelectColumns + "\n\tWHERE " + strings.Join(where, " AND ") + "\n\tORDER BY sort_score DESC, room_id ASC\n\tLIMIT ?", args + return roomListOwnerSelectColumns + "\n\tWHERE " + strings.Join(where, " AND ") + "\n\tORDER BY sort_score DESC, room_id ASC\n\tLIMIT ?", args } func normalizeRoomListSQLCountryCode(countryCode string) string { diff --git a/services/room-service/internal/storage/mysql/room_list_test.go b/services/room-service/internal/storage/mysql/room_list_test.go new file mode 100644 index 00000000..2861e24d --- /dev/null +++ b/services/room-service/internal/storage/mysql/room_list_test.go @@ -0,0 +1,125 @@ +package mysql + +import ( + "context" + "strings" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "hyapp/pkg/appcode" + roomservice "hyapp/services/room-service/internal/room/service" +) + +func TestListRoomListEntriesByOwnersScansPinPlaceholders(t *testing.T) { + db, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("sqlmock: %v", err) + } + defer db.Close() + + repository := &Repository{db: db} + rows := sqlmock.NewRows([]string{ + "app_code", + "room_id", + "room_short_id", + "visible_region_id", + "owner_country_code", + "owner_user_id", + "title", + "cover_url", + "mode", + "status", + "locked", + "heat", + "online_count", + "seat_count", + "occupied_seat_count", + "sort_score", + "created_at_ms", + "updated_at_ms", + "is_pinned", + "pin_list_rank", + "pin_weight", + "pinned_until_ms", + }).AddRow( + appcode.Default, + "room-agency-owner", + "10001", + int64(7101), + "US", + int64(6201), + "Agency Host", + "", + "voice", + "active", + false, + int64(0), + int64(1), + int64(8), + int64(1), + int64(99), + int64(1700000000000), + int64(1700000001000), + int64(0), + int64(0), + int64(0), + int64(0), + ) + mock.ExpectQuery(`(?s)SELECT .*0 AS is_pinned.*0 AS pin_list_rank.*0 AS pin_weight.*0 AS pinned_until_ms.*FROM room_list_entries.*owner_user_id IN \(\?,\?\).*ORDER BY created_at_ms DESC, room_id ASC`). + WithArgs(appcode.Default, "active", int64(6201), int64(6202), 11). + WillReturnRows(rows) + + entries, err := repository.ListRoomListEntries(context.Background(), roomservice.RoomListQuery{ + AppCode: appcode.Default, + Tab: "new", + OwnerUserIDs: []int64{6201, 6202}, + Limit: 11, + }) + if err != nil { + t.Fatalf("owner list query should scan placeholder pin columns: %v", err) + } + if err := mock.ExpectationsWereMet(); err != nil { + t.Fatalf("sql expectations: %v", err) + } + if len(entries) != 1 || entries[0].RoomID != "room-agency-owner" { + t.Fatalf("unexpected owner list entries: %+v", entries) + } + if entries[0].IsPinned || entries[0].PinListRank != 0 || entries[0].PinWeight != 0 || entries[0].PinnedUntilMS != 0 { + t.Fatalf("owner list pin placeholders must scan as zero values: %+v", entries[0]) + } +} + +func TestBuildRoomListByOwnersQuerySQLKeepsHotCursorOrdering(t *testing.T) { + sqlText, args := buildRoomListByOwnersQuerySQL(roomservice.RoomListQuery{ + AppCode: appcode.Default, + Tab: "hot", + OwnerUserIDs: []int64{6201, 6202}, + CursorSortScore: 900, + CursorRoomID: "room-cursor", + Limit: 21, + }) + + for _, fragment := range []string{ + "0 AS is_pinned", + "0 AS pin_list_rank", + "0 AS pin_weight", + "0 AS pinned_until_ms", + "owner_user_id IN (?,?)", + "(sort_score < ? OR (sort_score = ? AND room_id > ?))", + "ORDER BY sort_score DESC, room_id ASC", + } { + if !strings.Contains(sqlText, fragment) { + t.Fatalf("owner hot SQL missing %q in:\n%s", fragment, sqlText) + } + } + + wantArgs := []any{appcode.Default, "active", int64(6201), int64(6202), int64(900), int64(900), "room-cursor", 21} + if len(args) != len(wantArgs) { + t.Fatalf("owner hot args length mismatch: got=%v want=%v", args, wantArgs) + } + for i := range args { + if args[i] != wantArgs[i] { + t.Fatalf("owner hot args[%d] mismatch: got=%v want=%v; all=%v", i, args[i], wantArgs[i], args) + } + } +}