A:剩余diamagnetic提交

This commit is contained in:
jimmy committed 2025-08-15 19:05:30 +08:00
1 parent 04bb8099ac
commit 0981ef5d3e
8 files changed
+541

No files matched your search

@@ -0,0 +1,213 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Data;
using Data.Models;
namespace Shell.Utils;
public static class NetworkApiHelper
{
private static readonly Dictionary<string, object> _networkInterfaceCache = new();
private static DateTime _cacheLastUpdateTime = DateTime.MinValue;
private static readonly TimeSpan _cacheExpireTime = TimeSpan.FromMinutes(5);
public static Dictionary<string, object> NetworkInterfaceCache => _networkInterfaceCache;
public static DateTime CacheLastUpdateTime => _cacheLastUpdateTime;
public static TimeSpan CacheExpireTime => _cacheExpireTime;
public static void UpdateCacheTime() => _cacheLastUpdateTime = DateTime.Now;
public static async Task<List<GlobalNetworkAggregatedBase>> GetAllNetworkCardsHourlyDataAsync(int hours)
{
var result = new List<GlobalNetworkAggregatedBase>();
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;
}
public 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;
}
public 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;
}
}
public static async Task<List<object>> GetTrafficTrendsAsync(string timeRange, string interfaceId, long startTime, long endTime)
{
var points = new List<object>();
try
{
if (timeRange == "hour")
{
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;
}
public static string GetPortServiceName(int port)
{
return port switch
{
21 => "FTP",
22 => "SSH",
23 => "Telnet",
25 => "SMTP",
53 => "DNS",
80 => "HTTP",
110 => "POP3",
123 => "NTP",
143 => "IMAP",
443 => "HTTPS",
993 => "IMAPS",
995 => "POP3S",
1433 => "SQL Server",
3306 => "MySQL",
3389 => "RDP",
5432 => "PostgreSQL",
6379 => "Redis",
8080 => "HTTP-Alt",
8443 => "HTTPS-Alt",
_ when port >= 1024 && port <= 5000 => "User Port",
_ when port > 5000 && port < 32768 => "Service Port",
_ when port >= 32768 => "Dynamic Port",
_ => "Unknown"
};
}
}
@@ -0,0 +1,128 @@
using Microsoft.Win32;
using System;
using System.IO;
using System.Reflection;
using Common.Logger;
namespace Shell.Utils;
/// <summary>
/// Windows开机自启动辅助类
/// </summary>
public static class WindowsStartupHelper
{
private const string REGISTRY_KEY = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
private const string APP_NAME = "NetVizor";
/// <summary>
/// 设置开机自启动
/// </summary>
/// <param name="enable">是否启用自启动</param>
/// <returns>操作是否成功</returns>
public static bool SetStartup(bool enable)
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(REGISTRY_KEY, true);
if (key == null)
{
Log.Error4Ctx("无法打开注册表项");
return false;
}
if (enable)
{
// 获取当前执行文件的路径
var executablePath = Assembly.GetExecutingAssembly().Location;
// 如果是.NET Core/.NET 5+应用,使用exe文件路径
if (executablePath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
{
executablePath = Path.ChangeExtension(executablePath, ".exe");
}
// 如果exe文件不存在,尝试从当前目录查找
if (!File.Exists(executablePath))
{
var currentDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var exeName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) + ".exe";
executablePath = Path.Combine(currentDir ?? "", exeName);
}
if (!File.Exists(executablePath))
{
Log.Error4Ctx($"找不到可执行文件: {executablePath}");
return false;
}
// 添加自启动项
key.SetValue(APP_NAME, $"\"{executablePath}\"");
Log.Info($"已设置开机自启动: {executablePath}");
}
else
{
// 删除自启动项
if (key.GetValue(APP_NAME) != null)
{
key.DeleteValue(APP_NAME);
Log.Info("已取消开机自启动");
}
}
return true;
}
catch (Exception ex)
{
Log.Error4Ctx($"设置开机自启动失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 检查是否已设置开机自启动
/// </summary>
/// <returns>是否已设置自启动</returns>
public static bool IsStartupEnabled()
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(REGISTRY_KEY, false);
if (key == null)
{
return false;
}
var value = key.GetValue(APP_NAME)?.ToString();
return !string.IsNullOrEmpty(value);
}
catch (Exception ex)
{
Log.Warning($"检查开机自启动状态失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 获取当前设置的启动路径
/// </summary>
/// <returns>启动路径,如果未设置则返回null</returns>
public static string? GetStartupPath()
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(REGISTRY_KEY, false);
if (key == null)
{
return null;
}
var value = key.GetValue(APP_NAME)?.ToString();
return value?.Trim('"'); // 移除引号
}
catch (Exception ex)
{
Log.Warning($"获取开机自启动路径失败: {ex.Message}");
return null;
}
}
}
+63
View File
@@ -0,0 +1,63 @@
import { globalIgnores } from 'eslint/config';
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript';
import pluginVue from 'eslint-plugin-vue';
import pluginVitest from '@vitest/eslint-plugin';
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting';
import tsParser from '@typescript-eslint/parser';
import vuePlugin from 'eslint-plugin-vue';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import prettier from 'eslint-config-prettier';
export default defineConfigWithVueTs({
name: 'app/files-to-lint',
files: ['**/*.{ts,mts,tsx,vue}']
}, globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']), pluginVue.configs['flat/essential'], vueTsConfigs.recommended,
// 重要:确保自定义规则配置优先级更高
{
rules: {
// 完全禁用未使用变量的检查 - 更强力的方式
'@typescript-eslint/no-unused-vars': 'off',
'no-unused-vars': 'off'
// 或者使用你之前的配置(二选一)
/*
'@typescript-eslint/no-unused-vars': ['warn', {
'varsIgnorePattern': '^_',
'argsIgnorePattern': '^_',
// 添加更多宽松配置
'ignoreRestSiblings': true,
'caughtErrors': 'none'
}],
*/
}
}, {
...pluginVitest.configs.recommended,
files: ['src/**/__tests__/*'],
languageOptions: {
parser: tsParser,
ecmaVersion: 'latest',
sourceType: 'module',
globals: {
window: 'readonly',
document: 'readonly'
}
},
plugins: {
vue: vuePlugin,
'@typescript-eslint': tsPlugin
},
rules: {
// ❗ 严重错误(保留)
'no-undef': 'error',
'vue/no-unused-components': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
// ✅ 风格类(宽松)
'vue/multi-word-component-names': 'off',
'vue/max-attributes-per-line': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
// ✅ 开发方便
'no-console': 'off',
'no-debugger': 'warn'
}
}, {
...prettier
}, skipFormatting);
@@ -0,0 +1,36 @@
import type { IBridge, LogParam, MessagePayload } from './IBridge'
export class CSharpBridge implements IBridge {
private eventListeners = new Map<string, Set<(...args: any[]) => void>>()
log(param: LogParam): void {
console.log(`[${param.level.toUpperCase()}] ${param.message}`, param.data)
}
async invokeMethod(methodName: string, ...args: any[]): Promise<any> {
// 默认实现
console.warn(`CSharpBridge.invokeMethod not implemented: ${methodName}`, args)
return Promise.resolve(null)
}
addEventListener(eventName: string, callback: (...args: any[]) => void): void {
if (!this.eventListeners.has(eventName)) {
this.eventListeners.set(eventName, new Set())
}
this.eventListeners.get(eventName)!.add(callback)
}
removeEventListener(eventName: string, callback: (...args: any[]) => void): void {
const listeners = this.eventListeners.get(eventName)
if (listeners) {
listeners.delete(callback)
}
}
protected emit(eventName: string, ...args: any[]): void {
const listeners = this.eventListeners.get(eventName)
if (listeners) {
listeners.forEach(callback => callback(...args))
}
}
}
@@ -0,0 +1,19 @@
// 桥接接口定义
export interface IBridge {
log(param: LogParam): void
invokeMethod(methodName: string, ...args: any[]): Promise<any>
addEventListener(eventName: string, callback: (...args: any[]) => void): void
removeEventListener(eventName: string, callback: (...args: any[]) => void): void
}
export interface LogParam {
level: 'info' | 'warn' | 'error' | 'debug'
message: string
data?: any
}
export interface MessagePayload<T = any> {
type: string
data: T
timestamp?: number
}
@@ -0,0 +1,36 @@
import { CSharpBridge } from './CSharpBridge'
import type { LogParam } from './IBridge'
export class TauriBridge extends CSharpBridge {
async invokeMethod(methodName: string, ...args: any[]): Promise<any> {
try {
// 如果运行在 Tauri 环境中
if (window.__TAURI__) {
const { invoke } = window.__TAURI__.tauri
return await invoke(methodName, ...args)
}
// 否则使用父类的默认实现
return super.invokeMethod(methodName, ...args)
} catch (error) {
console.error(`TauriBridge.invokeMethod error:`, error)
throw error
}
}
log(param: LogParam): void {
// 在 Tauri 环境中,可以使用更高级的日志记录
super.log(param)
}
}
// 扩展 Window 接口
declare global {
interface Window {
__TAURI__?: {
tauri: {
invoke: (cmd: string, ...args: any[]) => Promise<any>
}
}
}
}
+2
View File
@@ -0,0 +1,2 @@
declare const _default: import("vite").UserConfigFnObject;
export default _default;
+44
View File
@@ -0,0 +1,44 @@
import { fileURLToPath, URL } from 'node:url';
import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue';
import AutoImport from 'unplugin-auto-import/vite';
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers';
import Components from 'unplugin-vue-components/vite';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd());
return {
plugins: [
vue(),
AutoImport({
imports: [
'vue',
{
'naive-ui': [
'useDialog',
'useMessage',
'useNotification',
'useLoadingBar'
]
}
]
}),
Components({
resolvers: [NaiveUiResolver()]
})
],
// 防止 Vite 掩盖 Rust 错误
clearScreen: false,
server: {
port: 3000,
strictPort: true,
host: "0.0.0.0",
watch: {},
},
build: {},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
};
});