using Dpz.Core.Public.ViewModel.Sitemaps;

namespace Dpz.Core.Service.RepositoryServiceImpl;

/// <summary>
/// 站点地图存储服务
/// </summary>
public class SitemapService(IRepository<SitemapDocument> repository, IMapper mapper)
    : ISitemapService
{
    /// <inheritdoc />
    [Cache(ExpirationSeconds = ExpirationTime.Day, Prefix = "sitemap")]
    public async Task<VmSitemapFile?> GetLatestAsync(
        string name,
        CancellationToken cancellationToken = default
    )
    {
        var filter = Builders<SitemapDocument>.Filter.Eq(x => x.Name, name);
        var cursor = await repository
            .SearchFor(filter)
            .SortByDescending(x => x.Date)
            .Limit(1)
            .ToCursorAsync(cancellationToken);
        var entity = await cursor.FirstOrDefaultAsync(cancellationToken);
        if (entity == null)
        {
            return null;
        }

        return mapper.Map<VmSitemapFile>(entity);
    }

    /// <inheritdoc />
    [InvalidateCache(Methods = [nameof(GetLatestAsync)])]
    public async Task SaveAsync(
        IReadOnlyCollection<VmSitemapFile> files,
        CancellationToken cancellationToken = default
    )
    {
        var entities = files
            .Select(file => new SitemapDocument
            {
                Name = file.Name,
                Date = DateTime.Now,
                Xml = file.Xml,
                UrlCount = file.UrlCount,
                ByteCount = Encoding.UTF8.GetByteCount(file.Xml),
            })
            .ToList();
        await repository.InsertAsync(entities, cancellationToken);
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

这段代码是一个基于 C# 的站点地图(Sitemap)存储服务实现,主要用于将站点地图数据保存到 MongoDB,并支持缓存和查询最新版本。下面我会逐步解释代码的各个部分。

1. 命名空间与类声明

using Dpz.Core.Public.ViewModel.Sitemaps;
namespace Dpz.Core.Service.RepositoryServiceImpl;
  • 引入 Dpz.Core.Public.ViewModel.Sitemaps,说明 VmSitemapFile 这个视图模型位于该命名空间。
  • 当前类所在的命名空间是 Dpz.Core.Service.RepositoryServiceImpl,通常表明这是仓储接口的一个具体实现。
public class SitemapService(IRepository<SitemapDocument> repository, IMapper mapper)
    : ISitemapService
  • SitemapService 继承自 ISitemapService 接口,并提供具体实现。
  • 使用了主构造函数(C# 12 特性),直接在参数列表中注入两个依赖:
    • IRepository<SitemapDocument>:泛型仓储,用于操作 SitemapDocument 文档。
    • IMapper:AutoMapper 或类似的对象映射器,用于将实体转换为视图模型。
  • 这种写法省去了显式的字段声明和构造器赋值,编译器会隐式生成对应的私有字段。

2. 获取最新站点地图

[Cache(ExpirationSeconds = ExpirationTime.Day, Prefix = "sitemap")]
public async Task<VmSitemapFile?> GetLatestAsync(
    string name,
    CancellationToken cancellationToken = default
)
  • [Cache] 特性表示这个方法的结果会被缓存,缓存过期时间设置为 ExpirationTime.Day(假设是一天),缓存键前缀为 "sitemap"
  • 方法接收一个 name(站点地图名称,比如 sitemap.xml)和一个可选的取消令牌。

方法内部逻辑:

var filter = Builders<SitemapDocument>.Filter.Eq(x => x.Name, name);
  • 使用 MongoDB 的 Builders<SitemapDocument>.Filter 构建过滤条件,查询 Name 字段等于传入 name 的文档。
var cursor = await repository
    .SearchFor(filter)
    .SortByDescending(x => x.Date)
    .Limit(1)
    .ToCursorAsync(cancellationToken);
  • 通过仓储的 SearchFor 方法执行查询,按 Date 字段降序排序,只取第一条(最新的记录),然后转换为异步游标。
  • SortByDescending(x => x.Date) 表示按日期从新到旧排列,这样第一条就是最新版本。
var entity = await cursor.FirstOrDefaultAsync(cancellationToken);
if (entity == null)
{
    return null;
}
return mapper.Map<VmSitemapFile>(entity);
  • 从游标中读取第一个实体(或默认值)。
  • 如果为空,返回 null,表示没有找到对应名称的站点地图。
  • 否则使用 IMapperSitemapDocument 实体映射为 VmSitemapFile 视图模型并返回。

3. 保存站点地图

[InvalidateCache(Methods = [nameof(GetLatestAsync)])]
public async Task SaveAsync(
    IReadOnlyCollection<VmSitemapFile> files,
    CancellationToken cancellationToken = default
)
  • [InvalidateCache] 特性表明在执行此方法后,需要使指定的缓存项失效。
  • Methods 参数明确指向 GetLatestAsync 方法,即保存新数据后,会清除之前缓存的“最新”数据,避免下次获取到旧缓存。
  • 方法接收一个 IReadOnlyCollection<VmSitemapFile> 集合,用于批量保存。

方法内部逻辑:

var entities = files
    .Select(file => new SitemapDocument
    {
        Name = file.Name,
        Date = DateTime.Now,
        Xml = file.Xml,
        UrlCount = file.UrlCount,
        ByteCount = Encoding.UTF8.GetByteCount(file.Xml),
    })
    .ToList();
  • 将每个 VmSitemapFile 转换成 SitemapDocument 实体:
    • Name:站点地图文件名。
    • Date:当前时间,作为保存时间戳。
    • Xml:xml 内容的字符串。
    • UrlCount:URL 数量。
    • ByteCount:计算 XML 字符串按 UTF-8 编码后的字节数大小。
await repository.InsertAsync(entities, cancellationToken);
  • 调用仓储的异步插入方法,将所有实体批量写入数据库。

4. 整体设计解读

  • 仓储模式:通过 IRepository<T> 抽象了数据库访问,使得上层服务无需关心 MongoDB 具体操作细节。
  • 缓存策略:使用 [Cache][InvalidateCache] 特性,实现了查询结果缓存和写入后自动失效,提升了读取性能。
  • 文档存储:每个站点地图快照作为一条独立文档存储,Name 用于区分不同文件,Date 用于标识版本。
  • 版本历史:由于不会覆盖旧记录,每次保存都会新增一条记录,通过查询最新日期即可取出当前有效版本,同时保留了历史版本(如果需要回溯)。
  • 数据流量优化:记录 ByteCount 可以快速获知 XML 大小,便于统计或限制,而无需每次重新计算。
  • 异步安全:方法均支持 CancellationToken,符合现代响应式应用的要求。

总的来说,这是一个典型的“按名字存取最新站点地图”的服务实现,利用了缓存和 MongoDB 的灵活查询特性,适合用于 SEO 站点地图的生成与发布场景。

评论加载中...