实现了管理员后台 但是还未添加任何鉴权手段

This commit is contained in:
root 2024-11-30 17:58:20 +08:00
parent bc589471f3
commit c3a4faae8f
9 changed files with 922 additions and 142 deletions

View file

@ -2,6 +2,7 @@
using Serilog;
using System.Text.Json;
using System.Text.Json.Serialization;
using MySql.Data.MySqlClient;
namespace iFileProxy.Config
{
@ -74,7 +75,7 @@ namespace iFileProxy.Config
{
public string SavePath { get; set; } = "./proxy_tmp/";
public uint ThreadNum { get; set; } = 1;
public uint MaxAllowedFileSize { get; set; }
public long MaxAllowedFileSize { get; set; }
public uint MaxParallelTasks { get; set; } = 4;
public uint MaxQueueLength {get; set; } = 60;
public string Aria2cPath { get; set; } = "./bin/aria2c";
@ -96,7 +97,7 @@ namespace iFileProxy.Config
public Common Common { get; set; }
[JsonPropertyName("Databases")]
public DB[] Databases { get; set; }
public List<DB> Databases { get; set; }
}
public partial class Common

View file

@ -0,0 +1,359 @@
using iFileProxy.Config;
using iFileProxy.Models;
using iFileProxy.Services;
using Microsoft.AspNetCore.Mvc;
using Serilog;
using System.Text.Json;
namespace iFileProxy.Controllers
{
[Route("[controller]")]
[ApiController]
public class ManagementController(TaskManager taskManager, DatabaseGateService dbGateService) : ControllerBase
{
public TaskManager _taskManager = taskManager;
public DatabaseGateService _dbGateService = dbGateService;
private readonly Serilog.ILogger _logger = Log.Logger.ForContext<DatabaseGateService>();
// 查看任务详情
// 删除任务
// 停止任务
// 查看任务Process信息
// 立即执行任务
// 查看系统配置
// 获取全部任务信息
/// <summary>
/// 获取任务列表(支持分页)
/// </summary>
/// <param name="page">页码从1开始</param>
/// <param name="pageSize">每页数量</param>
/// <param name="status">可选的任务状态过滤</param>
/// <returns>分页后的任务列表</returns>
[HttpGet("GetTaskList")]
public ActionResult<CommonRsp> GetTaskList([FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] TaskState? status = null)
{
try
{
var result = _dbGateService.GetPagedTaskList(page, pageSize, status);
result.Data.ForEach(task =>
{
if (task.Status == TaskState.Queuing)
task.QueuePosition = _taskManager.GetQueuePosition(task.TaskId);
});
return Ok(new CommonRsp
{
Retcode = 0,
Message = "success",
Data = result
});
}
catch (Exception ex)
{
_logger.Error($"获取任务列表失败: {ex.Message}");
return Ok(new CommonRsp
{
Retcode = 1,
Message = "获取任务列表失败"
});
}
}
[HttpGet("GetSystemConfig")]
public ActionResult<CommonRsp> GetSystemConfig()
{
var c = AppConfig.GetCurrConfig();
c.Database.Databases.ForEach(db => {
db.Password = "**********";
});
return Ok(new CommonRsp { Retcode = 0 , Data = c,Message = "succ"});
}
/// <summary>
/// 删除指定任务
/// </summary>
/// <param name="taskId">任务ID</param>
/// <returns>删除结果</returns>
[HttpDelete("DeleteTask/{taskId}")]
public ActionResult<CommonRsp> DeleteTask(string taskId)
{
try
{
// 先检查任务是否存在
var taskInfo = JsonSerializer.Deserialize<List<TaskInfo>>(_dbGateService.GetTaskInfoByTid(taskId))[0];
if (taskInfo == null)
{
return Ok(new CommonRsp
{
Retcode = 1,
Message = "任务不存在"
});
}
// 如果任务正在运行,先尝试中断
if (taskInfo.Status == TaskState.Running)
{
_taskManager.TryKillTask(taskInfo);
}
// 删除任务记录
var result = _dbGateService.DeleteTaskByTid(taskId);
return Ok(new CommonRsp
{
Retcode = result ? 0 : 1,
Message = result ? "删除成功" : "删除失败"
});
}
catch (Exception ex)
{
_logger.Error($"删除任务失败: {ex.Message}");
return Ok(new CommonRsp
{
Retcode = 1,
Message = "删除任务失败"
});
}
}
/// <summary>
/// 获取任务详细信息
/// </summary>
/// <param name="taskId">任务ID</param>
/// <returns>任务详细信息</returns>
[HttpGet("GetTaskDetail/{taskId}")]
public ActionResult<CommonRsp> GetTaskDetail(string taskId)
{
try
{
var taskInfo = _dbGateService.GetTaskDetail(taskId);
if (taskInfo == null)
{
return Ok(new CommonRsp
{
Retcode = 1,
Message = "任务不存在"
});
}
return Ok(new CommonRsp
{
Retcode = 0,
Message = "success",
Data = taskInfo
});
}
catch (Exception ex)
{
_logger.Error($"获取任务详情失败: {ex.Message}");
return Ok(new CommonRsp
{
Retcode = 1,
Message = "获取任务详情失败"
});
}
}
/// <summary>
/// 终止正在运行的任务
/// </summary>
/// <param name="taskId">任务ID</param>
/// <returns>操作结果</returns>
[HttpPost("KillTask/{taskId}")]
public ActionResult<CommonRsp> KillTask(string taskId)
{
try
{
// 先获取任务信息
var taskInfo = _dbGateService.GetTaskDetail(taskId);
if (taskInfo == null)
{
return Ok(new CommonRsp
{
Retcode = 1,
Message = "任务不存在"
});
}
// 检查任务是否在运行
if (taskInfo.Status != TaskState.Running)
{
return Ok(new CommonRsp
{
Retcode = 1,
Message = "任务不在运行状态"
});
}
// 尝试终止任务
_taskManager.TryKillTask(taskInfo);
return Ok(new CommonRsp
{
Retcode = 0,
Message = "任务已终止"
});
}
catch (Exception ex)
{
_logger.Error($"终止任务失败: {ex.Message}");
return Ok(new CommonRsp
{
Retcode = 1,
Message = "终止任务失败"
});
}
}
/// <summary>
/// 获取任务状态概览数据
/// </summary>
/// <returns>各状态任务的数量统计</returns>
[HttpGet("GetTaskOverview")]
public ActionResult<CommonRsp> GetTaskOverview()
{
try
{
var overview = _dbGateService.GetTaskStatusOverview();
return Ok(new CommonRsp
{
Retcode = 0,
Message = "success",
Data = overview
});
}
catch (Exception ex)
{
_logger.Error($"获取任务概览失败: {ex.Message}");
return Ok(new CommonRsp
{
Retcode = 1,
Message = "获取任务概览失败"
});
}
}
/// <summary>
/// 获取正在运行任务的进程信息
/// </summary>
/// <param name="taskId">任务ID</param>
/// <returns>进程详细信息</returns>
[HttpGet("GetProcessInfo/{taskId}")]
public ActionResult<CommonRsp> GetProcessInfo(string taskId)
{
try
{
var processInfo = _taskManager.GetProcessInfo(taskId);
if (processInfo == null)
{
return Ok(new CommonRsp
{
Retcode = 1,
Message = "任务不存在或未在运行状态"
});
}
return Ok(new CommonRsp
{
Retcode = 0,
Message = "success",
Data = processInfo
});
}
catch (Exception ex)
{
_logger.Error($"获取进程信息失败: {ex.Message}");
return Ok(new CommonRsp
{
Retcode = 1,
Message = "获取进程信息失败"
});
}
}
/// <summary>
/// 重试失败的任务
/// </summary>
/// <param name="taskId">任务ID</param>
/// <returns>重试结果</returns>
[HttpPost("RetryTask/{taskId}")]
public ActionResult<CommonRsp> RetryTask(string taskId)
{
try
{
var result = _taskManager.RetryTask(taskId);
return Ok(new CommonRsp
{
Retcode = result ? 0 : 1,
Message = result ? "任务重试已开始" : "任务重试失败"
});
}
catch (Exception ex)
{
_logger.Error($"重试任务失败: {ex.Message}");
return Ok(new CommonRsp
{
Retcode = 1,
Message = "重试任务失败"
});
}
}
/// <summary>
/// 将任务移动到等待队列最前面
/// </summary>
/// <param name="taskId">任务ID</param>
/// <returns>操作结果</returns>
[HttpPost("PrioritizeTask/{taskId}")]
public ActionResult<CommonRsp> PrioritizeTask(string taskId)
{
try
{
var result = _taskManager.PrioritizeTask(taskId);
return Ok(new CommonRsp
{
Retcode = result ? 0 : 1,
Message = result ? "任务已移至队列首位" : "调整任务优先级失败"
});
}
catch (Exception ex)
{
_logger.Error($"调整任务优先级失败: {ex.Message}");
return Ok(new CommonRsp
{
Retcode = 1,
Message = "调整任务优先级失败"
});
}
}
/// <summary>
/// 立即执行指定任务
/// </summary>
/// <param name="taskId">任务ID</param>
/// <returns>操作结果</returns>
[HttpPost("ExecuteImmediately/{taskId}")]
public ActionResult<CommonRsp> ExecuteImmediately(string taskId)
{
try
{
var result = _taskManager.ExecuteImmediately(taskId);
return Ok(new CommonRsp
{
Retcode = result ? 0 : 1,
Message = result ? "任务已开始执行" : "立即执行任务失败"
});
}
catch (Exception ex)
{
_logger.Error($"立即执行任务失败: {ex.Message}");
return Ok(new CommonRsp
{
Retcode = 1,
Message = "立即执行任务失败"
});
}
}
}
}

31
src/Models/PagedResult.cs Normal file
View file

@ -0,0 +1,31 @@
/// <summary>
/// 通用分页结果模型
/// </summary>
/// <typeparam name="T">数据项类型</typeparam>
public class PagedResult<T>
{
/// <summary>
/// 总记录数
/// </summary>
public long Total { get; set; }
/// <summary>
/// 每页数量
/// </summary>
public int PageSize { get; set; }
/// <summary>
/// 当前页码
/// </summary>
public int CurrentPage { get; set; }
/// <summary>
/// 总页数
/// </summary>
public int TotalPages { get; set; }
/// <summary>
/// 当前页的数据列表
/// </summary>
public List<T> Data { get; set; } = new();
}

77
src/Models/ProcessInfo.cs Normal file
View file

@ -0,0 +1,77 @@
using System.Diagnostics;
using ThreadState = System.Diagnostics.ThreadState;
namespace iFileProxy.Models
{
/// <summary>
/// 进程详细信息模型
/// </summary>
public class ProcessInfo
{
/// <summary>
/// 进程ID
/// </summary>
public int ProcessId { get; set; }
/// <summary>
/// 进程名称
/// </summary>
public string ProcessName { get; set; } = string.Empty;
/// <summary>
/// 启动时间
/// </summary>
public DateTime StartTime { get; set; }
/// <summary>
/// 运行时长(秒)
/// </summary>
public double RunningTime { get; set; }
/// <summary>
/// 线程数
/// </summary>
public int ThreadCount { get; set; }
/// <summary>
/// 命令行参数
/// </summary>
public string CommandLine { get; set; } = string.Empty;
/// <summary>
/// 进程优先级
/// </summary>
public ProcessPriorityClass PriorityClass { get; set; }
/// <summary>
/// 工作目录
/// </summary>
public string WorkingDirectory { get; set; } = string.Empty;
/// <summary>
/// 线程信息列表
/// </summary>
public List<ThreadInfo> Threads { get; set; } = new();
}
/// <summary>
/// 线程信息模型
/// </summary>
public class ThreadInfo
{
/// <summary>
/// 线程ID
/// </summary>
public int ThreadId { get; set; }
/// <summary>
/// 线程状态
/// </summary>
public ThreadState ThreadState { get; set; }
/// <summary>
/// 线程优先级
/// </summary>
public ThreadPriorityLevel Priority { get; set; }
}
}

View file

@ -36,6 +36,10 @@
/// 任务因为各种原因被取消
/// </summary>
Canceled = 7,
/// <summary>
/// 其他
/// </summary>
Other = 999
}
/// <summary>
/// 任务添加状态

View file

@ -17,6 +17,21 @@ namespace iFileProxy
var builder = WebApplication.CreateBuilder(args);
// Ìí¼Ó CORS ·þÎñ
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowFrontend",
builder =>
{
builder
.WithOrigins("http://localhost:3000") // ǰ¶ËµØÖ·
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
// Add services to the container.
builder.Services.AddControllers();
@ -61,7 +76,9 @@ namespace iFileProxy
}
// 错误处理中间件
app.UseMiddleware<ErrorHandlerMiddleware>();
app.UseMiddleware<ErrorHandlerMiddleware>();
app.UseCors("AllowFrontend");
app.UseHttpsRedirection();

View file

@ -5,13 +5,10 @@ using iFileProxy.Helpers;
using MySql.Data.MySqlClient;
using iFileProxy.Models;
using Newtonsoft.Json;
using System.Text;
namespace iFileProxy.Services
{
/// <summary>
/// 数据库访问网关服务
/// 提供统一的数据库操作接口,管理数据库连接和查询
/// </summary>
public class DatabaseGateService
{
Database _db;
@ -20,10 +17,6 @@ namespace iFileProxy.Services
Dictionary<string, DB> _dbDictionary = [];
/// <summary>
/// 初始化数据库网关服务
/// </summary>
/// <param name="appConfig">应用程序配置</param>
public DatabaseGateService(AppConfig appConfig)
{
_logger.Information("Initializing DatabaseGateService...");
@ -41,7 +34,7 @@ namespace iFileProxy.Services
}
/// <summary>
/// 加载数据库配置字典
/// 加载数据库描述字典
/// </summary>
public void LoadDbDict()
{
@ -51,14 +44,13 @@ namespace iFileProxy.Services
_logger.Debug($"Db Config: {item.Description} <=> {item.DatabaseName} Loaded.");
}
}
/// <summary>
/// 获取并打开指定数据库的连接
/// 获取一个指定数据库的连接
/// </summary>
/// <param name="db_desc">数据库描述符,对应配置文件中的 Description 字段</param>
/// <returns>已打开的数据库连接</returns>
/// <exception cref="Exception">当找不到匹配的数据库配置时抛出</exception>
/// <exception cref="NoNullAllowedException">当必需的配置字段为空时抛出</exception>
/// <param name="db_desc">数据库描述字段 对应AppConfig的description字段</param>
/// <returns></returns>
/// <exception cref="Exception">若某些不允许为空的字段出现空值 则抛出此异常</exception>
///
public MySqlConnection GetAndOpenDBConn(string db_desc)
{
if (!_dbDictionary.TryGetValue(db_desc, out DB Db))
@ -80,19 +72,15 @@ namespace iFileProxy.Services
{
conn = new MySqlConnection(db_connstr);
conn.Open();
return conn;
}
catch (Exception ex)
{
_logger.Fatal($"获取Mysql连接时出现异常:{ex.Message}");
throw;
}
return conn;
}
/// <summary>
/// 测试所有数据库配置的连接
/// </summary>
/// <returns>测试是否全部成功</returns>
public bool TestDbConfig()
{
foreach (var db in _dbDictionary)
@ -118,10 +106,11 @@ namespace iFileProxy.Services
}
/// <summary>
/// 执行查询并返回 JSON 格式的结果
/// 获取一个json格式的数据表
/// </summary>
/// <param name="sqlCmd">SQL 命令对象</param>
/// <returns>JSON 格式的查询结果</returns>
/// <param name="sql"></param>
/// <param name="conn"></param>
/// <returns></returns>
public static string QueryTableData(MySqlCommand sqlCmd)
{
DataTable dataTable = new();
@ -131,43 +120,34 @@ namespace iFileProxy.Services
}
/// <summary>
/// 执行查询并返回 JSON 格式的结果
/// 获取一个json格式的数据表
/// </summary>
/// <param name="sql">SQL 查询语句</param>
/// <param name="dbConfName">数据库配置名称</param>
/// <returns>JSON 格式的查询结果</returns>
public string QueryTableData(string sql, string dbConfName)
/// <param name="sql"></param>
/// <param name="conn"></param>
/// <returns></returns>
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);
}
/// <summary>
/// 执行非查询SQL语句
/// 内部查询数据专用 当此方法暴露给C端可能造成sql注入等安全问题
/// </summary>
/// <param name="sql">SQL语句</param>
/// <param name="dbConfName">数据库配置名称</param>
/// <returns>受影响的行数</returns>
/// <remarks>
/// 警告此方法仅供内部使用直接暴露给客户端可能导致SQL注入风险
/// </remarks>
/// <param name="dbConfName">配置文件中的Description字段</param>
/// <returns>影响的行数</returns>
public int Query(string sql, string dbConfName)
{
{
using MySqlCommand sqlCmd = new (sql, GetAndOpenDBConn(dbConfName));
int n = sqlCmd.ExecuteNonQuery();
_logger.Debug($"查询完成, 受影响的行数: {n}");
return n;
}
/// <summary>
/// 检查缓存依赖关系
/// </summary>
/// <param name="taskId">任务ID</param>
/// <param name="ipAddr">IP地址</param>
/// <returns>相关的任务信息列表</returns>
public List<TaskInfo> CheckCacheDependencies(string taskId, string ipAddr)
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";
MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
@ -178,7 +158,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)
{
@ -188,12 +168,6 @@ namespace iFileProxy.Services
finally { conn.Close(); }
}
/// <summary>
/// 根据状态和IP地址获取任务列表
/// </summary>
/// <param name="ipAddr">IP地址</param>
/// <param name="status">任务状态</param>
/// <returns>JSON格式的任务列表</returns>
public string GetTaskListByStateAndIp(string ipAddr, TaskState status)
{
string sql = $"SELECT * FROM t_tasks_info WHERE client_ip = @ip_addr AND `status` = @status";
@ -202,7 +176,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)
@ -213,11 +187,7 @@ namespace iFileProxy.Services
finally { conn.Close(); }
}
/// <summary>
/// 根据IP地址获取任务列表
/// </summary>
/// <param name="ipAddr">IP地址</param>
/// <returns>JSON格式的任务列表</returns>
public string GetTaskListByIP(string ipAddr)
{
string sql = $"SELECT * FROM t_tasks_info WHERE client_ip = @ip_addr";
@ -235,15 +205,9 @@ namespace iFileProxy.Services
}
finally { conn.Close(); }
}
/// <summary>
/// 根据任务ID获取任务信息
/// </summary>
/// <param name="tid">任务ID</param>
/// <returns>JSON格式的任务信息</returns>
public string GetTaskInfoByTid(string tid)
{
string sql = $"SELECT * FROM t_tasks_info WHERE `tid` =@tid";
string sql = $"SELECT * FROM t_tasks_info WHERE `tid` = @tid";
MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
try
{
@ -259,14 +223,6 @@ namespace iFileProxy.Services
finally { conn.Close(); }
}
/// <summary>
/// 查询任务信息
/// </summary>
/// <param name="fileName">文件名</param>
/// <param name="url">URL</param>
/// <param name="size">文件大小</param>
/// <param name="status">任务状态</param>
/// <returns>任务信息对象如果未找到则返回null</returns>
public TaskInfo? QueryTaskInfo(string fileName, string url, long size, TaskState status)
{
string sql = $"SELECT * FROM t_tasks_info WHERE url = @url AND size = @size AND `status` = @status AND file_name = @fileName";
@ -295,11 +251,6 @@ namespace iFileProxy.Services
}
/// <summary>
/// 插入任务数据
/// </summary>
/// <param name="taskInfo">任务信息对象</param>
/// <returns>是否插入成功</returns>
public bool InsertTaskData(TaskInfo taskInfo)
{
_logger.Debug(System.Text.Json.JsonSerializer.Serialize(taskInfo));
@ -334,15 +285,7 @@ namespace iFileProxy.Services
}
return true;
}
/// <summary>
/// 更新指定字段的数据
/// </summary>
/// <param name="fieldsName">字段名</param>
/// <param name="key">主键值</param>
/// <param name="val">更新值</param>
/// <returns>是否更新成功</returns>
public bool UpdateFieldsData(string fieldsName, string key, string val)
public bool UpdateFieldsData(string fieldsName, string key,string val)
{
string sql = $"UPDATE t_tasks_info set `{fieldsName}` = @Data WHERE `tid` = @tid";
MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
@ -370,11 +313,6 @@ namespace iFileProxy.Services
}
/// <summary>
/// 更新任务状态
/// </summary>
/// <param name="taskInfo">任务信息对象</param>
/// <returns>是否更新成功</returns>
public bool UpdateTaskStatus(TaskInfo taskInfo)
{
string sql = @"UPDATE t_tasks_info set `status` = @status , update_time = Now() WHERE `tid` = @tid";
@ -404,21 +342,16 @@ namespace iFileProxy.Services
}
}
/// <summary>
/// 更新任务的哈希值
/// </summary>
/// <param name="taskInfo">任务信息对象</param>
/// <returns>是否更新成功</returns>
public bool UpdateTaskHash(TaskInfo taskInfo)
{
return UpdateFieldsData("hash", taskInfo.TaskId, MasterHelper.GetFileHash(Path.Combine(_appConfig.DownloadOptions.SavePath, taskInfo.FileName), FileHashAlgorithm.MD5));
}
/// <summary>
/// 删除指定IP地址所有任务信息
/// 删除指定IP的任务信息
/// </summary>
/// <param name="ipAddr">IP地址</param>
/// <returns>受影响的行数,-1表示操作失败</returns>
/// <param name="c"></param>
/// <returns></returns>
public int DeleteTaskInfoByIpAddr(string ipAddr)
{
try
@ -432,12 +365,6 @@ namespace iFileProxy.Services
}
}
/// <summary>
/// 尝试初始化数据库
/// </summary>
/// <remarks>
/// 创建必要的数据库表结构
/// </remarks>
public void TryInitialDB()
{
string sql = """
@ -472,14 +399,6 @@ namespace iFileProxy.Services
conn.Close();
}
}
/// <summary>
/// 执行标量查询
/// </summary>
/// <typeparam name="T">返回值类型</typeparam>
/// <param name="sql">SQL查询语句</param>
/// <param name="parameters">查询参数</param>
/// <returns>查询结果</returns>
public T ExecuteScalar<T>(string sql, Dictionary<string, object> parameters)
{
using var conn = GetAndOpenDBConn("iFileProxy_Db");
@ -488,16 +407,10 @@ namespace iFileProxy.Services
{
cmd.Parameters.AddWithValue(param.Key, param.Value);
}
return (T)cmd.ExecuteScalar();
}
/// <summary>
/// 执行查询并返回实体列表
/// </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");
@ -509,12 +422,6 @@ 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");
@ -525,5 +432,147 @@ namespace iFileProxy.Services
}
return cmd.ExecuteNonQuery();
}
/// <summary>
/// 获取分页的任务列表
/// </summary>
/// <param name="page">页码从1开始</param>
/// <param name="pageSize">每页数量</param>
/// <param name="status">可选的任务状态过滤</param>
/// <returns>包含分页信息的任务列表</returns>
public PagedResult<TaskInfo> GetPagedTaskList(int page, int pageSize, TaskState? status = null)
{
// 构建基础SQL
var sql = new StringBuilder("SELECT * FROM t_tasks_info");
var parameters = new Dictionary<string, object>();
// 添加状态过滤
if (status.HasValue)
{
sql.Append(" AND status = @status");
parameters.Add("@status", (int)status.Value);
}
// 添加分页
sql.Append(" ORDER BY add_time DESC LIMIT @offset, @limit");
parameters.Add("@offset", (page - 1) * pageSize);
parameters.Add("@limit", pageSize);
// 获取总记录数
var countSql = "SELECT COUNT(*) FROM t_tasks_info" +
(status.HasValue ? " AND status = @status" : "");
var totalCount = ExecuteScalar<long>(countSql, parameters);
// 获取分页数据
var tasks = ExecuteQuery<TaskInfo>(sql.ToString(), parameters);
// 返回结果
return new PagedResult<TaskInfo>
{
Total = totalCount,
PageSize = pageSize,
CurrentPage = page,
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize),
Data = tasks
};
}
/// <summary>
/// 根据任务ID删除任务
/// </summary>
/// <param name="taskId">任务ID</param>
/// <returns>是否删除成功</returns>
public bool DeleteTaskByTid(string taskId)
{
try
{
var sql = "DELETE FROM t_tasks_info WHERE tid = @taskId";
var parameters = new Dictionary<string, object>
{
{ "@taskId", taskId }
};
var affectedRows = ExecuteNonQuery(sql, parameters);
return affectedRows > 0;
}
catch (Exception ex)
{
_logger.Error($"删除任务失败 [TaskId: {taskId}]: {ex.Message}");
return false;
}
}
/// <summary>
/// 获取任务的详细信息
/// </summary>
/// <param name="taskId">任务ID</param>
/// <returns>任务详细信息</returns>
public TaskInfo GetTaskDetail(string taskId)
{
try
{
var sql = """
SELECT * FROM t_tasks_info WHERE tid = @taskId
""";
var parameters = new Dictionary<string, object>
{
{ "@taskId", taskId }
};
var taskInfo = ExecuteQuery<TaskInfo>(sql, parameters).FirstOrDefault();
return taskInfo;
}
catch (Exception ex)
{
_logger.Error($"获取任务详情失败 [TaskId: {taskId}]: {ex.Message}");
throw;
}
}
/// <summary>
/// 获取各状态任务的数量统计
/// </summary>
/// <returns>以任务状态为键,数量为值的字典</returns>
public Dictionary<TaskState, int> GetTaskStatusOverview()
{
try
{
var sql = """
SELECT
status,
COUNT(*) as count
FROM t_tasks_info
GROUP BY status
""";
var result = new Dictionary<TaskState, int>();
// 初始化所有状态的计数为0
foreach (TaskState state in Enum.GetValues(typeof(TaskState)))
{
result[state] = 0;
}
// 获取数据库中的实际计数
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");
var count = reader.GetInt32("count");
result[status] = count;
}
return result;
}
catch (Exception ex)
{
_logger.Error($"获取任务状态统计失败: {ex.Message}");
throw;
}
}
}
}

View file

@ -39,6 +39,7 @@ namespace iFileProxy.Services
add_task_num = _pendingTasks.Count;
for (int i = 0; i < add_task_num; i++) // 运行的任务中不足最大并行数并且有正在队列的task时候添加足够多的任务
{
// 获取下一个任务信息
TaskInfo nextTask = _pendingTasks.Dequeue();
Task.Run(() => StartTaskAsync(nextTask)).ConfigureAwait(false);
}
@ -190,7 +191,7 @@ namespace iFileProxy.Services
/// </summary>
/// <param name="taskInfo">任务信息</param>
/// <returns></returns>
public async Task StartTaskAsync(TaskInfo taskInfo)
public async Task StartTaskAsync(TaskInfo taskInfo, bool OnTaskCompletedEvent = true)
{
if (_runningTasks.ContainsKey(taskInfo.TaskId))
{
@ -219,7 +220,7 @@ namespace iFileProxy.Services
aria2c.Start();
_runningTasks[taskInfo.TaskId].Process = aria2c;
if (taskInfo.Status != TaskState.Running)
{
{
taskInfo.Status = TaskState.Running;
_dbGateService.UpdateTaskStatus(taskInfo);
}
@ -245,15 +246,23 @@ namespace iFileProxy.Services
taskInfo.Hash = MasterHelper.GetFileHash(Path.Combine(_appConfig.DownloadOptions.SavePath, taskInfo.FileName), FileHashAlgorithm.MD5);
_dbGateService.UpdateTaskHash(taskInfo);
}
// 触发任务完成事件
OnTaskCompleted(taskInfo);
}
catch (Exception ex)
{
_logger.Fatal($"执行下载任务时候出现致命问题: {ex.Message}");
taskInfo.Status= TaskState.Error;
_dbGateService.UpdateTaskStatus(taskInfo);
throw;
}
finally
{
if (OnTaskCompletedEvent)
{
// 触发任务完成事件
OnTaskCompleted(taskInfo);
}
}
}
@ -301,7 +310,7 @@ namespace iFileProxy.Services
/// 获取task在队列中的位置
/// </summary>
/// <param name="taskId"></param>
/// <returns></returns>
/// <returns>成功时返回队列中的位置 失败时返回-1</returns>
public int GetQueuePosition(string taskId)
{
int position = -1; // 默认值表示未找到
@ -332,11 +341,6 @@ namespace iFileProxy.Services
return new ServerTaskLoadInfo { Queuing = _pendingTasks.Count , Running = _runningTasks.Count};
}
/// <summary>
/// 尝试中断指定的任务
/// </summary>
/// <param name="taskInfo">要中断的任务信息</param>
/// <returns>是否成功中断任务</returns>
public void TryKillTask(TaskInfo taskInfo)
{
try
@ -349,7 +353,7 @@ namespace iFileProxy.Services
// 从运行任务字典中移除
_runningTasks.Remove(taskInfo.TaskId);
// 更新任务状态为错误
// 更新任务状态为已取消
taskInfo.Status = TaskState.Canceled;
_dbGateService.UpdateTaskStatus(taskInfo);
@ -358,7 +362,7 @@ namespace iFileProxy.Services
if (File.Exists(filePath))
{
File.Delete(filePath);
_logger.Debug($"已删除未完成的文件: {filePath}");
_logger.Debug($"已删除未下载完成的文件: {filePath}");
}
// 删除aria2c的临时文件
@ -369,6 +373,7 @@ namespace iFileProxy.Services
}
_logger.Information($"任务 {taskInfo.TaskId} 已中断");
OnTaskCompleted(taskInfo);
}
else
{
@ -382,6 +387,243 @@ namespace iFileProxy.Services
}
}
/// <summary>
/// 获取任务的进程详细信息
/// </summary>
/// <param name="taskId">任务ID</param>
/// <returns>进程详细信息如果任务不存在或未运行则返回null</returns>
public ProcessInfo? GetProcessInfo(string taskId)
{
try
{
if (!_runningTasks.TryGetValue(taskId, out var taskInfo) || taskInfo.Process == null)
{
return null;
}
var process = taskInfo.Process;
// 刷新进程信息
process.Refresh();
var processInfo = new ProcessInfo
{
ProcessId = process.Id,
ProcessName = process.ProcessName,
StartTime = process.StartTime,
RunningTime = (DateTime.Now - process.StartTime).TotalSeconds,
ThreadCount = process.Threads.Count,
PriorityClass = process.PriorityClass,
WorkingDirectory = process.StartInfo.WorkingDirectory,
CommandLine = process.StartInfo.Arguments
};
// 获取线程信息
foreach (ProcessThread thread in process.Threads)
{
try
{
processInfo.Threads.Add(new ThreadInfo
{
ThreadId = thread.Id,
ThreadState = thread.ThreadState,
Priority = thread.PriorityLevel
});
}
catch (Exception ex)
{
_logger.Warning($"获取线程信息失败: {ex.Message}");
// 继续处理下一个线程
continue;
}
}
return processInfo;
}
catch (Exception ex)
{
_logger.Error($"获取进程信息失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 重试失败的任务
/// </summary>
/// <param name="taskId">任务ID</param>
/// <returns>重试是否成功</returns>
public bool RetryTask(string taskId)
{
try
{
var taskInfo = JsonSerializer.Deserialize<List<TaskInfo>>(_dbGateService.GetTaskInfoByTid(taskId))?[0];
if (taskInfo == null)
{
_logger.Warning($"任务不存在: {taskId}");
return false;
}
// 检查任务状态是否为失败状态
if (taskInfo.Status != TaskState.Error && taskInfo.Status != TaskState.Canceled)
{
_logger.Warning($"任务 {taskId} 状态为 {taskInfo.Status},不能重试");
return false;
}
// 重置任务状态
taskInfo.Status = TaskState.NoInit;
taskInfo.UpdateTime = DateTime.Now;
_dbGateService.UpdateTaskStatus(taskInfo);
lock (_taskLock)
{
// 如果有等待的任务或达到最大并行数,加入队列
if (_pendingTasks.Count > 0 || _runningTasks.Count >= _appConfig.DownloadOptions.MaxParallelTasks)
{
_pendingTasks.Enqueue(taskInfo);
taskInfo.Status = TaskState.Queuing;
_dbGateService.UpdateTaskStatus(taskInfo);
_logger.Information($"任务 {taskId} 已加入等待队列");
}
else
{
// 直接启动任务
Task.Run(() => StartTaskAsync(taskInfo)).ConfigureAwait(false);
}
}
return true;
}
catch (Exception ex)
{
_logger.Error($"重试任务失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 将任务移动到等待队列最前面
/// </summary>
/// <param name="taskId">任务ID</param>
/// <returns>是否成功</returns>
public bool PrioritizeTask(string taskId)
{
try
{
lock (_taskLock)
{
// 如果队列为空,无需处理
if (_pendingTasks.Count == 0)
{
_logger.Warning("等待队列为空,无法进行优先级调整");
return false;
}
// 找到指定任务
var tempQueue = new Queue<TaskInfo>();
TaskInfo? targetTask = null;
while (_pendingTasks.Count > 0)
{
var task = _pendingTasks.Dequeue();
if (task.TaskId == taskId)
{
targetTask = task;
}
else
{
tempQueue.Enqueue(task);
}
}
// 如果找到目标任务,将其放到新队列的最前面
if (targetTask != null)
{
_pendingTasks.Enqueue(targetTask);
while (tempQueue.Count > 0)
{
_pendingTasks.Enqueue(tempQueue.Dequeue());
}
_logger.Information($"任务 {taskId} 已移至队列首位");
return true;
}
else
{
// 如果没找到,还原队列
while (tempQueue.Count > 0)
{
_pendingTasks.Enqueue(tempQueue.Dequeue());
}
_logger.Warning($"未在等待队列中找到任务: {taskId}");
return false;
}
}
}
catch (Exception ex)
{
_logger.Error($"调整任务优先级失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 立即执行指定任务
/// </summary>
/// <param name="taskId">任务ID</param>
/// <returns>是否成功</returns>
public bool ExecuteImmediately(string taskId)
{
try
{
lock (_taskLock)
{
// 找到指定任务
var tempQueue = new Queue<TaskInfo>();
TaskInfo? targetTask = null;
while (_pendingTasks.Count > 0)
{
var task = _pendingTasks.Dequeue();
if (task.TaskId == taskId)
{
targetTask = task;
}
else
{
tempQueue.Enqueue(task);
}
}
// 还原其他任务到队列
while (tempQueue.Count > 0)
{
_pendingTasks.Enqueue(tempQueue.Dequeue());
}
// 如果找到目标任务
if (targetTask != null)
{
_logger.Information($"正在立即执行任务: {taskId}");
// 启动任务
Task.Run(() => StartTaskAsync(targetTask, false)).ConfigureAwait(false);
return true;
}
else
{
_logger.Warning($"未在等待队列中找到任务: {taskId}");
return false;
}
}
}
catch (Exception ex)
{
_logger.Error($"立即执行任务失败: {ex.Message}");
return false;
}
}
//public bool DeleteTask(HttpContext c)
//{

View file

@ -13,7 +13,7 @@
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.1.0" />
</ItemGroup>
</Project>