From 7666ab109daf28d99941781e7c135d18fd8e198f Mon Sep 17 00:00:00 2001 From: jianzhichu Date: Sat, 20 Dec 2025 12:19:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Controllers/AuthController.cs | 20 +- Controllers/ConfigController.cs | 97 +++++----- Controllers/FollowController.cs | 48 ++--- Controllers/LogsController.cs | 4 +- Controllers/VideoController.cs | 56 +++--- Program.cs | 182 ++++++++++++------ .../PublishProfiles/FolderProfile.pubxml.user | 2 +- README.md | 8 +- appsettings.json | 6 +- dto/DouyinResponse.cs | 168 ++++++++++++++++ extension/DouyinExceptionFilter.cs | 66 +++++++ repository/DouyinVideoRepository.cs | 4 +- service/DouyinVideoService.cs | 4 +- 13 files changed, 462 insertions(+), 203 deletions(-) create mode 100644 dto/DouyinResponse.cs create mode 100644 extension/DouyinExceptionFilter.cs diff --git a/Controllers/AuthController.cs b/Controllers/AuthController.cs index dac4acf..3db8340 100644 --- a/Controllers/AuthController.cs +++ b/Controllers/AuthController.cs @@ -35,7 +35,7 @@ namespace dy.net.Controllers public async Task UpdatePwd(UpdatePwdRequest user) { var (code, erro) = await _userService.UpdatePwd(user); - return Ok(new { code, erro }); + return ApiResult.Success("", erro); } @@ -47,7 +47,7 @@ namespace dy.net.Controllers public async Task GetUserAvatar() { var user = await _userService.GetUser(); - return Ok(new { code = 0, error = "", data = new { user?.Avatar, user?.Id, user?.UserName } }); + return ApiResult.Success(new { user?.Avatar, user?.Id, user?.UserName }); } /// @@ -64,7 +64,7 @@ namespace dy.net.Controllers long maxFileSize = 5 * 1024 * 1024; // 限制文件大小为5MB if (file.Length > maxFileSize) { - return Ok(new { code = -1, erro = "文件最大只能上传5M" }); + return ApiResult.Fail("文件最大只能上传5M"); } var fileName = $"{IdGener.GetGuid()}_{file.FileName}"; var filePath = webHostEnvironment.IsProduction() ? @@ -86,15 +86,16 @@ namespace dy.net.Controllers catch (Exception ex) { Serilog.Log.Error($"delete file error ,{ex.Message}"); + return ApiResult.Fail(ex.Message); } var update = await _userService.UpdateAvatar(fileName); - return Ok(new { code = update ? 0 : -1, erro = update ? "" : "上传失败", data = update ? fileName : "" }); + return ApiResult.SuccOrFail(update , update ? fileName : "", update ? "" : "上传失败"); } } else { - return Ok(new { code = -1, erro = "空文件" }); + return ApiResult.Fail("无效的文件"); } } @@ -110,7 +111,7 @@ namespace dy.net.Controllers { if (loginUserInfo == null) { - return Ok(new { code = -1, erro = "参数不能为空" }); + return ApiResult.Fail("参数不能为空"); } else { @@ -118,7 +119,7 @@ namespace dy.net.Controllers if (user == null) { - return Ok(new { code = -1, erro = "用户名或密码不正确" }); + return ApiResult.Fail("用户名或密码不正确"); } else { @@ -126,12 +127,11 @@ namespace dy.net.Controllers { var tokenString = GenerateJwtToken(user.UserName); - - return Ok(new { code = 0, erro = "", token = tokenString, expires = 24 * 60 * 60 * 1000,data=user.UserName }); + return Ok(new { code = 0, erro = "", token = tokenString, expires = 24 * 60 * 60 * 1000, data = user.UserName }); } else { - return Ok(new { code = -1, erro = "用户名或密码不正确" }); + return ApiResult.Fail("用户名或密码不正确"); } } } diff --git a/Controllers/ConfigController.cs b/Controllers/ConfigController.cs index 80bad08..fee8670 100644 --- a/Controllers/ConfigController.cs +++ b/Controllers/ConfigController.cs @@ -53,7 +53,7 @@ namespace dy.net.Controllers cookies = await douyinCookieService.GetAllAsync() }; - return Ok(new { code = 0, data = dto }); + return ApiResult.Success(dto); } @@ -64,40 +64,38 @@ namespace dy.net.Controllers /// /// [HttpPost("importConf")] - public async Task ImportConf(AppConfigImportDto dto) + public async Task ImportConf(AppConfigImportDto dto) { - if (dto == null) - return BadRequest("json数据为空"); + if (dto == null) + return ApiResult.Fail("json数据为空"); - var follows = dto.follows; - if (follows != null && follows.Count > 0) - { - var add = await douyinFollowService.AddHandFollows(follows); - if (add) - Serilog.Log.Debug("关注列表导入成功"); - } + var follows = dto.follows; + if (follows != null && follows.Count > 0) + { + var add = await douyinFollowService.AddHandFollows(follows); + if (add) + Serilog.Log.Debug("关注列表导入成功"); + } - var conf = dto.conf; - if (conf != null) - { - var update = await commonService.UpdateConfig(conf); - if (update) - Serilog.Log.Debug("系统配置导入成功"); - } + var conf = dto.conf; + if (conf != null) + { + var update = await commonService.UpdateConfig(conf); + if (update) + Serilog.Log.Debug("系统配置导入成功"); + } var cookies = dto.cookies; if (cookies != null && cookies.Count > 0) { - var importCookies=await douyinCookieService.ImportCookies(cookies); + var importCookies = await douyinCookieService.ImportCookies(cookies); if (importCookies) { Serilog.Log.Debug("抖音Cookie配置导入成功"); } } - - - return Ok(new {code=0,data=true}); + return ApiResult.Success(); } /// /// 分页查询 @@ -108,18 +106,13 @@ namespace dy.net.Controllers PageRequestDto dto) { var (list, totalCount) = await dyCookieService.GetPagedAsync(dto.PageIndex, dto.PageSize); - return Ok(new + return ApiResult.Success(new { - code = 0, - data = new - { - data = list, - total = totalCount, - pageIndex = dto.PageIndex, - pageSize = dto.PageSize - } - } - ); + data = list, + total = totalCount, + pageIndex = dto.PageIndex, + pageSize = dto.PageSize + }); } @@ -131,7 +124,7 @@ namespace dy.net.Controllers public async Task GetAllList() { var follows = await douyinFollowService.GetGroupByCookieAsync(); - return Ok(new { code = 0, data = follows }); + return ApiResult.Success(follows); } /// /// 新增用户Cookie @@ -143,10 +136,10 @@ namespace dy.net.Controllers var result = await dyCookieService.Add(dyUserCookies); if (result) { - await ReStartJob(); - return Ok(new { code = 0 }); + ReStartJob(); + return ApiResult.Success(); } - return BadRequest(new { code = -1, message = "添加失败" }); + return ApiResult.Fail("添加失败"); } /// @@ -162,20 +155,20 @@ namespace dy.net.Controllers var result = await dyCookieService.Add(dyUserCookies); if (result) { - await ReStartJob(); - return Ok(new { code = 0 }); + ReStartJob(); + return ApiResult.Success(); } - return BadRequest(new { code = -1, message = "添加失败" }); + return ApiResult.Fail("添加失败"); } else { var result = await dyCookieService.UpdateAsync(dyUserCookies); if (result) { - await ReStartJob(); - return Ok(new { code = 0 }); + ReStartJob(); + return ApiResult.Success(); } - return BadRequest(new { code = -1, message = "更新失败" }); + return ApiResult.Fail("更新失败"); } } @@ -189,16 +182,16 @@ namespace dy.net.Controllers var count = await dyCookieService.DeleteByIdsAsync(new List { id }); if (count > 0) { - await ReStartJob(); + ReStartJob(); } - return Ok(new { code = 0, deletedCount = count }); + return ApiResult.Success(count); } [HttpGet("GetConfig")] public IActionResult GetConfig() { var data = commonService.GetConfig(); - return Ok(new { code = 0, data = data }); + return ApiResult.Success(data); } [HttpPost("UpdateConfig")] @@ -207,9 +200,9 @@ namespace dy.net.Controllers var data = await commonService.UpdateConfig(config); if (data) { - await ReStartJob(); + ReStartJob(); } - return Ok(new { code = 0, data = data }); + return ApiResult.Success(data); } /// @@ -223,11 +216,11 @@ namespace dy.net.Controllers var config = commonService.GetConfig(); if (config != null) await quartzJobService.InitOrReStartAllJobs(config.Cron.ToString()); - return Ok(new { code = 0, error = "" }); + return ApiResult.Success(); } - private async Task ReStartJob() + private void ReStartJob() { var config = commonService.GetConfig(); if (config != null) @@ -241,7 +234,7 @@ namespace dy.net.Controllers [HttpGet("mytag")] public async Task GetMyTag() { - return Ok(new { code = 0, data = Appsettings.Get("tagName") }); + return ApiResult.Success(Appsettings.Get("tagName")); } [HttpGet("checktag")] @@ -255,7 +248,7 @@ namespace dy.net.Controllers } else { - return BadRequest(new { code = -1, message = "请求失败" }); + return ApiResult.Fail("请求失败"); } } } diff --git a/Controllers/FollowController.cs b/Controllers/FollowController.cs index 44c02ca..6c6a8b9 100644 --- a/Controllers/FollowController.cs +++ b/Controllers/FollowController.cs @@ -34,25 +34,17 @@ namespace dy.net.Controllers { if (string.IsNullOrWhiteSpace(dto.MySelfId)) { - return Ok(new - { - code = 0, - data = new { } - }); + return ApiResult.Success(new { }); } var (list, totalCount) = await _douyinFollowService.GetPagedAsync(dto); - return Ok(new + + return ApiResult.Success(new { - code = 0, - data = new - { - data = list, - total = totalCount, - pageIndex = dto.PageIndex, - pageSize = dto.PageSize - } - } - ); + data = list, + total = totalCount, + pageIndex = dto.PageIndex, + pageSize = dto.PageSize + }); } /// /// 重新同步-单次 @@ -64,13 +56,13 @@ namespace dy.net.Controllers //后台异步 _douyinQuartzJobService.StartFollowJobOnceAsync(); await Task.Delay(1000); - return Ok(new { code = 0 }); + return ApiResult.Success(); } [HttpPost("add")] public async Task AddFollow(DouyinFollowed followed) { var res = await _douyinFollowService.AddAsync(followed); - return Ok(new { code = res ? 0 : -1, msg = res ? "" : "添加失败,或者已存在相同secuid和uid" }); + return ApiResult.SuccOrFail(res,"", res ? "" : "添加失败,或者已存在相同secuid和uid"); } /// @@ -88,29 +80,17 @@ namespace dy.net.Controllers { if (!DouyinFileNameHelper.IsValidWithoutSpecialChars(dto.SavePath)) { - return Ok(new - { - code = -1, - msg = "请输入有效文件夹名称(字母数字中文简体)" - }); + return ApiResult.Fail("请输入有效文件夹名称(字母数字中文简体)"); } if (dto.SavePath.Length > 15) { - return Ok(new - { - code = -1, - msg = "请输入有效文件夹名称(最长15)" - }); + return ApiResult.Fail("请输入有效文件夹名称(最长15)"); } } } var result= await _douyinFollowService.OpenOrCloseSync(dto); - return Ok(new - { - code = result ? 0 : -1, - data = result - }); + return ApiResult.SuccOrFail(result, result); } /// @@ -129,7 +109,7 @@ namespace dy.net.Controllers public async Task DeleteFollow(FollowUpdateDto dto) { var result= await _douyinFollowService.DeleteFollow(dto); - return Ok(new { code = result ? 0 : -1, data = result }); + return ApiResult.SuccOrFail(result, result); } } } diff --git a/Controllers/LogsController.cs b/Controllers/LogsController.cs index 2ad199a..e614d46 100644 --- a/Controllers/LogsController.cs +++ b/Controllers/LogsController.cs @@ -52,11 +52,11 @@ namespace dy.net.Controllers { var _logDirectory = Path.Combine(Directory.GetCurrentDirectory(), "logs"); var files = logInfoService.GetLogFiles(_logDirectory); - return Ok(new {code=0,data=files}); + return ApiResult.Success(files); } catch (Exception ex) { - return StatusCode(500, new { message = "获取日志列表失败", error = ex.Message }); + return ApiResult.Fail("获取日志列表失败," + ex.Message); } } diff --git a/Controllers/VideoController.cs b/Controllers/VideoController.cs index f680454..a5e8104 100644 --- a/Controllers/VideoController.cs +++ b/Controllers/VideoController.cs @@ -28,16 +28,12 @@ namespace dy.net.Controllers public async Task GetPagedAsync(DouyinVideoPageRequestDto dto) { var (list, totalCount) = await douyinVideoService.GetPagedAsync(dto); - return Ok(new + return ApiResult.Success(new { - code = 0, - data = new - { - data = list, - total = totalCount, - pageIndex = dto.PageIndex, - pageSize = dto.PageSize - } + data = list, + total = totalCount, + pageIndex = dto.PageIndex, + pageSize = dto.PageSize }); } @@ -50,11 +46,7 @@ namespace dy.net.Controllers public async Task GetStaticsAsync() { var data = await douyinVideoService.GetStatics(); - return Ok(new - { - code = 0, - data - }); + return ApiResult.Success(data); } /// @@ -72,13 +64,13 @@ namespace dy.net.Controllers if (viedo == null) { - return NotFound($"视频不存在:{vid}"); + return ApiResult.Fail($"视频不存在:{vid}"); } return PlayViedo(viedo); } catch (Exception ex) { - return StatusCode(500, $"视频加载失败:{ex.Message}"); + return ApiResult.Fail($"视频加载失败:{ex.Message}"); } } @@ -91,7 +83,7 @@ namespace dy.net.Controllers // 2. 验证文件是否存在 if (!System.IO.File.Exists(videoFullPath)) { - return NotFound($"视频文件不存在:{videoFullPath}"); + return ApiResult.Fail($"视频文件不存在:{videoFullPath}"); } // 3. 获取文件信息(大小、类型) @@ -141,20 +133,20 @@ namespace dy.net.Controllers if (viedo == null) { - return NotFound($"视频不存在:{vid}"); + return ApiResult.Fail($"视频不存在:{vid}"); } var expectedKey = (viedo.FileHash + viedo.AuthorId).Md5(); if (expectedKey != k) { - return NotFound($"视频地址无效"); + return ApiResult.Fail($"视频地址无效"); } return PlayViedo(viedo); } catch (Exception ex) { - return StatusCode(500, $"视频加载失败:{ex.Message}"); + return ApiResult.Fail($"视频加载失败:{ex.Message}"); } } @@ -187,18 +179,18 @@ namespace dy.net.Controllers { if (dto == null) { - return Ok(new { code = -1, data = false }); + return ApiResult.Fail("参数错误"); } else { var result = await douyinVideoService.ReDownloadViedoAsync(dto); if (result) { - return Ok(new { code = 0, data = true }); + return ApiResult.Success(true); } else { - return Ok(new { code = -1, data = false }); + return ApiResult.Fail("错误"); } } } @@ -212,14 +204,14 @@ namespace dy.net.Controllers { if (string.IsNullOrWhiteSpace(vid)) { - return Ok(new { code = -1, data = false }); + return ApiResult.Fail("参数错误"); } else { var video = await douyinVideoService.GetById(vid); if (video == null) { - return Ok(new { code = -1, data = false }); + return ApiResult.Fail("请求失败"); } else { @@ -233,13 +225,13 @@ namespace dy.net.Controllers VideoTitle = video.VideoTitle, VideoSavePath = video.VideoSavePath }); - Serilog.Log.Debug($"前面的日志,你错了,这条视频是永久删除..哈哈--{video.VideoTitle}"); - return Ok(new { code = 0, data = true }); + Serilog.Log.Debug($"前面的日志,你错了,这条视频是永久删除..哈哈--{video.VideoTitle}"); + return ApiResult.Success(); } else { - return Ok(new { code = -1, data = false }); + return ApiResult.Fail(); } } @@ -254,7 +246,7 @@ namespace dy.net.Controllers [HttpGet("vdelete/get")] public async Task GetDeleteVideo() { - return Ok(new { code = 0, data = await douyinCommonService.GetDouyinDeleteVideos() }); + return ApiResult.Success(await douyinCommonService.GetDouyinDeleteVideos()); } /// @@ -263,11 +255,9 @@ namespace dy.net.Controllers /// /// [HttpGet("top{top}")] - public async Task GetLast([FromRoute]int top = 5) + public async Task GetLastSyncTop([FromRoute]int top = 5) { - var data =await douyinVideoService.GetLastTop(top); - - return Ok(new { code = 0, data = data }); + return ApiResult.Success(await douyinVideoService.GetLastSyncTop(top)); } } } diff --git a/Program.cs b/Program.cs index d036a9d..e83b1ca 100644 --- a/Program.cs +++ b/Program.cs @@ -1,73 +1,67 @@ -using dy.net.extension; +using dy.net.extension; using dy.net.service; using dy.net.utils; using Serilog; +using System.Drawing; using System.Text; namespace dy.net { public class Program { - // + // 常量定义 private static string DefaultListenUrl = "http://*:10101"; private const string SpaRootPath = "app/dist"; private const string SpaSourcePath = "app/"; private const string SwaggerDocTitle = "dy.net WebApi Docs"; /// - /// ʱע⣬false,ǰ˲޸ĿͼƬƵѡ + /// 打包时注意,如果是false,前端不允许修改开启下载图片和视频的选项 /// public static void Main(string[] args) { - //Console.ForegroundColor = ConsoleColor.Yellow; - // ʼṩ + + // 初始化编码提供器 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - // WebӦ + // 构建Web应用 var builder = WebApplication.CreateBuilder(args); - //from docker yaml file dockerfile appsettings.json + //from docker yaml file 环境变量 或者 dockerfile 或appsettings.json DefaultListenUrl = builder.Configuration.GetValue(SystemStaticUtil.ASPNETCORE_URLS) ?? DefaultListenUrl; var isDevelopment = builder.Environment.IsDevelopment(); - // + // 配置主机 ConfigureHost(builder, isDevelopment); - // ÷ + // 配置服务 ConfigureServices(builder.Services, builder.Configuration, builder.Environment); - // Ӧ + // 构建应用 var app = builder.Build(); Log.Debug("ffmpeg is on"); - // м + // 配置中间件 ConfigureMiddleware(app, builder.Environment); - // ʼӦ÷ + // 初始化应用服务 InitApplicationServices(app, isDevelopment); Serilog.Log.Debug("dy.sync service is starting..."); Log.Debug("dy.sync service is started successfully"); - Console.WriteLine("------------------------------------------------------------------------"); - Console.WriteLine(@" __ \\ \ / ___|\ \ / \ | ___| - | |\ / \___ \ \ / \ | | - | | | | | |\ | | -____/ _|_)_____/ _| _| \_|\____| - -"); - //Console.ResetColor(); + Console.WriteLine(); app.Run(); } /// - /// + /// 配置主机设置 /// private static void ConfigureHost(WebApplicationBuilder builder, bool isDevelopment) { - // üַ + // 设置监听地址 builder.WebHost.UseUrls(DefaultListenUrl); - // ļ + // 配置配置文件 builder.Host.ConfigureAppConfiguration((context, config) => { config.SetBasePath(Directory.GetCurrentDirectory()) @@ -75,7 +69,7 @@ ____/ _|_)_____/ _| _| \_|\____| .AddEnvironmentVariables(); }); - // ־ + // 配置日志 builder.Host.ConfigureLogging(logging => logging.ClearProviders()) .UseSerilog(); @@ -83,86 +77,83 @@ ____/ _|_)_____/ _| _| \_|\____| } /// - /// ע + /// 配置依赖注入服务 /// private static void ConfigureServices(IServiceCollection services, IConfiguration config, IWebHostEnvironment environment) { - + PrintApp(); services.AddSingleton(new Appsettings (config)); - // ѩID + // 雪花ID生成器 services.AddSnowFlakeId(options => options.WorkId = new Random().Next(1, 100)); - // MVC - services.AddControllers(); + - // HTTPͻ + + // MVC控制器+异常拦截器 + services.AddControllers().AddGlobalExceptionFilter(); + + // HTTP客户端 services.AddHttpClients(); - // ݿ + // 数据库 services.AddSqlsugar(config); - // ʱ + // 定时任务 services.AddQuartzService(); - // ִͷע + // 仓储和服务注册 services.AddServicesFromNamespace("dy.net.repository") .AddServicesFromNamespace("dy.net.service"); services.AddSingleton(); - ////ͼƬϳƵ-Ҫffmpeg֧,ܴ - //if (downImageVideo) - //{ - // //ö̬dy.image - // Assembly assembly = Assembly.LoadFrom(Path.Combine(AppContext.BaseDirectory, "dy.image.dll")); - // services.AddServicesFromNamespace("dy.image", assembly); - //} - // SPA̬ļ֧ + + // SPA静态文件支持 services.AddSpaStaticFiles(options => options.RootPath = SpaRootPath); - // Swagger + // 开发环境启用Swagger if (environment.IsDevelopment()) { services.AddSwagger(); } - // Ӧѹ + // 响应压缩 services.AddResponseCompression(); - // JWT֤ + // JWT认证 services.ConfigureJwtAuthentication(); } /// - /// м + /// 配置中间件 /// private static void ConfigureMiddleware(WebApplication app, IWebHostEnvironment environment) { - // Ӧѹ + // 响应压缩 app.UseResponseCompression(); - // SwaggerUI + // 开发环境启用SwaggerUI if (environment.IsDevelopment()) { app.UseCustomSwaggerUI(options => options.Title = SwaggerDocTitle); } - // · + // 路由 app.UseRouting(); - // ֤Ȩ + // 认证授权 app.UseAuthentication(); app.UseAuthorization(); - // ļϴ· + // 配置文件上传路径 //ConfigureUploadPath(app, isDevelopment); - // API·ӳ + // API路由映射 app.MapControllers(); app.UseStaticFiles(); - // SPA + // 生产环境启用SPA if (!environment.IsDevelopment()) { app.UseSpaStaticFiles(); @@ -171,7 +162,7 @@ ____/ _|_)_____/ _| _| \_|\____| } /// - /// ʼӦ÷ + /// 初始化应用服务数据 /// private static void InitApplicationServices(WebApplication app,bool isDevelopment) { @@ -180,26 +171,26 @@ ____/ _|_)_____/ _| _| \_|\____| try { - // ʼû + // 初始化用户 var userService = services.GetRequiredService(); userService.InitUser(); - // ʼCookie + // 初始化Cookie var cookieService = services.GetRequiredService(); cookieService.InitCookie(); - // ʼ + // 初始化配置 var commonService = services.GetRequiredService(); var config = commonService.InitConfig(); - // Ƶ--ϰ汾 + // 更新视频类型--兼容老版本 commonService.UpdateCollectViedoType(); - // òƷͬ״̬Ϊδͬ + // 重置博主作品同步状态为未同步 commonService.UpdateAllCookieSyncedToZero(); if (!isDevelopment) { - // ʱ + // 启动定时任务 var quartzJobService = services.GetRequiredService(); quartzJobService.InitOrReStartAllJobs(config?.Cron <= 0 ? "30" : config.Cron.ToString()); } @@ -212,7 +203,78 @@ ____/ _|_)_____/ _| _| \_|\____| } + private static void PrintApp() + { + Console.ForegroundColor = ConsoleColor.DarkCyan; + Console.WriteLine(); + + // 步骤1:定义原始ASCII艺术字行(无缩进) + List originalArtLines = new List + { + " __ __ ", + " ___/ /_ __ ___ __ _____ ____ ___ ___ / /_", + "/ _ / // / (_- indentedArtLines = new List(); + foreach (var line in originalArtLines) + { + indentedArtLines.Add(indent + line); // 每行开头拼接缩进空格 + } + + // 步骤4:找到缩进后最长行的长度(避免越界) + int maxLength = 0; + foreach (var line in indentedArtLines) + { + if (line.Length > maxLength) maxLength = line.Length; + } + + // 步骤5:按列推进打印(从左到右,包含缩进) + for (int col = 0; col < maxLength; col++) + { + Console.SetCursorPosition(0, 0); // 重置光标到第一行开头 + + for (int row = 0; row < indentedArtLines.Count; row++) + { + // 定位到「当前列、当前行」(已包含缩进偏移) + Console.SetCursorPosition(col, row); + + // 打印字符(无字符则补空格) + if (col < indentedArtLines[row].Length) + { + Console.Write(indentedArtLines[row][col]); + } + else + { + Console.Write(' '); + } + } + + Thread.Sleep(20); // 列推进速度(可调整:10=快,50=慢) + } + + // 打印完成后,光标移到艺术字下方 + Console.SetCursorPosition(0, indentedArtLines.Count); + + //string asciiArt = @"================================================================================================================="; + //string asciiArt = $@"——————————————————————{DateTime.Now:yyyy-MM-dd HH:mm:ss}——————————————————————"; + + //foreach (char ch in asciiArt) + //{ + // Console.Write(ch); + // Thread.Sleep(2); + //} + Console.WriteLine(); + Console.ResetColor(); + + } - } } \ No newline at end of file diff --git a/Properties/PublishProfiles/FolderProfile.pubxml.user b/Properties/PublishProfiles/FolderProfile.pubxml.user index 618c482..06e69f6 100644 --- a/Properties/PublishProfiles/FolderProfile.pubxml.user +++ b/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. <_PublishTargetUrl>E:\code\dysync\bin\Release\net6.0\publish\ - True|2025-12-19T01:50:16.9517676Z||;True|2025-12-19T09:49:47.0871734+08:00||;False|2025-12-19T09:49:36.7638757+08:00||;True|2025-12-19T09:35:13.2489248+08:00||;True|2025-12-14T19:09:59.9031104+08:00||;True|2025-12-13T21:21:10.0022707+08:00||;True|2025-12-11T22:07:16.6229994+08:00||;True|2025-12-11T22:05:56.7363206+08:00||;True|2025-12-11T22:01:07.5862406+08:00||;True|2025-12-11T22:00:43.5796594+08:00||;True|2025-12-08T21:20:37.8369271+08:00||;False|2025-12-08T21:20:21.1646252+08:00||;True|2025-12-08T20:51:50.6845619+08:00||;True|2025-12-08T16:43:45.2425324+08:00||;False|2025-12-08T16:42:23.0308163+08:00||;True|2025-12-08T16:41:52.4103868+08:00||;True|2025-12-08T16:41:49.8972006+08:00||;False|2025-12-08T16:41:39.5343442+08:00||;True|2025-12-08T12:40:19.2430740+08:00||;True|2025-12-08T12:02:30.6005542+08:00||;True|2025-12-08T11:34:29.6673185+08:00||;False|2025-12-08T11:30:56.2857042+08:00||;True|2025-12-08T10:52:43.7427594+08:00||;True|2025-12-08T07:05:03.2845531+08:00||;True|2025-12-07T23:02:10.0037815+08:00||;True|2025-12-07T22:48:25.7859648+08:00||;True|2025-12-07T20:49:13.6683281+08:00||;True|2025-12-07T08:47:30.1236366+08:00||;True|2025-12-06T13:14:32.4410202+08:00||;True|2025-12-06T13:07:29.7182102+08:00||;True|2025-12-06T13:05:46.2855366+08:00||;False|2025-12-06T13:05:40.3550904+08:00||;True|2025-12-06T13:03:47.8773880+08:00||;True|2025-12-06T13:03:28.9521030+08:00||;True|2025-12-05T23:11:51.5026151+08:00||;True|2025-12-05T12:46:26.1415079+08:00||;False|2025-12-05T12:46:19.1274332+08:00||;True|2025-12-05T09:04:36.7111717+08:00||;True|2025-12-05T08:22:51.7343317+08:00||;True|2025-12-05T08:20:32.0383739+08:00||;True|2025-12-04T21:58:29.4112766+08:00||;True|2025-12-04T21:58:10.0382218+08:00||;True|2025-12-04T21:57:55.3894059+08:00||;True|2025-12-04T21:10:38.8943797+08:00||;True|2025-12-04T21:09:31.5024569+08:00||;True|2025-12-04T08:22:47.9617427+08:00||;False|2025-12-04T08:22:41.3759733+08:00||;True|2025-12-04T08:14:02.2478893+08:00||;True|2025-12-04T07:57:00.2208139+08:00||;True|2025-12-03T23:58:13.3693303+08:00||;True|2025-12-03T23:57:40.4257893+08:00||;True|2025-12-03T23:56:38.8717769+08:00||;True|2025-12-03T23:56:24.0663568+08:00||;False|2025-12-03T23:55:36.1902974+08:00||;True|2025-12-03T23:51:30.6688504+08:00||;True|2025-12-03T23:34:17.1648971+08:00||;False|2025-12-03T23:34:07.7649161+08:00||;True|2025-12-03T22:32:56.2435003+08:00||;True|2025-12-03T22:27:45.6059666+08:00||;True|2025-12-03T15:55:28.0186597+08:00||;False|2025-12-03T15:54:17.5032724+08:00||;True|2025-12-03T15:53:10.2273509+08:00||;True|2025-12-02T22:11:37.4645042+08:00||;False|2025-12-02T22:10:47.1320259+08:00||;True|2025-12-02T14:39:58.8932130+08:00||;True|2025-12-02T14:36:37.1529072+08:00||;True|2025-12-02T12:58:22.0951548+08:00||;True|2025-12-02T09:30:33.7066474+08:00||;True|2025-12-02T09:30:14.5481844+08:00||;True|2025-12-02T09:30:00.3123364+08:00||;False|2025-12-02T09:29:54.4615520+08:00||;True|2025-12-02T09:13:01.7995414+08:00||;False|2025-12-02T09:12:55.8360281+08:00||;True|2025-12-02T09:12:31.0156791+08:00||;True|2025-12-02T08:55:11.3773395+08:00||;True|2025-12-02T08:53:21.3927713+08:00||;False|2025-12-02T08:48:55.8638037+08:00||;True|2025-12-02T07:29:25.2192447+08:00||;False|2025-12-02T07:29:10.2504665+08:00||;True|2025-12-02T07:28:29.0769286+08:00||;True|2025-12-01T21:53:07.6474834+08:00||;True|2025-12-01T21:44:28.1674315+08:00||;True|2025-12-01T21:18:47.2119609+08:00||;True|2025-12-01T20:51:19.9948301+08:00||;True|2025-12-01T20:50:36.9904697+08:00||;True|2025-12-01T20:44:10.1695142+08:00||;True|2025-12-01T19:27:58.0479456+08:00||;True|2025-12-01T19:16:02.2288214+08:00||;False|2025-12-01T19:15:56.0372115+08:00||;True|2025-12-01T19:15:16.4854821+08:00||;False|2025-12-01T19:15:06.7001019+08:00||;True|2025-12-01T17:10:38.9739466+08:00||;False|2025-12-01T17:10:18.8928139+08:00||;True|2025-12-01T17:03:59.3171589+08:00||;True|2025-12-01T17:03:12.5681656+08:00||;True|2025-12-01T16:42:51.4415025+08:00||;True|2025-12-01T01:29:59.6975567+08:00||;True|2025-12-01T01:29:45.1898652+08:00||;False|2025-12-01T01:29:33.1787721+08:00||;True|2025-12-01T01:27:33.1297605+08:00||; + True|2025-12-20T04:16:22.5224516Z||;True|2025-12-19T09:50:16.9517676+08:00||;True|2025-12-19T09:49:47.0871734+08:00||;False|2025-12-19T09:49:36.7638757+08:00||;True|2025-12-19T09:35:13.2489248+08:00||;True|2025-12-14T19:09:59.9031104+08:00||;True|2025-12-13T21:21:10.0022707+08:00||;True|2025-12-11T22:07:16.6229994+08:00||;True|2025-12-11T22:05:56.7363206+08:00||;True|2025-12-11T22:01:07.5862406+08:00||;True|2025-12-11T22:00:43.5796594+08:00||;True|2025-12-08T21:20:37.8369271+08:00||;False|2025-12-08T21:20:21.1646252+08:00||;True|2025-12-08T20:51:50.6845619+08:00||;True|2025-12-08T16:43:45.2425324+08:00||;False|2025-12-08T16:42:23.0308163+08:00||;True|2025-12-08T16:41:52.4103868+08:00||;True|2025-12-08T16:41:49.8972006+08:00||;False|2025-12-08T16:41:39.5343442+08:00||;True|2025-12-08T12:40:19.2430740+08:00||;True|2025-12-08T12:02:30.6005542+08:00||;True|2025-12-08T11:34:29.6673185+08:00||;False|2025-12-08T11:30:56.2857042+08:00||;True|2025-12-08T10:52:43.7427594+08:00||;True|2025-12-08T07:05:03.2845531+08:00||;True|2025-12-07T23:02:10.0037815+08:00||;True|2025-12-07T22:48:25.7859648+08:00||;True|2025-12-07T20:49:13.6683281+08:00||;True|2025-12-07T08:47:30.1236366+08:00||;True|2025-12-06T13:14:32.4410202+08:00||;True|2025-12-06T13:07:29.7182102+08:00||;True|2025-12-06T13:05:46.2855366+08:00||;False|2025-12-06T13:05:40.3550904+08:00||;True|2025-12-06T13:03:47.8773880+08:00||;True|2025-12-06T13:03:28.9521030+08:00||;True|2025-12-05T23:11:51.5026151+08:00||;True|2025-12-05T12:46:26.1415079+08:00||;False|2025-12-05T12:46:19.1274332+08:00||;True|2025-12-05T09:04:36.7111717+08:00||;True|2025-12-05T08:22:51.7343317+08:00||;True|2025-12-05T08:20:32.0383739+08:00||;True|2025-12-04T21:58:29.4112766+08:00||;True|2025-12-04T21:58:10.0382218+08:00||;True|2025-12-04T21:57:55.3894059+08:00||;True|2025-12-04T21:10:38.8943797+08:00||;True|2025-12-04T21:09:31.5024569+08:00||;True|2025-12-04T08:22:47.9617427+08:00||;False|2025-12-04T08:22:41.3759733+08:00||;True|2025-12-04T08:14:02.2478893+08:00||;True|2025-12-04T07:57:00.2208139+08:00||;True|2025-12-03T23:58:13.3693303+08:00||;True|2025-12-03T23:57:40.4257893+08:00||;True|2025-12-03T23:56:38.8717769+08:00||;True|2025-12-03T23:56:24.0663568+08:00||;False|2025-12-03T23:55:36.1902974+08:00||;True|2025-12-03T23:51:30.6688504+08:00||;True|2025-12-03T23:34:17.1648971+08:00||;False|2025-12-03T23:34:07.7649161+08:00||;True|2025-12-03T22:32:56.2435003+08:00||;True|2025-12-03T22:27:45.6059666+08:00||;True|2025-12-03T15:55:28.0186597+08:00||;False|2025-12-03T15:54:17.5032724+08:00||;True|2025-12-03T15:53:10.2273509+08:00||;True|2025-12-02T22:11:37.4645042+08:00||;False|2025-12-02T22:10:47.1320259+08:00||;True|2025-12-02T14:39:58.8932130+08:00||;True|2025-12-02T14:36:37.1529072+08:00||;True|2025-12-02T12:58:22.0951548+08:00||;True|2025-12-02T09:30:33.7066474+08:00||;True|2025-12-02T09:30:14.5481844+08:00||;True|2025-12-02T09:30:00.3123364+08:00||;False|2025-12-02T09:29:54.4615520+08:00||;True|2025-12-02T09:13:01.7995414+08:00||;False|2025-12-02T09:12:55.8360281+08:00||;True|2025-12-02T09:12:31.0156791+08:00||;True|2025-12-02T08:55:11.3773395+08:00||;True|2025-12-02T08:53:21.3927713+08:00||;False|2025-12-02T08:48:55.8638037+08:00||;True|2025-12-02T07:29:25.2192447+08:00||;False|2025-12-02T07:29:10.2504665+08:00||;True|2025-12-02T07:28:29.0769286+08:00||;True|2025-12-01T21:53:07.6474834+08:00||;True|2025-12-01T21:44:28.1674315+08:00||;True|2025-12-01T21:18:47.2119609+08:00||;True|2025-12-01T20:51:19.9948301+08:00||;True|2025-12-01T20:50:36.9904697+08:00||;True|2025-12-01T20:44:10.1695142+08:00||;True|2025-12-01T19:27:58.0479456+08:00||;True|2025-12-01T19:16:02.2288214+08:00||;False|2025-12-01T19:15:56.0372115+08:00||;True|2025-12-01T19:15:16.4854821+08:00||;False|2025-12-01T19:15:06.7001019+08:00||;True|2025-12-01T17:10:38.9739466+08:00||;False|2025-12-01T17:10:18.8928139+08:00||;True|2025-12-01T17:03:59.3171589+08:00||;True|2025-12-01T17:03:12.5681656+08:00||;True|2025-12-01T16:42:51.4415025+08:00||;True|2025-12-01T01:29:59.6975567+08:00||;True|2025-12-01T01:29:45.1898652+08:00||;False|2025-12-01T01:29:33.1787721+08:00||; diff --git a/README.md b/README.md index d3dbf16..0766386 100644 --- a/README.md +++ b/README.md @@ -96,8 +96,8 @@ Cookie 及 `sec_user_id` 是同步功能的核心,需严格按步骤获取, | 镜像标签 | 架构 | | ----------------- | -------------- | -| `beta_1.8.9` | x86_64 (amd64) | -| `arm_1.8.9` | ARM64 | +| `beta_1.9.0` | x86_64 (amd64) | +| `arm_1.9.0` | ARM64 | ### 最新镜像查看 [镜像列表](http://nas.synology2023.online:10108/api/docker/dysync/1) @@ -115,7 +115,7 @@ docker run -d --restart=always \ -v /opt/dysync/uper:/app/uper \ -p 10103:10101 \ --name dysync2025 \ - ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_1.8.9 + ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_1.9.0 # 注意:-p 后面的容器端口,可以用环境变量类似:ASPNETCORE_URLS = http://+:10108 指定 ``` @@ -129,7 +129,7 @@ version: '3.8' services: dysync: - image: ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_1.8.9 + image: ccr.ccs.tencentyun.com/jianzhichu/dysync:beta_1.9.0 container_name: dysync2025 # 容器名称 restart: unless-stopped # 始终重启容器,除非容器被手动停止或Docker服务停止 ports: diff --git a/appsettings.json b/appsettings.json index c21115f..bdd1dff 100644 --- a/appsettings.json +++ b/appsettings.json @@ -1,7 +1,7 @@ { "dbconn": "", - //"tagName": "arm_1.8.9", - //"tagName": "dev_1.8.9", - "tagName": "beta_1.8.9", + //"tagName": "arm_1.9.0", + //"tagName": "dev_1.9.0", + "tagName": "beta_1.9.0", "dbtype": "Sqlite" } \ No newline at end of file diff --git a/dto/DouyinResponse.cs b/dto/DouyinResponse.cs new file mode 100644 index 0000000..d70213a --- /dev/null +++ b/dto/DouyinResponse.cs @@ -0,0 +1,168 @@ +using Microsoft.AspNetCore.Mvc; +using System.Text.Json.Serialization; + +namespace dy.net.dto +{ + public class DouyinApiResponse + { + /// + /// 响应状态码(自定义业务码,非HTTP状态码) + /// + [JsonPropertyName("code")] + public int Code { get; set; } + + /// + /// 响应数据 + /// + [JsonPropertyName("data")] + public T Data { get; set; } + + /// + /// 响应消息(成功/失败描述) + /// + [JsonPropertyName("msg")] + public string Msg { get; set; } + [JsonPropertyName("message")] + public string Message { get; set; } + [JsonPropertyName("error")] + public string Error { get; set; } + + /// + /// 响应时间戳(UTC时间) + /// + [JsonPropertyName("timestamp")] + public DateTime Timestamp { get; set; } = DateTime.UtcNow; + } + + + /// + /// 响应状态码常量 + /// + public static class ResponseCode + { + public const int Success = 0; // 操作成功 + public const int ServerError = -1; // 服务器错误 + } + + + /// + /// 直接返回 IActionResult 的API响应工具类 + /// 无需控制器手动拼接 Ok()/BadRequest(),一步返回标准化响应 + /// + public static class ApiResult + { + #region 成功响应 + /// + /// 成功响应(带数据) + /// + /// 数据类型 + /// 响应数据 + /// 成功消息(默认:操作成功) + /// IActionResult(HTTP 200) + public static IActionResult Success(T data, string message = "操作成功") + { + var response = new DouyinApiResponse + { + Code = ResponseCode.Success, + Data = data, + Message = message, + Error = message, + Msg = message, + }; + return new OkObjectResult(response); // HTTP 200 + } + + /// + /// + /// + /// + /// + /// + /// + /// + public static IActionResult SuccOrFail(int code,T t,string message = "") + { + if (code == ResponseCode.Success) + { + return Success(t, message); + } + else + { + return Fail(message); + } + } + + /// + /// + /// + /// + /// + /// + /// + /// + public static IActionResult SuccOrFail(bool result, T t, string message = "") + { + if (result) + { + return Success(t, message); + } + else + { + return Fail(message); + } + } + /// + /// 成功响应(无数据) + /// + /// 成功消息(默认:操作成功) + /// IActionResult(HTTP 200) + public static IActionResult Success(string message = "操作成功") + { + return Success(null, message); + } + #endregion + + #region 失败响应 + /// + /// 失败响应 + /// + /// 数据类型 + /// 错误消息 + /// 附加数据 + /// IActionResult + public static IActionResult Fail(string message="请求失败", int businessCode = ResponseCode.ServerError, T data = default) + { + var response = new DouyinApiResponse + { + Code = businessCode, + Data = data, + Message = message, + Error = message, + Msg = message, + }; + return new OkObjectResult(response); + } + + /// + /// 失败响应(无附加数据) + /// + /// 错误消息 + /// 自定义业务码 + /// IActionResult + public static IActionResult Fail(string message, int businessCode = ResponseCode.ServerError) + { + return Fail(message, businessCode); + } + /// + /// 失败响应(无附加数据) + /// + /// 错误消息 + /// IActionResult + public static IActionResult Fail(string message="请求失败") + { + return Fail(message, ResponseCode.ServerError); + } + #endregion + } + +} diff --git a/extension/DouyinExceptionFilter.cs b/extension/DouyinExceptionFilter.cs new file mode 100644 index 0000000..5741c22 --- /dev/null +++ b/extension/DouyinExceptionFilter.cs @@ -0,0 +1,66 @@ +using dy.net.dto; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using System.Net; + +namespace dy.net.extension +{ + public class DouyinExceptionFilter : IExceptionFilter + { + + + /// + /// 异常拦截核心方法 + /// + public void OnException(ExceptionContext context) + { + // 1. 记录异常日志(区分业务异常和系统异常) + LogException(context); + + // 2. 构建标准化响应结果 + var response = BuildExceptionResponse(context.Exception); + + // 3. 设置响应结果(终止异常传播) + context.Result = new ObjectResult(response); + context.ExceptionHandled = true; + } + + /// + /// 记录异常日志 + /// + private void LogException(ExceptionContext ex) + { + Serilog.Log.Error($"系统异常:{ex.Exception.Message},{ex.HttpContext.Request.Path}"); + } + + /// + /// 根据异常类型构建响应结果 + /// + private static DouyinApiResponse BuildExceptionResponse(Exception ex) + { + return new DouyinApiResponse + { + Code = ResponseCode.ServerError, + Message = "服务器内部错误,请稍后重试", + Data = null, + Timestamp = DateTime.UtcNow + }; + } + } + + + /// + /// 全局异常过滤器扩展(简化注册) + /// + public static class GlobalExceptionFilterExtensions + { + public static IMvcBuilder AddGlobalExceptionFilter(this IMvcBuilder mvcBuilder) + { + mvcBuilder.AddMvcOptions(options => + { + options.Filters.Add(); + }); + return mvcBuilder; + } + } +} diff --git a/repository/DouyinVideoRepository.cs b/repository/DouyinVideoRepository.cs index 0d20418..6bc96bb 100644 --- a/repository/DouyinVideoRepository.cs +++ b/repository/DouyinVideoRepository.cs @@ -17,9 +17,9 @@ namespace dy.net.repository - public async Task> GetTopsOrderByCreateTime(int top) + public async Task> GetTopsOrderBySyncTime(int top) { - return await Db.Queryable().Select(x=>new DouyinVideoTopDto { Title=x.VideoTitle,Time=x.CreateTime.ToString("yyyy-MM-dd HH:mm:ss")}).Take(top).OrderByDescending(x=>x.Time).ToListAsync(); + return await Db.Queryable().Select(x=>new DouyinVideoTopDto { Title=x.VideoTitle,Time=x.SyncTime.ToString("yyyy-MM-dd HH:mm:ss")}).Take(top).OrderByDescending(x=>x.Time).ToListAsync(); } /// /// diff --git a/service/DouyinVideoService.cs b/service/DouyinVideoService.cs index 96df51a..defeeaf 100644 --- a/service/DouyinVideoService.cs +++ b/service/DouyinVideoService.cs @@ -313,9 +313,9 @@ namespace dy.net.service - public async Task> GetLastTop(int top=5) + public async Task> GetLastSyncTop(int top=5) { - return await _dyCollectVideoRepository.GetTopsOrderByCreateTime(top); + return await _dyCollectVideoRepository.GetTopsOrderBySyncTime(top); } }