diff --git a/apis/floor/apis.go b/apis/floor/apis.go index 0d24b086..cbeff8c3 100644 --- a/apis/floor/apis.go +++ b/apis/floor/apis.go @@ -153,6 +153,55 @@ func GetFloor(c *fiber.Ctx) (err error) { return Serialize(c, &floor) } +// GetFloorSurroundings +// +// @Summary Get Surrounding Floors +// @Description 获取指定楼层前后各 size 个楼层,用于跳转定位 +// @Tags Floor +// @Produce application/json +// @Router /floors/{id}/surroundings [get] +// @Param id path int true "floor id" +// @Param size query int false "上下各加载的楼层数" default(10) +// @Success 200 {array} Floor +func GetFloorSurroundings(c *fiber.Ctx) error { + floorID, err := c.ParamsInt("id") + if err != nil { + return err + } + + var query SurroundingModel + err = common.ValidateQuery(c, &query) + if err != nil { + return err + } + + // get target floor's hole_id and ranking + var target Floor + err = DB.Select("hole_id", "ranking").First(&target, floorID).Error + if err != nil { + return err + } + + // load floors within ranking range + var floors Floors + querySet, err := MakeFloorQuerySet(c) + if err != nil { + return err + } + err = querySet. + Where("hole_id = ? AND ranking BETWEEN ? AND ?", + target.HoleID, + target.Ranking-query.Size, + target.Ranking+query.Size). + Order("ranking ASC"). + Find(&floors).Error + if err != nil { + return err + } + + return Serialize(c, &floors) +} + // CreateFloor // // @Summary Create A Floor diff --git a/apis/floor/routes.go b/apis/floor/routes.go index 2647e3f8..6c631225 100644 --- a/apis/floor/routes.go +++ b/apis/floor/routes.go @@ -13,6 +13,7 @@ func RegisterRoutes(app fiber.Router) { app.Get("/holes/:id/floors", ListFloorsInAHole) app.Get("/floors", ListFloorsOld) app.Get("/floors/:id", GetFloor) + app.Get("/floors/:id/surroundings", GetFloorSurroundings) app.Post("/holes/:id/floors", utils.MiddlewareHasAnsweredQuestions, CreateFloor) app.Post("/floors", utils.MiddlewareHasAnsweredQuestions, CreateFloorOld) app.Put("/floors/:id", ModifyFloor) diff --git a/apis/floor/schemas.go b/apis/floor/schemas.go index 8b398201..89d64543 100644 --- a/apis/floor/schemas.go +++ b/apis/floor/schemas.go @@ -22,6 +22,10 @@ type ListOldModel struct { Search string `query:"s" json:"s"` } +type SurroundingModel struct { + Size int `json:"size" query:"size" default:"10" validate:"min=1,max=50"` +} + type CreateModel struct { Content string `json:"content" validate:"required"` // Admin and Operator only diff --git a/tests/floor_test.go b/tests/floor_test.go index d9667668..f2c4c875 100644 --- a/tests/floor_test.go +++ b/tests/floor_test.go @@ -221,3 +221,32 @@ func TestDeleteFloor(t *testing.T) { DB.Where("hole_id = ?", hole.ID).Offset(1).First(&floor) testAPI(t, "delete", "/api/floors/"+strconv.Itoa(floor.ID), 200, data) } + +func TestGetFloorSurroundings(t *testing.T) { + // division 7 的第一个 hole 有 50 个 floor (ranking 0-49) + var hole Hole + DB.Where("division_id = ?", 7).First(&hole) + + // 取中间位置的 floor + var midFloor Floor + DB.Where("hole_id = ? AND ranking = ?", hole.ID, 25).First(&midFloor) + + // 默认 size=10,应返回 ranking 15-35 共 21 个 + var floors Floors + testAPIModel(t, "get", "/api/floors/"+strconv.Itoa(midFloor.ID)+"/surroundings", 200, &floors) + assert.EqualValues(t, 21, len(floors)) + + // 自定义 size=5,应返回 ranking 20-30 共 11 个 + data := Map{"size": 5} + testAPIModelWithQuery(t, "get", "/api/floors/"+strconv.Itoa(midFloor.ID)+"/surroundings", 200, &floors, data) + assert.EqualValues(t, 11, len(floors)) + + // 边界:第一个 floor (ranking=0), size=10 → ranking 0-10 共 11 个 + var firstFloor Floor + DB.Where("hole_id = ? AND ranking = ?", hole.ID, 0).First(&firstFloor) + testAPIModel(t, "get", "/api/floors/"+strconv.Itoa(firstFloor.ID)+"/surroundings", 200, &floors) + assert.EqualValues(t, 11, len(floors)) + + // 404 + testCommon(t, "get", "/api/floors/"+strconv.Itoa(largeInt)+"/surroundings", 404) +}