package controllers import ( "strings" "time" "server/models" beego "github.com/beego/beego/v2/server/web" ) // ============================================================= // 平台官网 - 文章管理 /platform/website/article/* // - 分类管理 /platform/website/category/* // 表:yz_platform_website_article / yz_platform_website_category // ============================================================= type PlatformWebsiteArticleController struct { beego.Controller } type platformWebsiteArticlePayload struct { Title *string `json:"title"` Author *string `json:"author"` Cate *uint64 `json:"cate"` CateID *uint64 `json:"cate_id"` Content *string `json:"content"` Desc *string `json:"desc"` Image *string `json:"image"` IsTrans *int8 `json:"is_trans"` TransURL *string `json:"transurl"` Status *int8 `json:"status"` Tags []uint64 `json:"tags"` } // articleCateID 兼容前端 cate / cate_id 两种传参 func (p *platformWebsiteArticlePayload) articleCateID() (uint64, bool) { if p.Cate != nil { return *p.Cate, true } if p.CateID != nil { return *p.CateID, true } return 0, false } // List GET /platform/website/article/list // query: keyword、cate、status、startDate、endDate、page、pageSize func (c *PlatformWebsiteArticleController) List() { if _, ok := websiteAuth(&c.Controller); !ok { return } page, pageSize := websitePaging(&c.Controller) qs := models.Orm.QueryTable(new(models.PlatformWebsiteArticle)). Filter("delete_time__isnull", true) if kw := strings.TrimSpace(c.GetString("keyword")); kw != "" { qs = qs.Filter("title__icontains", kw) } if cate, err := c.GetInt64("cate"); err == nil && cate > 0 { qs = qs.Filter("cate_id", cate) } if st := websiteStatusParam(&c.Controller); st >= 0 { qs = qs.Filter("status", st) } if start := strings.TrimSpace(c.GetString("startDate")); start != "" { qs = qs.Filter("publish_time__gte", start+" 00:00:00") } if end := strings.TrimSpace(c.GetString("endDate")); end != "" { qs = qs.Filter("publish_time__lte", end+" 23:59:59") } total, err := qs.Count() if err != nil { jsonErr(&c.Controller, 500, 500, "获取失败: "+err.Error()) return } var rows []models.PlatformWebsiteArticle if _, err = qs.OrderBy("-top", "-recommend", "-id"). Limit(pageSize, (page-1)*pageSize). All(&rows, "ID", "CateID", "Title", "Author", "Desc", "Image", "IsTrans", "Status", "Top", "Recommend", "Views", "Likes", "PublishTime", "CreateTime", "UpdateTime"); err != nil { jsonErr(&c.Controller, 500, 500, "获取失败: "+err.Error()) return } cateIDs := make([]uint64, 0, len(rows)) for _, r := range rows { if r.CateID > 0 { cateIDs = append(cateIDs, r.CateID) } } cateMap := models.PlatformWebsiteCategoryNameMap(cateIDs) list := make([]map[string]interface{}, 0, len(rows)) for _, r := range rows { list = append(list, map[string]interface{}{ "id": r.ID, "cate_id": r.CateID, "cate": cateMap[r.CateID], "title": r.Title, "author": r.Author, "desc": r.Desc, "image": r.Image, "is_trans": r.IsTrans, "status": r.Status, "top": r.Top, "recommend": r.Recommend, "views": r.Views, "likes": r.Likes, "publish_time": r.PublishTime, "create_time": r.CreateTime, "update_time": r.UpdateTime, }) } websiteOK(&c.Controller, map[string]interface{}{"list": list, "total": total}) } // ListAll GET /platform/website/article/all — 不分页,供选择/关联使用 func (c *PlatformWebsiteArticleController) ListAll() { if _, ok := websiteAuth(&c.Controller); !ok { return } qs := models.Orm.QueryTable(new(models.PlatformWebsiteArticle)). Filter("delete_time__isnull", true) if kw := strings.TrimSpace(c.GetString("keyword")); kw != "" { qs = qs.Filter("title__icontains", kw) } if st := websiteStatusParam(&c.Controller); st >= 0 { qs = qs.Filter("status", st) } var rows []models.PlatformWebsiteArticle if _, err := qs.OrderBy("-id").All(&rows, "ID", "Title", "Status", "CateID", "PublishTime"); err != nil { jsonErr(&c.Controller, 500, 500, "获取失败: "+err.Error()) return } websiteOK(&c.Controller, rows) } // Recycle GET /platform/website/article/recycle — 回收站(已软删除的文章) func (c *PlatformWebsiteArticleController) Recycle() { if _, ok := websiteAuth(&c.Controller); !ok { return } page, pageSize := websitePaging(&c.Controller) qs := models.Orm.QueryTable(new(models.PlatformWebsiteArticle)). Filter("delete_time__isnull", false) if kw := strings.TrimSpace(c.GetString("keyword")); kw != "" { qs = qs.Filter("title__icontains", kw) } total, err := qs.Count() if err != nil { jsonErr(&c.Controller, 500, 500, "获取失败: "+err.Error()) return } var rows []models.PlatformWebsiteArticle if _, err = qs.OrderBy("-delete_time"). Limit(pageSize, (page-1)*pageSize). All(&rows, "ID", "CateID", "Title", "Author", "Desc", "Image", "Status", "Top", "Recommend", "Views", "Likes", "PublishTime", "CreateTime", "UpdateTime", "DeleteTime"); err != nil { jsonErr(&c.Controller, 500, 500, "获取失败: "+err.Error()) return } cateIDs := make([]uint64, 0, len(rows)) for _, r := range rows { if r.CateID > 0 { cateIDs = append(cateIDs, r.CateID) } } cateMap := models.PlatformWebsiteCategoryNameMap(cateIDs) list := make([]map[string]interface{}, 0, len(rows)) for _, r := range rows { list = append(list, map[string]interface{}{ "id": r.ID, "cate_id": r.CateID, "cate": cateMap[r.CateID], "title": r.Title, "author": r.Author, "desc": r.Desc, "image": r.Image, "status": r.Status, "top": r.Top, "recommend": r.Recommend, "views": r.Views, "likes": r.Likes, "publish_time": r.PublishTime, "create_time": r.CreateTime, "update_time": r.UpdateTime, "delete_time": r.DeleteTime, }) } websiteOK(&c.Controller, map[string]interface{}{"list": list, "total": total}) } // Restore POST /platform/website/article/:id/restore — 从回收站恢复 func (c *PlatformWebsiteArticleController) Restore() { if _, ok := websiteAuth(&c.Controller); !ok { return } id, ok := websiteID(&c.Controller) if !ok { return } // 清空 delete_time 属于「反软删除」,beego 的 Update(map) 不便表达,直接走 SQL res, err := models.Orm.Raw( "UPDATE yz_platform_website_article SET delete_time = NULL, update_time = ? WHERE id = ? AND delete_time IS NOT NULL", time.Now(), id, ).Exec() if err != nil { jsonErr(&c.Controller, 500, 500, "恢复失败: "+err.Error()) return } if affected, _ := res.RowsAffected(); affected == 0 { jsonErr(&c.Controller, 404, 404, "文章不存在或未被删除") return } websiteOK(&c.Controller, nil) } // Purge DELETE /platform/website/article/:id/purge — 彻底删除(仅限回收站中的记录) func (c *PlatformWebsiteArticleController) Purge() { if _, ok := websiteAuth(&c.Controller); !ok { return } id, ok := websiteID(&c.Controller) if !ok { return } n, err := models.Orm.QueryTable(new(models.PlatformWebsiteArticle)). Filter("id", id). Filter("delete_time__isnull", false). Delete() if err != nil { jsonErr(&c.Controller, 500, 500, "彻底删除失败: "+err.Error()) return } if n == 0 { jsonErr(&c.Controller, 404, 404, "仅可彻底删除回收站中的文章") return } _ = models.ClearPlatformWebsiteArticleTags(id) websiteOK(&c.Controller, nil) } // Get GET /platform/website/article/:id — 详情(含标签ID列表) func (c *PlatformWebsiteArticleController) Get() { if _, ok := websiteAuth(&c.Controller); !ok { return } id, ok := websiteID(&c.Controller) if !ok { return } var row models.PlatformWebsiteArticle if err := models.Orm.QueryTable(new(models.PlatformWebsiteArticle)). Filter("id", id). Filter("delete_time__isnull", true). One(&row); err != nil { jsonErr(&c.Controller, 404, 404, "文章不存在") return } cateName := "" if row.CateID > 0 { cateName = models.PlatformWebsiteCategoryNameMap([]uint64{row.CateID})[row.CateID] } c.Data["json"] = map[string]interface{}{ "code": 200, "msg": "success", "data": map[string]interface{}{ "id": row.ID, "cate_id": row.CateID, "cate": cateName, "title": row.Title, "author": row.Author, "desc": row.Desc, "image": row.Image, "content": row.Content, "is_trans": row.IsTrans, "transurl": row.TransURL, "status": row.Status, "top": row.Top, "recommend": row.Recommend, "views": row.Views, "likes": row.Likes, "publisher_id": row.PublisherID, "publish_time": row.PublishTime, "create_time": row.CreateTime, "update_time": row.UpdateTime, "tags": models.PlatformWebsiteArticleTagIDs(row.ID), }, } _ = c.ServeJSON() } // Create POST /platform/website/article func (c *PlatformWebsiteArticleController) Create() { if _, ok := websiteAuth(&c.Controller); !ok { return } var p platformWebsiteArticlePayload if !websiteBind(&c.Controller, &p) { return } title := "" if p.Title != nil { title = strings.TrimSpace(*p.Title) } if title == "" { jsonErr(&c.Controller, 400, 400, "文章标题不能为空") return } row := models.PlatformWebsiteArticle{Title: title} if p.Author != nil { row.Author = strings.TrimSpace(*p.Author) } if cate, has := p.articleCateID(); has { row.CateID = cate } if p.Content != nil { row.Content = *p.Content } if p.Desc != nil { row.Desc = *p.Desc } if p.Image != nil { row.Image = strings.ReplaceAll(*p.Image, "\\", "/") } if p.IsTrans != nil { row.IsTrans = *p.IsTrans } if row.IsTrans == 1 && p.TransURL != nil { url := strings.TrimSpace(*p.TransURL) row.TransURL = &url } if p.Status != nil { row.Status = *p.Status } // 直接提交待审核时同步记录发布时间 if row.Status == 2 { now := time.Now() row.PublishTime = &now } id, err := models.Orm.Insert(&row) if err != nil { jsonErr(&c.Controller, 500, 500, "创建失败: "+err.Error()) return } if err = models.SetPlatformWebsiteArticleTags(uint64(id), p.Tags); err != nil { jsonErr(&c.Controller, 500, 500, "标签保存失败: "+err.Error()) return } websiteOK(&c.Controller, map[string]interface{}{"id": id}) } // Update POST /platform/website/article/:id func (c *PlatformWebsiteArticleController) Update() { if _, ok := websiteAuth(&c.Controller); !ok { return } id, ok := websiteID(&c.Controller) if !ok { return } var p platformWebsiteArticlePayload if !websiteBind(&c.Controller, &p) { return } up := map[string]interface{}{} if p.Title != nil { title := strings.TrimSpace(*p.Title) if title == "" { jsonErr(&c.Controller, 400, 400, "文章标题不能为空") return } up["title"] = title } if p.Author != nil { up["author"] = strings.TrimSpace(*p.Author) } if cate, has := p.articleCateID(); has { up["cate_id"] = cate } if p.Content != nil { up["content"] = *p.Content } if p.Desc != nil { up["desc"] = *p.Desc } if p.Image != nil { up["image"] = strings.ReplaceAll(*p.Image, "\\", "/") } if p.IsTrans != nil { up["is_trans"] = *p.IsTrans } if p.TransURL != nil { url := strings.TrimSpace(*p.TransURL) if url == "" { up["transurl"] = nil } else { up["transurl"] = url } } if p.Status != nil { up["status"] = *p.Status if *p.Status == 2 { up["publish_time"] = time.Now() } } up["update_time"] = time.Now() n, err := models.Orm.QueryTable(new(models.PlatformWebsiteArticle)). Filter("id", id). Filter("delete_time__isnull", true). Update(up) if err != nil { jsonErr(&c.Controller, 500, 500, "更新失败: "+err.Error()) return } if n == 0 { jsonErr(&c.Controller, 404, 404, "文章不存在") return } // tags 传了才覆盖,未传则保持原有关联 if p.Tags != nil { if err = models.SetPlatformWebsiteArticleTags(id, p.Tags); err != nil { jsonErr(&c.Controller, 500, 500, "标签保存失败: "+err.Error()) return } } websiteOK(&c.Controller, nil) } // Delete DELETE /platform/website/article/:id func (c *PlatformWebsiteArticleController) Delete() { if _, ok := websiteAuth(&c.Controller); !ok { return } id, ok := websiteID(&c.Controller) if !ok { return } now := time.Now() n, err := models.Orm.QueryTable(new(models.PlatformWebsiteArticle)). Filter("id", id). Filter("delete_time__isnull", true). Update(map[string]interface{}{"delete_time": now, "update_time": now}) if err != nil { jsonErr(&c.Controller, 500, 500, "删除失败: "+err.Error()) return } if n == 0 { jsonErr(&c.Controller, 404, 404, "文章不存在") return } _ = models.ClearPlatformWebsiteArticleTags(id) websiteOK(&c.Controller, nil) } // Publish POST /platform/website/article/:id/publish // body: { uid } 选填,用于记录发布人(平台管理员ID) func (c *PlatformWebsiteArticleController) Publish() { claims, ok := websiteAuth(&c.Controller) if !ok { return } id, ok := websiteID(&c.Controller) if !ok { return } var p struct { UID *uint64 `json:"uid"` } // 发布人参数可选,缺失时回退为当前登录管理员 websiteBindOptional(&c.Controller, &p) publisher := uint64(claims.UserID) if p.UID != nil && *p.UID > 0 { publisher = *p.UID } now := time.Now() n, err := models.Orm.QueryTable(new(models.PlatformWebsiteArticle)). Filter("id", id). Filter("delete_time__isnull", true). Update(map[string]interface{}{ "status": 2, "publish_time": now, "publisher_id": publisher, "update_time": now, }) if err != nil { jsonErr(&c.Controller, 500, 500, "发布失败: "+err.Error()) return } if n == 0 { jsonErr(&c.Controller, 404, 404, "文章不存在") return } websiteOK(&c.Controller, nil) } // UnPublish POST /platform/website/article/:id/unpublish — 下架后状态置为「已隐藏」 func (c *PlatformWebsiteArticleController) UnPublish() { if _, ok := websiteAuth(&c.Controller); !ok { return } id, ok := websiteID(&c.Controller) if !ok { return } n, err := models.Orm.QueryTable(new(models.PlatformWebsiteArticle)). Filter("id", id). Filter("delete_time__isnull", true). Update(map[string]interface{}{"status": 3, "update_time": time.Now()}) if err != nil { jsonErr(&c.Controller, 500, 500, "下架失败: "+err.Error()) return } if n == 0 { jsonErr(&c.Controller, 404, 404, "文章不存在") return } websiteOK(&c.Controller, nil) } // updateArticleFlag 统一处理推荐/置顶标记 func (c *PlatformWebsiteArticleController) updateArticleFlag(field string, value int8, failMsg string) { if _, ok := websiteAuth(&c.Controller); !ok { return } id, ok := websiteID(&c.Controller) if !ok { return } n, err := models.Orm.QueryTable(new(models.PlatformWebsiteArticle)). Filter("id", id). Filter("delete_time__isnull", true). Update(map[string]interface{}{field: value, "update_time": time.Now()}) if err != nil { jsonErr(&c.Controller, 500, 500, failMsg+": "+err.Error()) return } if n == 0 { jsonErr(&c.Controller, 404, 404, "文章不存在") return } websiteOK(&c.Controller, nil) } // Recommend POST /platform/website/article/:id/recommend func (c *PlatformWebsiteArticleController) Recommend() { c.updateArticleFlag("recommend", 1, "推荐失败") } // UnRecommend POST /platform/website/article/:id/unrecommend func (c *PlatformWebsiteArticleController) UnRecommend() { c.updateArticleFlag("recommend", 0, "取消推荐失败") } // Top POST /platform/website/article/:id/top func (c *PlatformWebsiteArticleController) Top() { c.updateArticleFlag("top", 1, "置顶失败") } // UnTop POST /platform/website/article/:id/untop func (c *PlatformWebsiteArticleController) UnTop() { c.updateArticleFlag("top", 0, "取消置顶失败") } // ============================================================= // 分类管理 // ============================================================= type PlatformWebsiteCategoryController struct { beego.Controller } type platformWebsiteCategoryPayload struct { Name *string `json:"name"` CID *uint64 `json:"cid"` Image *string `json:"image"` Desc *string `json:"desc"` Sort *int `json:"sort"` Status *int8 `json:"status"` } // All GET /platform/website/category/all — 全量返回(前端自行组树),支持 keyword func (c *PlatformWebsiteCategoryController) All() { if _, ok := websiteAuth(&c.Controller); !ok { return } qs := models.Orm.QueryTable(new(models.PlatformWebsiteCategory)). Filter("delete_time__isnull", true) if kw := strings.TrimSpace(c.GetString("keyword")); kw != "" { qs = qs.Filter("name__icontains", kw) } var rows []models.PlatformWebsiteCategory if _, err := qs.OrderBy("sort", "id").All(&rows); err != nil { jsonErr(&c.Controller, 500, 500, "获取失败: "+err.Error()) return } websiteOK(&c.Controller, rows) } // List GET /platform/website/category/list — 分页 func (c *PlatformWebsiteCategoryController) List() { if _, ok := websiteAuth(&c.Controller); !ok { return } page, pageSize := websitePaging(&c.Controller) qs := models.Orm.QueryTable(new(models.PlatformWebsiteCategory)). Filter("delete_time__isnull", true) if kw := strings.TrimSpace(c.GetString("keyword")); kw != "" { qs = qs.Filter("name__icontains", kw) } total, err := qs.Count() if err != nil { jsonErr(&c.Controller, 500, 500, "获取失败: "+err.Error()) return } var rows []models.PlatformWebsiteCategory if _, err = qs.OrderBy("sort", "id").Limit(pageSize, (page-1)*pageSize).All(&rows); err != nil { jsonErr(&c.Controller, 500, 500, "获取失败: "+err.Error()) return } websiteOK(&c.Controller, map[string]interface{}{"list": rows, "total": total}) } // Get GET /platform/website/category/:id func (c *PlatformWebsiteCategoryController) Get() { if _, ok := websiteAuth(&c.Controller); !ok { return } id, ok := websiteID(&c.Controller) if !ok { return } var row models.PlatformWebsiteCategory if err := models.Orm.QueryTable(new(models.PlatformWebsiteCategory)). Filter("id", id). Filter("delete_time__isnull", true). One(&row); err != nil { jsonErr(&c.Controller, 404, 404, "分类不存在") return } websiteOK(&c.Controller, row) } // Create POST /platform/website/category func (c *PlatformWebsiteCategoryController) Create() { if _, ok := websiteAuth(&c.Controller); !ok { return } var p platformWebsiteCategoryPayload if !websiteBind(&c.Controller, &p) { return } if p.Name == nil || strings.TrimSpace(*p.Name) == "" { jsonErr(&c.Controller, 400, 400, "分类名称不能为空") return } row := models.PlatformWebsiteCategory{ Name: strings.TrimSpace(*p.Name), Status: 1, } if p.CID != nil { row.Cid = *p.CID } if p.Image != nil { row.Image = strings.ReplaceAll(*p.Image, "\\", "/") } if p.Desc != nil { row.Desc = *p.Desc } if p.Sort != nil { row.Sort = *p.Sort } if p.Status != nil { row.Status = *p.Status } id, err := models.Orm.Insert(&row) if err != nil { jsonErr(&c.Controller, 500, 500, "创建失败: "+err.Error()) return } websiteOK(&c.Controller, map[string]interface{}{"id": id}) } // Update POST /platform/website/category/:id func (c *PlatformWebsiteCategoryController) Update() { if _, ok := websiteAuth(&c.Controller); !ok { return } id, ok := websiteID(&c.Controller) if !ok { return } var p platformWebsiteCategoryPayload if !websiteBind(&c.Controller, &p) { return } up := map[string]interface{}{} if p.Name != nil { name := strings.TrimSpace(*p.Name) if name == "" { jsonErr(&c.Controller, 400, 400, "分类名称不能为空") return } up["name"] = name } if p.CID != nil { if *p.CID == id { jsonErr(&c.Controller, 400, 400, "父级分类不能是自己") return } up["cid"] = *p.CID } if p.Image != nil { up["image"] = strings.ReplaceAll(*p.Image, "\\", "/") } if p.Desc != nil { up["desc"] = *p.Desc } if p.Sort != nil { up["sort"] = *p.Sort } if p.Status != nil { up["status"] = *p.Status } if len(up) == 0 { jsonErr(&c.Controller, 400, 400, "无更新字段") return } up["update_time"] = time.Now() n, err := models.Orm.QueryTable(new(models.PlatformWebsiteCategory)). Filter("id", id). Filter("delete_time__isnull", true). Update(up) if err != nil { jsonErr(&c.Controller, 500, 500, "更新失败: "+err.Error()) return } if n == 0 { jsonErr(&c.Controller, 404, 404, "分类不存在") return } websiteOK(&c.Controller, nil) } // UpdateStatus PATCH /platform/website/category/:id/status — body { status } func (c *PlatformWebsiteCategoryController) UpdateStatus() { if _, ok := websiteAuth(&c.Controller); !ok { return } id, ok := websiteID(&c.Controller) if !ok { return } var p struct { Status *int8 `json:"status"` } if !websiteBind(&c.Controller, &p) || p.Status == nil { jsonErr(&c.Controller, 400, 400, "状态参数错误") return } n, err := models.Orm.QueryTable(new(models.PlatformWebsiteCategory)). Filter("id", id). Filter("delete_time__isnull", true). Update(map[string]interface{}{"status": *p.Status, "update_time": time.Now()}) if err != nil { jsonErr(&c.Controller, 500, 500, "更新失败: "+err.Error()) return } if n == 0 { jsonErr(&c.Controller, 404, 404, "分类不存在") return } websiteOK(&c.Controller, nil) } // Delete DELETE /platform/website/category/:id — 存在子分类时禁止删除 func (c *PlatformWebsiteCategoryController) Delete() { if _, ok := websiteAuth(&c.Controller); !ok { return } id, ok := websiteID(&c.Controller) if !ok { return } child, err := models.Orm.QueryTable(new(models.PlatformWebsiteCategory)). Filter("cid", id). Filter("delete_time__isnull", true). Count() if err != nil { jsonErr(&c.Controller, 500, 500, "删除失败: "+err.Error()) return } if child > 0 { jsonErr(&c.Controller, 400, 400, "该分类下存在子分类,请先删除子分类") return } now := time.Now() n, err := models.Orm.QueryTable(new(models.PlatformWebsiteCategory)). Filter("id", id). Filter("delete_time__isnull", true). Update(map[string]interface{}{"delete_time": now, "update_time": now}) if err != nil { jsonErr(&c.Controller, 500, 500, "删除失败: "+err.Error()) return } if n == 0 { jsonErr(&c.Controller, 404, 404, "分类不存在") return } websiteOK(&c.Controller, nil) }