From 67658c9398eed98ebd347ac604c7a991dc327343 Mon Sep 17 00:00:00 2001 From: zijiren233 Date: Sun, 12 Nov 2023 16:46:41 +0800 Subject: [PATCH] Feat: add admin api: room users and user rooms --- internal/db/db.go | 19 ++++++ internal/db/relation.go | 6 ++ internal/model/relation.go | 4 +- internal/model/user.go | 2 +- server/handlers/admin.go | 127 +++++++++++++++++++++++++++++++++++++ server/handlers/init.go | 33 +++++++--- server/handlers/room.go | 118 +++++++++++++++++----------------- server/model/room.go | 10 +++ server/model/user.go | 8 --- 9 files changed, 247 insertions(+), 80 deletions(-) diff --git a/internal/db/db.go b/internal/db/db.go index 98e1d11..6fed085 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -109,6 +109,14 @@ func WhereRoomID(roomID string) func(db *gorm.DB) *gorm.DB { } } +func PreloadRoomUserRelation(scopes ...func(*gorm.DB) *gorm.DB) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + return db.Preload("RoomUserRelations", func(db *gorm.DB) *gorm.DB { + return db.Scopes(scopes...) + }) + } +} + func WhereUserID(userID string) func(db *gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB { return db.Where("user_id = ?", userID) @@ -151,6 +159,17 @@ func WhereRoomNameLikeOrCreatorInOrIDLike(name string, ids []string, id string) } } +func WhereRoomNameLikeOrIDLike(name string, id string) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + switch dbType { + case conf.DatabaseTypePostgres: + return db.Where("name ILIKE ? OR id ILIKE ?", utils.LIKE(name), id) + default: + return db.Where("name LIKE ? OR id LIKE ?", utils.LIKE(name), id) + } + } +} + func WhereRoomNameLike(name string) func(db *gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB { switch dbType { diff --git a/internal/db/relation.go b/internal/db/relation.go index 3221485..224a322 100644 --- a/internal/db/relation.go +++ b/internal/db/relation.go @@ -81,3 +81,9 @@ func GetAllRoomUsersRelation(roomID string, scopes ...func(*gorm.DB) *gorm.DB) [ db.Where("room_id = ?", roomID).Scopes(scopes...).Find(&roomUserRelations) return roomUserRelations } + +func GetAllRoomUsersRelationCount(roomID string, scopes ...func(*gorm.DB) *gorm.DB) int64 { + var count int64 + db.Model(&model.RoomUserRelation{}).Where("room_id = ?", roomID).Scopes(scopes...).Count(&count) + return count +} diff --git a/internal/model/relation.go b/internal/model/relation.go index 35907b6..0e8de53 100644 --- a/internal/model/relation.go +++ b/internal/model/relation.go @@ -5,7 +5,7 @@ import ( "time" ) -type RoomUserStatus uint +type RoomUserStatus uint64 const ( RoomUserStatusBanned RoomUserStatus = iota + 1 @@ -26,7 +26,7 @@ func (r RoomUserStatus) String() string { } } -type RoomUserPermission uint32 +type RoomUserPermission uint64 const ( PermissionAll RoomUserPermission = 0xffffffff diff --git a/internal/model/user.go b/internal/model/user.go index dd0c932..8c1a4dc 100644 --- a/internal/model/user.go +++ b/internal/model/user.go @@ -43,7 +43,7 @@ type User struct { Providers []UserProvider `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` Username string `gorm:"not null;uniqueIndex"` Role Role `gorm:"not null;default:2"` - GroupUserRelations []RoomUserRelation `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` + RoomUserRelations []RoomUserRelation `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` Rooms []Room `gorm:"foreignKey:CreatorID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` Movies []Movie `gorm:"foreignKey:CreatorID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL"` StreamingVendorInfos []StreamingVendorInfo `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` diff --git a/server/handlers/admin.go b/server/handlers/admin.go index 9bfa1a4..0cf1f3d 100644 --- a/server/handlers/admin.go +++ b/server/handlers/admin.go @@ -127,6 +127,77 @@ func genUserListResp(us []*dbModel.User) []*model.UserInfoResp { return resp } +func GetRoomUsers(ctx *gin.Context) { + id := ctx.Query("id") + if len(id) != 32 { + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("room id error")) + return + } + + page, pageSize, err := GetPageAndPageSize(ctx) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) + return + } + + var desc = ctx.DefaultQuery("sort", "desc") == "desc" + + scopes := []func(db *gorm.DB) *gorm.DB{ + db.PreloadRoomUserRelation(db.WhereRoomID(id)), + } + + switch ctx.DefaultQuery("order", "name") { + case "createdAt": + if desc { + scopes = append(scopes, db.OrderByCreatedAtDesc) + } else { + scopes = append(scopes, db.OrderByCreatedAtAsc) + } + case "name": + if desc { + scopes = append(scopes, db.OrderByDesc("username")) + } else { + scopes = append(scopes, db.OrderByAsc("username")) + } + default: + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support order")) + return + } + + if keyword := ctx.Query("keyword"); keyword != "" { + // search mode, all, name, id + switch ctx.DefaultQuery("search", "all") { + case "all": + scopes = append(scopes, db.WhereUsernameLikeOrIDIn(keyword, db.GerUsersIDByIDLike(keyword))) + case "name": + scopes = append(scopes, db.WhereUsernameLike(keyword)) + case "id": + scopes = append(scopes, db.WhereIDIn(db.GerUsersIDByIDLike(keyword))) + } + } + + ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ + "total": db.GetAllUserCount(scopes...), + "list": genRoomUserListResp(db.GetAllUsers(append(scopes, db.Paginate(page, pageSize))...)), + })) +} + +func genRoomUserListResp(us []*dbModel.User) []*model.RoomUsersResp { + resp := make([]*model.RoomUsersResp, len(us)) + for i, v := range us { + resp[i] = &model.RoomUsersResp{ + UserID: v.ID, + Username: v.Username, + Role: v.Role, + JoinAt: v.RoomUserRelations[0].CreatedAt.UnixMilli(), + RoomID: v.RoomUserRelations[0].RoomID, + Status: v.RoomUserRelations[0].Status, + Permissions: v.RoomUserRelations[0].Permissions, + } + } + return resp +} + func ApprovePendingUser(ctx *gin.Context) { req := model.UserIDReq{} if err := model.Decode(ctx, &req); err != nil { @@ -259,6 +330,62 @@ func Rooms(ctx *gin.Context) { scopes = append(scopes, db.WhereRoomNameLike(keyword)) case "creator": scopes = append(scopes, db.WhereCreatorIDIn(db.GerUsersIDByUsernameLike(keyword))) + case "creatorId": + scopes = append(scopes, db.WhereCreatorID(keyword)) + case "id": + scopes = append(scopes, db.WhereIDLike(keyword)) + } + } + + ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ + "total": db.GetAllRoomsCount(scopes...), + "list": genRoomListResp(append(scopes, db.Paginate(page, pageSize))...), + })) +} + +func GetUserRooms(ctx *gin.Context) { + id := ctx.Query("id") + if len(id) != 32 { + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("user id error")) + return + } + page, pageSize, err := GetPageAndPageSize(ctx) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) + return + } + + var desc = ctx.DefaultQuery("sort", "desc") == "desc" + + scopes := []func(db *gorm.DB) *gorm.DB{ + db.WhereCreatorID(id), + } + + switch ctx.DefaultQuery("order", "name") { + case "createdAt": + if desc { + scopes = append(scopes, db.OrderByCreatedAtDesc) + } else { + scopes = append(scopes, db.OrderByCreatedAtAsc) + } + case "name": + if desc { + scopes = append(scopes, db.OrderByDesc("name")) + } else { + scopes = append(scopes, db.OrderByAsc("name")) + } + default: + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support order")) + return + } + + if keyword := ctx.Query("keyword"); keyword != "" { + // search mode, all, name, creator + switch ctx.DefaultQuery("search", "all") { + case "all": + scopes = append(scopes, db.WhereRoomNameLikeOrIDLike(keyword, keyword)) + case "name": + scopes = append(scopes, db.WhereRoomNameLike(keyword)) case "id": scopes = append(scopes, db.WhereIDLike(keyword)) } diff --git a/server/handlers/init.go b/server/handlers/init.go index 6f26997..9e82f45 100644 --- a/server/handlers/init.go +++ b/server/handlers/init.go @@ -34,21 +34,36 @@ func Init(e *gin.Engine) { admin.POST("/settings", EditAdminSettings) - admin.GET("/users", Users) + { + user := admin.Group("/user") - admin.GET("/rooms", Rooms) + // 查找用户 + user.GET("/list", Users) - admin.POST("/approve/user", ApprovePendingUser) + user.POST("/approve", ApprovePendingUser) - admin.POST("/approve/room", ApprovePendingRoom) + user.POST("/ban", BanUser) - admin.POST("/ban/user", BanUser) + user.POST("/unban", UnBanUser) - admin.POST("/ban/room", BanRoom) + // 查找某个用户的房间 + user.GET("/rooms", GetUserRooms) + } - admin.POST("/unban/user", UnBanUser) + { + room := admin.Group("/room") - admin.POST("/unban/room", UnBanRoom) + // 查找房间 + room.GET("/list", Rooms) + + room.POST("/room", ApprovePendingRoom) + + room.POST("/ban", BanRoom) + + room.POST("/unban", UnBanRoom) + + room.GET("/users", GetRoomUsers) + } } { @@ -83,7 +98,7 @@ func Init(e *gin.Engine) { needAuthRoom.POST("/settings", SetRoomSetting) - // needAuthRoom.GET("/users", RoomUsers) + needAuthRoom.GET("/users", RoomUsers) } { diff --git a/server/handlers/room.go b/server/handlers/room.go index 87de822..a61391d 100644 --- a/server/handlers/room.go +++ b/server/handlers/room.go @@ -264,63 +264,61 @@ func SetRoomSetting(ctx *gin.Context) { ctx.Status(http.StatusNoContent) } -// func RoomUsers(ctx *gin.Context) { -// room := ctx.MustGet("room").(*op.Room) -// page, pageSize, err := GetPageAndPageSize(ctx) -// if err != nil { -// ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) -// return -// } - -// var desc = ctx.DefaultQuery("sort", "desc") == "desc" - -// scopes := []func(db *gorm.DB) *gorm.DB{} -// roomUserRelationScopes := []func(db *gorm.DB) *gorm.DB{} - -// switch ctx.DefaultQuery("status", "active") { -// case "pending": -// roomUserRelationScopes = append(roomUserRelationScopes, db.WhereRoomUserStatus(dbModel.RoomUserStatusPending)) -// case "banned": -// roomUserRelationScopes = append(roomUserRelationScopes, db.WhereRoomUserStatus(dbModel.RoomUserStatusBanned)) -// case "active": -// roomUserRelationScopes = append(roomUserRelationScopes, db.WhereRoomUserStatus(dbModel.RoomUserStatusActive)) -// default: -// ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support role")) -// return -// } - -// switch ctx.DefaultQuery("order", "name") { -// case "join": -// if desc { -// roomUserRelationScopes = append(roomUserRelationScopes, db.OrderByCreatedAtDesc) -// } else { -// roomUserRelationScopes = append(roomUserRelationScopes, db.OrderByCreatedAtAsc) -// } -// case "name": -// if desc { -// scopes = append(scopes, db.OrderByDesc("username")) -// } else { -// scopes = append(scopes, db.OrderByAsc("username")) -// } -// default: -// ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support order")) -// return -// } - -// if keyword := ctx.Query("keyword"); keyword != "" { -// // search mode, all, name, id -// switch ctx.DefaultQuery("search", "all") { -// case "all": -// scopes = append(scopes, db.WhereUsernameLikeOrIDIn(keyword, db.GerUsersIDByIDLike(keyword))) -// case "name": -// scopes = append(scopes, db.WhereUsernameLike(keyword)) -// case "id": -// scopes = append(scopes, db.WhereIDIn(db.GerUsersIDByIDLike(keyword))) -// } -// } - -// ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ -// "total": db.GetAllRoomUsersCount(room.ID, roomUserRelationScopes, scopes...), -// "list": genUserListResp(db.GetAllRoomUsers(room.ID, roomUserRelationScopes, append(scopes, db.Paginate(page, pageSize))...)), -// })) -// } +func RoomUsers(ctx *gin.Context) { + room := ctx.MustGet("room").(*op.Room) + page, pageSize, err := GetPageAndPageSize(ctx) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) + return + } + + var desc = ctx.DefaultQuery("sort", "desc") == "desc" + + preloadScopes := []func(db *gorm.DB) *gorm.DB{db.WhereRoomID(room.ID)} + scopes := []func(db *gorm.DB) *gorm.DB{} + + switch ctx.DefaultQuery("status", "active") { + case "pending": + preloadScopes = append(preloadScopes, db.WhereRoomUserStatus(dbModel.RoomUserStatusPending)) + case "banned": + preloadScopes = append(preloadScopes, db.WhereRoomUserStatus(dbModel.RoomUserStatusBanned)) + case "active": + preloadScopes = append(preloadScopes, db.WhereRoomUserStatus(dbModel.RoomUserStatusActive)) + } + + switch ctx.DefaultQuery("order", "name") { + case "join": + if desc { + preloadScopes = append(preloadScopes, db.OrderByCreatedAtDesc) + } else { + preloadScopes = append(preloadScopes, db.OrderByCreatedAtAsc) + } + case "name": + if desc { + scopes = append(scopes, db.OrderByDesc("username")) + } else { + scopes = append(scopes, db.OrderByAsc("username")) + } + default: + ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("not support order")) + return + } + + if keyword := ctx.Query("keyword"); keyword != "" { + // search mode, all, name, id + switch ctx.DefaultQuery("search", "all") { + case "all": + scopes = append(scopes, db.WhereUsernameLikeOrIDIn(keyword, db.GerUsersIDByIDLike(keyword))) + case "name": + scopes = append(scopes, db.WhereUsernameLike(keyword)) + case "id": + scopes = append(scopes, db.WhereIDIn(db.GerUsersIDByIDLike(keyword))) + } + } + scopes = append(scopes, db.PreloadRoomUserRelation(preloadScopes...)) + + ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ + "total": db.GetAllUserCount(scopes...), + "list": genRoomUserListResp(db.GetAllUsers(append(scopes, db.Paginate(page, pageSize))...)), + })) +} diff --git a/server/model/room.go b/server/model/room.go index 1daa56f..42ac998 100644 --- a/server/model/room.go +++ b/server/model/room.go @@ -131,3 +131,13 @@ func (s *SetRoomSettingReq) Decode(ctx *gin.Context) error { func (s *SetRoomSettingReq) Validate() error { return nil } + +type RoomUsersResp struct { + UserID string `json:"userId"` + Username string `json:"username"` + Role dbModel.Role `json:"role"` + JoinAt int64 `json:"joinAt"` + RoomID string `json:"roomId"` + Status dbModel.RoomUserStatus `json:"status"` + Permissions dbModel.RoomUserPermission `json:"permissions"` +} diff --git a/server/model/user.go b/server/model/user.go index 164971f..5937326 100644 --- a/server/model/user.go +++ b/server/model/user.go @@ -91,11 +91,3 @@ func (u *UserIDReq) Validate() error { } return nil } - -type RoomUserInfoResp struct { - ID string `json:"id"` - Username string `json:"username"` - JoinedAt int64 `json:"joinedAt"` - Status string `json:"status"` - Permission dbModel.RoomUserPermission `json:"permission"` -}