package handler import ( "errors" "net/http" "strconv" "github.com/freefire/jiang13-bbs/service" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // ===== 板块管理(仅站长;路由层 RequirePerm(PermBoards) 兜底) ===== // adminBoardBody 板块新建/更新请求体 type adminBoardBody struct { Name string `json:"name"` Description string `json:"description"` Icon string `json:"icon"` ColorIndex *int `json:"color_index"` SortOrder *int `json:"sort_order"` PostPolicy string `json:"post_policy"` Visible *bool `json:"visible"` } // toBoardInput 把请求体转 service 入参 func toBoardInput(b adminBoardBody) service.BoardInput { return service.BoardInput{ Name: b.Name, Description: b.Description, Icon: b.Icon, ColorIndex: b.ColorIndex, SortOrder: b.SortOrder, PostPolicy: b.PostPolicy, Visible: b.Visible, } } // AdminListBoards 板块列表(管理端:含隐藏板块;附帖子计数) func (h *Handlers) AdminListBoards(c *gin.Context) { boards, err := h.Board.List() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "获取板块列表失败"}) return } c.JSON(http.StatusOK, gin.H{"boards": nonNilSlice(boards)}) } // AdminCreateBoard 创建板块 func (h *Handlers) AdminCreateBoard(c *gin.Context) { var body adminBoardBody if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效"}) return } board, err := h.Board.Create(toBoardInput(body)) if err != nil { respondBoardError(c, err) return } c.JSON(http.StatusOK, gin.H{"board": board}) } // AdminUpdateBoard 更新板块 func (h *Handlers) AdminUpdateBoard(c *gin.Context) { id, ok := parseBoardID(c) if !ok { return } var body adminBoardBody if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效"}) return } board, err := h.Board.Update(id, toBoardInput(body)) if err != nil { respondBoardError(c, err) return } c.JSON(http.StatusOK, gin.H{"board": board}) } // AdminDeleteBoard 软删板块(仍有帖子则拒绝) func (h *Handlers) AdminDeleteBoard(c *gin.Context) { id, ok := parseBoardID(c) if !ok { return } if err := h.Board.Delete(id); err != nil { respondBoardError(c, err) return } c.JSON(http.StatusOK, gin.H{"ok": true}) } // adminReorderBody 排序请求体 type adminReorderBody struct { IDs []uint `json:"ids"` } // AdminReorderBoards 批量设置 sort_order func (h *Handlers) AdminReorderBoards(c *gin.Context) { var body adminReorderBody if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效"}) return } if err := h.Board.Reorder(body.IDs); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "排序失败"}) return } c.JSON(http.StatusOK, gin.H{"ok": true}) } // AdminListBoardModerators 板块版主列表 func (h *Handlers) AdminListBoardModerators(c *gin.Context) { id, ok := parseBoardID(c) if !ok { return } mods, err := h.Board.ListModerators(id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "获取版主失败"}) return } c.JSON(http.StatusOK, gin.H{"moderators": nonNilSlice(mods)}) } // adminBoardModBody 添加版主请求体(按用户名定位) type adminBoardModBody struct { Username string `json:"username"` } // AdminAddBoardModerator 按用户名添加板块版主 func (h *Handlers) AdminAddBoardModerator(c *gin.Context) { boardID, ok := parseBoardID(c) if !ok { return } var body adminBoardModBody if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效"}) return } userID := h.Auth.GetUserIDByUsername(body.Username) if userID == 0 { c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"}) return } if err := h.Board.AddModerator(boardID, userID); err != nil { respondBoardError(c, err) return } c.JSON(http.StatusOK, gin.H{"ok": true}) } // AdminRemoveBoardModerator 移除版主 func (h *Handlers) AdminRemoveBoardModerator(c *gin.Context) { boardID, ok := parseBoardID(c) if !ok { return } uid, err := strconv.ParseUint(c.Param("uid"), 10, 64) if err != nil || uid == 0 { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的用户 ID"}) return } if err := h.Board.RemoveModerator(boardID, uint(uid)); err != nil { respondBoardError(c, err) return } c.JSON(http.StatusOK, gin.H{"ok": true}) } // parseBoardID 解析路径中的板块 ID func parseBoardID(c *gin.Context) (uint, bool) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil || id == 0 { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的板块 ID"}) return 0, false } return uint(id), true } // respondBoardError 板块业务错误映射 func respondBoardError(c *gin.Context, err error) { switch { case errors.Is(err, gorm.ErrRecordNotFound): c.JSON(http.StatusNotFound, gin.H{"error": "板块不存在"}) case errors.Is(err, service.ErrBoardNameRequired), errors.Is(err, service.ErrProtectedOwner): c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) case errors.Is(err, service.ErrBoardInUse): c.JSON(http.StatusConflict, gin.H{"error": err.Error()}) default: c.JSON(http.StatusInternalServerError, gin.H{"error": "操作失败"}) } }