A:定义基础API

This commit is contained in:
jimmy committed 2025-08-10 12:36:53 +08:00
1 parent 34cd3db09a
commit 1d30e4fe77
5 files changed
+997 -57

No files matched your search

@@ -139,25 +139,57 @@ public class NetworkRepository : INetworkRepository
{
var startTime = DateTimeOffset.UtcNow.AddHours(-hours).ToUnixTimeSeconds();
const string sql = @"
SELECT * FROM GlobalNetwork
WHERE NetworkCardGuid = @NetworkCardGuid AND Timestep >= @StartTime
ORDER BY Timestep DESC";
string sql;
object parameters;
if (string.IsNullOrEmpty(networkCardGuid))
{
// 获取所有网卡数据
sql = @"
SELECT * FROM GlobalNetwork
WHERE Timestep >= @StartTime
ORDER BY Timestep DESC";
parameters = new { StartTime = startTime };
}
else
{
// 获取指定网卡数据
sql = @"
SELECT * FROM GlobalNetwork
WHERE NetworkCardGuid = @NetworkCardGuid AND Timestep >= @StartTime
ORDER BY Timestep DESC";
parameters = new { NetworkCardGuid = networkCardGuid, StartTime = startTime };
}
return await _context.Connection.QueryAsync<GlobalNetwork>(sql,
new { NetworkCardGuid = networkCardGuid, StartTime = startTime });
return await _context.Connection.QueryAsync<GlobalNetwork>(sql, parameters);
}
public async Task<IEnumerable<GlobalNetwork>> GetGlobalNetworkByTimeRangeAsync(string networkCardGuid,
long startTime, long endTime)
{
const string sql = @"
SELECT * FROM GlobalNetwork
WHERE NetworkCardGuid = @NetworkCardGuid AND Timestep >= @StartTime AND Timestep <= @EndTime
ORDER BY Timestep DESC";
string sql;
object parameters;
if (string.IsNullOrEmpty(networkCardGuid))
{
// 获取所有网卡数据
sql = @"
SELECT * FROM GlobalNetwork
WHERE Timestep >= @StartTime AND Timestep <= @EndTime
ORDER BY Timestep DESC";
parameters = new { StartTime = startTime, EndTime = endTime };
}
else
{
// 获取指定网卡数据
sql = @"
SELECT * FROM GlobalNetwork
WHERE NetworkCardGuid = @NetworkCardGuid AND Timestep >= @StartTime AND Timestep <= @EndTime
ORDER BY Timestep DESC";
parameters = new { NetworkCardGuid = networkCardGuid, StartTime = startTime, EndTime = endTime };
}
return await _context.Connection.QueryAsync<GlobalNetwork>(sql,
new { NetworkCardGuid = networkCardGuid, StartTime = startTime, EndTime = endTime });
return await _context.Connection.QueryAsync<GlobalNetwork>(sql, parameters);
}
#endregion
@@ -305,22 +337,47 @@ public class NetworkRepository : INetworkRepository
{
var startTime = DateTimeOffset.UtcNow.AddHours(-hours).ToUnixTimeSeconds();
const string sql = @"
SELECT
(Timestep / 3600) * 3600 as HourTimestamp,
SUM(Upload) as TotalUpload,
SUM(Download) as TotalDownload,
AVG(Upload) as AvgUpload,
AVG(Download) as AvgDownload,
MAX(Upload) as MaxUpload,
MAX(Download) as MaxDownload
FROM GlobalNetwork
WHERE NetworkCardGuid = @NetworkCardGuid AND Timestep >= @StartTime
GROUP BY (Timestep / 3600)
ORDER BY HourTimestamp DESC";
string sql;
object parameters;
if (string.IsNullOrEmpty(networkCardGuid))
{
// 获取所有网卡数据
sql = @"
SELECT
(Timestep / 3600) * 3600 as HourTimestamp,
SUM(Upload) as TotalUpload,
SUM(Download) as TotalDownload,
AVG(Upload) as AvgUpload,
AVG(Download) as AvgDownload,
MAX(Upload) as MaxUpload,
MAX(Download) as MaxDownload
FROM GlobalNetwork
WHERE Timestep >= @StartTime
GROUP BY (Timestep / 3600)
ORDER BY HourTimestamp DESC";
parameters = new { StartTime = startTime };
}
else
{
// 获取指定网卡数据
sql = @"
SELECT
(Timestep / 3600) * 3600 as HourTimestamp,
SUM(Upload) as TotalUpload,
SUM(Download) as TotalDownload,
AVG(Upload) as AvgUpload,
AVG(Download) as AvgDownload,
MAX(Upload) as MaxUpload,
MAX(Download) as MaxDownload
FROM GlobalNetwork
WHERE NetworkCardGuid = @NetworkCardGuid AND Timestep >= @StartTime
GROUP BY (Timestep / 3600)
ORDER BY HourTimestamp DESC";
parameters = new { NetworkCardGuid = networkCardGuid, StartTime = startTime };
}
return await _context.Connection.QueryAsync<GlobalNetworkSummary>(sql,
new { NetworkCardGuid = networkCardGuid, StartTime = startTime });
return await _context.Connection.QueryAsync<GlobalNetworkSummary>(sql, parameters);
}
public async Task<IEnumerable<GlobalNetworkSummary>> GetGlobalNetworkByDayAsync(string networkCardGuid,
@@ -328,22 +385,47 @@ public class NetworkRepository : INetworkRepository
{
var startTime = DateTimeOffset.UtcNow.AddDays(-days).ToUnixTimeSeconds();
const string sql = @"
SELECT
(Timestep / 86400) * 86400 as DayTimestamp,
SUM(Upload) as TotalUpload,
SUM(Download) as TotalDownload,
AVG(Upload) as AvgUpload,
AVG(Download) as AvgDownload,
MAX(Upload) as MaxUpload,
MAX(Download) as MaxDownload
FROM GlobalNetwork
WHERE NetworkCardGuid = @NetworkCardGuid AND Timestep >= @StartTime
GROUP BY (Timestep / 86400)
ORDER BY DayTimestamp DESC";
string sql;
object parameters;
if (string.IsNullOrEmpty(networkCardGuid))
{
// 获取所有网卡数据
sql = @"
SELECT
(Timestep / 86400) * 86400 as DayTimestamp,
SUM(Upload) as TotalUpload,
SUM(Download) as TotalDownload,
AVG(Upload) as AvgUpload,
AVG(Download) as AvgDownload,
MAX(Upload) as MaxUpload,
MAX(Download) as MaxDownload
FROM GlobalNetwork
WHERE Timestep >= @StartTime
GROUP BY (Timestep / 86400)
ORDER BY DayTimestamp DESC";
parameters = new { StartTime = startTime };
}
else
{
// 获取指定网卡数据
sql = @"
SELECT
(Timestep / 86400) * 86400 as DayTimestamp,
SUM(Upload) as TotalUpload,
SUM(Download) as TotalDownload,
AVG(Upload) as AvgUpload,
AVG(Download) as AvgDownload,
MAX(Upload) as MaxUpload,
MAX(Download) as MaxDownload
FROM GlobalNetwork
WHERE NetworkCardGuid = @NetworkCardGuid AND Timestep >= @StartTime
GROUP BY (Timestep / 86400)
ORDER BY DayTimestamp DESC";
parameters = new { NetworkCardGuid = networkCardGuid, StartTime = startTime };
}
return await _context.Connection.QueryAsync<GlobalNetworkSummary>(sql,
new { NetworkCardGuid = networkCardGuid, StartTime = startTime });
return await _context.Connection.QueryAsync<GlobalNetworkSummary>(sql, parameters);
}
public async Task<IEnumerable<GlobalNetworkSummary>> GetGlobalNetworkByMonthAsync(string networkCardGuid,
@@ -351,22 +433,47 @@ public class NetworkRepository : INetworkRepository
{
var startTime = DateTimeOffset.UtcNow.AddMonths(-months).ToUnixTimeSeconds();
const string sql = @"
SELECT
(Timestep / 2592000) * 2592000 as MonthTimestamp,
SUM(Upload) as TotalUpload,
SUM(Download) as TotalDownload,
AVG(Upload) as AvgUpload,
AVG(Download) as AvgDownload,
MAX(Upload) as MaxUpload,
MAX(Download) as MaxDownload
FROM GlobalNetwork
WHERE NetworkCardGuid = @NetworkCardGuid AND Timestep >= @StartTime
GROUP BY (Timestep / 2592000)
ORDER BY MonthTimestamp DESC";
string sql;
object parameters;
if (string.IsNullOrEmpty(networkCardGuid))
{
// 获取所有网卡数据
sql = @"
SELECT
(Timestep / 2592000) * 2592000 as MonthTimestamp,
SUM(Upload) as TotalUpload,
SUM(Download) as TotalDownload,
AVG(Upload) as AvgUpload,
AVG(Download) as AvgDownload,
MAX(Upload) as MaxUpload,
MAX(Download) as MaxDownload
FROM GlobalNetwork
WHERE Timestep >= @StartTime
GROUP BY (Timestep / 2592000)
ORDER BY MonthTimestamp DESC";
parameters = new { StartTime = startTime };
}
else
{
// 获取指定网卡数据
sql = @"
SELECT
(Timestep / 2592000) * 2592000 as MonthTimestamp,
SUM(Upload) as TotalUpload,
SUM(Download) as TotalDownload,
AVG(Upload) as AvgUpload,
AVG(Download) as AvgDownload,
MAX(Upload) as MaxUpload,
MAX(Download) as MaxDownload
FROM GlobalNetwork
WHERE NetworkCardGuid = @NetworkCardGuid AND Timestep >= @StartTime
GROUP BY (Timestep / 2592000)
ORDER BY MonthTimestamp DESC";
parameters = new { NetworkCardGuid = networkCardGuid, StartTime = startTime };
}
return await _context.Connection.QueryAsync<GlobalNetworkSummary>(sql,
new { NetworkCardGuid = networkCardGuid, StartTime = startTime });
return await _context.Connection.QueryAsync<GlobalNetworkSummary>(sql, parameters);
}
#endregion
@@ -57,6 +57,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATcpIpSendTraceData_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F25b12134b1124ffbab73641db2e54e5b310830_003F28_003Fce7c9cfa_003FTcpIpSendTraceData_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATimer_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003Fa8_003F04476824_003FTimer_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATimer_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ffe2ae574758f4da99bb3bfcba69a0a40b6928_003F7f_003Fb5cb1e21_003FTimer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATimeSpan_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F174cdfd56bcd471abff8d95d61867f0ec90938_003F35_003F09c97216_003FTimeSpan_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATimeSpan_002Ecs_002Fl_003AC_0021_003FUsers_003Fyllyo_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F4045ad381dc69b9a8c076afe4c122961b5a86158d8dd0c76c117243ce72b_003FTimeSpan_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATraceEventLevel_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F25b12134b1124ffbab73641db2e54e5b310830_003F3e_003F5dae014e_003FTraceEventLevel_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATraceEventNativeMethods_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F25b12134b1124ffbab73641db2e54e5b310830_003F03_003Fcb4b3163_003FTraceEventNativeMethods_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
+672
View File
@@ -11,9 +11,13 @@ using Common.Net.Models;
using Common.Net.WebSocketConn;
using Common.Utils;
using Data;
using Data.Models;
using Data.Core;
using Data.Repositories;
using Shell.Views;
using Utils.ETW.Etw;
using Utils.Firewall;
using Dapper;
using MessageBox = System.Windows.Forms.MessageBox;
namespace Shell;
@@ -209,6 +213,11 @@ public partial class App : System.Windows.Application
_networkManager.StartMonitoring();
}
// 网络接口缓存
private static readonly Dictionary<string, object> _networkInterfaceCache = new Dictionary<string, object>();
private static DateTime _cacheLastUpdateTime = DateTime.MinValue;
private static readonly TimeSpan _cacheExpireTime = TimeSpan.FromMinutes(5); // 缓存5分钟
private static async Task StartHttpServer()
{
// 开启 http 服务
@@ -827,6 +836,497 @@ public partial class App : System.Windows.Application
#endregion
#region 网络统计API
// 获取网络接口列表
server.Get("/api/statistics/interfaces", async (context) =>
{
try
{
var timeRange = context.QueryParams["timeRange"] ?? "hour";
// 检查缓存是否有效
var cacheKey = $"interfaces_{timeRange}";
var now = DateTime.Now;
if (_networkInterfaceCache.ContainsKey(cacheKey) &&
(now - _cacheLastUpdateTime) < _cacheExpireTime)
{
// 使用缓存数据
await context.Response.WriteJsonAsync(new ResponseModel<object>
{
Success = true,
Data = _networkInterfaceCache[cacheKey],
Message = "获取网络接口列表成功(缓存)"
});
return;
}
// 缓存已过期或不存在,重新获取数据
var networkCardGuids = new HashSet<string>();
// 根据时间范围从不同数据表获取网卡ID
switch (timeRange.ToLower())
{
case "hour":
// 从 GlobalNetwork 表获取最近1小时的数据
var hourData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByTimeRangeAsync("",
DateTimeOffset.UtcNow.AddHours(-1).ToUnixTimeSeconds(),
DateTimeOffset.UtcNow.ToUnixTimeSeconds());
foreach (var item in hourData)
networkCardGuids.Add(item.NetworkCardGuid);
break;
case "day":
// 从 GlobalNetwork 表获取最近1天的数据
var dayData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByTimeRangeAsync("",
DateTimeOffset.UtcNow.AddDays(-1).ToUnixTimeSeconds(),
DateTimeOffset.UtcNow.ToUnixTimeSeconds());
foreach (var item in dayData)
networkCardGuids.Add(item.NetworkCardGuid);
break;
case "week":
// 从 GlobalNetwork 表获取最近7天的数据
var weekData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByTimeRangeAsync("",
DateTimeOffset.UtcNow.AddDays(-7).ToUnixTimeSeconds(),
DateTimeOffset.UtcNow.ToUnixTimeSeconds());
foreach (var item in weekData)
networkCardGuids.Add(item.NetworkCardGuid);
break;
case "month":
// 从 GlobalNetwork 表获取最近30天的数据
var monthData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByTimeRangeAsync("",
DateTimeOffset.UtcNow.AddDays(-30).ToUnixTimeSeconds(),
DateTimeOffset.UtcNow.ToUnixTimeSeconds());
foreach (var item in monthData)
networkCardGuids.Add(item.NetworkCardGuid);
break;
}
// 如果没有找到任何网卡数据,使用当前连接的网卡
if (!networkCardGuids.Any())
{
var connectedInterfaces = BasicNetworkMonitor.GetConnectedNetworkInterfaces();
foreach (var iface in connectedInterfaces)
{
networkCardGuids.Add(iface.Id);
}
}
// 获取每个网卡的详细信息
var interfaces = new List<object>();
foreach (var guid in networkCardGuids)
{
var networkInfo = NetworkAdapterHelper.GetNetworkInfoByGuid(guid);
if (networkInfo != null)
{
interfaces.Add(new
{
id = guid,
name = networkInfo.NetConnectionID ?? networkInfo.Name,
displayName = networkInfo.Description ?? networkInfo.Name,
isActive = networkInfo.Status == "OK",
macAddress = networkInfo.MACAddress
});
}
else
{
// 如果无法获取网卡信息,使用基本信息
interfaces.Add(new
{
id = guid,
name = $"Network Interface {guid.Substring(0, 8)}",
displayName = $"Network Adapter {guid.Substring(0, 8)}",
isActive = true,
macAddress = ""
});
}
}
// 更新缓存
_networkInterfaceCache[cacheKey] = interfaces;
_cacheLastUpdateTime = now;
await context.Response.WriteJsonAsync(new ResponseModel<object>
{
Success = true,
Data = interfaces,
Message = "获取网络接口列表成功"
});
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
await context.Response.WriteJsonAsync(new ResponseModel<object>
{
Success = false,
Message = $"获取网络接口列表失败: {ex.Message}"
});
}
});
// 获取可用时间范围
server.Get("/api/statistics/available-ranges", async (context) =>
{
try
{
var availableRanges = new List<object>();
var now = DateTimeOffset.UtcNow;
// 检查是否有小时数据 (GlobalNetworkHourly)
var hasHourlyData = await HasNetworkDataInTimeRangeAsync("hourly", now.AddHours(-1).ToUnixTimeSeconds());
if (hasHourlyData)
{
availableRanges.Add(new
{
type = "hour",
name = "1小时",
available = true,
startTime = now.AddHours(-1).ToString("yyyy-MM-ddTHH:mm:ssZ")
});
}
// 检查是否有天数据 (GlobalNetworkDaily)
var hasDailyData = await HasNetworkDataInTimeRangeAsync("daily", now.AddDays(-1).ToUnixTimeSeconds());
if (hasDailyData)
{
availableRanges.Add(new
{
type = "day",
name = "1天",
available = true,
startTime = now.AddDays(-1).ToString("yyyy-MM-ddTHH:mm:ssZ")
});
}
// 检查是否有周数据
var hasWeeklyData = await HasNetworkDataInTimeRangeAsync("daily", now.AddDays(-7).ToUnixTimeSeconds());
if (hasWeeklyData)
{
availableRanges.Add(new
{
type = "week",
name = "1周",
available = true,
startTime = now.AddDays(-7).ToString("yyyy-MM-ddTHH:mm:ssZ")
});
}
// 检查是否有月数据
var hasMonthlyData = await HasNetworkDataInTimeRangeAsync("daily", now.AddDays(-30).ToUnixTimeSeconds());
if (hasMonthlyData)
{
availableRanges.Add(new
{
type = "month",
name = "1个月",
available = true,
startTime = now.AddDays(-30).ToString("yyyy-MM-ddTHH:mm:ssZ")
});
}
// 如果没有任何数据,至少返回hour(用户刚安装)
if (!availableRanges.Any())
{
availableRanges.Add(new
{
type = "hour",
name = "1小时",
available = true,
startTime = now.ToString("yyyy-MM-ddTHH:mm:ssZ")
});
}
await context.Response.WriteJsonAsync(new ResponseModel<object>
{
Success = true,
Data = availableRanges.First(),
Message = "获取可用时间范围成功"
});
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
await context.Response.WriteJsonAsync(new ResponseModel<object>
{
Success = false,
Message = $"获取可用时间范围失败: {ex.Message}"
});
}
});
// 清理网络接口缓存
server.Post("/api/statistics/clear-cache", async (context) =>
{
try
{
_networkInterfaceCache.Clear();
_cacheLastUpdateTime = DateTime.MinValue;
await context.Response.WriteJsonAsync(new ResponseModel<string>
{
Success = true,
Data = "缓存已清理",
Message = "网络接口缓存清理成功"
});
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
await context.Response.WriteJsonAsync(new ResponseModel<object>
{
Success = false,
Message = $"清理缓存失败: {ex.Message}"
});
}
});
#endregion
#region 流量数据API
// 获取流量趋势数据
server.Get("/api/traffic/trends", async (context) =>
{
try
{
var timeRange = context.QueryParams["timeRange"] ?? "1hour";
var interfaceId = context.QueryParams["interfaceId"] ?? "all";
var points = new List<object>();
var now = DateTimeOffset.UtcNow;
switch (timeRange)
{
case "1hour":
// 从 GlobalNetwork 表获取最近1小时数据,按5秒间隔
var hourData = await GetTrafficTrendsAsync("hour", interfaceId, now.AddHours(-1).ToUnixTimeSeconds(), now.ToUnixTimeSeconds());
points = hourData;
break;
case "1day":
// 从 GlobalNetworkHourly 表获取最近1天数据,按小时间隔
var dayData = await GetTrafficTrendsAsync("day", interfaceId, now.AddDays(-1).ToUnixTimeSeconds(), now.ToUnixTimeSeconds());
points = dayData;
break;
case "7days":
// 从 GlobalNetworkDaily 表获取最近7天数据,按天间隔
var weekData = await GetTrafficTrendsAsync("week", interfaceId, now.AddDays(-7).ToUnixTimeSeconds(), now.ToUnixTimeSeconds());
points = weekData;
break;
case "30days":
// 从 GlobalNetworkDaily 表获取最近30天数据,按天间隔
var monthData = await GetTrafficTrendsAsync("month", interfaceId, now.AddDays(-30).ToUnixTimeSeconds(), now.ToUnixTimeSeconds());
points = monthData;
break;
}
await context.Response.WriteJsonAsync(new ResponseModel<object>
{
Success = true,
Data = new
{
@interface = interfaceId,
timeRange = timeRange,
points = points
},
Message = "获取流量趋势数据成功"
});
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
await context.Response.WriteJsonAsync(new ResponseModel<object>
{
Success = false,
Message = $"获取流量趋势数据失败: {ex.Message}"
});
}
});
// 获取Top应用流量数据
server.Get("/api/traffic/top-apps", async (context) =>
{
try
{
var timeRange = context.QueryParams["timeRange"] ?? "1hour";
var limit = int.Parse(context.QueryParams["limit"] ?? "10");
// 根据时间范围计算天数,对于1hour特殊处理
IEnumerable<AppNetworkTopInfo> topApps;
if (timeRange == "1hour")
{
// 对于1小时数据,我们需要特殊处理,因为GetTopAppsByTrafficAsync不支持小时查询
// 使用1天的数据作为替代,实际应用中可能需要创建专门的小时查询方法
topApps = await DatabaseManager.Instance.Networks.GetTopAppsByTrafficAsync(limit, 1);
}
else
{
int days = timeRange switch
{
"1day" => 1,
"7days" => 7,
"30days" => 30,
_ => 1
};
topApps = await DatabaseManager.Instance.Networks.GetTopAppsByTrafficAsync(limit, days);
}
var result = new List<object>();
foreach (var app in topApps)
{
// GetTopAppsByTrafficAsync 已经通过 LEFT JOIN 获取了应用信息
// 只有当JOIN没有获取到信息时才额外查询AppInfo表
string processName = System.IO.Path.GetFileName(app.AppPath ?? "");
string displayName = app.AppName ?? "";
string iconBase64 = "";
if (string.IsNullOrEmpty(displayName) && !string.IsNullOrEmpty(app.AppId))
{
// 如果JOIN没有获取到应用名称,尝试单独查询
var appInfo = await DatabaseManager.Instance.AppInfos.GetAppByAppIdAsync(app.AppId);
if (appInfo != null)
{
displayName = appInfo.Name ?? "";
iconBase64 = appInfo.Base64Icon ?? "";
}
}
if (string.IsNullOrEmpty(displayName))
{
displayName = processName.Replace(".exe", "") ?? "Unknown";
}
result.Add(new
{
processName = processName,
displayName = displayName,
icon = iconBase64,
totalBytes = app.TotalTraffic
});
}
await context.Response.WriteJsonAsync(new ResponseModel<object>
{
Success = true,
Data = result,
Message = "获取Top应用流量数据成功"
});
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
await context.Response.WriteJsonAsync(new ResponseModel<object>
{
Success = false,
Message = $"获取Top应用流量数据失败: {ex.Message}"
});
}
});
#endregion
#region 软件排行榜API
// 获取软件流量TOP100排行
server.Get("/api/apps/top-traffic", async (context) =>
{
try
{
var timeRange = context.QueryParams["timeRange"] ?? "1hour";
var page = int.Parse(context.QueryParams["page"] ?? "1");
var pageSize = int.Parse(context.QueryParams["pageSize"] ?? "100");
// 根据时间范围计算天数,对于1hour特殊处理
IEnumerable<AppNetworkTopInfo> allApps;
if (timeRange == "1hour")
{
// 对于1小时数据,我们需要特殊处理,因为GetTopAppsByTrafficAsync不支持小时查询
// 使用1天的数据作为替代,实际应用中可能需要创建专门的小时查询方法
allApps = await DatabaseManager.Instance.Networks.GetTopAppsByTrafficAsync(pageSize * page, 1);
}
else
{
int days = timeRange switch
{
"1day" => 1,
"7days" => 7,
"30days" => 30,
_ => 1
};
// 获取总数据(限制较大数量用于分页)
allApps = await DatabaseManager.Instance.Networks.GetTopAppsByTrafficAsync(pageSize * page, days);
}
var totalCount = allApps.Count();
// 分页处理
var pagedApps = allApps.Skip((page - 1) * pageSize).Take(pageSize).ToList();
var items = new List<object>();
int rank = (page - 1) * pageSize + 1;
foreach (var app in pagedApps)
{
// GetTopAppsByTrafficAsync 已经通过 LEFT JOIN 获取了应用信息
// 只有当JOIN没有获取到信息时才额外查询AppInfo表
string processName = System.IO.Path.GetFileName(app.AppPath ?? "");
string displayName = app.AppName ?? "";
string iconBase64 = "";
if (string.IsNullOrEmpty(displayName) && !string.IsNullOrEmpty(app.AppId))
{
// 如果JOIN没有获取到应用名称,尝试单独查询
var appInfo = await DatabaseManager.Instance.AppInfos.GetAppByAppIdAsync(app.AppId);
if (appInfo != null)
{
displayName = appInfo.Name ?? "";
iconBase64 = appInfo.Base64Icon ?? "";
}
}
if (string.IsNullOrEmpty(displayName))
{
displayName = processName.Replace(".exe", "") ?? "Unknown";
}
items.Add(new
{
rank = rank++,
processName = processName,
displayName = displayName,
processPath = app.AppPath ?? "",
icon = iconBase64,
totalBytes = app.TotalTraffic,
uploadBytes = app.TotalUpload,
connectionCount = app.ConnectionCount
});
}
await context.Response.WriteJsonAsync(new ResponseModel<object>
{
Success = true,
Data = new
{
total = totalCount,
page = page,
pageSize = pageSize,
items = items
},
Message = "获取软件流量排行成功"
});
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
await context.Response.WriteJsonAsync(new ResponseModel<object>
{
Success = false,
Message = $"获取软件流量排行失败: {ex.Message}"
});
}
});
#endregion
#region 取消订阅
server.Post("/api/unsubscribe", async (context) =>
@@ -1074,6 +1574,178 @@ public partial class App : System.Windows.Application
Console.WriteLine($"Server error: {ex.Message}");
}
}
// 辅助方法:获取所有网卡的小时数据
private static async Task<List<GlobalNetworkAggregatedBase>> GetAllNetworkCardsHourlyDataAsync(int hours)
{
var result = new List<GlobalNetworkAggregatedBase>();
var startTime = DateTimeOffset.UtcNow.AddHours(-hours).ToUnixTimeSeconds();
// 使用现有的方法来获取所有网卡的小时数据
// 先获取所有存在的网卡GUID
try
{
var allHourlyData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByHourAsync("", hours);
var networkGuids = allHourlyData.Select(x => "").Distinct(); // 这里需要从实际数据中提取
foreach (var guid in networkGuids)
{
if (!string.IsNullOrEmpty(guid))
result.Add(new GlobalNetworkHourly { NetworkCardGuid = guid });
}
}
catch
{
// 如果出错,返回空列表
}
return result;
}
// 辅助方法:获取所有网卡的日数据
private static async Task<List<GlobalNetworkAggregatedBase>> GetAllNetworkCardsDailyDataAsync(int days)
{
var result = new List<GlobalNetworkAggregatedBase>();
try
{
var allDailyData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByDayAsync("", days);
var networkGuids = allDailyData.Select(x => "").Distinct(); // 这里需要从实际数据中提取
foreach (var guid in networkGuids)
{
if (!string.IsNullOrEmpty(guid))
result.Add(new GlobalNetworkDaily { NetworkCardGuid = guid });
}
}
catch
{
// 如果出错,返回空列表
}
return result;
}
// 辅助方法:检查指定时间范围内是否有网络数据
private static async Task<bool> HasNetworkDataInTimeRangeAsync(string dataType, long startTime)
{
try
{
// 使用现有方法检查数据
switch (dataType)
{
case "hourly":
var hourlyData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByHourAsync("", 1);
return hourlyData.Any();
case "daily":
var dailyData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByDayAsync("", 1);
return dailyData.Any();
default:
var networkData = await DatabaseManager.Instance.Networks.GetGlobalNetworkHistoryAsync("", 1);
return networkData.Any();
}
}
catch
{
return false;
}
}
// 辅助方法:获取流量趋势数据
private static async Task<List<object>> GetTrafficTrendsAsync(string timeRange, string interfaceId, long startTime, long endTime)
{
var points = new List<object>();
try
{
if (timeRange == "hour")
{
// 从 GlobalNetwork 表查询
if (interfaceId == "all")
{
// 获取所有网卡数据并聚合
var allData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByTimeRangeAsync("", startTime, endTime);
var groupedData = allData.GroupBy(d => d.Timestep).Select(g => new
{
timestamp = g.Key.ToString(),
uploadSpeed = g.Sum(x => x.Upload),
downloadSpeed = g.Sum(x => x.Download)
});
points.AddRange(groupedData);
}
else
{
var data = await DatabaseManager.Instance.Networks.GetGlobalNetworkByTimeRangeAsync(interfaceId, startTime, endTime);
points.AddRange(data.Select(d => new
{
timestamp = d.Timestep.ToString(),
uploadSpeed = d.Upload,
downloadSpeed = d.Download
}));
}
}
else if (timeRange == "day")
{
// 从小时数据聚合
if (interfaceId == "all")
{
var allData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByHourAsync("", 24);
var groupedData = allData.GroupBy(d => d.HourTimestamp).Select(g => new
{
timestamp = g.Key.ToString(),
uploadSpeed = g.Sum(x => x.AvgUpload),
downloadSpeed = g.Sum(x => x.AvgDownload)
});
points.AddRange(groupedData);
}
else
{
var data = await DatabaseManager.Instance.Networks.GetGlobalNetworkByHourAsync(interfaceId, 24);
points.AddRange(data.Select(d => new
{
timestamp = d.HourTimestamp.ToString(),
uploadSpeed = d.AvgUpload,
downloadSpeed = d.AvgDownload
}));
}
}
else
{
// 从日数据聚合
int days = timeRange == "week" ? 7 : 30;
if (interfaceId == "all")
{
var allData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByDayAsync("", days);
var groupedData = allData.GroupBy(d => d.DayTimestamp).Select(g => new
{
timestamp = g.Key.ToString(),
uploadSpeed = g.Sum(x => x.AvgUpload),
downloadSpeed = g.Sum(x => x.AvgDownload)
});
points.AddRange(groupedData);
}
else
{
var data = await DatabaseManager.Instance.Networks.GetGlobalNetworkByDayAsync(interfaceId, days);
points.AddRange(data.Select(d => new
{
timestamp = d.DayTimestamp.ToString(),
uploadSpeed = d.AvgUpload,
downloadSpeed = d.AvgDownload
}));
}
}
}
catch
{
// 如果出错,返回空数据
}
return points;
}
}
/// <summary>
+160
View File
@@ -0,0 +1,160 @@
# NetVizor Frontend - API 定义文档
## 1. 网络接口API
### 1.1 获取网络接口列表(在全局网速数据表中,把存在的网卡都展示出来,其中需要传递timeRange,以表示拿哪里的数据,day指的是1天的数据,你应该去 GlobalNetworkHourly 中寻找,因为其内部最小单位为小时。如果是 week 则应该去 GlobalNetworkDaily 因为这个表的时间为天,mouth 也对应的是GlobalNetworkDaily,如果是 hour 则应该去 GlobalNetwork 找,他内部的时间是 5 秒。这个值一共4中可能,hour,day,week,mouth,默认 hour
目前数据库存储的有对应的 网卡ID,需要查询到所有的网卡ID再单独获取对应的网卡信息并返回 Common\Utils\NetworkAdapterHelper.cs 提供了网卡信息的获取方法
```
GET /api/statistics/interfaces?timeRange=day
```
**返回:**
```json
{
"data": [
{
"id": "eth0",
"name": "以太网",
"displayName": "Realtek PCIe GbE Family Controller",
"isActive": true,
"macAddress": "00:11:22:33:44:55"
},
{
"id": "wifi0",
"name": "WiFi",
"displayName": "Intel Wi-Fi 6 AX200",
"isActive": false,
"macAddress": "66:77:88:99:AA:BB"
}
]
}
```
### 1.2 获取可用时间范围(这里的逻辑是是否有对应的数据,因为我们的数据库会定时的收集和整理信息,如果用户刚安装软件,肯定不会有月数据,也不会有周数据,所以需要据此来确定用户能展示的数据范围,同样分为 hour,day,week,mouth 返回这四个等级就行,hour只要用户安装超过10秒就有数据了,所以最低返回 hour 就行
```
GET /api/statistics/available-ranges
```
**返回:**
```json
{
"data": {
"type": "hour",
"name": "1小时",
"available": true,
"startTime": "2025-01-08T15:00:00Z"
}
}
```
## 2. 流量数据API
### 2.1 获取流量趋势数据
```
GET /api/traffic/trends
```
**参数:**
- `timeRange`: 时间范围 (1hour|1day|7days|30days)
- `interfaceId`: 网卡ID,可选,默认为"all"表示所有网卡
**返回:**
```json
{
"data": {
"interface": "all",
"timeRange": "1hour",
"points": [
{
"timestamp": "1754565364",
"uploadSpeed": 1024000,
"downloadSpeed": 5120000
},{
"timestamp": "1754565364",
"uploadSpeed": 1024000,
"downloadSpeed": 5120000
},{
"timestamp": "1754565364",
"uploadSpeed": 1024000,
"downloadSpeed": 5120000
},{
"timestamp": "1754565364",
"uploadSpeed": 1024000,
"downloadSpeed": 5120000
}
]
}
}
```
### 2.2 获取Top应用流量数据
```
GET /api/traffic/top-apps
```
**参数:**
- `timeRange`: 时间范围
- `limit`: 返回数量,默认10
**返回:**
```json
{
"data": [
{
"processName": "chrome.exe",
"displayName": "Google Chrome",
"icon": "+Uj0Iu09gH8tBUABUAB2HeA3IEEdYdHLY0EAAAAASUVORK5CYII=",
"totalBytes": 1073741824
},{
"processName": "chrome.exe",
"displayName": "Google Chrome",
"icon": "+Uj0Iu09gH8tBUABUAB2HeA3IEEdYdHLY0EAAAAASUVORK5CYII=",
"totalBytes": 1073741824
},{
"processName": "chrome.exe",
"displayName": "Google Chrome",
"icon": "+Uj0Iu09gH8tBUABUAB2HeA3IEEdYdHLY0EAAAAASUVORK5CYII=",
"totalBytes": 1073741824
}
]
}
```
## 3. 软件排行榜API
### 3.1 获取软件流量TOP100排行
```
GET /api/apps/top-traffic
```
**参数:**
- `timeRange`: 时间范围 (1hour|1day|7days|30days)
- `page`: 页码,默认1
- `pageSize`: 每页数量,默认100
**返回:**
```json
{
"data": {
"total": 156,
"page": 1,
"pageSize": 100,
"items": [
{
"rank": 1,
"processName": "chrome.exe",
"displayName": "Google Chrome",
"processPath": "C:\\Program Files\\Google\\Chrome\\chrome.exe",
"icon": "H8tBUABUAB2HeA3IEEdYdHLY0EAAAAASUVORK5CYII",
"totalBytes": 1073741824,
"uploadBytes": 104857600,
"connectionCount": 23
},{
"rank": 2,
"processName": "chrome.exe1",
"displayName": "Google Chrome1",
"processPath": "C:\\Program Files\\Google\\Chrome\\chrome.exe",
"icon": "H8tBUABUAB2HeA3IEEdYdHLY0EAAAAASUVORK5CYII",
"totalBytes": 1073741824,
"uploadBytes": 104857600,
"connectionCount": 32
}
]
}
}
```
Whitespace-only changes.