using System.Threading;
using Dpz.Core.Public.ViewModel.Request;
using Dpz.Core.Public.ViewModel.RequestEvent;
using Dpz.Core.Public.ViewModel.Response;

namespace Dpz.Core.Service.RepositoryServiceImpl;

public class PictureRecordService(
    IRepository<PictureRecord> repository,
    IMapper mapper,
    IFusionCache fusionCache,
    IMediator mediator
) : AbstractCacheService(fusionCache), IPictureRecordService
{
    protected override TimeSpan CacheDefaultExpiration => TimeSpan.FromDays(1);

    private readonly IFusionCache _fusionCache = fusionCache;

    public async Task<List<string>> GetTagsAsync()
    {
        return await GetOrSetCacheAsync<List<string>>(
            nameof(GetTagsAsync),
            async (_, cancellationToken) =>
            {
                var tags = await repository
                    .MongodbQueryable
                    //.Select(x => x.Tags)
                    .SelectMany(x => x.Tags)
                    .GroupBy(x => x)
                    .Select(x => x.Key)
                    .ToListAsync(cancellationToken);
                return tags;
            }
        );
    }

    public async Task<ICollection<PictureRecordResponse>> GetBannerAsync()
    {
        return await GetOrSetCacheAsync<List<PictureRecordResponse>>(
            nameof(GetBannerAsync),
            async (_, _) =>
            {
                var pagedList = await repository
                    .SearchFor(x => x.Tags.Any(y => y == "banner"))
                    .OrderByDescending(x => x.UploadTime)
                    .ToListAsync<PictureRecord, PictureRecordResponse>();
                pagedList.ForEach(x => x.AccessUrl += "!banner");
                return pagedList;
            }
        );
    }

    public async Task<IPagedList<PictureRecordResponse>> GetPagesAsync(
        List<string>? tags,
        string? description,
        int pageIndex = 1,
        int pageSize = 20,
        string? account = null
    )
    {
        return await GetOrSetPagedListAsync<PictureRecordResponse>(
            nameof(GetPagesAsync),
            async _ =>
            {
                var filterEmpty = Builders<PictureRecord>.Filter.Empty;
                var filters = new List<FilterDefinition<PictureRecord>>();

                var clearTags =
                    tags?.Where(x => !string.IsNullOrEmpty(x)).Select(x => x).ToList() ?? [];
                if (clearTags.Count > 0)
                {
                    var filter = Builders<PictureRecord>.Filter.AnyIn(x => x.Tags, clearTags);
                    filters.Add(filter);
                }

                if (!string.IsNullOrEmpty(description))
                {
                    var filter = Builders<PictureRecord>.Filter.Regex(
                        x => x.Description,
                        new BsonRegularExpression(description, "i")
                    );
                    filters.Add(filter);
                }

                if (!string.IsNullOrEmpty(account))
                {
                    var filter = Builders<PictureRecord>.Filter.Eq(x => x.Creator.Id, account);
                    filters.Add(filter);
                }

                var filterResult =
                    filters.Count > 0 ? Builders<PictureRecord>.Filter.And(filters) : filterEmpty;

                return await repository
                    .SearchFor(filterResult)
                    .SortByDescending(x => x.Id)
                    .ToPagedListAsync<PictureRecord, PictureRecordResponse>(pageIndex, pageSize);
            },
            new
            {
                tags,
                description,
                pageIndex,
                pageSize,
                account,
            }
        );
    }

    public async Task<PictureRecordResponse?> GetPictureRecordAsync(string id)
    {
        return await GetOrSetCacheAsync<PictureRecordResponse?>(
            nameof(GetPictureRecordAsync),
            async (_, _) =>
            {
                var entity = await repository.TryGetAsync(id);
                return entity == null ? null : mapper.Map<PictureRecordResponse>(entity);
            },
            new { id }
        );
    }

    public async Task<PictureRecordResponse> CreateRecordAsync(
        CreatePictureRecordRequest request,
        CancellationToken cancellationToken = default
    )
    {
        var entity = mapper.Map<PictureRecord>(request);
        await repository.InsertAsync(entity, cancellationToken);
        if (entity.Tags.Any(x => x == "banner"))
        {
            await _fusionCache.RemoveAsync(CacheKey.HomeContainerKey, token: cancellationToken);
        }

        await ClearCacheAsync();
        return mapper.Map<PictureRecordResponse>(entity);
    }

    public async Task<PictureRecordResponse?> ChangeInformationAsync(
        EditPictureRecordRequest request,
        CancellationToken cancellationToken = default
    )
    {
        if (!ObjectId.TryParse(request.Id, out var oid))
        {
            return null;
        }

        var update = Builders<PictureRecord>
            .Update.Set(x => x.Category, request.Category)
            .Set(x => x.Description, request.Description)
            .Set(x => x.Tags, request.Tags);

        await repository.UpdateAsync(x => x.Id == oid, update, cancellationToken);
        if (request.Tags.Any(x => x == "banner"))
        {
            await _fusionCache.RemoveAsync(CacheKey.HomeContainerKey, token: cancellationToken);
        }

        await ClearCacheAsync();

        var entity = await repository.FindAsync(oid, cancellationToken);
        return entity == null ? null : mapper.Map<PictureRecordResponse>(entity);
    }

    public async Task DeleteAsync(string id)
    {
        var entity = await repository.TryGetAsync(id);
        if (entity == null)
        {
            return;
        }
        await repository.DeleteAsync(entity.Id);
        await mediator.Send(new RemoveImagesRequest { Images = [entity.AccessUrl] });
        await ClearCacheAsync();
    }

    public async Task ClearCacheAsync()
    {
        var removeTags = new[]
        {
            CachePrefixKey + nameof(GetTagsAsync),
            CachePrefixKey + nameof(GetPagesAsync),
            CachePrefixKey + nameof(GetBannerAsync),
            CachePrefixKey + nameof(GetPictureRecordAsync),
        };

        await _fusionCache.RemoveByTagAsync(removeTags);
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

PictureRecordService 代码分析

这个 PictureRecordService 是一个图片记录服务类,主要处理图片记录的相关操作,包括标签管理、横幅图片获取、分页查询、创建、修改和删除图片记录等功能。下面是详细的功能解释:

主要功能

1. 缓存功能

  • 继承自 AbstractCacheService,提供了缓存功能
  • 默认缓存过期时间为1天 (TimeSpan.FromDays(1))
  • 使用 IFusionCache 进行缓存管理
  • 提供了 ClearCacheAsync() 方法来清除相关缓存

2. 标签管理

  • GetTagsAsync(): 获取所有图片标签的列表(去重)

3. 横幅图片获取

  • GetBannerAsync(): 获取标记为"banner"的图片列表,并按上传时间降序排列
  • 返回的图片URL会自动追加"!banner"后缀

4. 分页查询

  • GetPagesAsync(): 根据标签、描述和账户进行分页查询图片记录
  • 支持多标签筛选
  • 支持描述模糊搜索(不区分大小写)
  • 支持按账户筛选
  • 结果按ID降序排列

5. 单个图片记录操作

  • GetPictureRecordAsync(): 根据ID获取单个图片记录
  • CreateRecordAsync(): 创建新的图片记录
  • ChangeInformationAsync(): 修改图片记录的信息(分类、描述和标签)
  • DeleteAsync(): 删除图片记录,并发送删除图片文件的请求

技术特点

  1. 缓存策略:

    • 使用缓存提高性能
    • 当相关数据变更时自动清除缓存
    • 特别是当操作涉及"banner"标签时,会清除首页容器缓存
  2. 数据库操作:

    • 使用MongoDB进行数据存储
    • 使用构建器模式创建查询条件
    • 支持分页查询
  3. 其他技术:

    • 使用AutoMapper进行对象映射
    • 使用MediatR进行事件处理(如图片删除时发送删除图片文件的请求)
  4. 异常处理:

    • 在修改操作时检查ObjectId的有效性
    • 在删除操作时检查记录是否存在

使用场景

这个服务类适用于需要管理图片记录的系统,特别是:

  • 需要展示横幅图片的网站
  • 需要按标签分类管理图片的系统
  • 需要支持多条件筛选和分页查询的图片库

服务通过缓存机制优化了频繁查询的性能,并通过事件机制处理了图片文件与记录的一致性。

评论加载中...