package service import ( "context" "cms-api/internal/dao" "cms-api/internal/model" "strings" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" ) type sArticle struct{} func Article() *sArticle { return &sArticle{} } // GetById 根据ID获取文章信息 func (s *sArticle) GetById(ctx context.Context, id int) (*model.Article, error) { article, err := dao.Article.GetById(ctx, id) if err != nil { return nil, err } if article != nil { // 增加浏览次数 go func() { dao.Article.IncrementViewCount(context.Background(), id) }() } return article, nil } // GetBySlug 根据URL别名获取文章 func (s *sArticle) GetBySlug(ctx context.Context, slug string) (*model.Article, error) { article, err := dao.Article.GetBySlug(ctx, slug) if err != nil { return nil, err } if article != nil { // 增加浏览次数 go func() { dao.Article.IncrementViewCount(context.Background(), article.Id) }() } return article, nil } // List 获取文章列表 func (s *sArticle) List(ctx context.Context, req *model.ArticleListRequest) (*model.PageResponse, error) { var ( page = req.Page pageSize = req.PageSize ) if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 10 } // 设置分页参数 req.Page = page req.PageSize = pageSize // 获取列表 articles, total, err := dao.Article.List(ctx, req) if err != nil { return nil, err } return &model.PageResponse{ List: articles, Total: total, Page: page, PageSize: pageSize, TotalPages: (total + pageSize - 1) / pageSize, }, nil } // Create 创建文章 func (s *sArticle) Create(ctx context.Context, req *model.CreateArticleRequest) error { // 检查URL别名是否存在 if req.Slug != "" { existArticle, err := dao.Article.GetBySlug(ctx, req.Slug) if err != nil { return err } if existArticle != nil { return gerror.New("URL别名已存在") } } else { // 如果没有提供slug,则根据标题生成 req.Slug = s.generateSlug(req.Title) } // 获取当前用户ID authorId := gconv.Int(g.RequestFromCtx(ctx).GetCtxVar("admin_id")) if authorId == 0 { authorId = gconv.Int(g.RequestFromCtx(ctx).GetCtxVar("user_id")) } // 处理HTML内容(如果是Markdown,需要转换) htmlContent := req.Content if req.Content != "" { // 这里可以添加Markdown到HTML的转换逻辑 htmlContent = s.markdownToHTML(req.Content) } // 创建文章 article := &model.Article{ Title: req.Title, Slug: req.Slug, Summary: req.Summary, Content: req.Content, HtmlContent: htmlContent, CoverImage: req.CoverImage, CategoryId: req.CategoryId, Tags: req.Tags, AuthorId: authorId, IsPublished: req.IsPublished, IsFeatured: req.IsFeatured, IsTop: req.IsTop, SeoTitle: req.SeoTitle, SeoDescription: req.SeoDescription, SeoKeywords: req.SeoKeywords, CreatedAt: gtime.Now(), UpdatedAt: gtime.Now(), } if req.IsPublished == 1 { article.PublishedAt = gtime.Now() } _, err := dao.Article.Create(ctx, article) return err } // Update 更新文章 func (s *sArticle) Update(ctx context.Context, id int, req *model.UpdateArticleRequest) error { // 检查文章是否存在 article, err := s.GetById(ctx, id) if err != nil { return err } if article == nil { return gerror.New("文章不存在") } // 检查URL别名是否被其他文章使用 if req.Slug != "" && req.Slug != article.Slug { existArticle, err := dao.Article.GetBySlug(ctx, req.Slug) if err != nil { return err } if existArticle != nil && existArticle.Id != id { return gerror.New("URL别名已被使用") } } // 处理HTML内容 htmlContent := req.Content if req.Content != "" { htmlContent = s.markdownToHTML(req.Content) } // 更新文章 updateData := g.Map{ "title": req.Title, "slug": req.Slug, "summary": req.Summary, "content": req.Content, "html_content": htmlContent, "cover_image": req.CoverImage, "category_id": req.CategoryId, "tags": req.Tags, "is_published": req.IsPublished, "is_featured": req.IsFeatured, "is_top": req.IsTop, "seo_title": req.SeoTitle, "seo_description": req.SeoDescription, "seo_keywords": req.SeoKeywords, "updated_at": gtime.Now(), } // 如果从草稿变为发布状态,设置发布时间 if req.IsPublished == 1 && article.IsPublished == 0 { updateData["published_at"] = gtime.Now() } return dao.Article.Update(ctx, id, updateData) } // Delete 删除文章 func (s *sArticle) Delete(ctx context.Context, id int) error { // 检查文章是否存在 article, err := s.GetById(ctx, id) if err != nil { return err } if article == nil { return gerror.New("文章不存在") } return dao.Article.Delete(ctx, id) } // UpdateStatus 更新文章状态 func (s *sArticle) UpdateStatus(ctx context.Context, id int, isPublished int) error { // 检查文章是否存在 article, err := s.GetById(ctx, id) if err != nil { return err } if article == nil { return gerror.New("文章不存在") } updateData := g.Map{ "is_published": isPublished, "updated_at": gtime.Now(), } // 如果是发布状态,设置发布时间 if isPublished == 1 && article.IsPublished == 0 { updateData["published_at"] = gtime.Now() } return dao.Article.Update(ctx, id, updateData) } // SetFeatured 设置推荐状态 func (s *sArticle) SetFeatured(ctx context.Context, id int, isFeatured int) error { // 检查文章是否存在 article, err := s.GetById(ctx, id) if err != nil { return err } if article == nil { return gerror.New("文章不存在") } return dao.Article.Update(ctx, id, g.Map{ "is_featured": isFeatured, "updated_at": gtime.Now(), }) } // SetTop 设置置顶状态 func (s *sArticle) SetTop(ctx context.Context, id int, isTop int) error { // 检查文章是否存在 article, err := s.GetById(ctx, id) if err != nil { return err } if article == nil { return gerror.New("文章不存在") } return dao.Article.Update(ctx, id, g.Map{ "is_top": isTop, "updated_at": gtime.Now(), }) } // GetStats 获取文章统计信息 func (s *sArticle) GetStats(ctx context.Context) (map[string]interface{}, error) { // 获取总文章数 totalCount, err := dao.Article.GetCount(ctx) if err != nil { return nil, err } // 获取已发布文章数 publishedCount, err := dao.Article.GetPublishedCount(ctx) if err != nil { return nil, err } // 获取草稿数 draftCount := totalCount - publishedCount // 获取推荐文章数 featuredCount, err := dao.Article.GetFeaturedCount(ctx) if err != nil { return nil, err } return map[string]interface{}{ "total_count": totalCount, "published_count": publishedCount, "draft_count": draftCount, "featured_count": featuredCount, }, nil } // GetFeatured 获取推荐文章 func (s *sArticle) GetFeatured(ctx context.Context, limit int) ([]*model.Article, error) { if limit <= 0 { limit = 5 } return dao.Article.GetFeatured(ctx, limit) } // GetLatest 获取最新文章 func (s *sArticle) GetLatest(ctx context.Context, limit int) ([]*model.Article, error) { if limit <= 0 { limit = 10 } return dao.Article.GetLatest(ctx, limit) } // generateSlug 生成URL别名 func (s *sArticle) generateSlug(title string) string { // 简单的slug生成逻辑,实际项目中可能需要更复杂的处理 slug := strings.ToLower(title) slug = strings.ReplaceAll(slug, " ", "-") slug = strings.ReplaceAll(slug, " ", "-") // 全角空格 // 移除特殊字符,只保留字母、数字、中文和连字符 // 这里简化处理,实际项目中可能需要更完善的slug生成逻辑 return slug } // markdownToHTML 将Markdown转换为HTML func (s *sArticle) markdownToHTML(markdown string) string { // 这里应该使用Markdown解析库,如github.com/russross/blackfriday // 暂时直接返回原内容,实际项目中需要实现Markdown解析 return markdown } // Search 搜索文章 func (s *sArticle) Search(ctx context.Context, keyword string, page, pageSize int) (*model.PageResponse, error) { if page <= 0 { page = 1 } if pageSize <= 0 { pageSize = 10 } articles, total, err := dao.Article.Search(ctx, keyword, page, pageSize) if err != nil { return nil, err } return &model.PageResponse{ List: articles, Total: total, Page: page, PageSize: pageSize, TotalPages: (total + pageSize - 1) / pageSize, }, nil }