using dy.net.dto; using dy.net.model; using dy.net.repository; using dy.net.utils; using System.ComponentModel; using System.Threading.Tasks; namespace dy.net.service { public class DyCollectVideoService { private readonly DyCollectVideoRepository _dyCollectVideoRepository; public DyCollectVideoService(DyCollectVideoRepository dyCollectVideoRepository) { _dyCollectVideoRepository = dyCollectVideoRepository; } public async Task batchInsert(List videos) { // 边界处理:如果传入的列表为空,直接返回成功(或根据业务返回false) if (videos == null || !videos.Any()) return true; // 1. 提取待插入的所有AwemeId(去重,减少数据库查询压力) var newAwemeIds = videos.Select(x => x.AwemeId) .Distinct() .ToList(); // 2. 查询数据库中已存在的AwemeId(只查需要的字段,提高效率) var existingAwemeIds = await _dyCollectVideoRepository .Query(x => newAwemeIds.Contains(x.AwemeId)) // 使用Query方法构建查询 .Select(x => x.AwemeId) // 只获取AwemeId,减少数据传输 .ToListAsync(); // 3. 过滤出数据库中不存在的视频(只保留新记录) var videosToInsert = videos .Where(video => !existingAwemeIds.Contains(video.AwemeId)) .ToList(); Serilog.Log.Debug($"batchInsert: 待插入{videos.Count}条,已存在{existingAwemeIds.Count}条,需要插入{videosToInsert.Count}条"); // 4. 如果没有需要插入的新记录,直接返回成功 if (!videosToInsert.Any()) { Serilog.Log.Debug($"batchInsert: 所有视频都已存在,无需插入"); return true; } // 5. 批量插入过滤后的新记录 var insertedCount = await _dyCollectVideoRepository.InsertRangeAsync(videosToInsert); Serilog.Log.Debug($"batchInsert: 实际插入{insertedCount}条记录"); // 返回是否插入成功(至少插入一条) return insertedCount > 0; } public async Task GetStatics() { List allList = await this._dyCollectVideoRepository.GetAllAsync(); if (!allList.Any()) return new VideoStaticsDto(); // 只统计文件实际存在的视频 var list = allList.Where(x => !string.IsNullOrWhiteSpace(x.VideoSavePath) && System.IO.File.Exists(x.VideoSavePath) ).ToList(); Serilog.Log.Debug($"统计视频:数据库记录 {allList.Count} 条,文件存在 {list.Count} 条"); if (!list.Any()) return new VideoStaticsDto(); // 按 viedoCate(Tag1/Tag2 组合)分组统计分类 var Categories = list .GroupBy(x => { // 使用与模型 ViedoCate 属性相同的逻辑:Tag1 + "/" + Tag2 var tag1 = string.IsNullOrWhiteSpace(x.Tag1) ? "" : x.Tag1; var tag2 = string.IsNullOrWhiteSpace(x.Tag2) ? "" : x.Tag2; var viedoCate = tag1 + "/" + tag2; // 如果 Tag1 和 Tag2 都为空,标记为"未分类" if (string.IsNullOrWhiteSpace(tag1) && string.IsNullOrWhiteSpace(tag2)) { return "未分类"; } return viedoCate; }) .Select(x => new VideoStaticsItemDto { Name = x.Key, Count = x.LongCount() }) .OrderByDescending(p => p.Count) .ToList(); var data = new VideoStaticsDto { AuthorCount = list.Select(x => x.AuthorId).Distinct().Count(), CategoryCount = Categories.Count, // 使用实际分类数量 VideoCount = list.Count, Categories = Categories, FavoriteCount = list.Count(x => x.ViedoType == "1"), CollectCount = list.Count(x => x.ViedoType == "2"), FollowCount = list.Count(x => x.ViedoType == "3"), VideoSizeTotal = ByteToGbConverter.ConvertBytesToGb(list.Sum(x => x.FileSize)), VideoFavoriteSize = ByteToGbConverter.ConvertBytesToGb(list.Where(x => x.ViedoType == "1").Sum(x => x.FileSize)), VideoCollectSize = ByteToGbConverter.ConvertBytesToGb(list.Where(x => x.ViedoType == "2").Sum(x => x.FileSize)), VideoFollowSize = ByteToGbConverter.ConvertBytesToGb(list.Where(x => x.ViedoType == "3").Sum(x => x.FileSize)), //TotalDiskSize= ByteToGbConverter.GetHostTotalDiskSpaceGB(), }; data.Authors = list.GroupBy(x => x.Author).Select(x => new VideoStaticsItemDto { Name = x.Key, Count = x.LongCount(), Icon = x.FirstOrDefault().AuthorAvatarUrl }).OrderByDescending(d => d.Count).ToList(); return data; } //分页查询 public async Task<(List list, int totalCount)> GetPagedAsync(int pageIndex, int pageSize, string tag = null, List tags = null, string author = null, string viedoType = null, List? dates = null, bool? isDeleted = null, string sortField = null, string sortOrder = null) { return await _dyCollectVideoRepository.GetPagedAsync(pageIndex, pageSize, tag, tags, author, viedoType, dates, isDeleted, sortField, sortOrder); } /// /// 获取所有视频类型列表(用于下拉框) /// /// public async Task> GetAllCategoriesAsync() { try { // 获取所有视频记录 var allVideos = await _dyCollectVideoRepository.GetAllAsync(); Serilog.Log.Debug($"获取视频类型列表:共 {allVideos?.Count ?? 0} 条视频记录"); // 提取所有非空的 viedoCate(Tag1/Tag2 组合),去重并排序 // 使用与模型 ViedoCate 属性相同的逻辑:Tag1 + "/" + Tag2 var categories = allVideos .Select(x => { // 使用与模型 ViedoCate 属性相同的逻辑 var tag1 = string.IsNullOrWhiteSpace(x.Tag1) ? "" : x.Tag1; var tag2 = string.IsNullOrWhiteSpace(x.Tag2) ? "" : x.Tag2; var viedoCate = tag1 + "/" + tag2; return viedoCate; }) .Where(x => !string.IsNullOrWhiteSpace(x) && x != "/") // 过滤空值和只有 "/" 的情况 .Distinct() .OrderBy(x => x) .ToList(); // 检查是否存在空类型的视频(Tag1 和 Tag2 都为空) var hasEmptyCategory = allVideos.Any(x => string.IsNullOrWhiteSpace(x.Tag1) && string.IsNullOrWhiteSpace(x.Tag2)); if (hasEmptyCategory) { // 在列表开头添加"未分类"选项 categories.Insert(0, "未分类"); } Serilog.Log.Debug($"提取到的视频类型数量:{categories.Count},类型列表:{string.Join(", ", categories)}"); return categories; } catch (Exception ex) { Serilog.Log.Error(ex, "获取视频类型列表失败"); return new List(); } } /// /// 根据ID获取视频信息 /// /// 视频ID /// public async Task GetByIdAsync(string id) { return await _dyCollectVideoRepository.GetByIdAsync(id); } /// /// 清理数据库中文件不存在的视频记录 /// /// 清理的记录数量 public async Task CleanupMissingFilesAsync() { try { var allVideos = await _dyCollectVideoRepository.GetAllAsync(); if (!allVideos.Any()) return 0; // 找出需要清理的视频ID: // 1. VideoSavePath 不为空但文件不存在的记录 // 2. VideoSavePath 为空或null的记录(这些记录没有视频文件,也应该被清理) var missingFileIds = allVideos .Where(x => // 情况1:有路径但文件不存在 (!string.IsNullOrWhiteSpace(x.VideoSavePath) && !System.IO.File.Exists(x.VideoSavePath)) || // 情况2:路径为空(这些记录没有视频文件,也应该清理) string.IsNullOrWhiteSpace(x.VideoSavePath) ) .Select(x => x.Id) .ToList(); if (!missingFileIds.Any()) { Serilog.Log.Debug("清理检查:所有视频文件都存在,无需清理"); return 0; } var pathEmptyCount = allVideos.Count(x => string.IsNullOrWhiteSpace(x.VideoSavePath)); var fileMissingCount = allVideos.Count(x => !string.IsNullOrWhiteSpace(x.VideoSavePath) && !System.IO.File.Exists(x.VideoSavePath)); Serilog.Log.Debug($"清理检查:发现 {missingFileIds.Count} 条需要清理的记录(路径为空:{pathEmptyCount} 条,文件不存在:{fileMissingCount} 条)"); // 批量删除 var deletedCount = await _dyCollectVideoRepository.DeleteByIdsAsync(missingFileIds); Serilog.Log.Debug($"清理完成:删除了 {deletedCount} 条记录"); return deletedCount; } catch (Exception ex) { Serilog.Log.Error(ex, "清理文件不存在的记录失败"); throw; } } /// /// 删除指定作者的所有视频数据 /// /// 作者名称 /// 删除的记录数量 public async Task DeleteAuthorVideosAsync(string authorName) { try { var allVideos = await _dyCollectVideoRepository.GetAllAsync(); if (!allVideos.Any()) return 0; // 找出该作者的所有视频ID var authorVideoIds = allVideos .Where(x => !string.IsNullOrWhiteSpace(x.Author) && x.Author.Equals(authorName, StringComparison.OrdinalIgnoreCase)) .Select(x => x.Id) .ToList(); if (!authorVideoIds.Any()) { Serilog.Log.Debug($"删除作者视频:未找到作者 {authorName} 的视频记录"); return 0; } Serilog.Log.Debug($"删除作者视频:发现作者 {authorName} 的 {authorVideoIds.Count} 条视频记录"); // 批量删除 var deletedCount = await _dyCollectVideoRepository.DeleteByIdsAsync(authorVideoIds); Serilog.Log.Debug($"删除完成:删除了 {deletedCount} 条记录"); return deletedCount; } catch (Exception ex) { Serilog.Log.Error(ex, $"删除作者 {authorName} 的视频失败"); throw; } } } }