using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
using Dpz.Core.Service.Mediator.Features.Article.Queries;
using Dpz.Core.Web.Library.Api;

namespace Dpz.Core.Web.Controllers;

public partial class ArticleController(IArticleService articleService, IMediator mediator)
    : Controller
{
    public async Task<IActionResult> Index(
        [FromServices] IHomeCacheService cacheService,
        [PageMetadataParameter] string? tag = "",
        [Range(10, 100)] int pageSize = 10,
        int pageIndex = 1,
        CancellationToken cancellationToken = default
    )
    {
        if (!ModelState.IsValid)
        {
            return NotFound();
        }

        var tags = string.IsNullOrEmpty(tag) ? [] : new[] { tag };
        this.SetTitle(string.IsNullOrEmpty(tag) ? "文章列表" : $"标签-{tag}-文章列表");
        var list = await articleService.GetPagesAsync(
            pageIndex,
            pageSize,
            tags: tags,
            cancellationToken: cancellationToken
        );
        if (Request.Headers["X-PJAX"] == "true" && Request.Query["_pjax"] == "#article-list")
        {
            return PartialView("_ArticleListPartial", list);
        }

        var model = new ArticleIndexModel
        {
            LikeArticle = await cacheService.GetRandomArticlesAsync(cancellationToken),
            List = list,
            News = await cacheService.GetLatestArticlesAsync(cancellationToken),
            Tags = await cacheService.GetArticleTagsAsync(cancellationToken),
        };
        return View(model);
    }

    public async Task<IActionResult> Read(
        string id = "",
        string text = "",
        CancellationToken cancellationToken = default
    )
    {
        var article = await mediator.Send(
            new ArticleReadRequest { Id = id, Text = text },
            cancellationToken
        );
        if (article == null)
        {
            return NotFound();
        }

        await articleService.ViewAsync(id, cancellationToken);
        this.SetTitle(article.Title);

        var pageMetaPage = new VmPageMetadata
        {
            Description = ClearIntroductionRegex().Replace(article.Introduction ?? "", ""),
            Keywords = [article.Title],
            Relations = ["Article", "Read", id],
        };
        ViewData["PageMetadata"] = pageMetaPage;
        ViewData["Text"] = text;
        return View(article);
    }

    [GeneratedRegex("<[^>]*>")]
    private static partial Regex ClearIntroductionRegex();

    public async Task<IActionResult> Archive(
        int pageIndex = 1,
        int pageSize = 50,
        CancellationToken cancellationToken = default
    )
    {
        this.SetTitle("文章归档");
        var archive = await articleService.GetArchivePagedAsync(
            pageIndex,
            pageSize,
            cancellationToken
        );
        return View(archive);
    }

    [HttpGet]
    public async Task<IActionResult> ArchiveLoadMore(
        int pageIndex = 1,
        int pageSize = 50,
        CancellationToken cancellationToken = default
    )
    {
        var archive = await articleService.GetArchivePagedAsync(
            pageIndex,
            pageSize,
            cancellationToken
        );
        return PartialView("_ArchiveItemsPartial", archive);
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

代码解释

这是一个 ASP.NET Core MVC 控制器类,用于处理文章相关的 Web 请求。以下是详细说明:

类定义

public partial class ArticleController(IArticleService articleService, IMediator mediator) : Controller
  • 使用 C# 12 的主构造函数语法,直接在类声明中注入依赖
  • 依赖注入了 IArticleService(文章服务)和 IMediator(中介者模式,用于处理查询/命令)
  • partial 关键字表示这是部分类,可能在其他文件中有其他部分

方法详解

1. Index 方法 - 文章列表页

public async Task<IActionResult> Index(...)

功能:显示文章列表,支持按标签筛选和分页

参数

  • cacheService: 从 DI 容器注入的缓存服务
  • tag: 标签过滤参数(带自定义特性 PageMetadataParameter
  • pageSize: 每页大小(10-100,默认10)
  • pageIndex: 当前页码(默认1)
  • cancellationToken: 取消令牌

逻辑流程

  1. 验证模型状态,无效则返回 404
  2. 处理标签参数(空字符串转为空数组)
  3. 设置页面标题
  4. 获取分页文章列表
  5. PJAX 支持:如果是 PJAX 请求,仅返回部分视图(局部刷新)
  6. 否则返回完整视图,包含:
    • 随机推荐文章
    • 文章列表
    • 最新文章
    • 标签列表

2. Read 方法 - 文章详情页

public async Task<IActionResult> Read(...)

功能:显示文章详细内容

参数

  • id: 文章ID
  • text: 额外的文本参数
  • cancellationToken: 取消令牌

逻辑流程

  1. 通过 Mediator 发送 ArticleReadRequest 查询请求
  2. 文章不存在则返回 404
  3. 异步增加文章浏览次数
  4. 设置页面标题
  5. 构建 SEO 元数据:
    • Description: 从文章简介中移除 HTML 标签(使用正则)
    • Keywords: 文章标题
    • Relations: 关系标识(Article/Read/id)
  6. 将数据存入 ViewData 并返回视图

3. ClearIntroductionRegex - 正则表达式生成器

[GeneratedRegex("<[^>]*>")]
private static partial Regex ClearIntroductionRegex();
  • 使用 .NET 7+ 的源代码生成器特性
  • 编译时生成正则表达式,性能更优
  • 用于匹配并移除 HTML 标签(<...>

4. Archive 方法 - 文章归档页

public async Task<IActionResult> Archive(...)

功能:显示文章归档列表(按时间分组)

参数

  • pageIndex: 页码(默认1)
  • pageSize: 每页大小(默认50)

逻辑流程

  1. 设置页面标题为"文章归档"
  2. 获取分页归档数据
  3. 返回归档视图

5. ArchiveLoadMore 方法 - 归档懒加载

[HttpGet]
public async Task<IActionResult> ArchiveLoadMore(...)

功能:用于"加载更多"功能,返回部分视图

特点

  • 仅支持 GET 请求
  • 返回 _ArchiveItemsPartial 部分视图
  • 用于实现无刷新加载更多归档内容

技术亮点

  1. 依赖注入: 使用主构造函数简化代码
  2. CQRS 模式: 通过 Mediator 处理查询
  3. PJAX 支持: 实现页面局部刷新
  4. 正则表达式优化: 使用源生成器提升性能
  5. SEO 友好: 设置页面元数据
  6. 异步编程: 全部使用异步方法
  7. 取消令牌: 支持请求取消
  8. 数据验证: 使用 [Range] 等特性

这是一个现代化的、性能优化的 ASP.NET Core 控制器实现。

评论加载中...