package handler import ( "errors" "net/http" "strconv" "github.com/freefire/jiang13-bbs/markdown" "github.com/freefire/jiang13-bbs/middleware" "github.com/freefire/jiang13-bbs/model" "github.com/freefire/jiang13-bbs/service" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // ===== 公开接口 ===== // AnnouncementsList 已发布公告列表(分页:page/size,默认 size=20) func (h *Handlers) AnnouncementsList(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) size, _ := strconv.Atoi(c.DefaultQuery("size", "20")) list, total, err := h.Announcement.ListPublishedPage(page, size) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "获取公告失败"}) return } c.JSON(http.StatusOK, gin.H{ "announcements": nonNilSlice(list), "total": total, "page": page, }) } // AnnouncementDetail 已发布公告详情(按读者能力脱敏 [hide]) func (h *Handlers) AnnouncementDetail(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的公告 ID"}) return } a, err := h.Announcement.GetPublished(uint(id)) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { c.JSON(http.StatusNotFound, gin.H{"error": "公告不存在"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "获取公告失败"}) return } caps := markdown.ViewerCaps{} if claims := middleware.CurrentUser(c); claims != nil { caps.LoggedIn = true if model.RoleLevel(model.Role(claims.Role)) >= model.RoleLevel(model.RoleAdmin) { caps.Bypass = true } } // 公告不可评论:回复块若有残留也永不解锁(保存期已拒收) sanitized, _ := markdown.SanitizeForViewer(a.Content, caps) out := *a out.Content = sanitized c.JSON(http.StatusOK, gin.H{"announcement": out}) } // ===== 管理接口(RequireStaff + PermAnnouncements,前端不做权限判定) ===== // AdminListAnnouncements 全部公告(含草稿) func (h *Handlers) AdminListAnnouncements(c *gin.Context) { list, err := h.Announcement.ListAll() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "获取公告失败"}) return } c.JSON(http.StatusOK, gin.H{"announcements": nonNilSlice(list)}) } // AdminCreateAnnouncement 新建公告 func (h *Handlers) AdminCreateAnnouncement(c *gin.Context) { var in service.AnnouncementInput if err := c.ShouldBindJSON(&in); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效"}) return } a, err := h.Announcement.Create(&in) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"announcement": a}) } // AdminUpdateAnnouncement 编辑公告 func (h *Handlers) AdminUpdateAnnouncement(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的公告 ID"}) return } var in service.AnnouncementInput if err := c.ShouldBindJSON(&in); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效"}) return } a, err := h.Announcement.Update(uint(id), &in) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { c.JSON(http.StatusNotFound, gin.H{"error": "公告不存在"}) return } c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"announcement": a}) } // AdminToggleAnnouncementPin 切换公告置顶 func (h *Handlers) AdminToggleAnnouncementPin(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的公告 ID"}) return } a, err := h.Announcement.TogglePin(uint(id)) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { c.JSON(http.StatusNotFound, gin.H{"error": "公告不存在"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "更新失败"}) return } c.JSON(http.StatusOK, gin.H{"announcement": a}) } // AdminDeleteAnnouncement 删除公告 func (h *Handlers) AdminDeleteAnnouncement(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的公告 ID"}) return } if err := h.Announcement.Delete(uint(id)); err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { c.JSON(http.StatusNotFound, gin.H{"error": "公告不存在"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "删除失败"}) return } c.JSON(http.StatusOK, gin.H{"ok": true}) }