This commit is contained in:
jianzhichu
2025-12-20 12:19:49 +08:00
parent 0549688e6f
commit 7666ab109d
13 changed files with 462 additions and 203 deletions
+10 -10
View File
@@ -35,7 +35,7 @@ namespace dy.net.Controllers
public async Task<IActionResult> 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<IActionResult> 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 });
}
/// <summary>
@@ -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("用户名或密码不正确");
}
}
}
+45 -52
View File
@@ -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
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("importConf")]
public async Task<IActionResult> ImportConf(AppConfigImportDto dto)
public async Task<IActionResult> 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();
}
/// <summary>
/// 分页查询
@@ -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<IActionResult> GetAllList()
{
var follows = await douyinFollowService.GetGroupByCookieAsync();
return Ok(new { code = 0, data = follows });
return ApiResult.Success(follows);
}
/// <summary>
/// 新增用户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("添加失败");
}
/// <summary>
@@ -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<string> { 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);
}
/// <summary>
@@ -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<IActionResult> 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("请求失败");
}
}
}
+14 -34
View File
@@ -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
});
}
/// <summary>
/// 重新同步-单次
@@ -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<IActionResult> 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");
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -129,7 +109,7 @@ namespace dy.net.Controllers
public async Task<IActionResult> DeleteFollow(FollowUpdateDto dto)
{
var result= await _douyinFollowService.DeleteFollow(dto);
return Ok(new { code = result ? 0 : -1, data = result });
return ApiResult.SuccOrFail(result, result);
}
}
}
+2 -2
View File
@@ -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);
}
}
+23 -33
View File
@@ -28,16 +28,12 @@ namespace dy.net.Controllers
public async Task<IActionResult> 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<IActionResult> GetStaticsAsync()
{
var data = await douyinVideoService.GetStatics();
return Ok(new
{
code = 0,
data
});
return ApiResult.Success(data);
}
/// <summary>
@@ -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<IActionResult> GetDeleteVideo()
{
return Ok(new { code = 0, data = await douyinCommonService.GetDouyinDeleteVideos() });
return ApiResult.Success(await douyinCommonService.GetDouyinDeleteVideos());
}
/// <summary>
@@ -263,11 +255,9 @@ namespace dy.net.Controllers
/// <param name="top"></param>
/// <returns></returns>
[HttpGet("top{top}")]
public async Task<IActionResult> GetLast([FromRoute]int top = 5)
public async Task<IActionResult> 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));
}
}
}
+122 -60
View File
@@ -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";
/// <summary>
/// 打包时注意,如果是false,前端不允许修改开启下载图片和视频的选项
/// 打包时注意,如果是false,前端不允许修改开启下载图片和视频的选项
/// </summary>
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<string>(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();
}
/// <summary>
/// 配置主机设置
/// 配置主机设置
/// </summary>
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 @@ ____/ _|_)_____/ _| _| \_|\____|
}
/// <summary>
/// 配置依赖注入服务
/// 配置依赖注入服务
/// </summary>
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<FFmpegHelper>();
////下载图片合成视频-需要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();
}
/// <summary>
/// 配置中间件
/// 配置中间件
/// </summary>
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 @@ ____/ _|_)_____/ _| _| \_|\____|
}
/// <summary>
/// 初始化应用服务数据
/// 初始化应用服务数据
/// </summary>
private static void InitApplicationServices(WebApplication app,bool isDevelopment)
{
@@ -180,26 +171,26 @@ ____/ _|_)_____/ _| _| \_|\____|
try
{
// 初始化用户
// 初始化用户
var userService = services.GetRequiredService<AdminUserService>();
userService.InitUser();
// 初始化Cookie
// 初始化Cookie
var cookieService = services.GetRequiredService<DouyinCookieService>();
cookieService.InitCookie();
// 初始化配置
// 初始化配置
var commonService = services.GetRequiredService<DouyinCommonService>();
var config = commonService.InitConfig();
// 更新视频类型--兼容老版本
// 更新视频类型--兼容老版本
commonService.UpdateCollectViedoType();
// 重置博主作品同步状态为未同步
// 重置博主作品同步状态为未同步
commonService.UpdateAllCookieSyncedToZero();
if (!isDevelopment)
{
// 启动定时任务
// 启动定时任务
var quartzJobService = services.GetRequiredService<DouyinQuartzJobService>();
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<string> originalArtLines = new List<string>
{
" __ __ ",
" ___/ /_ __ ___ __ _____ ____ ___ ___ / /_",
"/ _ / // / (_-</ // / _ \\__/ / _ \\/ -_) __/",
"\\_,_/\\_, (_)___/\\_, /_//_/\\__(_)_//_/\\__/\\__/ ",
" /___/ /___/ "
};
// 步骤2:设置缩进字符数(可自由修改:4、8、10 等)
int indentCount = 4;
string indent = new string(' ', indentCount); // 生成对应数量的空格
// 步骤3:给每行添加缩进
List<string> indentedArtLines = new List<string>();
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();
}
}
}
@@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<Project>
<PropertyGroup>
<_PublishTargetUrl>E:\code\dysync\bin\Release\net6.0\publish\</_PublishTargetUrl>
<History>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||;</History>
<History>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||;</History>
<LastFailureDetails />
</PropertyGroup>
<ItemGroup>
+4 -4
View File
@@ -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:
+3 -3
View File
@@ -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"
}
+168
View File
@@ -0,0 +1,168 @@
using Microsoft.AspNetCore.Mvc;
using System.Text.Json.Serialization;
namespace dy.net.dto
{
public class DouyinApiResponse<T>
{
/// <summary>
/// 响应状态码(自定义业务码,非HTTP状态码)
/// </summary>
[JsonPropertyName("code")]
public int Code { get; set; }
/// <summary>
/// 响应数据
/// </summary>
[JsonPropertyName("data")]
public T Data { get; set; }
/// <summary>
/// 响应消息(成功/失败描述)
/// </summary>
[JsonPropertyName("msg")]
public string Msg { get; set; }
[JsonPropertyName("message")]
public string Message { get; set; }
[JsonPropertyName("error")]
public string Error { get; set; }
/// <summary>
/// 响应时间戳(UTC时间)
/// </summary>
[JsonPropertyName("timestamp")]
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
}
/// <summary>
/// 响应状态码常量
/// </summary>
public static class ResponseCode
{
public const int Success = 0; // 操作成功
public const int ServerError = -1; // 服务器错误
}
/// <summary>
/// 直接返回 IActionResult 的API响应工具类
/// 无需控制器手动拼接 Ok()/BadRequest(),一步返回标准化响应
/// </summary>
public static class ApiResult
{
#region
/// <summary>
/// 成功响应(带数据)
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <param name="data">响应数据</param>
/// <param name="message">成功消息(默认:操作成功)</param>
/// <returns>IActionResultHTTP 200</returns>
public static IActionResult Success<T>(T data, string message = "操作成功")
{
var response = new DouyinApiResponse<T>
{
Code = ResponseCode.Success,
Data = data,
Message = message,
Error = message,
Msg = message,
};
return new OkObjectResult(response); // HTTP 200
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="code"></param>
/// <param name="t"></param>
/// <param name="message"></param>
/// <returns></returns>
public static IActionResult SuccOrFail<T>(int code,T t,string message = "")
{
if (code == ResponseCode.Success)
{
return Success(t, message);
}
else
{
return Fail(message);
}
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="result"></param>
/// <param name="t"></param>
/// <param name="message"></param>
/// <returns></returns>
public static IActionResult SuccOrFail<T>(bool result, T t, string message = "")
{
if (result)
{
return Success(t, message);
}
else
{
return Fail(message);
}
}
/// <summary>
/// 成功响应(无数据)
/// </summary>
/// <param name="message">成功消息(默认:操作成功)</param>
/// <returns>IActionResultHTTP 200</returns>
public static IActionResult Success(string message = "操作成功")
{
return Success<object>(null, message);
}
#endregion
#region
/// <summary>
/// 失败响应
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <param name="message">错误消息</param>
/// <param name="data">附加数据</param>
/// <returns>IActionResult</returns>
public static IActionResult Fail<T>(string message="请求失败", int businessCode = ResponseCode.ServerError, T data = default)
{
var response = new DouyinApiResponse<T>
{
Code = businessCode,
Data = data,
Message = message,
Error = message,
Msg = message,
};
return new OkObjectResult(response);
}
/// <summary>
/// 失败响应(无附加数据)
/// </summary>
/// <param name="message">错误消息</param>
/// <param name="businessCode">自定义业务码</param>
/// <returns>IActionResult</returns>
public static IActionResult Fail(string message, int businessCode = ResponseCode.ServerError)
{
return Fail<object>(message, businessCode);
}
/// <summary>
/// 失败响应(无附加数据)
/// </summary>
/// <param name="message">错误消息</param>
/// <returns>IActionResult</returns>
public static IActionResult Fail(string message="请求失败")
{
return Fail<object>(message, ResponseCode.ServerError);
}
#endregion
}
}
+66
View File
@@ -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
{
/// <summary>
/// 异常拦截核心方法
/// </summary>
public void OnException(ExceptionContext context)
{
// 1. 记录异常日志(区分业务异常和系统异常)
LogException(context);
// 2. 构建标准化响应结果
var response = BuildExceptionResponse(context.Exception);
// 3. 设置响应结果(终止异常传播)
context.Result = new ObjectResult(response);
context.ExceptionHandled = true;
}
/// <summary>
/// 记录异常日志
/// </summary>
private void LogException(ExceptionContext ex)
{
Serilog.Log.Error($"系统异常:{ex.Exception.Message},{ex.HttpContext.Request.Path}");
}
/// <summary>
/// 根据异常类型构建响应结果
/// </summary>
private static DouyinApiResponse<object> BuildExceptionResponse(Exception ex)
{
return new DouyinApiResponse<object>
{
Code = ResponseCode.ServerError,
Message = "服务器内部错误,请稍后重试",
Data = null,
Timestamp = DateTime.UtcNow
};
}
}
/// <summary>
/// 全局异常过滤器扩展(简化注册)
/// </summary>
public static class GlobalExceptionFilterExtensions
{
public static IMvcBuilder AddGlobalExceptionFilter(this IMvcBuilder mvcBuilder)
{
mvcBuilder.AddMvcOptions(options =>
{
options.Filters.Add<DouyinExceptionFilter>();
});
return mvcBuilder;
}
}
}
+2 -2
View File
@@ -17,9 +17,9 @@ namespace dy.net.repository
public async Task<List<DouyinVideoTopDto>> GetTopsOrderByCreateTime(int top)
public async Task<List<DouyinVideoTopDto>> GetTopsOrderBySyncTime(int top)
{
return await Db.Queryable<DouyinVideo>().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<DouyinVideo>().Select(x=>new DouyinVideoTopDto { Title=x.VideoTitle,Time=x.SyncTime.ToString("yyyy-MM-dd HH:mm:ss")}).Take(top).OrderByDescending(x=>x.Time).ToListAsync();
}
/// <summary>
///
+2 -2
View File
@@ -313,9 +313,9 @@ namespace dy.net.service
public async Task<List<DouyinVideoTopDto>> GetLastTop(int top=5)
public async Task<List<DouyinVideoTopDto>> GetLastSyncTop(int top=5)
{
return await _dyCollectVideoRepository.GetTopsOrderByCreateTime(top);
return await _dyCollectVideoRepository.GetTopsOrderBySyncTime(top);
}
}