feat: 视频预览/数据看板/下载配置 三项业务功能
- 新增视频预览:VideoPlayer 组件、VideoController/DyCollectVideoService/Repository、VideoPageRequestDTO 对应后端改动 - 新增数据看板:workplace/statics.vue、RecordTable、相关 store/http/axios 改动 - 新增下载配置:DownloadConfigService、ConfigController、AppSet.vue、AppConfig - 其他:DyHttpClientService / 同步 Job(Collect, Favorit) / Cookie 表 / CommonService / ServiceExtension 的配套调整 - 仓库卫生:.gitignore 追加 feiniu 过滤;运维脚本归档到 docs/scripts/
This commit is contained in:
@@ -41,12 +41,18 @@ namespace dy.net.service
|
||||
.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;
|
||||
@@ -55,16 +61,47 @@ namespace dy.net.service
|
||||
|
||||
public async Task<VideoStaticsDto> GetStatics()
|
||||
{
|
||||
|
||||
List<DyCollectVideo> list = await this._dyCollectVideoRepository.GetAllAsync();
|
||||
List<DyCollectVideo> 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();
|
||||
var Categories = list.GroupBy(x => x.Tag1).Select(x => new VideoStaticsItemDto { Name = x.Key, Count = x.LongCount() }).OrderByDescending(p => p.Count).ToList();
|
||||
Categories.Where(x => string.IsNullOrWhiteSpace(x.Name)).ToList().ForEach(x => x.Name = "其他");
|
||||
|
||||
// 按 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 = list.Select(x => x.Tag1).Distinct().Count(),
|
||||
CategoryCount = Categories.Count, // 使用实际分类数量
|
||||
VideoCount = list.Count,
|
||||
Categories = Categories,
|
||||
FavoriteCount = list.Count(x => x.ViedoType == "1"),
|
||||
@@ -85,9 +122,154 @@ namespace dy.net.service
|
||||
|
||||
//分页查询
|
||||
|
||||
public async Task<(List<DyCollectVideo> list, int totalCount)> GetPagedAsync(int pageIndex, int pageSize, string tag = null, string author = null, string viedoType = null, List<string>? dates = null)
|
||||
public async Task<(List<DyCollectVideo> list, int totalCount)> GetPagedAsync(int pageIndex, int pageSize, string tag = null, List<string> tags = null, string author = null, string viedoType = null, List<string>? dates = null, bool? isDeleted = null, string sortField = null, string sortOrder = null)
|
||||
{
|
||||
return await _dyCollectVideoRepository.GetPagedAsync(pageIndex, pageSize, tag, author, viedoType, dates);
|
||||
return await _dyCollectVideoRepository.GetPagedAsync(pageIndex, pageSize, tag, tags, author, viedoType, dates, isDeleted, sortField, sortOrder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有视频类型列表(用于下拉框)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<List<string>> 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<string>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据ID获取视频信息
|
||||
/// </summary>
|
||||
/// <param name="id">视频ID</param>
|
||||
/// <returns></returns>
|
||||
public async Task<DyCollectVideo> GetByIdAsync(string id)
|
||||
{
|
||||
return await _dyCollectVideoRepository.GetByIdAsync(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理数据库中文件不存在的视频记录
|
||||
/// </summary>
|
||||
/// <returns>清理的记录数量</returns>
|
||||
public async Task<int> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除指定作者的所有视频数据
|
||||
/// </summary>
|
||||
/// <param name="authorName">作者名称</param>
|
||||
/// <returns>删除的记录数量</returns>
|
||||
public async Task<int> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user