添加命令行参数支持和未处理异常提示

This commit is contained in:
root 2024-12-13 17:56:57 +08:00
parent db1b7bbb7d
commit 510c70cb83
10 changed files with 281 additions and 168 deletions

View file

@ -69,7 +69,10 @@ namespace iFileProxy.Config
}
var databaseHelper = serviceProvider.GetRequiredService<DatabaseGateService>();
databaseHelper.TestDbConfig();
if (databaseHelper.TestDbConfig())
{
_logger.Information("数据库配置验证成功.");
}
}
else
{
@ -120,40 +123,40 @@ namespace iFileProxy.Config
public partial class Common
{
[JsonPropertyName("Host")]
public string Host { get; set; }
public string Host { get; set; } = string.Empty;
[JsonPropertyName("Port")]
public int Port { get; set; } = 3306;
public uint Port { get; set; } = 3306;
[JsonPropertyName("User")]
public string User { get; set; }
public string User { get; set; } = string.Empty;
[JsonPropertyName("Password")]
public string Password { get; set; }
public string Password { get; set; } = string.Empty;
}
public partial class DB
{
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("Host")]
public string Host { get; set; }
public string? Host { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("Port")]
public long? Port { get; set; } = 3306;
public uint? Port { get; set; } = null;
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("User")]
public string User { get; set; }
public string? User { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("Password")]
public string Password { get; set; }
public string? Password { get; set; }
[JsonPropertyName("DatabaseName")]
public string DatabaseName { get; set; }
public string? DatabaseName { get; set; }
[JsonPropertyName("Description")]
public string Description { get; set; }
public string? Description { get; set; }
}
public class StreamProxyOptions

View file

@ -0,0 +1,37 @@
using iFileProxy.Config;
using iFileProxy.Helpers;
using iFileProxy.Services;
namespace iFileProxy.Handlers
{
public class CmdArgsHandler
{
public static void ParseArgs(string[] args)
{
if (args.Length == 0)
return;
Dictionary<string, string> helpStrings = [];
helpStrings.Add("--init-db", "初始化数据库");
helpStrings.Add("--dev-logger", "启用开发者logger");
helpStrings.Add("--disable-startup-check", "禁用启动时程序配置自检");
helpStrings.Add("--url=<Url>", "设置程序app.run() Url 如: http://0.0.0.0:1145");
if (args.Contains("-h") || args.Contains("--help"))
{
Console.WriteLine("命令行参数帮助: ");
foreach (var hs in helpStrings)
{
Console.WriteLine($"\t{hs.Key}\t{hs.Value}");
}
Environment.Exit(0);
}
CommandLineArgsHelper cmdlineHelper = new(args);
if (cmdlineHelper.GetBooleanValue("--init-db"))
{
DatabaseGateService databaseGateService = new(AppConfig.GetCurrConfig());
databaseGateService.TryInitialDB();
}
}
}
}

View file

@ -0,0 +1,51 @@
using Newtonsoft.Json;
using Serilog;
namespace iFileProxy.Helpers
{
public class CommandLineArgsHelper(string[] args)
{
private readonly Dictionary<string, string> _parameters = ParseArgs(args);
// 解析命令行参数
private static Dictionary<string, string> ParseArgs(string[] args)
{
var parameters = new Dictionary<string, string>();
for (int i = 0; i < args.Length; i++)
{
var arg = args[i];
// 如果参数是键值对形式
if (arg.Contains('='))
{
var parts = arg.Split(['='], 2);
if (parts.Length == 2)
{
parameters[parts[0].TrimStart('-')] = parts[1];
}
}
else
{
// 如果参数是没有值的标志性参数(比如 --dev-logger
parameters[arg.TrimStart('-')] = "true";
}
}
return parameters;
}
// 获取指定参数的值若无值则返回null
public string? GetStringValue(string key)
{
return _parameters.ContainsKey(key) ? _parameters[key] : null;
}
// 获取指定参数的布尔值,若参数值是 "true" 则返回true否则返回false
public bool GetBooleanValue(string key)
{
return _parameters.ContainsKey(key) && _parameters[key].Equals("true", StringComparison.CurrentCultureIgnoreCase);
}
}
}

View file

@ -24,17 +24,13 @@ namespace iFileProxy.Middleware
await _next(context);
if (context.Response.HasStarted)
{
_logger.Debug($"响应已经开始 错误处理中间件无法再修改其响应内容");
_logger.Debug($"响应已经开始,无法再修改其响应内容");
return;
}
if (context.Response.StatusCode == 404)
{
context.Response.OnStarting(async () =>
{
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonSerializer.Serialize(new CommonRsp { Retcode = 404, Message = "你正在请求的资源不存在!" }));
}
);
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonSerializer.Serialize(new CommonRsp { Retcode = 404, Message = "你正在请求的资源不存在!" }));
}
}
catch (Exception ex)
@ -46,7 +42,7 @@ namespace iFileProxy.Middleware
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
var code = HttpStatusCode.InternalServerError; // 500 if unexpected
_logger.Fatal("崩溃数据: {exception}\n上下文信息: {context}", exception, JsonSerializer.Serialize(MasterHelper.ExtractDebugInfo(context)));
_logger.Error("崩溃数据: {exception}\n上下文信息: {context}", exception, JsonSerializer.Serialize(MasterHelper.ExtractDebugInfo(context)));
switch (exception)
{

View file

@ -14,9 +14,9 @@ namespace iFileProxy.Middleware
public async Task InvokeAsync(HttpContext context)
{
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
var token = context.Request.Headers.Authorization.FirstOrDefault()?.Split(" ").Last();
var fingerprint = context.Request.Headers["X-Device-Fingerprint"].FirstOrDefault();
var userAgent = context.Request.Headers["User-Agent"].FirstOrDefault();
var userAgent = context.Request.Headers.UserAgent.FirstOrDefault();
var ip = MasterHelper.GetClientIPAddr(context);
if (token != null)

View file

@ -1,12 +1,13 @@
using iFileProxy.Config;
using iFileProxy.Middleware;
using iFileProxy.Helpers;
using iFileProxy.Handlers;
using iFileProxy.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Serilog;
using System.Text;
using System.Diagnostics;
using MySql.Data.MySqlClient;
namespace iFileProxy
{
@ -14,11 +15,16 @@ namespace iFileProxy
{
public static void Main(string[] args)
{
SerilogConfig.CreateLogger();
Serilog.ILogger logger = Log.Logger.ForContext<Program>();
// 绑定异常捕获事件
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
SerilogConfig.CreateLogger(args: args);
CommandLineArgsHelper argsHelper = new (args);
Console.Write(" "); // 补全日志第一行开头的空白
CmdArgsHandler.ParseArgs(args);
var builder = WebApplication.CreateBuilder(args);
// CORS配置
@ -73,6 +79,10 @@ namespace iFileProxy
// 初始化缓存管理服务
LocalCacheManager localCacheManager = new(app.Services);
if (!argsHelper.GetBooleanValue("disable-startup-check"))
// 初始化验证配置文件
AppConfig.CheckAppConfig(app.Services);
// 1. 错误处理(放在请求管道的最前面)
app.UseMiddleware<ErrorHandlerMiddleware>();
@ -118,9 +128,45 @@ namespace iFileProxy
// 11. 启动应用
var dbGateService = app.Services.GetRequiredService<DatabaseGateService>();
SerilogConfig.CreateLogger(dbGateService);
SerilogConfig.CreateLogger(dbGateService,args);
app.Run();
if (!argsHelper.GetStringValue("url").IsNullOrEmpty())
app.Run(argsHelper.GetStringValue("url"));
else
app.Run();
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var logger = Log.ForContext<AppDomain>();
Exception exc = (Exception)e.ExceptionObject;
// 直接记录原始异常信息,不重新抛出
if (exc is MySqlException mySqlEx)
{
if (mySqlEx.Message.Contains("is not allowed to connect to this MySQL server", StringComparison.CurrentCulture))
{
logger.Fatal( "远程主机不允许你连接到该数据库, 请检查数据库服务端是否将本机IP加白! ");
}
else if (mySqlEx.Message.Contains("Unable to connect to any of the specified MySQL hosts"))
{
logger.Fatal("无法连接到远程数据库, 因为远程主机不可达, 请检查你的网络或数据库配置!!");
}
}
else if (exc is IOException ioEx)
{
if (ioEx.Message.Contains("address already in use."))
{
logger.Fatal("程序服务端口被占用!!!");
}
}
// 记录所有未处理的异常信息
logger.Fatal(exc, "未处理的异常: {ExceptionMessage}", exc.Message);
// 优雅退出应用程序
Environment.Exit(1);
}
}
}

View file

@ -6,29 +6,34 @@
using iFileProxy.Sinks;
using iFileProxy.Services;
using ZstdSharp.Unsafe;
using iFileProxy.Helpers;
public static class SerilogConfig
{
private static DatabaseGateService? _dbGateService;
public static void CreateLogger(DatabaseGateService? dbGateService = null)
public static void CreateLogger(DatabaseGateService? dbGateService = null, string[]? args = null)
{
_dbGateService = dbGateService; // 保存实例以供后续使用
var baseLogPath = Path.Combine(AppContext.BaseDirectory, "logs");
var appName = AppDomain.CurrentDomain.FriendlyName;
// 确保日志目录存在
if (!Directory.Exists(baseLogPath))
{
Directory.CreateDirectory(baseLogPath);
}
var loggerConfiguration = new LoggerConfiguration();
var loggerConfiguration = new LoggerConfiguration()
#if RELEASE
.MinimumLevel.Information()
#else
.MinimumLevel.Debug()
#endif
if (new CommandLineArgsHelper(args).GetBooleanValue("dev-logger"))
{
loggerConfiguration.MinimumLevel.Debug();
}
else
loggerConfiguration.MinimumLevel.Information();
loggerConfiguration
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
.Enrich.FromLogContext()
@ -43,7 +48,7 @@
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {ClientIp} {Message:lj} {contentType} {queryString}{NewLine}{Exception}",
rollingInterval: RollingInterval.Day,
fileSizeLimitBytes: 1073741824)) // 1GB
// 警告日志写入单独的文件
// 警告日志写入单独的文件
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(evt => evt.Level == LogEventLevel.Warning)
.WriteTo.File(
@ -69,6 +74,7 @@
fileSizeLimitBytes: 1073741824))
.Enrich.WithProperty("node_ip", GetIpAddress());
// 只有在提供了 DatabaseGateService 时才添加数据库 Sink
if (_dbGateService != null)
{

View file

@ -18,77 +18,13 @@ namespace iFileProxy.Services
Dictionary<string, DB> _dbDictionary = [];
private const string CREATE_TASKS_TABLE_SQL = """
CREATE TABLE IF NOT EXISTS `t_tasks_info` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '',
`tid` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'id',
`file_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`client_ip` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'IP',
`add_time` datetime NOT NULL COMMENT '',
`update_time` datetime NOT NULL COMMENT '',
`status` int(11) NULL DEFAULT NULL COMMENT '',
`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'url',
`size` int(11) NULL DEFAULT NULL COMMENT '',
`hash` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'hash',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
""";
private const string CREATE_USERS_TABLE_SQL = """
CREATE TABLE IF NOT EXISTS `t_users` (
`user_id` varchar(36) NOT NULL,
`username` varchar(50) NOT NULL,
`nickname` varchar(50) NOT NULL,
`email` varchar(100) NOT NULL,
`password_hash` varchar(255) NOT NULL,
`mask` int NOT NULL DEFAULT 0,
`state` int NOT NULL DEFAULT 0,
`create_time` datetime NOT NULL,
`last_login_time` datetime NULL,
`last_login_ip` varchar(45) NULL,
PRIMARY KEY (`user_id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
""";
private const string CREATE_USER_EVENTS_TABLE_SQL = """
CREATE TABLE IF NOT EXISTS `t_user_events` (
`event_id` varchar(36) NOT NULL,
`user_id` varchar(36) NOT NULL,
`event_type` int NOT NULL,
`event_time` datetime NOT NULL,
`event_ip` varchar(45) NULL,
`event_detail` text NULL,
PRIMARY KEY (`event_id`),
KEY `user_id` (`user_id`),
CONSTRAINT `fk_user_events_user` FOREIGN KEY (`user_id`) REFERENCES `t_users` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
""";
private const string CREATE_SYSTEM_LOGS_TABLE_SQL = """
CREATE TABLE IF NOT EXISTS `t_system_logs` (
`log_id` varchar(36) NOT NULL,
`level` varchar(20) NOT NULL,
`message` text NOT NULL,
`exception` text,
`properties` json,
`timestamp` datetime NOT NULL,
PRIMARY KEY (`log_id`),
KEY `idx_timestamp` (`timestamp`),
KEY `idx_level` (`level`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
""";
private const string CREATE_DOWNLOAD_HISTORY_TABLE_SQL = """
CREATE TABLE IF NOT EXISTS `t_download_history` (
`tid` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'UUID',
`time` datetime NULL DEFAULT NULL COMMENT '',
`client_ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'IP',
PRIMARY KEY (`tid`) USING BTREE,
CONSTRAINT `t_download_history_ibfk_1` FOREIGN KEY (`tid`) REFERENCES `t_tasks_info` (`tid`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
""";
private Dictionary<string, string> createDBSqls = new Dictionary<string, string> {
{ "任务信息",@"CREATE TABLE `t_tasks_info` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增键', `tid` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '任务id', `file_name` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '文件名', `client_ip` varchar(46) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '客户端IP', `add_time` datetime NOT NULL COMMENT '任务在何时被添加', `update_time` datetime NOT NULL COMMENT '任务状态更新时间', `status` int(1) NULL DEFAULT NULL COMMENT '任务状态', `url` varchar(8192) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '文件url', `size` bigint(32) NULL DEFAULT NULL COMMENT '文件大小', `hash` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '文件hash', `tag` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '标记', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `tid`(`tid`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 139915 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;"},
{ "用户", @"CREATE TABLE `t_users` ( `user_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '昵称', `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '电子邮箱', `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名', `password_hash` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '密码哈希值', `mask` int(11) NOT NULL DEFAULT 0 COMMENT '权限掩码', `state` int(11) NOT NULL DEFAULT 0 COMMENT '状态', `create_time` datetime NOT NULL COMMENT '账号创建时间', `last_login_time` datetime NULL DEFAULT NULL COMMENT '上次登录时间', `last_login_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '上次登录IP', PRIMARY KEY (`user_id`) USING BTREE, UNIQUE INDEX `username`(`username`) USING BTREE, UNIQUE INDEX `email`(`email`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;"},
{ "用户事件", @"CREATE TABLE `t_user_events` ( `event_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `user_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `event_type` int(11) NOT NULL, `event_time` datetime NOT NULL, `event_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `event_detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, PRIMARY KEY (`event_id`) USING BTREE, INDEX `user_id`(`user_id`) USING BTREE, CONSTRAINT `t_user_events_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `t_users` (`user_id`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;"},
{ "系统日志", @"CREATE TABLE `t_system_logs` ( `log_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `level` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `exception` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, `properties` json NULL, `timestamp` datetime NOT NULL, PRIMARY KEY (`log_id`) USING BTREE, INDEX `idx_timestamp`(`timestamp`) USING BTREE, INDEX `idx_level`(`level`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;" },
{"下载历史",@"CREATE TABLE `t_download_history` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键', `tid` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '任务UUID', `time` datetime NULL DEFAULT NULL COMMENT '触发时间', `client_ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '客户端IP', PRIMARY KEY (`id`) USING BTREE, INDEX `tid`(`tid`) USING BTREE, CONSTRAINT `t_download_history_ibfk_1` FOREIGN KEY (`tid`) REFERENCES `t_tasks_info` (`tid`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB AUTO_INCREMENT = 532 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;" }
};
public DatabaseGateService(AppConfig appConfig)
{
@ -97,13 +33,13 @@ namespace iFileProxy.Services
_appConfig = appConfig;
try
{
LoadDbDict();
_logger.Information("Done.");
}
catch (Exception e)
{
_logger.Fatal($"程序异常: {e.Message}");
}
LoadDbDict();
}
/// <summary>
@ -154,7 +90,7 @@ namespace iFileProxy.Services
try
{
var conn = new MySqlConnection(builder.ConnectionString);
var conn = new MySqlConnection(builder.ConnectionString);
conn.Open();
return conn;
}
@ -178,7 +114,7 @@ namespace iFileProxy.Services
catch (Exception)
{
_logger.Fatal($"=========== 数据库: {db.Key} 测试失败! ===========");
return false;
throw;
}
}
return true;
@ -206,10 +142,10 @@ namespace iFileProxy.Services
/// <param name="sql"></param>
/// <param name="conn"></param>
/// <returns></returns>
public string QueryTableData(string sql,string dbConfName)
public string QueryTableData(string sql, string dbConfName)
{
DataTable dataTable = new();
using (MySqlDataAdapter adapter = new(new MySqlCommand(sql,GetAndOpenDBConn(dbConfName))))
using (MySqlDataAdapter adapter = new(new MySqlCommand(sql, GetAndOpenDBConn(dbConfName))))
adapter.Fill(dataTable);
return JsonConvert.SerializeObject(dataTable);
}
@ -221,8 +157,8 @@ namespace iFileProxy.Services
/// <param name="dbConfName">配置文件中的Description字段</param>
/// <returns>影响的行数</returns>
public int Query(string sql, string dbConfName)
{
using MySqlCommand sqlCmd = new (sql, GetAndOpenDBConn(dbConfName));
{
using MySqlCommand sqlCmd = new(sql, GetAndOpenDBConn(dbConfName));
int n = sqlCmd.ExecuteNonQuery();
_logger.Debug($"查询完成, 受影响的行数: {n}");
return n;
@ -242,7 +178,13 @@ namespace iFileProxy.Services
return n;
}
public List<TaskInfo> CheckCacheDependencies(string taskId,string ipAddr)
/// <summary>
/// 检查缓存依赖
/// </summary>
/// <param name="taskId">任务ID</param>
/// <param name="ipAddr">排除的IP地址</param>
/// <returns>依赖任务信息列表</returns>
public List<TaskInfo> CheckCacheDependencies(string taskId, string ipAddr)
{
string sql = $"SELECT * FROM t_tasks_info WHERE `status` = @status AND `tag` = @tag AND `client_ip` <> @ip_addr";
using MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
@ -253,7 +195,7 @@ namespace iFileProxy.Services
sqlCmd.Parameters.AddWithValue("@status", TaskState.Cached);
sqlCmd.Parameters.AddWithValue("@ip_addr", ipAddr);
return JsonConvert.DeserializeObject<List<TaskInfo>>( QueryTableData(sqlCmd));
return JsonConvert.DeserializeObject<List<TaskInfo>>(QueryTableData(sqlCmd));
}
catch (Exception e)
{
@ -271,7 +213,7 @@ namespace iFileProxy.Services
{
using MySqlCommand sqlCmd = new(sql, conn);
sqlCmd.Parameters.AddWithValue("@ip_addr", ipAddr);
sqlCmd.Parameters.AddWithValue ("@status", status);
sqlCmd.Parameters.AddWithValue("@status", status);
return QueryTableData(sqlCmd);
}
catch (Exception e)
@ -289,7 +231,7 @@ namespace iFileProxy.Services
using MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
try
{
using MySqlCommand sqlCmd = new (sql,conn);
using MySqlCommand sqlCmd = new(sql, conn);
sqlCmd.Parameters.AddWithValue("@ip_addr", ipAddr);
return QueryTableData(sqlCmd);
}
@ -322,7 +264,8 @@ namespace iFileProxy.Services
{
string sql = $"SELECT * FROM t_tasks_info WHERE url = @url AND size = @size AND `status` = @status AND file_name = @fileName";
using MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
try {
try
{
MySqlCommand sqlCmd = new MySqlCommand(sql, conn);
sqlCmd.Parameters.AddWithValue("@url", url);
sqlCmd.Parameters.AddWithValue("@size", size);
@ -337,10 +280,12 @@ namespace iFileProxy.Services
else
return null;
}
catch (Exception e){
catch (Exception e)
{
return null;
}
finally {
finally
{
conn.Close();
}
@ -380,21 +325,21 @@ namespace iFileProxy.Services
}
return true;
}
public bool UpdateFieldsData(string fieldsName, string taskUUID,object val)
public bool UpdateFieldsData(string fieldsName, string taskUUID, object val)
{
string sql = $"UPDATE t_tasks_info set `{fieldsName}` = @Data WHERE `tid` = @tid";
using MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
try
{
using MySqlCommand sqlCmd = new(sql, conn);
sqlCmd.Parameters.AddWithValue("@Data",val);
sqlCmd.Parameters.AddWithValue("@tid",taskUUID);
sqlCmd.Parameters.AddWithValue("@Data", val);
sqlCmd.Parameters.AddWithValue("@tid", taskUUID);
if (sqlCmd.ExecuteNonQuery() == 1)
{
return true;
}
else
return false;
return false;
}
catch (Exception)
{
@ -412,15 +357,15 @@ namespace iFileProxy.Services
{
string sql = @"UPDATE t_tasks_info set `status` = @status , update_time = Now() WHERE `tid` = @tid";
MySqlConnection conn = connection ?? GetAndOpenDBConn("iFileProxy_Db");
try
{
using MySqlCommand sqlCmd = new (sql, conn);
using MySqlCommand sqlCmd = new(sql, conn);
sqlCmd.Parameters.AddWithValue("@status", taskInfo.Status);
sqlCmd.Parameters.AddWithValue("@tid", taskInfo.TaskId);
if (sqlCmd.ExecuteNonQuery() >= 1)
{
_logger.Information($"Task: {taskInfo.TaskId} Status Change to {taskInfo.Status}");
{
_logger.Information($"Task: {taskInfo.TaskId} Status Change to {taskInfo.Status}");
return true;
}
else
@ -433,8 +378,8 @@ namespace iFileProxy.Services
throw;
}
finally
{
conn.Close ();
{
conn.Close();
}
}
@ -449,11 +394,11 @@ namespace iFileProxy.Services
/// <param name="c"></param>
/// <returns></returns>
public int DeleteTaskInfoByIpAddr(string ipAddr)
{
{
try
{
string sql = $"DELETE FROM `t_tasks_info` WHERE `client_ip` = '{ipAddr}'";
return Query(sql,DbConfigName.iFileProxy);
return Query(sql, DbConfigName.iFileProxy);
}
catch (Exception)
{
@ -466,13 +411,13 @@ namespace iFileProxy.Services
using var conn = GetAndOpenDBConn("iFileProxy_Db");
try
{
// 创建表
using (var cmd = new MySqlCommand(CREATE_TASKS_TABLE_SQL, conn))
cmd.ExecuteNonQuery();
using (var cmd = new MySqlCommand(CREATE_USERS_TABLE_SQL, conn))
cmd.ExecuteNonQuery();
using (var cmd = new MySqlCommand(CREATE_USER_EVENTS_TABLE_SQL, conn))
cmd.ExecuteNonQuery();
foreach (var sql in createDBSqls)
{
_logger.Information($"尝试创建 {sql.Key} 数据表....");
using MySqlCommand sqlcmd = new (sql.Value,conn);
sqlcmd.ExecuteNonQuery();
_logger.Information("Done.");
}
_logger.Information("数据库表结构初始化完成");
}
@ -482,6 +427,13 @@ namespace iFileProxy.Services
throw;
}
}
/// <summary>
/// 执行查询并获取单个指定类型的对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sql"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public T ExecuteScalar<T>(string sql, Dictionary<string, object> parameters)
{
using var conn = GetAndOpenDBConn("iFileProxy_Db");
@ -494,6 +446,13 @@ namespace iFileProxy.Services
return (T)cmd.ExecuteScalar();
}
/// <summary>
/// 执行SQL查询
/// </summary>
/// <typeparam name="T">预期返回的数据类型</typeparam>
/// <param name="sql">SQL语句</param>
/// <param name="parameters">参数列表</param>
/// <returns>所指定类型的数据</returns>
public List<T> ExecuteQuery<T>(string sql, Dictionary<string, object> parameters)
{
using var conn = GetAndOpenDBConn("iFileProxy_Db");
@ -505,6 +464,12 @@ namespace iFileProxy.Services
return JsonConvert.DeserializeObject<List<T>>(QueryTableData(cmd));
}
/// <summary>
/// 执行SQL查询
/// </summary>
/// <param name="sql">SQL语句</param>
/// <param name="parameters">参数列表</param>
/// <returns>受影响的行数</returns>
public int ExecuteNonQuery(string sql, Dictionary<string, object> parameters)
{
using var conn = GetAndOpenDBConn("iFileProxy_Db");
@ -630,7 +595,7 @@ namespace iFileProxy.Services
""";
var result = new Dictionary<TaskState, int>();
// 初始化所有状态的计数为0
foreach (TaskState state in Enum.GetValues(typeof(TaskState)))
{
@ -641,7 +606,7 @@ namespace iFileProxy.Services
using var conn = GetAndOpenDBConn("iFileProxy_Db");
using var cmd = new MySqlCommand(sql, conn);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
var status = (TaskState)reader.GetInt32("status");
@ -686,7 +651,7 @@ namespace iFileProxy.Services
{
var sql = @"INSERT INTO t_users (user_id, username, password_hash, mask, state, create_time, last_login_time, last_login_ip, nickname, email)
VALUES (@userId, @username, @passwordHash, @mask, @state, @createTime, @lastLoginTime, @lastLoginIp, @nickname, @email)";
var parameters = new Dictionary<string, object>
{
{ "@userId", user.UserId },
@ -709,7 +674,7 @@ namespace iFileProxy.Services
{
var sql = @"INSERT INTO t_user_events (event_id, user_id, event_type, event_time, event_ip, event_detail)
VALUES (@eventId, @userId, @eventType, @eventTime, @eventIp, @eventDetail)";
var parameters = new Dictionary<string, object>
{
{ "@eventId", userEvent.EventId },
@ -747,7 +712,7 @@ namespace iFileProxy.Services
last_login_ip = @lastLoginIp,
nickname = @nickname
WHERE user_id = @userId";
var parameters = new Dictionary<string, object>
{
{ "@userId", user.UserId },
@ -852,9 +817,9 @@ namespace iFileProxy.Services
}
public async Task<PagedResult<UserEventDetail>> GetPagedUserEventsAsync(
int page,
int pageSize,
string? userId = null,
int page,
int pageSize,
string? userId = null,
string? keyword = null)
{
// 构建基础SQL
@ -863,7 +828,7 @@ namespace iFileProxy.Services
FROM t_user_events e
LEFT JOIN t_users u ON e.user_id = u.user_id
WHERE 1=1");
var parameters = new Dictionary<string, object>();
// 添加用户ID过滤
@ -993,7 +958,7 @@ namespace iFileProxy.Services
(log_id, level, message, exception, properties, timestamp)
VALUES
(@logId, @level, @message, @exception, @properties, @timestamp)";
var parameters = new Dictionary<string, object>
{
{ "@logId", log.LogId },
@ -1009,8 +974,8 @@ namespace iFileProxy.Services
}
public async Task<PagedResult<SystemLog>> GetPagedSystemLogsAsync(
int page,
int pageSize,
int page,
int pageSize,
LogEventLevel? level = null,
string? keyword = null,
DateTime? startTime = null,
@ -1086,7 +1051,7 @@ namespace iFileProxy.Services
{
var sql = @"INSERT INTO t_download_history (tid, time, client_ip)
VALUES (@taskId, @time, @clientIp)";
var parameters = new Dictionary<string, object>
{
{ "@taskId", taskId },

View file

@ -32,7 +32,8 @@ namespace iFileProxy.Services
CACHE_LIFETIME = _appConfig.DownloadOptions.CacheLifetime;
_dbGateService = serviceProvider.GetRequiredService<DatabaseGateService>();
// 开始定时清理任务
_timer = new Timer((obj) => {
_timer = new Timer((obj) =>
{
lock (_lock)
{
CheckAndCleanCache();
@ -58,7 +59,7 @@ namespace iFileProxy.Services
{
using var cmd = new MySqlCommand(sql, conn);
using var adapter = new MySqlDataAdapter(cmd);
var dataTable = new DataTable();
using var dataTable = new DataTable();
adapter.Fill(dataTable);
taskInfos = JsonConvert.DeserializeObject<List<TaskInfo>>(
JsonConvert.SerializeObject(dataTable)
@ -69,6 +70,16 @@ namespace iFileProxy.Services
{
foreach (TaskInfo taskInfo in taskInfos)
{
var dependencies = _dbGateService.CheckCacheDependencies(taskInfo.TaskId, taskInfo.ClientIp);
// 获取依赖和已经过期的任务ID交集
var intersect = dependencies.Select(x => x.TaskId).Intersect(taskInfos.Select(x => x.TaskId)).ToList();
// 若依赖该缓存的任务也处于过期状态 则不进行缓存依赖检查
if (intersect.IndexOf(taskInfo.TaskId) == -1)
if (dependencies != null && dependencies.Count > 0)
{
_logger.Warning($"TaskId: {taskInfo.TaskId} 所产生的缓存文件正被 {string.Join(",", dependencies.Select(x => x.TaskId))} 所依赖, 不会继续执行清理任务");
continue;
}
string cacheFileName = Path.Combine(_appConfig.DownloadOptions.SavePath, taskInfo.FileName);
if (File.Exists(cacheFileName))
{
@ -85,22 +96,20 @@ namespace iFileProxy.Services
}
// 更新数据库状态
using (var conn = _dbGateService.GetAndOpenDBConn(DbConfigName.iFileProxy))
using var conn = _dbGateService.GetAndOpenDBConn(DbConfigName.iFileProxy);
// 更新标签
using (var cmd = new MySqlCommand(
"UPDATE t_tasks_info SET tag = @tag WHERE tid = @tid",
conn))
{
// 更新标签
using (var cmd = new MySqlCommand(
"UPDATE t_tasks_info SET tag = @tag WHERE tid = @tid",
conn))
{
cmd.Parameters.AddWithValue("@tag", "CLEANED");
cmd.Parameters.AddWithValue("@tid", taskInfo.TaskId);
cmd.ExecuteNonQuery();
}
// 更新状态
taskInfo.Status = TaskState.Cleaned;
_dbGateService.UpdateTaskStatus(taskInfo);
cmd.Parameters.AddWithValue("@tag", "CLEANED");
cmd.Parameters.AddWithValue("@tid", taskInfo.TaskId);
cmd.ExecuteNonQuery();
}
// 更新状态
taskInfo.Status = TaskState.Cleaned;
_dbGateService.UpdateTaskStatus(taskInfo);
}
}
}