using System.Collections.Specialized;
using System.Net.Http;
using System.Net.Http.Json;
using Dpz.Core.Public.ViewModel.Response;
using Dpz.Core.Shard.Service;
namespace Dpz.Core.Web.Library.Api.Service;
public class HomeCacheService(
IArticleService articleService,
IPictureRecordService pictureRecordService,
IHttpContextAccessor httpContextAccessor,
IHttpClientFactory httpClientFactory,
IFusionCache fusionCache,
IVideoShardService videoShardService,
ILogger<HomeCacheService> logger
) : IHomeCacheService
{
/// <summary>
/// 过期时间
/// </summary>
private readonly TimeSpan _expired = TimeSpan.FromDays(1);
private async Task<IList<PictureRecordResponse>> GetBannersAsync()
{
var banner = await pictureRecordService.GetBannerAsync();
return banner.ToList();
}
public async Task<HomeContainerModel> GetCacheDataAsync()
{
var model = await fusionCache.GetOrSetAsync<HomeContainerModel>(
CacheKey.HomeContainerKey,
async (_, cancellationToken) =>
{
var m = new HomeContainerModel
{
Blogs = await GetTopArticlesAsync(),
LikeArticle = await GetRandomArticlesAsync(cancellationToken),
News = await GetLatestArticlesAsync(cancellationToken),
Pictures = await GetBannersAsync(),
Tags = await GetArticleTagsAsync(cancellationToken),
};
return m;
},
options => options.SetDuration(_expired)
);
var video = await GetRandomVideoAsync();
model.Video = video;
return model;
}
private async Task<VmVideo?> GetRandomVideoAsync()
{
var videos = await videoShardService.GetVideosAsync();
if (videos.Count == 0)
{
return null;
}
return videos.OrderBy(_ => Guid.NewGuid()).First();
}
public async Task<List<string>> GetArticleTagsAsync(
CancellationToken cancellationToken = default
)
{
return await articleService.GetAllTagsAsync(cancellationToken);
}
public async Task<List<ArticleMiniResponse>> GetLatestArticlesAsync(
CancellationToken cancellationToken = default
)
{
return await articleService.GetLatestAsync(cancellationToken: cancellationToken);
}
public async Task<List<ArticleMiniResponse>> GetRandomArticlesAsync(
CancellationToken cancellationToken = default
)
{
return await articleService.GetRandomArticlesAsync(cancellationToken: cancellationToken);
}
public async Task<TodayNewsModel?> GetTodayNewsAsync()
{
var ipAddress = httpContextAccessor.HttpContext?.Request.GetIpAddress() ?? "";
var todayNewsKey = $"home:news:{ipAddress.Replace(":", "-")}";
return await fusionCache.GetOrSetAsync<TodayNewsModel?>(
todayNewsKey,
async (_, cancellationToke) =>
{
var httpClient = httpClientFactory.CreateClient("edge");
try
{
var parameters = new NameValueCollection { { "ip", ipAddress } };
var request = new HttpRequestMessage(
HttpMethod.Get,
"https://news.topurl.cn/api?" + parameters.ToQueryString()
);
var response = await httpClient.SendAsync(request, cancellationToke);
if (!response.IsSuccessStatusCode)
{
logger.LogWarning(
"get today news fail,status code:{Code}",
response.StatusCode
);
return null;
}
var data = await response.Content.ReadFromJsonAsync<NewsResponse>(
cancellationToken: cancellationToke
);
return data?.Data;
}
catch (Exception ex)
{
logger.LogError(ex, "get today news fail");
return null;
}
},
options => options.SetDuration(TimeSpan.FromHours(5))
);
}
private async Task<List<ArticleMiniResponse>> GetTopArticlesAsync()
{
var articleMinis = await articleService.GetTopArticlesAsync(-3, 10);
return articleMinis;
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
代码解释
这是一个 ASP.NET Core 应用程序中的首页缓存服务类,主要功能是提供首页所需的各种数据,并通过缓存机制优化性能。
核心功能概览
1. 类定义与依赖注入
public class HomeCacheService(...) : IHomeCacheService
- 使用了 C# 12 主构造函数语法,直接在类声明中注入依赖
- 注入的服务包括:
IArticleService: 文章服务IPictureRecordService: 图片记录服务IHttpContextAccessor: HTTP 上下文访问器IHttpClientFactory: HTTP 客户端工厂IFusionCache: 缓存服务(FusionCache 库)IVideoShardService: 视频分片服务ILogger: 日志记录器
2. 主要方法详解
GetCacheDataAsync() - 获取首页容器数据
public async Task<HomeContainerModel> GetCacheDataAsync()
- 核心方法,返回首页所有数据的容器模型
- 使用
fusionCache.GetOrSetAsync实现缓存穿透保护:- 缓存键:
CacheKey.HomeContainerKey - 缓存时长:1天(
_expired) - 缓存内容包括:
Blogs: 热门文章LikeArticle: 随机推荐文章News: 最新文章Pictures: 轮播图Tags: 文章标签
- 缓存键:
- 视频数据特殊处理:每次都重新获取随机视频(不使用缓存)
GetRandomVideoAsync() - 获取随机视频
private async Task<VmVideo?> GetRandomVideoAsync()
- 获取所有视频后,使用
OrderBy(_ => Guid.NewGuid())随机排序 - 返回第一个视频或
null(无视频时)
GetTodayNewsAsync() - 获取今日新闻
public async Task<TodayNewsModel?> GetTodayNewsAsync()
- 重要特性:
- 根据用户 IP 地址 构建缓存键(避免不同 IP 用户共享新闻缓存)
- 调用外部 API:
https://news.topurl.cn/api - 缓存时长:5 小时
- 异常处理:捕获并记录所有异常,返回
null
辅助方法
GetBannersAsync(): 获取轮播图GetTopArticlesAsync(): 获取置顶文章(前3天内,最多10条)GetLatestArticlesAsync(): 获取最新文章GetRandomArticlesAsync(): 获取随机推荐文章GetArticleTagsAsync(): 获取所有文章标签
设计亮点
分层缓存策略:
- 首页容器数据缓存 1 天
- 今日新闻缓存 5 小时
- 视频数据不缓存(实时随机)
IP 级别缓存隔离:新闻数据按 IP 缓存,避免不同地区用户看到相同内容
容错处理:外部 API 调用失败时返回
null,不影响其他功能异步编程:全面使用
async/await,支持取消令牌(CancellationToken)
使用场景
这个服务典型应用于博客或新闻网站的首页,通过缓存减少数据库查询和外部 API 调用,提升响应速度。
AI 正在分析代码…
评论加载中...