using AngleSharp.Html.Parser;
using Dpz.Core.Infrastructure;
using Dpz.Core.MessageQueue.Abstractions;
using Dpz.Core.MessageQueue.Models;
using Dpz.Core.Public.ViewModel.Messages;
using Dpz.Core.Service.Mediator;

namespace Dpz.Core.Web.Jobs.MessageHandlers;

/// <summary>
/// 删除 Markdown 内容消息处理器
/// 解析 Markdown 中的图片并继续发布删除图片消息
/// </summary>
public class DeleteMarkdownHandler(
    IMessagePublisher<RemoveImagesMessage> removeImagesPublisher,
    ILogger<DeleteMarkdownHandler> logger
) : IMessageHandler<DeleteMarkdownMessage>
{
    private readonly HtmlParser _htmlParser = new();

    public async Task<MessageHandlerResult> HandleAsync(
        DeleteMarkdownMessage message,
        CancellationToken cancellationToken
    )
    {
        try
        {
            if (message.MarkdownContents.Count == 0)
            {
                logger.LogInformation("没有需要处理的 Markdown 内容");
                return MessageHandlerResult.Ok();
            }

            var allImages = new List<string>();

            foreach (var markdown in message.MarkdownContents)
            {
                if (string.IsNullOrWhiteSpace(markdown))
                {
                    continue;
                }

                var images = await ExtractImagesFromMarkdownAsync(markdown);
                allImages.AddRange(images);
            }

            if (allImages.Count > 0)
            {
                // 批量发布删除图片队列消息
                var messages = allImages.Select(x => new RemoveImagesMessage { ImageUrl = x });
                await removeImagesPublisher.PublishBatchAsync(
                    messages,
                    cancellationToken: cancellationToken
                );

                logger.LogInformation(
                    "处理 DeleteMarkdown 完成,从 {MarkdownCount} 个 Markdown 中提取到 {ImageCount} 张图片并发布到队列",
                    message.MarkdownContents.Count,
                    allImages.Count
                );
            }
            else
            {
                logger.LogInformation(
                    "处理 DeleteMarkdown 完成,从 {MarkdownCount} 个 Markdown 中未提取到图片",
                    message.MarkdownContents.Count
                );
            }

            return MessageHandlerResult.Ok();
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "处理 DeleteMarkdown 消息失败");
            return MessageHandlerResult.Fail($"处理 DeleteMarkdown 消息失败: {ex.Message}");
        }
    }

    /// <summary>
    /// 从 Markdown 内容中提取图片 URL
    /// </summary>
    private async Task<List<string>> ExtractImagesFromMarkdownAsync(string markdown)
    {
        try
        {
            // 将 Markdown 转换为 HTML
            var htmlContent = markdown.MarkdownToHtml();

            // 解析 HTML
            var document = await _htmlParser.ParseDocumentAsync(htmlContent);
            var imageElements = document.GetElementsByTagName("img");

            // 提取图片 URL
            return imageElements.GetElementsImageUrls();
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "从 Markdown 提取图片失败");
            return [];
        }
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

这是一个用于处理删除 Markdown 内容的消息处理器类。让我详细解释一下这段代码的功能和实现:

类的作用

DeleteMarkdownHandler 是一个消息处理器,专门用于处理 DeleteMarkdownMessage 类型的消息。当需要删除 Markdown 内容时,这个处理器会:

  1. 解析 Markdown 中包含的图片
  2. 发布删除图片的消息到消息队列

依赖注入

通过构造函数注入两个依赖:

  • IMessagePublisher<RemoveImagesMessage>: 用于发布删除图片的消息
  • ILogger<DeleteMarkdownHandler>: 用于记录日志

核心处理流程 (HandleAsync 方法)

  1. 输入验证: 检查是否有 Markdown 内容需要处理
  2. 图片提取: 遍历所有 Markdown 内容,提取其中的图片 URL
  3. 批量发布: 将提取到的图片 URL 转换为 RemoveImagesMessage 并批量发布到消息队列
  4. 日志记录: 记录处理结果
  5. 异常处理: 捕获并记录任何处理过程中的异常

图片提取逻辑 (ExtractImagesFromMarkdownAsync 方法)

  1. Markdown 转 HTML: 使用 MarkdownToHtml() 扩展方法将 Markdown 转换为 HTML
  2. HTML 解析: 使用 AngleSharp 的 HtmlParser 解析 HTML 文档
  3. 图片元素查找: 查找所有 <img> 标签
  4. URL 提取: 使用 GetElementsImageUrls() 扩展方法提取图片 URL

设计优势

  1. 异步处理: 整个处理流程都是异步的,提高性能
  2. 批量操作: 使用 PublishBatchAsync 批量发布消息,提高效率
  3. 容错性: 完善的异常处理和日志记录
  4. 职责分离: 将图片删除作为单独的消息处理,遵循单一职责原则
  5. 可观测性: 详细的日志记录便于监控和调试

使用场景

这个处理器通常用于内容管理系统中,当用户删除包含图片的 Markdown 文章时,需要同步清理相关的图片资源,避免产生孤立文件。

评论加载中...