From 379ef5d85bc491cffd586ae97bf3d0bc832db47a Mon Sep 17 00:00:00 2001 From: zijiren233 Date: Sun, 22 Oct 2023 15:33:24 +0800 Subject: [PATCH] Feat: room list search --- internal/bootstrap/db.go | 2 +- internal/db/db.go | 90 +++++++++++++++++++++++++++++++++++++++- internal/db/room.go | 2 +- internal/db/user.go | 17 +++++++- server/handlers/room.go | 43 ++++++++++++++++--- utils/utils.go | 5 +++ 6 files changed, 148 insertions(+), 11 deletions(-) diff --git a/internal/bootstrap/db.go b/internal/bootstrap/db.go index 8dabcd9..0a6febb 100644 --- a/internal/bootstrap/db.go +++ b/internal/bootstrap/db.go @@ -119,7 +119,7 @@ func InitDatabase(ctx context.Context) error { log.Fatalf("failed to get sqlDB: %s", err.Error()) } initRawDB(sqlDB) - return db.Init(d) + return db.Init(d, conf.Conf.Database.Type) } func newDBLogger() logger.Interface { diff --git a/internal/db/db.go b/internal/db/db.go index 9cfac32..10d3eea 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -1,17 +1,24 @@ package db import ( + "fmt" + log "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/conf" "github.com/synctv-org/synctv/internal/model" + "github.com/synctv-org/synctv/utils" _ "github.com/synctv-org/synctv/utils/fastJSONSerializer" "gorm.io/gorm" ) -var db *gorm.DB +var ( + db *gorm.DB + dbType conf.DatabaseType +) -func Init(d *gorm.DB) error { +func Init(d *gorm.DB, t conf.DatabaseType) error { db = d + dbType = t return AutoMigrate(new(model.Movie), new(model.Room), new(model.User), new(model.RoomUserRelation), new(model.UserProvider)) } @@ -87,3 +94,82 @@ func OrderByIDAsc(db *gorm.DB) *gorm.DB { func OrderByIDDesc(db *gorm.DB) *gorm.DB { return db.Order("id desc") } + +func WithUser(db *gorm.DB) *gorm.DB { + return db.Preload("User") +} + +func WithUserAndProvider(db *gorm.DB) *gorm.DB { + return db.Preload("User").Preload("User.Provider") +} + +func WhereRoomID(roomID uint) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + return db.Where("room_id = ?", roomID) + } +} + +func WhereUserID(userID uint) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + return db.Where("user_id = ?", userID) + } +} + +func WhereCreatorID(creatorID uint) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + return db.Where("creator_id = ?", creatorID) + } +} + +// column cannot be a user parameter +func WhereEqual(column string, value interface{}) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + return db.Where(fmt.Sprintf("%s = ?", column), value) + } +} + +// column cannot be a user parameter +func WhereLike(column string, value string) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + switch dbType { + case conf.DatabaseTypePostgres: + return db.Where(fmt.Sprintf("%s ILIKE ?", column), utils.LIKE(value)) + default: + return db.Where(fmt.Sprintf("%s LIKE ?", column), utils.LIKE(value)) + } + } +} + +func WhereRoomNameLikeOrCreatorIn(name string, ids []uint) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + switch dbType { + case conf.DatabaseTypePostgres: + return db.Where("name ILIKE ? OR creator_id IN ?", utils.LIKE(name), ids) + default: + return db.Where("name LIKE ? OR creator_id IN ?", utils.LIKE(name), ids) + } + } +} + +func WhereRoomNameLike(name string) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + switch dbType { + case conf.DatabaseTypePostgres: + return db.Where("name ILIKE ?", utils.LIKE(name)) + default: + return db.Where("name LIKE ?", utils.LIKE(name)) + } + } +} + +func WhereCreatorIDIn(ids []uint) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + return db.Where("creator_id IN ?", ids) + } +} + +func Select(columns ...string) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + return db.Select(columns) + } +} diff --git a/internal/db/room.go b/internal/db/room.go index 88f2f9b..b6509fa 100644 --- a/internal/db/room.go +++ b/internal/db/room.go @@ -165,7 +165,7 @@ func GetAllRooms(scopes ...func(*gorm.DB) *gorm.DB) []*model.Room { func GetAllRoomsWithoutHidden(scopes ...func(*gorm.DB) *gorm.DB) []*model.Room { rooms := []*model.Room{} - db.Preload("Setting").Where("setting.hidden = ?", false).Scopes(scopes...).Find(&rooms) + db.Preload("Setting", "hidden = ?", false).Scopes(scopes...).Find(&rooms) return rooms } diff --git a/internal/db/user.go b/internal/db/user.go index f0a174f..dda2cba 100644 --- a/internal/db/user.go +++ b/internal/db/user.go @@ -2,6 +2,7 @@ package db import ( "errors" + "fmt" "github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/provider" @@ -91,6 +92,18 @@ func GetUserByUsername(username string) (*model.User, error) { return u, err } +func GetUserByUsernameLike(username string, scopes ...func(*gorm.DB) *gorm.DB) []*model.User { + var users []*model.User + db.Where(`username LIKE ?`, fmt.Sprintf("%%%s%%", username)).Scopes(scopes...).Find(&users) + return users +} + +func GerUsersIDByUsernameLike(username string, scopes ...func(*gorm.DB) *gorm.DB) []uint { + var ids []uint + db.Model(&model.User{}).Where(`username LIKE ?`, fmt.Sprintf("%%%s%%", username)).Scopes(scopes...).Pluck("id", &ids) + return ids +} + func GetUserByID(id uint) (*model.User, error) { u := &model.User{} err := db.Where("id = ?", id).First(u).Error @@ -100,9 +113,9 @@ func GetUserByID(id uint) (*model.User, error) { return u, err } -func GetUsersByRoomID(roomID uint) ([]model.User, error) { +func GetUsersByRoomID(roomID uint, scopes ...func(*gorm.DB) *gorm.DB) ([]model.User, error) { users := []model.User{} - err := db.Model(&model.RoomUserRelation{}).Where("room_id = ?", roomID).Find(&users).Error + err := db.Model(&model.RoomUserRelation{}).Where("room_id = ?", roomID).Scopes(scopes...).Find(&users).Error if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { return users, errors.New("room not found") } diff --git a/server/handlers/room.go b/server/handlers/room.go index 4722b74..983685e 100644 --- a/server/handlers/room.go +++ b/server/handlers/room.go @@ -66,6 +66,9 @@ func RoomList(ctx *gin.Context) { var desc = ctx.DefaultQuery("sort", "desc") == "desc" + // search mode, all, name, creator + var search = ctx.DefaultQuery("search", "all") + scopes := []func(db *gorm.DB) *gorm.DB{ db.Paginate(page, pageSize), } @@ -102,21 +105,51 @@ func RoomList(ctx *gin.Context) { } else { scopes = append(scopes, db.OrderByCreatedAtAsc) } - resp = genRoomsResp(resp, scopes...) + if keyword := ctx.Query("keyword"); keyword != "" { + switch search { + case "all": + scopes = append(scopes, db.WhereRoomNameLikeOrCreatorIn(keyword, db.GerUsersIDByUsernameLike(keyword))) + case "name": + scopes = append(scopes, db.WhereRoomNameLike(keyword)) + case "creator": + scopes = append(scopes, db.WhereCreatorIDIn(db.GerUsersIDByUsernameLike(keyword))) + } + } + resp = genRoomListResp(resp, scopes...) case "roomName": if desc { scopes = append(scopes, db.OrderByDesc("name")) } else { scopes = append(scopes, db.OrderByAsc("name")) } - resp = genRoomsResp(resp, scopes...) + if keyword := ctx.Query("keyword"); keyword != "" { + switch search { + case "all": + scopes = append(scopes, db.WhereRoomNameLikeOrCreatorIn(keyword, db.GerUsersIDByUsernameLike(keyword))) + case "name": + scopes = append(scopes, db.WhereRoomNameLike(keyword)) + case "creator": + scopes = append(scopes, db.WhereCreatorIDIn(db.GerUsersIDByUsernameLike(keyword))) + } + } + resp = genRoomListResp(resp, scopes...) case "roomId": if desc { scopes = append(scopes, db.OrderByIDDesc) } else { scopes = append(scopes, db.OrderByIDAsc) } - resp = genRoomsResp(resp, scopes...) + if keyword := ctx.Query("keyword"); keyword != "" { + switch search { + case "all": + scopes = append(scopes, db.WhereRoomNameLikeOrCreatorIn(keyword, db.GerUsersIDByUsernameLike(keyword))) + case "name": + scopes = append(scopes, db.WhereRoomNameLike(keyword)) + case "creator": + scopes = append(scopes, db.WhereCreatorIDIn(db.GerUsersIDByUsernameLike(keyword))) + } + } + resp = genRoomListResp(resp, scopes...) default: ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support order")) return @@ -128,8 +161,8 @@ func RoomList(ctx *gin.Context) { })) } -func genRoomsResp(resp []*model.RoomListResp, scopes ...func(db *gorm.DB) *gorm.DB) []*model.RoomListResp { - for _, r := range db.GetAllRooms(scopes...) { +func genRoomListResp(resp []*model.RoomListResp, scopes ...func(db *gorm.DB) *gorm.DB) []*model.RoomListResp { + for _, r := range db.GetAllRoomsWithoutHidden(scopes...) { resp = append(resp, &model.RoomListResp{ RoomId: r.ID, RoomName: r.Name, diff --git a/utils/utils.go b/utils/utils.go index 4a9c30e..0ec8f2d 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1,6 +1,7 @@ package utils import ( + "fmt" "math/rand" "net" "net/url" @@ -244,3 +245,7 @@ func OptFilePath(filePath *string) { *filePath = filepath.Join(flags.DataDir, *filePath) } } + +func LIKE(s string) string { + return fmt.Sprintf("%%%s%%", s) +}