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:
@@ -1,5 +1,6 @@
|
||||
using dy.net.model;
|
||||
using SqlSugar;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace dy.net.repository
|
||||
{
|
||||
@@ -17,10 +18,14 @@ namespace dy.net.repository
|
||||
/// </summary>
|
||||
/// <param name="pageIndex">页码(从1开始)</param>
|
||||
/// <param name="pageSize">每页数量</param>
|
||||
/// <param name="tag">可选标签过滤</param>
|
||||
/// <param name="tag">可选标签过滤(单个,兼容旧版本)</param>
|
||||
/// <param name="tags">可选标签过滤(多个)</param>
|
||||
/// <param name="author">可选作者过滤</param>
|
||||
/// <param name="isDeleted">删除状态筛选:null=全部,true=已删除(videoSavePath为空),false=未删除(videoSavePath不为空)</param>
|
||||
/// <param name="sortField">排序字段:syncTime-同步时间, viedoType-同步类型, viedoCate-视频类型, author-作者, videoTitle-视频名称, dyUser-用户</param>
|
||||
/// <param name="sortOrder">排序方向:asc-升序, desc-降序</param>
|
||||
/// <returns>分页结果(视频列表和总数)</returns>
|
||||
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)
|
||||
{
|
||||
|
||||
DateTime? start = null;
|
||||
@@ -35,16 +40,206 @@ namespace dy.net.repository
|
||||
start = Convert.ToDateTime(dates[0]);
|
||||
}
|
||||
|
||||
var where = this.Db.Queryable<DyCollectVideo>()
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(tag), x => x.Tag1 == tag)
|
||||
var where = this.Db.Queryable<DyCollectVideo>();
|
||||
|
||||
// 支持多个标签筛选(优先使用 tags,如果为空则使用 tag)
|
||||
// 注意:tags 匹配的是 viedoCate(Tag1/Tag2 组合),格式如 "美食/美食展示"
|
||||
// 特殊值 "未分类" 表示 Tag1 和 Tag2 都为空的视频
|
||||
if (tags != null && tags.Any())
|
||||
{
|
||||
// 过滤空值
|
||||
var validTags = tags.Where(t => !string.IsNullOrWhiteSpace(t)).ToList();
|
||||
if (validTags.Any())
|
||||
{
|
||||
// 检查是否包含"未分类"选项
|
||||
var hasEmptyCategory = validTags.Contains("未分类");
|
||||
var otherTags = validTags.Where(t => t != "未分类").ToList();
|
||||
|
||||
// 构建 OR 条件:对于每个 tag,匹配对应的 Tag1/Tag2 组合
|
||||
if (hasEmptyCategory && otherTags.Any())
|
||||
{
|
||||
// 同时包含"未分类"和其他类型
|
||||
// 使用内存过滤的方式,确保逻辑正确
|
||||
var matchingVideoIds = new List<string>();
|
||||
|
||||
// 获取所有视频(先应用其他筛选条件)
|
||||
var allVideos = await this.Db.Queryable<DyCollectVideo>()
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(author), x => x.Author == author)
|
||||
.WhereIF(start.HasValue, x => x.SyncTime >= start.Value)
|
||||
.WhereIF(end.HasValue, x => x.SyncTime <= end.Value)
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(viedoType) && viedoType != "*", x => x.ViedoType == viedoType)
|
||||
.WhereIF(isDeleted.HasValue && isDeleted.Value, x => string.IsNullOrWhiteSpace(x.VideoSavePath))
|
||||
.WhereIF(isDeleted.HasValue && !isDeleted.Value, x => !string.IsNullOrWhiteSpace(x.VideoSavePath))
|
||||
.ToListAsync();
|
||||
|
||||
// 在内存中过滤 viedoCate
|
||||
foreach (var video in allVideos)
|
||||
{
|
||||
var viedoCate = (string.IsNullOrWhiteSpace(video.Tag1) ? "" : video.Tag1) + "/" + (string.IsNullOrWhiteSpace(video.Tag2) ? "" : video.Tag2);
|
||||
|
||||
// 检查是否是"未分类"或匹配其他标签
|
||||
bool isMatch = (string.IsNullOrWhiteSpace(video.Tag1) && string.IsNullOrWhiteSpace(video.Tag2)) || // 未分类
|
||||
otherTags.Contains(viedoCate); // 其他类型
|
||||
|
||||
if (isMatch)
|
||||
{
|
||||
matchingVideoIds.Add(video.Id);
|
||||
}
|
||||
}
|
||||
|
||||
if (matchingVideoIds.Any())
|
||||
{
|
||||
// 使用 ID 列表重新构建查询
|
||||
where = this.Db.Queryable<DyCollectVideo>()
|
||||
.Where(x => matchingVideoIds.Contains(x.Id));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有匹配的数据,返回空结果
|
||||
where = this.Db.Queryable<DyCollectVideo>().Where(x => false);
|
||||
}
|
||||
}
|
||||
else if (hasEmptyCategory)
|
||||
{
|
||||
// 只选择了"未分类"
|
||||
where = where.Where(x => string.IsNullOrWhiteSpace(x.Tag1) && string.IsNullOrWhiteSpace(x.Tag2));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 只选择了其他类型,构建所有条件的 OR
|
||||
// 使用更简单的方法:先获取所有符合条件的视频 ID,然后使用 In 方法
|
||||
// 这样可以避免复杂的表达式树构建
|
||||
|
||||
var matchingVideoIds = new List<string>();
|
||||
|
||||
// 获取所有视频
|
||||
var allVideos = await this.Db.Queryable<DyCollectVideo>()
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(author), x => x.Author == author)
|
||||
.WhereIF(start.HasValue, x => x.SyncTime >= start.Value)
|
||||
.WhereIF(end.HasValue, x => x.SyncTime <= end.Value)
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(viedoType) && viedoType != "*", x => x.ViedoType == viedoType)
|
||||
.WhereIF(isDeleted.HasValue && isDeleted.Value, x => string.IsNullOrWhiteSpace(x.VideoSavePath))
|
||||
.WhereIF(isDeleted.HasValue && !isDeleted.Value, x => !string.IsNullOrWhiteSpace(x.VideoSavePath))
|
||||
.ToListAsync();
|
||||
|
||||
// 在内存中过滤 viedoCate
|
||||
foreach (var video in allVideos)
|
||||
{
|
||||
var viedoCate = (string.IsNullOrWhiteSpace(video.Tag1) ? "" : video.Tag1) + "/" + (string.IsNullOrWhiteSpace(video.Tag2) ? "" : video.Tag2);
|
||||
|
||||
if (otherTags.Contains(viedoCate))
|
||||
{
|
||||
matchingVideoIds.Add(video.Id);
|
||||
}
|
||||
}
|
||||
|
||||
if (matchingVideoIds.Any())
|
||||
{
|
||||
// 使用 ID 列表重新构建查询
|
||||
where = this.Db.Queryable<DyCollectVideo>()
|
||||
.Where(x => matchingVideoIds.Contains(x.Id));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有匹配的数据,返回空结果
|
||||
where = this.Db.Queryable<DyCollectVideo>().Where(x => false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(tag))
|
||||
{
|
||||
// 兼容旧版本的单标签筛选
|
||||
if (tag.Contains("/"))
|
||||
{
|
||||
var parts = tag.Split('/', 2);
|
||||
var tag1 = parts[0];
|
||||
var tag2 = parts.Length > 1 ? parts[1] : "";
|
||||
where = where.Where(x => x.Tag1 == tag1 && (string.IsNullOrWhiteSpace(tag2) || x.Tag2 == tag2 || string.IsNullOrWhiteSpace(x.Tag2)));
|
||||
}
|
||||
else
|
||||
{
|
||||
where = where.Where(x => x.Tag1 == tag);
|
||||
}
|
||||
}
|
||||
|
||||
// 应用其他筛选条件
|
||||
where = where
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(author), x => x.Author == author)
|
||||
.WhereIF(start.HasValue, x => x.SyncTime >= start.Value)
|
||||
.WhereIF(end.HasValue, x => x.SyncTime <= end.Value)
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(viedoType) && viedoType != "*", x => x.ViedoType == viedoType);
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(viedoType) && viedoType != "*", x => x.ViedoType == viedoType)
|
||||
.WhereIF(isDeleted.HasValue && isDeleted.Value, x => string.IsNullOrWhiteSpace(x.VideoSavePath)) // 已删除:videoSavePath为空
|
||||
.WhereIF(isDeleted.HasValue && !isDeleted.Value, x => !string.IsNullOrWhiteSpace(x.VideoSavePath)); // 未删除:videoSavePath不为空
|
||||
|
||||
|
||||
var totalCount = await where.CountAsync();
|
||||
var list = await where.OrderByDescending(x=>x.SyncTime).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
|
||||
|
||||
// 构建排序逻辑
|
||||
var isAsc = !string.IsNullOrWhiteSpace(sortOrder) && sortOrder.ToLower() == "asc";
|
||||
var query = where;
|
||||
|
||||
// 调试日志
|
||||
Console.WriteLine($"Repository排序参数 - sortField: {sortField}, sortOrder: {sortOrder}, isAsc: {isAsc}");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(sortField))
|
||||
{
|
||||
switch (sortField.ToLower())
|
||||
{
|
||||
case "synctime":
|
||||
query = isAsc ? query.OrderBy(x => x.SyncTime) : query.OrderByDescending(x => x.SyncTime);
|
||||
Console.WriteLine($"应用排序: SyncTime {(isAsc ? "升序" : "降序")}");
|
||||
break;
|
||||
case "viedotype":
|
||||
query = isAsc ? query.OrderBy(x => x.ViedoType) : query.OrderByDescending(x => x.ViedoType);
|
||||
Console.WriteLine($"应用排序: ViedoType {(isAsc ? "升序" : "降序")}");
|
||||
break;
|
||||
case "viedocate":
|
||||
// 视频类型需要按Tag1和Tag2排序,SqlSugar使用OrderBy链式调用
|
||||
if (isAsc)
|
||||
{
|
||||
query = query.OrderBy(x => x.Tag1).OrderBy(x => x.Tag2);
|
||||
}
|
||||
else
|
||||
{
|
||||
query = query.OrderByDescending(x => x.Tag1).OrderByDescending(x => x.Tag2);
|
||||
}
|
||||
Console.WriteLine($"应用排序: ViedoCate {(isAsc ? "升序" : "降序")}");
|
||||
break;
|
||||
case "author":
|
||||
query = isAsc ? query.OrderBy(x => x.Author) : query.OrderByDescending(x => x.Author);
|
||||
Console.WriteLine($"应用排序: Author {(isAsc ? "升序" : "降序")}");
|
||||
break;
|
||||
case "videotitle":
|
||||
query = isAsc ? query.OrderBy(x => x.VideoTitle) : query.OrderByDescending(x => x.VideoTitle);
|
||||
Console.WriteLine($"应用排序: VideoTitle {(isAsc ? "升序" : "降序")}");
|
||||
break;
|
||||
case "dyuser":
|
||||
// 用户字段需要先查询用户信息,这里先按CookieId排序,后续会设置DyUser
|
||||
query = isAsc ? query.OrderBy(x => x.CookieId) : query.OrderByDescending(x => x.CookieId);
|
||||
Console.WriteLine($"应用排序: DyUser {(isAsc ? "升序" : "降序")}");
|
||||
break;
|
||||
default:
|
||||
// 默认按同步时间降序
|
||||
query = query.OrderByDescending(x => x.SyncTime);
|
||||
Console.WriteLine($"未识别的排序字段,使用默认排序: SyncTime 降序");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 默认按同步时间降序
|
||||
query = query.OrderByDescending(x => x.SyncTime);
|
||||
Console.WriteLine("无排序字段,使用默认排序: SyncTime 降序");
|
||||
}
|
||||
|
||||
var list = await query.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
|
||||
Console.WriteLine($"查询完成,返回 {list.Count} 条记录");
|
||||
if (list.Any())
|
||||
{
|
||||
Console.WriteLine($"第一条记录的同步时间: {list.First().SyncTime}");
|
||||
Console.WriteLine($"最后一条记录的同步时间: {list.Last().SyncTime}");
|
||||
}
|
||||
if (list.Any()) {
|
||||
var users= await this.Db.Queryable<DyUserCookies>().ToListAsync();
|
||||
foreach (var item in list)
|
||||
|
||||
Reference in New Issue
Block a user