using Dpz.Core.MessageQueue.Abstractions;
using Dpz.Core.Public.ViewModel.Messages;
using Dpz.Core.Public.ViewModel.RequestEvent;

namespace Dpz.Core.Service.RepositoryServiceImpl;

public class TimelineService(
    IRepository<Timeline> repository,
    IMapper mapper,
    IMediator mediator,
    IFusionCache fusionCache,
    IMessagePublisher<DeleteMarkdownMessage> deleteMarkdownPublisher
) : AbstractCacheService(fusionCache), ITimelineService
{
    public async Task<List<VmTimeline>> GetTimelinesAsync(
        string account,
        CancellationToken cancellationToken = default
    )
    {
        return await GetOrSetCacheAsync<List<VmTimeline>>(
            nameof(GetTimelinesAsync),
            async (_, ct) =>
            {
                var list = await repository
                    .SearchFor(x => x.Author.Id == account)
                    .OrderByDescending(x => x.Date)
                    .ThenByDescending(x => x.CreateTime)
                    .ToListAsync(ct);
                return mapper.Map<List<VmTimeline>>(list);
            },
            new { account },
            cancellationToken: cancellationToken
        );
    }

    public async Task SaveAsync(VmTimeline viewModel, CancellationToken cancellationToken = default)
    {
        var entity = mapper.Map<Timeline>(viewModel);
        if (entity == null)
        {
            throw new ArgumentException("viewModel is null", nameof(viewModel));
        }

        if (entity.Id != ObjectId.Empty)
        {
            entity = await repository.TryGetAsync(
                viewModel.Id,
                cancellationToken: cancellationToken
            );
            if (entity == null)
            {
                return;
            }

            var request = new EditMarkdownRequest
            {
                Markdown = viewModel.Content,
                OriginalMarkdown = entity.Content,
            };
            await mediator.Send(request, cancellationToken);

            var update = Builders<Timeline>
                .Update.Set(x => x.Title, viewModel.Title)
                .Set(x => x.Content, viewModel.Content)
                .Set(x => x.Date, viewModel.Date)
                .Set(x => x.More, viewModel.More)
                .Set(x => x.LastUpdateTime, DateTime.Now);
            await repository.UpdateAsync(x => x.Id == entity.Id, update, cancellationToken);
            await RemoveByPrefixAsync(cancellationToken);
            return;
        }

        await repository.InsertAsync(entity, cancellationToken);
        await RemoveByPrefixAsync(cancellationToken);
    }

    public async Task DeleteAsync(string[] id, CancellationToken cancellationToken = default)
    {
        var ids = id.Select(x => ObjectId.TryParse(x, out var oid) ? oid : (ObjectId?)null)
            .Where(x => x.HasValue)
            .Select(x => x!.Value)
            .ToList();
        var filter = Builders<Timeline>.Filter.In(x => x.Id, ids);
        var list = await repository.SearchFor(filter).ToListAsync(cancellationToken);
        var contents = list.Select(x => x.Content).Where(x => !string.IsNullOrEmpty(x)).ToList();

        // 发布消息到队列,异步处理 Markdown 中的图片删除
        if (contents.Count > 0)
        {
            await deleteMarkdownPublisher.PublishAsync(
                new DeleteMarkdownMessage { MarkdownContents = contents },
                cancellationToken: cancellationToken
            );
        }

        await repository.DeleteAsync(filter, cancellationToken);
        await RemoveByPrefixAsync(cancellationToken);
    }

    public async Task<IPagedList<VmTimeline>> GetPageAsync(
        int pageIndex,
        int pageSize,
        string? content = null,
        string? account = null,
        CancellationToken cancellationToken = default
    )
    {
        return await GetOrSetPagedListAsync<VmTimeline>(
            nameof(GetPageAsync),
            async (_, _) =>
            {
                var predicate = repository.SearchFor(x => true);
                if (!string.IsNullOrEmpty(content))
                {
                    predicate = predicate.Where(x =>
                        x.Title.Contains(content) || x.Content.Contains(content)
                    );
                }

                if (!string.IsNullOrEmpty(account))
                {
                    predicate = predicate.Where(x => x.Author.Id == account);
                }

                return await predicate
                    .OrderByDescending(x => x.Date)
                    .ToPagedListAsync<Timeline, VmTimeline>(pageIndex, pageSize, cancellationToken);
            },
            new
            {
                pageIndex,
                pageSize,
                content,
                account,
            },
            cancellationToken: cancellationToken
        );
    }

    public async Task<VmTimeline?> FindAsync(
        string id,
        CancellationToken cancellationToken = default
    )
    {
        return await GetOrSetCacheAsync<VmTimeline?>(
            nameof(FindAsync),
            async (_, ct) =>
            {
                var entity = await repository.TryGetAsync(id, cancellationToken: ct);
                return entity == null ? null : mapper.Map<VmTimeline>(entity);
            },
            new { id },
            cancellationToken: cancellationToken
        );
    }

    protected override TimeSpan CacheDefaultExpiration => TimeSpan.FromDays(7);
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

这是一个时间线服务类的实现,主要用于管理时间线数据的CRUD操作。让我详细解释一下这个代码:

类结构和依赖注入

public class TimelineService(
    IRepository<Timeline> repository,
    IMapper mapper,
    IMediator mediator,
    IFusionCache fusionCache,
    IMessagePublisher<DeleteMarkdownMessage> deleteMarkdownPublisher
) : AbstractCacheService(fusionCache), ITimelineService

这个类继承自 AbstractCacheService 并实现了 ITimelineService 接口,使用了C# 12的主构造函数语法。依赖注入了:

  • IRepository<Timeline>: 数据仓储接口
  • IMapper: 对象映射器(AutoMapper)
  • IMediator: 中介者模式实现(MediatR)
  • IFusionCache: 缓存服务
  • IMessagePublisher<DeleteMarkdownMessage>: 消息发布器

主要方法分析

1. GetTimelinesAsync - 获取用户时间线列表

public async Task<List<VmTimeline>> GetTimelinesAsync(string account, CancellationToken cancellationToken = default)
  • 根据用户账号获取时间线列表
  • 使用缓存机制提高性能
  • 按日期和创建时间降序排序

2. SaveAsync - 保存时间线

public async Task SaveAsync(VmTimeline viewModel, CancellationToken cancellationToken = default)
  • 支持新增和更新操作
  • 如果实体ID不为空,执行更新操作:
    • 发送 EditMarkdownRequest 到中介者处理Markdown编辑
    • 使用MongoDB的Update构建器更新特定字段
  • 如果是新实体,直接插入
  • 操作完成后清除相关缓存

3. DeleteAsync - 删除时间线

public async Task DeleteAsync(string[] id, CancellationToken cancellationToken = default)
  • 批量删除时间线
  • 将字符串ID转换为ObjectId
  • 提取要删除记录中的Markdown内容
  • 通过消息队列异步处理Markdown中的图片删除
  • 删除数据库记录并清除缓存

4. GetPageAsync - 分页查询

public async Task<IPagedList<VmTimeline>> GetPageAsync(...)
  • 支持分页查询
  • 可按内容和账号进行过滤
  • 使用缓存机制
  • 按日期降序排序

5. FindAsync - 查找单个时间线

public async Task<VmTimeline?> FindAsync(string id, CancellationToken cancellationToken = default)
  • 根据ID查找单个时间线
  • 使用缓存机制
  • 返回可空的VmTimeline对象

设计特点

  1. 缓存策略: 继承自 AbstractCacheService,所有查询操作都使用缓存,默认缓存7天
  2. 异步处理: 删除操作中的图片清理通过消息队列异步处理,避免阻塞主流程
  3. 数据映射: 使用AutoMapper在实体和视图模型之间转换
  4. 错误处理: 对无效输入进行验证和异常处理
  5. MongoDB集成: 使用MongoDB特定的操作如ObjectId、Builders等
  6. 中介者模式: 使用MediatR处理复杂的业务逻辑(如Markdown编辑)

这是一个典型的现代.NET应用服务层实现,体现了依赖注入、缓存、异步处理、消息队列等最佳实践。

评论加载中...