namespace Dpz.Core.Service.RepositoryServiceImpl;
public class VideoService(IRepository<Video> repository, IMapper mapper, IFusionCache fusionCache)
: IVideoService
{
public async Task<List<VmVideo>> GetVideosAsync()
{
var list = await repository.SearchFor(x => true).ToListAsync();
return list == null || list.Count == 0 ? [] : mapper.Map<List<VmVideo>>(list);
}
public async Task SaveInformationAsync(VmVideo video)
{
if (video == null || string.IsNullOrEmpty(video.Id))
{
throw new ArgumentNullException(
nameof(video),
"argument video or video.id is null or empty"
);
}
var inVideo = mapper.Map<Video>(video);
var filter = Builders<Video>.Filter.Eq(x => x.Id, inVideo.Id);
var find = await repository.Collection.FindAsync(filter);
var entity = await find.FirstOrDefaultAsync();
if (entity == null)
{
inVideo.CommentCount = 0;
inVideo.DanmakuCount = 0;
inVideo.PlayCount = 0;
await repository.InsertAsync(inVideo);
return;
}
var update = Builders<Video>
.Update.Set(x => x.Name, inVideo.Name)
.Set(x => x.Description, inVideo.Description)
.Set(x => x.Tags, inVideo.Tags)
.Set(x => x.Title, inVideo.Title)
.Set(x => x.SubTitle, inVideo.SubTitle)
.Set(x => x.VideoTitle, inVideo.VideoTitle);
await repository.UpdateAsync(x => x.Id == inVideo.Id, update);
}
public async Task<VmVideo?> GetVideoAsync(string id)
{
var entity = await repository.SearchFor(x => x.Id == id).FirstOrDefaultAsync();
if (entity == null)
{
return null;
}
return mapper.Map<VmVideo>(entity);
}
public async Task AddPlayAsync(string id)
{
var update = Builders<Video>.Update.Inc(x => x.PlayCount, 1);
await repository.UpdateAsync(x => x.Id == id, update);
}
/// <summary>
/// 更新视频评论数(仅由消息队列使用)
/// </summary>
public async Task UpdateCommentCountAsync(
string videoId,
int count,
CancellationToken cancellationToken = default
)
{
// 视频使用字符串ID,直接更新
var update = Builders<Video>.Update.Set(x => x.CommentCount, count);
await repository.UpdateAsync(x => x.Id == videoId, update, cancellationToken);
}
public async Task UpdateDanmakuCountAsync(
string videoId,
int count,
CancellationToken cancellationToken = default
)
{
// 更新数据库
var update = Builders<Video>.Update.Set(x => x.DanmakuCount, count);
await repository.UpdateAsync(x => x.Id == videoId, update, cancellationToken);
// 更新单个视频缓存
var singleVideoKey = $"{CacheKey.VideoListKey}:{videoId}";
var cachedVideo = await fusionCache.TryGetAsync<VmVideo>(
singleVideoKey,
token: cancellationToken
);
if (cachedVideo.HasValue)
{
cachedVideo.Value.DanmakuCount = count;
await fusionCache.SetAsync(
singleVideoKey,
cachedVideo.Value,
TimeSpan.FromDays(1),
[CacheKey.VideoListKey],
token: cancellationToken
);
}
// 更新视频列表缓存
var listCache = await fusionCache.TryGetAsync<List<VmVideo>>(
CacheKey.VideoListKey,
token: cancellationToken
);
if (listCache.HasValue)
{
var video = listCache.Value.FirstOrDefault(x => x.Id == videoId);
if (video != null)
{
video.DanmakuCount = count;
await fusionCache.SetAsync(
CacheKey.VideoListKey,
listCache.Value,
TimeSpan.FromDays(1),
[CacheKey.VideoListKey],
token: cancellationToken
);
}
}
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
这是一个基于 .NET 的视频服务实现类,使用了依赖注入、MongoDB 和缓存技术。让我详细解释一下这个代码:
类结构和依赖
public class VideoService(IRepository<Video> repository, IMapper mapper, IFusionCache fusionCache)
: IVideoService
这个类使用了 C# 12 的主构造函数语法,注入了三个依赖:
IRepository<Video>: MongoDB 仓储模式,用于数据库操作IMapper: AutoMapper,用于对象映射(Video ↔ VmVideo)IFusionCache: 缓存组件,用于提高性能
主要功能方法
1. 获取所有视频
public async Task<List<VmVideo>> GetVideosAsync()
- 从数据库获取所有视频记录
- 使用 AutoMapper 将实体对象转换为视图模型
- 返回空列表而不是 null(防御性编程)
2. 保存视频信息
public async Task SaveInformationAsync(VmVideo video)
- 实现了 Upsert 操作(插入或更新)
- 参数验证:检查视频对象和ID是否为空
- 如果视频不存在,创建新记录并初始化计数器
- 如果存在,只更新基本信息字段(名称、描述、标签等)
3. 获取单个视频
public async Task<VmVideo?> GetVideoAsync(string id)
- 根据ID查询单个视频
- 返回可空类型,遵循现代 C# 最佳实践
4. 增加播放次数
public async Task AddPlayAsync(string id)
- 使用 MongoDB 的原子操作
$inc增加播放计数 - 避免了读取-修改-写入的竞态条件
5. 更新评论数
public async Task UpdateCommentCountAsync(...)
- 专门用于消息队列调用
- 直接设置评论数量,而不是增量操作
6. 更新弹幕数(最复杂的方法)
public async Task UpdateDanmakuCountAsync(...)
这个方法展示了多层缓存策略:
- 更新数据库:直接设置弹幕数量
- 更新单个视频缓存:
- 尝试获取单个视频的缓存
- 如果存在,更新弹幕数并重新缓存
- 更新视频列表缓存:
- 获取整个视频列表缓存
- 找到对应视频并更新弹幕数
- 重新缓存整个列表
技术特点
1. 异步编程
- 所有方法都是异步的,提高并发性能
- 支持
CancellationToken用于取消操作
2. MongoDB 特性
- 使用
Builders<Video>构建查询和更新操作 - 利用 MongoDB 的原子操作(
$inc,$set)
3. 缓存策略
- 使用 FusionCache 进行多级缓存
- 缓存过期时间设置为1天
- 使用缓存标签进行批量失效
4. 错误处理
- 参数验证和异常抛出
- 空值检查和防御性编程
设计模式
- 仓储模式: 通过
IRepository<Video>抽象数据访问 - 依赖注入: 构造函数注入所有依赖
- 映射模式: 使用 AutoMapper 分离领域模型和视图模型
- 缓存模式: 多层缓存提高性能
这个服务类展现了现代 .NET 应用的典型架构模式,具有良好的可测试性、可维护性和性能优化。
AI 正在分析代码…
评论加载中...