框架优化

This commit is contained in:
hsc
2025-12-19 16:58:34 +08:00
parent 69aaa5c4e5
commit 4da28d08a8
46 changed files with 1178 additions and 793 deletions

View File

@@ -1,47 +0,0 @@
using BaseFrame.ViewModels;
using BaseFrame.ViewModels.Dialogs;
using BaseFrame.Views;
using BaseFrame.Views.Dialogs;
using Castle.DynamicProxy;
using ECCS.Views;
using System.Configuration;
using System.Data;
using System.Reflection;
using System.Windows;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace BaseFrame
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : PrismApplication
{
protected override Window CreateShell()
{
return Container.Resolve<ShellView>();
}
protected override void OnInitialized()
{
base.OnInitialized();
var regionManager = Container.Resolve<IRegionManager>();
regionManager.RequestNavigate("ShellViewManager", "MonitorView");
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
//注册视图
containerRegistry.RegisterForNavigation<MonitorView>("MonitorView");
containerRegistry.RegisterForNavigation<SettingView>("SettingView");
containerRegistry.RegisterForNavigation<UpdateInfoView>("UpdateInfoView");
//注册弹窗
containerRegistry.RegisterDialog<MessageBoxView, MessageBoxViewModel>("MessageBox");
}
}
}

View File

@@ -1,66 +0,0 @@
using Newtonsoft.Json;
using System;
using System.IO;
namespace ECCS
{
public sealed class MultidimensionalTableConfig
{
private static readonly object _lock = new();
private static MultidimensionalTableConfig? _instance;
/// <summary>
/// 单例实例(首次访问自动从文件加载)
/// </summary>
public static MultidimensionalTableConfig Instance
{
get
{
lock (_lock)
{
if (_instance == null)
{
_instance = Load();
}
return _instance;
}
}
}
/// <summary>
/// exe 目录下的 config.json
/// </summary>
[JsonIgnore]
private static string ConfigFilePath =>
Path.Combine(AppContext.BaseDirectory, "config.json");
// ===== 配置项 =====
public string AppToken { get; set; }
public string DefaultTableId { get; set; }
public bool DefaultTableIsDeleted { get; set; }
/// <summary>
/// 保存到 config.json
/// </summary>
public void Save()
{
var json = JsonConvert.SerializeObject(this, Formatting.Indented);
File.WriteAllText(ConfigFilePath, json);
}
/// <summary>
/// 从 config.json 加载
/// </summary>
private static MultidimensionalTableConfig Load()
{
if (!File.Exists(ConfigFilePath))
{
return new MultidimensionalTableConfig();
}
var json = File.ReadAllText(ConfigFilePath);
return JsonConvert.DeserializeObject<MultidimensionalTableConfig>(json)
?? new MultidimensionalTableConfig();
}
}
}

View File

@@ -1,160 +0,0 @@
using Newtonsoft.Json;
namespace Common.DTOS
{
public class FeishuResponse<T>
{
[JsonProperty("code")]
public int Code { get; set; }
[JsonProperty("msg")]
public string Msg { get; set; }
[JsonProperty("data")]
public T Data { get; set; }
}
#region
public class CreateBitableAppData
{
[JsonProperty("app")]
public AppInfo App { get; set; }
}
public class AppInfo
{
[JsonProperty("app_token")]
public string AppToken { get; set; }
[JsonProperty("default_table_id")]
public string DefaultTableId { get; set; }
[JsonProperty("folder_token")]
public string FolderToken { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
}
#endregion
#region
public class CreateTableData
{
[JsonProperty("default_view_id")]
public string DefaultViewId { get; set; }
[JsonProperty("field_id_list")]
public List<string> FieldIdList { get; set; }
[JsonProperty("table_id")]
public string TableId { get; set; }
}
#endregion
#region
public class QueryTableListData
{
[JsonProperty("has_more")]
public bool HasMore { get; set; }
[JsonProperty("items")]
public List<TableItem> Items { get; set; }
[JsonProperty("page_token")]
public string PageToken { get; set; }
[JsonProperty("total")]
public int Total { get; set; }
}
public class TableItem
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("revision")]
public int Revision { get; set; }
[JsonProperty("table_id")]
public string TableId { get; set; }
}
#endregion
#region
public class CreateRecordData
{
[JsonProperty("record")]
public RecordInfo Record { get; set; }
}
public class RecordInfo
{
/// <summary>
/// 字段数据(字段名 -> 值)
/// </summary>
[JsonProperty("fields")]
public Dictionary<string, object> Fields { get; set; }
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("record_id")]
public string RecordId { get; set; }
}
#endregion
#region
public class QueryRecordListData
{
[JsonProperty("has_more")]
public bool HasMore { get; set; }
[JsonProperty("items")]
public List<RecordItem> Items { get; set; }
[JsonProperty("total")]
public int Total { get; set; }
}
public class RecordItem
{
[JsonProperty("record_id")]
public string RecordId { get; set; }
[JsonProperty("fields")]
public Dictionary<string, object> Fields { get; set; }
}
#endregion
#region
public class CreateRecordsRequest
{
[JsonProperty("records")]
public List<CreateRecordItem> Records { get; set; }
}
public class CreateRecordItem
{
/// <summary>
/// 字段数据(字段名 -> 值)
/// </summary>
[JsonProperty("fields")]
public Dictionary<string, object> Fields { get; set; }
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("record_id")]
public string RecordId { get; set; }
}
#endregion
}

112
Common/MiniDump.cs Normal file
View File

@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
public static class MiniDump
{
[Flags]
public enum Option : uint
{
Normal = 0x00000000,
WithDataSegs = 0x00000001,
WithFullMemory = 0x00000002,
WithHandleData = 0x00000004,
FilterMemory = 0x00000008,
ScanMemory = 0x00000010,
WithUnloadedModules = 0x00000020,
WithIndirectlyReferencedMemory = 0x00000040,
FilterModulePaths = 0x00000080,
WithProcessThreadData = 0x00000100,
WithPrivateReadWriteMemory = 0x00000200,
WithoutOptionalData = 0x00000400,
WithFullMemoryInfo = 0x00000800,
WithThreadInfo = 0x00001000,
WithCodeSegs = 0x00002000,
WithoutAuxiliaryState = 0x00004000,
WithFullAuxiliaryState = 0x00008000,
WithPrivateWriteCopyMemory = 0x00010000,
IgnoreInaccessibleMemory = 0x00020000,
ValidTypeFlags = 0x0003ffff,
}
enum ExceptionInfo
{
None,
Present
}
[StructLayout(LayoutKind.Sequential, Pack = 4)] // Pack=4 is important! So it works also for x64!
struct MiniDumpExceptionInformation
{
public uint ThreadId;
public IntPtr ExceptionPointers;
[MarshalAs(UnmanagedType.Bool)]
public bool ClientPointers;
}
// MiniDumpWriteDump function declarations
[DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, ref MiniDumpExceptionInformation expParam, IntPtr userStreamParam, IntPtr callbackParam);
[DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, IntPtr expParam, IntPtr userStreamParam, IntPtr callbackParam);
[DllImport("kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
static extern uint GetCurrentThreadId();
static bool Write(SafeHandle fileHandle, Option options, ExceptionInfo exceptionInfo, Exception exception = null)
{
Process currentProcess = Process.GetCurrentProcess();
IntPtr currentProcessHandle = currentProcess.Handle;
uint currentProcessId = (uint)currentProcess.Id;
MiniDumpExceptionInformation exp;
exp.ThreadId = GetCurrentThreadId();
exp.ClientPointers = false;
exp.ExceptionPointers = IntPtr.Zero;
if (exceptionInfo == ExceptionInfo.Present && exception != null)
{
// Get exception pointers
exp.ExceptionPointers = Marshal.GetExceptionPointers();
}
// Write dump
bool result = exp.ExceptionPointers == IntPtr.Zero
? MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint)options, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)
: MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint)options, ref exp, IntPtr.Zero, IntPtr.Zero);
if (exception != null)
{
}
return result;
}
public static Boolean TryDump(String dmpPath, Option dmpType = Option.Normal, Exception exception = null)
{
var path = Path.Combine(Environment.CurrentDirectory, dmpPath);
var dir = Path.GetDirectoryName(path);
if (dir != null && !Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
using (var fs = new FileStream(path, FileMode.Create))
{
return Write(fs.SafeFileHandle, dmpType, exception == null ? ExceptionInfo.None : ExceptionInfo.Present, exception);
}
}
}
}

View File

@@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36221.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ECCS", "BaseFrame\ECCS.csproj", "{FC10E4D4-0AA3-487B-B023-F8D2949E45ED}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "Model\Model.csproj", "{6D9764D9-B4DA-43E2-A9D7-40A6C871A6B3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logger", "Logger\Logger.csproj", "{9150C6A9-AE8D-42C9-8B2D-9DD04A3E7E74}"
@@ -15,16 +13,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{C769E6C6-55E9-40C3-A611-9EFAB101BE6A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LAEPS", "LAEPS\LAEPS.csproj", "{5EC9A233-D154-4B77-6911-063269A883E9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FC10E4D4-0AA3-487B-B023-F8D2949E45ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC10E4D4-0AA3-487B-B023-F8D2949E45ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC10E4D4-0AA3-487B-B023-F8D2949E45ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC10E4D4-0AA3-487B-B023-F8D2949E45ED}.Release|Any CPU.Build.0 = Release|Any CPU
{6D9764D9-B4DA-43E2-A9D7-40A6C871A6B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6D9764D9-B4DA-43E2-A9D7-40A6C871A6B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6D9764D9-B4DA-43E2-A9D7-40A6C871A6B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -45,6 +41,10 @@ Global
{C769E6C6-55E9-40C3-A611-9EFAB101BE6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C769E6C6-55E9-40C3-A611-9EFAB101BE6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C769E6C6-55E9-40C3-A611-9EFAB101BE6A}.Release|Any CPU.Build.0 = Release|Any CPU
{5EC9A233-D154-4B77-6911-063269A883E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5EC9A233-D154-4B77-6911-063269A883E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5EC9A233-D154-4B77-6911-063269A883E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5EC9A233-D154-4B77-6911-063269A883E9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -1,7 +1,7 @@
<prism:PrismApplication x:Class="BaseFrame.App"
<prism:PrismApplication x:Class="LAEPS.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BaseFrame"
xmlns:local="clr-namespace:LAEPS"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:prism="http://prismlibrary.com/">
<Application.Resources>

69
LAEPS/App.xaml.cs Normal file
View File

@@ -0,0 +1,69 @@
using LAEPS.ViewModels;
using LAEPS.ViewModels.Dialogs;
using LAEPS.Views;
using LAEPS.Views.Dialogs;
using Castle.DynamicProxy;
using LAEPS.Views;
using System.Configuration;
using System.Data;
using System.Reflection;
using System.Windows;
using static System.Runtime.InteropServices.JavaScript.JSType;
using LAEPS.PubEvent;
using Notifications.Wpf.Core;
using Logger;
using Common;
namespace LAEPS
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : PrismApplication
{
protected override Window CreateShell()
{
//UI线程未捕获异常处理事件
this.DispatcherUnhandledException += OnDispatcherUnhandledException;
//Task线程内未捕获异常处理事件
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
////多线程异常
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
return Container.Resolve<ShellView>();
}
private void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
//通常全局异常捕捉的都是致命信息
LoggerHelper.Error(e.Exception.Message,e.Exception.StackTrace);
}
private void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
LoggerHelper.Error(e.Exception.Message, e.Exception.StackTrace);
}
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
//记录dump文件
Exception ex = e.ExceptionObject as Exception;
MiniDump.TryDump($"dumps\\Error_{DateTime.Now:yyyy-MM-dd HH-mm-ss-ms}.dmp", MiniDump.Option.WithFullMemory, ex);
}
protected override void OnInitialized()
{
var login=Container.Resolve<LoginView>();
login.Show();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
//注册视图
containerRegistry.RegisterForNavigation<MainView>("MainView");
containerRegistry.RegisterForNavigation<SettingView>("SettingView");
containerRegistry.RegisterForNavigation<UpdateInfoView>("UpdateInfoView");
//注册弹窗
containerRegistry.RegisterDialog<MessageBoxView, MessageBoxViewModel>("MessageBox");
// 注册通知管理器
INotificationManager NotificationManager = new NotificationManager();
containerRegistry.RegisterInstance<INotificationManager>(NotificationManager);
}
}
}

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace BaseFrame.Converters
namespace LAEPS.Converters
{
public class BooleanToVisibilityConverter : IValueConverter
{

View File

@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace LAEPS.Helpers
{
public static class PasswordBoxHelper
{
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.RegisterAttached(
"Password",
typeof(string),
typeof(PasswordBoxHelper),
new FrameworkPropertyMetadata(
string.Empty,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnPasswordPropertyChanged));
public static void SetPassword(DependencyObject d, string value)
=> d.SetValue(PasswordProperty, value);
public static string GetPassword(DependencyObject d)
=> (string)d.GetValue(PasswordProperty);
private static void OnPasswordPropertyChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (d is PasswordBox pb)
{
pb.PasswordChanged -= PasswordChanged;
if (pb.Password != (string)e.NewValue)
{
pb.Password = (string)e.NewValue;
}
pb.PasswordChanged += PasswordChanged;
}
}
private static void PasswordChanged(object sender, RoutedEventArgs e)
{
if (sender is PasswordBox pb)
{
SetPassword(pb, pb.Password);
}
}
}
}

View File

@@ -38,6 +38,7 @@
<PackageReference Include="MaterialDesignThemes" Version="5.3.0" />
<PackageReference Include="MaterialDesignThemes.MahApps" Version="5.3.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Notifications.Wpf.Core" Version="2.0.1" />
<PackageReference Include="Prism.Unity" Version="9.0.537" />
</ItemGroup>

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LAEPS.PubEvent
{
public class LoginSuccessEvent:PubSubEvent
{
}
}

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BaseFrame.PubEvent
namespace LAEPS.PubEvent
{
public class OverlayEvent : PubSubEvent<bool>
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BaseFrame.PubEvent
namespace LAEPS.PubEvent
{
public class WaitingEvent : PubSubEvent<bool>
{

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -1,11 +1,11 @@
using BaseFrame.PubEvent;
using LAEPS.PubEvent;
using ControlzEx.Standard;
using ECCS.ViewModels;
using LAEPS.ViewModels;
using Prism.Commands;
using Prism.Mvvm;
using System.Windows.Input;
namespace BaseFrame.ViewModels.Dialogs
namespace LAEPS.ViewModels.Dialogs
{
public class MessageBoxViewModel : DialogViewModelBase
{

View File

@@ -0,0 +1,40 @@
using LAEPS.PubEvent;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace LAEPS.ViewModels
{
public class LoginViewModel:BindableBase
{
#region
private string _Password;
public string Password
{
get => _Password;
set => SetProperty(ref _Password, value);
}
private string _Account;
public string Account
{
get => _Account;
set => SetProperty(ref _Account, value);
}
#endregion
public ICommand LoginCommand { get; set; }
private IEventAggregator _eventAggregator;
public LoginViewModel(IContainerProvider containerProvider)
{
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
LoginCommand = new AsyncDelegateCommand(OnLogin);
}
private async Task OnLogin()
{
_eventAggregator.GetEvent<LoginSuccessEvent>().Publish();
}
}
}

View File

@@ -1,5 +1,6 @@
using BaseFrame.PubEvent;
using ECCS.ViewModels;
using LAEPS.PubEvent;
using LAEPS.ViewModels;
using Logger;
using NLog;
using Prism.Dialogs;
using Prism.Events;
@@ -11,9 +12,9 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Input;
namespace BaseFrame.ViewModels
namespace LAEPS.ViewModels
{
public class MonitorViewModel : NavigateViewModelBase
public class MainViewModel : NavigateViewModelBase
{
#region
@@ -24,11 +25,14 @@ namespace BaseFrame.ViewModels
#endregion
public MonitorViewModel(IContainerProvider containerProvider) : base(containerProvider)
public MainViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
}
#region

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ECCS.ViewModels
namespace LAEPS.ViewModels
{
public class SettingViewModel : NavigateViewModelBase
{

View File

@@ -1,6 +1,9 @@
using BaseFrame.Views;
using LAEPS.PubEvent;
using LAEPS.Views;
using MaterialDesignThemes.Wpf;
using Notifications.Wpf.Core;
using Prism.Events;
using Prism.Ioc;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -9,7 +12,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace BaseFrame.ViewModels
namespace LAEPS.ViewModels
{
public class ShellViewModel : BindableBase
{
@@ -29,29 +32,45 @@ namespace BaseFrame.ViewModels
public ICommand MaximizeCommand { get; set; }
public ICommand CloseCommand { get; set; }
public ICommand NavigateCommand { get; set; }
public ICommand LoadCommand { get; set; }
#endregion
private IEventAggregator _eventAggregator;
private IRegionManager _regionManager;
private IContainerProvider _containerProvider;
private INotificationManager _notificationManager;
public ShellViewModel( IContainerProvider containerProvider)
{
_containerProvider= containerProvider;
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
_regionManager = containerProvider.Resolve<IRegionManager>();
_notificationManager = containerProvider.Resolve<INotificationManager>();
LeftDrawerOpenCommand = new DelegateCommand(LeftDrawerOpen);
MinimizeCommand = new DelegateCommand<Window>(MinimizeWindow);
MaximizeCommand = new DelegateCommand<Window>(MaximizeWindow);
CloseCommand = new DelegateCommand<Window>(CloseWindow);
NavigateCommand = new DelegateCommand<string>(Navigate);
LoadCommand = new DelegateCommand(Load);
//订阅登录成功事件
_eventAggregator.GetEvent<LoginSuccessEvent>().Subscribe(() =>
{
Application.Current.MainWindow.Show();
_regionManager.RequestNavigate("ShellViewManager", "MainView");
});
}
#region
private void Load()
{
_notificationManager.ShowAsync(new NotificationContent { Title = "登录成功", Message = "", Type = NotificationType.Success });
}
private void Navigate(string content)
{
switch (content)
{
case "监控界面":
_regionManager.RequestNavigate("ShellViewManager", "MonitorView");
case "界面":
_regionManager.RequestNavigate("ShellViewManager", "MainView");
break;
case "设置界面":

View File

@@ -6,7 +6,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ECCS.ViewModels
namespace LAEPS.ViewModels
{
public class UpdateInfoViewModel : NavigateViewModelBase
{

View File

@@ -1,18 +1,21 @@
using System;
using Notifications.Wpf.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ECCS.ViewModels
namespace LAEPS.ViewModels
{
public abstract class DialogViewModelBase : BindableBase,IDialogAware
{
public IEventAggregator _eventAggregator;
public DialogCloseListener RequestClose { get; set; }
public IEventAggregator _eventAggregator;
private INotificationManager _notificationManager;
public DialogViewModelBase(IContainerProvider containerProvider)
{
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
_notificationManager = containerProvider.Resolve<INotificationManager>();
}
#region Dialog

View File

@@ -1,21 +1,23 @@
using System;
using Notifications.Wpf.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ECCS.ViewModels
namespace LAEPS.ViewModels
{
public abstract class NavigateViewModelBase : BindableBase, INavigationAware
{
public DialogCloseListener RequestClose { get; set; }
public IEventAggregator _eventAggregator;
public IDialogService _dialogService;
private INotificationManager _notificationManager;
public NavigateViewModelBase(IContainerProvider containerProvider)
{
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
_dialogService = containerProvider.Resolve<IDialogService>();
_notificationManager = containerProvider.Resolve<INotificationManager>();
}
#region Navigation

View File

@@ -1,9 +1,9 @@
<UserControl x:Class="BaseFrame.Views.Dialogs.MessageBoxView"
<UserControl x:Class="LAEPS.Views.Dialogs.MessageBoxView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BaseFrame.Views.Dialogs"
xmlns:local="clr-namespace:LAEPS.Views.Dialogs"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
xmlns:prism="http://prismlibrary.com/"

View File

@@ -13,7 +13,7 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace BaseFrame.Views.Dialogs
namespace LAEPS.Views.Dialogs
{
/// <summary>
/// MessageBoxView.xaml 的交互逻辑

151
LAEPS/Views/LoginView.xaml Normal file
View File

@@ -0,0 +1,151 @@
<mah:MetroWindow xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:helpers="clr-namespace:LAEPS.Helpers"
x:Class="LAEPS.Views.LoginView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:local="clr-namespace:LAEPS.Views"
mc:Ignorable="d"
Title="学习交流平台"
WindowStartupLocation="CenterScreen"
Height="315"
Width="420"
ResizeMode="NoResize">
<Window.Resources>
<Style TargetType="TextBox"
BasedOn="{StaticResource MahApps.Styles.TextBox}">
<Setter Property="FontSize"
Value="14" />
<Setter Property="BorderThickness"
Value="0,0,0,2" />
<Setter Property="BorderBrush"
Value="#E0E0E0" />
<Setter Property="Padding"
Value="5,8" />
<Setter Property="Background"
Value="Transparent" />
</Style>
<Style TargetType="PasswordBox"
BasedOn="{StaticResource MahApps.Styles.PasswordBox}">
<Setter Property="FontSize"
Value="14" />
<Setter Property="BorderThickness"
Value="0,0,0,2" />
<Setter Property="BorderBrush"
Value="#E0E0E0" />
<Setter Property="Padding"
Value="5,8" />
<Setter Property="Background"
Value="Transparent" />
</Style>
<Style TargetType="Button"
BasedOn="{StaticResource MahApps.Styles.Button.Flat}">
<Setter Property="FontSize"
Value="15" />
<Setter Property="FontWeight"
Value="SemiBold" />
<Setter Property="Foreground"
Value="White" />
<Setter Property="Background"
Value="#2196F3" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="Margin"
Value="0,20,0,0" />
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<!-- 表单区域 -->
<Border Grid.Row="0"
Background="White"
Margin="20,20,20,0"
CornerRadius="5"
BorderThickness="1"
BorderBrush="#E0E0E0"
Padding="30,20">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Content="用户登录"
HorizontalAlignment="Center"
FontSize="15"
Padding="3" />
<StackPanel Grid.Row="1">
<!-- 用户名输入 -->
<StackPanel Orientation="Vertical"
HorizontalAlignment="Center">
<StackPanel Orientation="Horizontal"
Margin="17">
<materialDesign:PackIcon Kind="Account" VerticalAlignment="Center"/>
<TextBox Text="{Binding Account}"
mah:TextBoxHelper.Watermark="请输入账号"
mah:TextBoxHelper.ClearTextButton="True"
VerticalContentAlignment="Center"
Width="180"
Height="30"
Padding="0" />
</StackPanel>
</StackPanel>
<!-- 密码输入 -->
<StackPanel Orientation="Vertical"
HorizontalAlignment="Center">
<StackPanel Orientation="Horizontal"
Margin="7">
<materialDesign:PackIcon Kind="Lock"
VerticalAlignment="Center" />
<PasswordBox helpers:PasswordBoxHelper.Password="{Binding Password, Mode=TwoWay}"
mah:TextBoxHelper.Watermark="请输入密码"
mah:TextBoxHelper.ClearTextButton="True"
VerticalContentAlignment="Center"
Width="180"
Height="30"
Padding="0" />
</StackPanel>
</StackPanel>
</StackPanel>
<!-- 登录按钮 -->
<Button Grid.Row="2"
Command="{Binding LoginCommand}"
Content="登 录"
Width="120"
Height="33"
mah:ControlsHelper.CornerRadius="5">
<Button.Effect>
<DropShadowEffect BlurRadius="8"
ShadowDepth="3"
Opacity="0.5" />
</Button.Effect>
</Button>
</Grid>
</Border>
<!-- 底部版权信息 -->
<TextBlock Grid.Row="2"
Text="© 2025 大学生学习交流平台"
Foreground="#777"
FontSize="12"
HorizontalAlignment="Center"
Margin="0,10" />
</Grid>
</mah:MetroWindow>

View File

@@ -0,0 +1,38 @@
using LAEPS.PubEvent;
using MahApps.Metro.Controls;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Path = System.IO.Path;
namespace LAEPS.Views
{
/// <summary>
/// Login.xaml 的交互逻辑
/// </summary>
public partial class LoginView : MetroWindow
{
public LoginView(IEventAggregator eventAggregator)
{
InitializeComponent();
//订阅登录成功事件
eventAggregator.GetEvent<LoginSuccessEvent>().Subscribe(() =>
{
this.Close();
});
}
}
}

View File

@@ -1,4 +1,4 @@
<UserControl x:Class="BaseFrame.Views.MonitorView"
<UserControl x:Class="LAEPS.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

View File

@@ -13,14 +13,14 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace BaseFrame.Views
namespace LAEPS.Views
{
/// <summary>
/// MainView.xaml 的交互逻辑
/// </summary>
public partial class MonitorView : UserControl
public partial class MainView : UserControl
{
public MonitorView()
public MainView()
{
InitializeComponent();
}

View File

@@ -1,4 +1,4 @@
<UserControl x:Class="ECCS.Views.SettingView"
<UserControl x:Class="LAEPS.Views.SettingView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

View File

@@ -13,7 +13,7 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace ECCS.Views
namespace LAEPS.Views
{
/// <summary>
/// SettingView.xaml 的交互逻辑

View File

@@ -1,4 +1,4 @@
<Window x:Class="BaseFrame.Views.ShellView"
<Window x:Class="LAEPS.Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -17,7 +17,11 @@
<WindowChrome.WindowChrome>
<WindowChrome GlassFrameThickness="-1" />
</WindowChrome.WindowChrome>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<materialDesign:DrawerHost x:Name="MainDrawerHost"
IsLeftDrawerOpen="{Binding IsLeftDrawerOpen, Mode=TwoWay}">
@@ -30,7 +34,7 @@
Margin="16"
Foreground="{DynamicResource PrimaryHueMidBrush}" />
<Separator Margin="0,0,0,8" />
<Button Content="监控界面"
<Button Content="界面"
Command="{Binding NavigateCommand}"
CommandParameter="{Binding Content, RelativeSource={RelativeSource Self}}"
Style="{StaticResource MaterialDesignFlatButton}"
@@ -87,29 +91,9 @@
<materialDesign:PackIcon Kind="Tools"
Foreground="White" />
</MenuItem.Icon>
<MenuItem Header="系统设置"
<MenuItem Header="注销登录"
Foreground="Black" />
<MenuItem Header="数据"
Foreground="Black">
<MenuItem Header="数据查询"
Foreground="Black" />
</MenuItem>
<MenuItem Header="同星CAN"
Foreground="Black">
<MenuItem Header="连接/断开"
Foreground="Black" />
<MenuItem Header="通道映射"
Foreground="Black" />
<MenuItem Header="加载数据库"
Foreground="Black" />
</MenuItem>
<MenuItem Header="调试"
Foreground="Black">
<MenuItem Header="自动运行"
Foreground="Black" />
<MenuItem Header="清空数据库"
Foreground="Black" />
</MenuItem>
</MenuItem>

View File

@@ -1,5 +1,4 @@
using BaseFrame.PubEvent;
using LAEPS.PubEvent;
using Prism.Events;
using System;
using System.Collections.Generic;
@@ -15,7 +14,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace BaseFrame.Views
namespace LAEPS.Views
{
/// <summary>
/// ShellView.xaml 的交互逻辑

View File

@@ -1,4 +1,4 @@
<UserControl x:Class="ECCS.Views.UpdateInfoView"
<UserControl x:Class="LAEPS.Views.UpdateInfoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

View File

@@ -13,7 +13,7 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace ECCS.Views
namespace LAEPS.Views
{
/// <summary>
/// UpdateInfoView.xaml 的交互逻辑

View File

@@ -13,6 +13,16 @@ namespace Logger
public static readonly ILogger Logger = LogManager.GetLogger("InfoLogger");
public static readonly ILogger sqlLogger = LogManager.GetLogger("SqlLogger");
public static void Error(string message, string stackTrace)
{
if (!string.IsNullOrEmpty(stackTrace))
{
string location = GetProjectStackLine(stackTrace);
message = $"{message} ({location})";
}
Logger.Error(message);
}
// 解析堆栈,找到项目文件路径和行号
public static string GetProjectStackLine(string stackTrace)
{
@@ -24,7 +34,7 @@ namespace Logger
foreach (var line in lines)
{
// 匹配你项目的命名空间路径
if (line.Contains("BaseFrame"))
if (line.Contains("BOB"))
{
// 提取 "in 文件路径:line 行号"
var match = Regex.Match(line, @"in (.+?):line (\d+)");
@@ -38,5 +48,6 @@ namespace Logger
return lines[0].Trim(); // 如果找不到就返回第一条
}
}
}

View File

@@ -0,0 +1,27 @@
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model.Entity
{
public class BaseEntity
{
/// <summary>
/// 主键Id
/// </summary>
[SugarColumn(ColumnName = "id", ColumnDescription = "主键Id", IsPrimaryKey = true, CreateTableFieldSort = 0)]
public virtual long Id { get; set; }
/// <summary>
/// 删除状态
/// </summary>
[SugarColumn(ColumnName = "IsDel", ColumnDescription = "删除状态0、未删除1、已删除", ColumnDataType = "tinyint", DefaultValue = "0", CreateTableFieldSort = 106)]
public virtual byte IsDel { get; set; }
[SugarColumn(ColumnName = "CreateTime")]
public DateTime CreateTime { get; set; }
}
}

View File

@@ -6,4 +6,12 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SqlSugarCore" Version="5.1.4.210" />
</ItemGroup>
<ItemGroup>
<Folder Include="DTOS\" />
</ItemGroup>
</Project>

View File

@@ -1,97 +1,132 @@
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Security.Cryptography;
namespace ORM
{
public static class DatabaseConfig
public class DatabaseConfig
{
/// <summary>
/// 当前数据库类型
/// 数据库连接类型
/// </summary>
public static DbType DbConnectionType { get; private set; }
/// <summary>
/// 数据库连接字符串
/// SQLite 数据库文件路径
/// </summary>
public static string DbConnectionString { get; private set; }
/// <summary>
/// 默认 SQLite 文件路径(仅记录,不自动启用)
/// </summary>
public static string DefaultDbFilePath { get; private set; }
/// <summary>
/// 租户 Id
/// 数据租户Id
/// </summary>
public static int TenantId { get; private set; }
/// <summary>
/// 雪花算法DatacenterId值范围0至31
/// </summary>
public static int SnowFlakeDatacenterId { get; private set; }
public static int SnowFlakeWorkId { get; private set; }
static DatabaseConfig()
{
InitDefaultDbPath(); // 只生成路径,不指定 DB 类型/连接
}
/// <summary>
/// 生成默认 DB 路径 EXE目录/SQLDB/Data.db
/// 雪花算法WorkID值范围0至31
/// </summary>
private static void InitDefaultDbPath()
{
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
public static int SnowFlakeWorkId { get; private set; }
#region
private static void InitSqlite()
{
DbConnectionType = DbType.Sqlite;
// 获取程序运行目录
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
// 确保 SQLDB 文件夹存在
string folder = Path.Combine(baseDir, "SQLDB");
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
DefaultDbFilePath = Path.Combine(folder, "Data.db");
// 拼接数据库文件路径
string DBPath = Path.Combine(folder, "SQL.db");
DbConnectionString = $"Data Source={DBPath};Version=3;";
}
/// <summary>
/// 手动设置数据库连接
/// </summary>
public static void SetConnection(string conn, DbType dbType)
private static void InitMySql(string Server, int Port, string Database, string Uid,string Pwd)
{
DbConnectionType = DbType.MySql;
DbConnectionString =
$"Server={Server};Port={Port};Database={Database};Uid={Uid};Pwd={Pwd};";
}
private static void InitSqlServer(string server,int port,string database,string user,string password)
{
DbConnectionType = DbType.SqlServer;
DbConnectionString =
$"Data Source={server},{port};Initial Catalog={database};user={user};Password={password};";
}
private static void InitSqlServerLocalDb()
{
DbConnectionType = DbType.SqlServer;
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
// 确保 SQLDB 文件夹存在
string folder = Path.Combine(baseDir, "SQLDB");
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
// 拼接数据库文件路径
string DBPath = Path.Combine(folder, "SQL.db");
DbConnectionString =$@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog={DBPath};Integrated Security=True;";
}
#endregion
/// <summary>
/// 设置数据库连接字符串(可手动覆盖)
/// </summary>
public static void SetDbConnection(string strConnection)
{
if (string.IsNullOrEmpty(strConnection))
{
if (string.IsNullOrWhiteSpace(conn))
throw new Exception("数据库连接字符串为空");
DbConnectionString = conn;
DbConnectionType = dbType;
}
DbConnectionString = strConnection;
}
/// <summary>
/// 如果未 SetConnection默认使用 SQLite + 默认路径
/// 设置数据租户Id
/// </summary>
public static void EnsureConnection()
public static void SetTenant(int nTenantId)
{
if (string.IsNullOrEmpty(DbConnectionString))
if (nTenantId <= 10000)
{
DbConnectionString = $"Data Source={DefaultDbFilePath};";
DbConnectionType = DbType.Sqlite;
}
}
/// <summary>
/// 设置租户并计算雪花算法节点
/// </summary>
public static void SetTenant(int tenantId)
{
if (tenantId <= 10000)
throw new Exception("数据租户Id值需大于10000");
}
TenantId = nTenantId;
SetSnowFlake((nTenantId - 10000) % 1024);
}
TenantId = tenantId;
int snowId = (tenantId - 10000) % 1024;
if (snowId is < 0 or > 1023)
/// <summary>
/// 将指定数字转换为雪花算法DatacenterId和WorkID
/// </summary>
private static void SetSnowFlake(int nSnowFlakeId)
{
if (nSnowFlakeId > 1023 || nSnowFlakeId < 0)
{
throw new Exception("雪花算法机器码值范围0至1023");
SnowFlakeDatacenterId = snowId >> 5;
SnowFlakeWorkId = snowId & 31;
}
SnowFlakeDatacenterId = nSnowFlakeId >> 5;
SnowFlakeWorkId = nSnowFlakeId & 31;
}
/// <summary>
/// 检测数据库连接
/// </summary>
public static void CreateDatabaseAndCheckConnection(bool createDatabase = false, bool checkConnection = false)
{
//数据库不存在则创建数据库
if (createDatabase)
{
SqlSugarContext.DbContext.DbMaintenance.CreateDatabase();
}
// 检查数据库连接
if (checkConnection && !SqlSugarContext.DbContext.Ado.IsValidConnection())
{
throw new Exception("连接数据库失败");
}
}
}
}

View File

@@ -6,9 +6,7 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SqlSugarCore" Version="5.1.4.206" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Logger\Logger.csproj" />

85
ORM/SqlSugarContext.cs Normal file
View File

@@ -0,0 +1,85 @@
using Logger;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ORM
{
public class SqlSugarContext
{
/// <summary>
/// SqlSugarScope单例模式
/// </summary>
public static readonly SqlSugarScope DbContext = new SqlSugarScope(
new ConnectionConfig()
{
DbType = DatabaseConfig.DbConnectionType,
ConnectionString = DatabaseConfig.DbConnectionString, // 连接符字串
//ConfigId = "Db1",//管理多个数据库
IsAutoCloseConnection = true, // 自动关闭连接
InitKeyType = InitKeyType.Attribute // 通过实体类上的特性初始化
},
db =>
{
// 执行超时时间,单位秒
db.Ado.CommandTimeOut = 30;
// 每次SQL执行前事件
db.Aop.OnLogExecuting = (sql, pars) =>
{
#if DEBUG
// 确保 SQL 只通过 sqlLogger 记录
LoggerHelper.sqlLogger.Info("Executing SQL: {0} with parameters: {1}", sql, pars); // 使用 NLog 记录 SQL
#endif
};
// SQL执行完
db.Aop.OnLogExecuted = (sql, pars) =>
{
// 执行时间超过1秒记录慢日志
if (db.Ado.SqlExecutionTime.TotalSeconds > 1)
{
LoggerHelper.sqlLogger.Warn("SQL Slow Execution. Time: {0}, File: {1}, Line: {2}, Method: {3}, SQL: {4}",
db.Ado.SqlExecutionTime.ToString(), // SQL 执行时间
db.Ado.SqlStackTrace.FirstFileName, // 代码 CS 文件名
db.Ado.SqlStackTrace.FirstLine, // 代码行数
db.Ado.SqlStackTrace.FirstMethodName, // 代码方法名
sql); // 使用 NLog 记录慢 SQL 日志
}
};
// SQL 报错
db.Aop.OnError = (exp) =>
{
// 确保 SQL 错误日志仅通过 sqlLogger 记录
LoggerHelper.sqlLogger.Error(exp, "SQL Error: {0}", exp.Sql); // 使用 NLog 记录 SQL 错误日志
};
// 数据过滤器:例如在新增数据时生成雪花 Id
db.Aop.DataExecuting = (oldValue, entityInfo) =>
{
// 新增操作
if (entityInfo.OperationType == DataFilterType.InsertByObject)
{
// 主键(long)赋值雪花 Id
if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.EntityColumnInfo.PropertyInfo.PropertyType == typeof(long))
{
var id = ((dynamic)entityInfo.EntityValue).Id;
if (id == null || id == 0)
{
SnowFlakeSingle.WorkId = DatabaseConfig.SnowFlakeWorkId;
SnowFlakeSingle.DatacenterId = DatabaseConfig.SnowFlakeDatacenterId;
entityInfo.SetValue(SnowFlakeSingle.Instance.NextId());
}
}
}
};
}
);
}
}

396
ORM/SqlSugarRepository.cs Normal file
View File

@@ -0,0 +1,396 @@
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace ORM
{
public partial class SqlSugarRepository<TEntity> : SimpleClient<TEntity> where TEntity : class, new()
{
/// <summary>
/// 实体集合
/// </summary>
public ISugarQueryable<TEntity> Entities => Context.Queryable<TEntity>();
/// <summary>
/// 构造函数
/// </summary>
public SqlSugarRepository(ISqlSugarClient context = null) : base(context)
{
// 绑定数据库操作对象
Context = SqlSugarContext.DbContext;
// 备忘GetConnectionScopeWithAttr会导致不能触发Aop.OnLogExecuting、Aop.OnLogExecuted等事件
// 详见https://www.donet5.com/Home/Doc?typeId=2405之2.1、方法说明)
// 详见https://www.donet5.com/Home/Doc?typeId=2246之2.2、根据特性获取之4、多租户设置AOP
// base.Context = SqlSugarContext.DbContext.GetConnectionScopeWithAttr<TEntity>();
// 根据特性获取,适合一个实体和库是一对一的情况
}
#region
/// <summary>
/// 检查是否存在
/// </summary>
/// <param name="whereExpression"></param>
/// <returns></returns>
public bool IsExists(Expression<Func<TEntity, bool>> whereExpression)
{
return Entities.Any(whereExpression);
}
/// <summary>
/// 检查是否存在
/// </summary>
/// <param name="whereExpression"></param>
/// <returns></returns>
public async Task<bool> IsExistsAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await Entities.AnyAsync(whereExpression);
}
/// <summary>
/// 通过主键获取实体
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
public TEntity Single(dynamic Id)
{
return Entities.InSingle(Id);
}
/// <summary>
/// 获取一个实体
/// </summary>
/// <param name="whereExpression"></param>
/// <returns></returns>
public TEntity Single(Expression<Func<TEntity, bool>> whereExpression)
{
return Entities.Single(whereExpression);
}
/// <summary>
/// 获取一个实体
/// </summary>
/// <param name="whereExpression"></param>
/// <returns></returns>
public Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return Entities.SingleAsync(whereExpression);
}
/// <summary>
/// 获取一个实体
/// </summary>
/// <param name="whereExpression"></param>
/// <returns></returns>
public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> whereExpression)
{
return Entities.First(whereExpression);
}
/// <summary>
/// 获取一个实体
/// </summary>
/// <param name="whereExpression"></param>
/// <returns></returns>
public async Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return await Entities.FirstAsync(whereExpression);
}
/// <summary>
/// 获取列表
/// </summary>
/// <returns></returns>
public List<TEntity> ToList()
{
return Entities.ToList();
}
/// <summary>
/// 获取列表
/// </summary>
/// <returns></returns>
public Task<List<TEntity>> ToListAsync()
{
return Entities.ToListAsync();
}
/// <summary>
/// 获取列表
/// </summary>
/// <param name="whereExpression"></param>
/// <returns></returns>
public List<TEntity> ToList(Expression<Func<TEntity, bool>> whereExpression)
{
return Entities.Where(whereExpression).ToList();
}
/// <summary>
/// 获取列表
/// </summary>
/// <param name="whereExpression"></param>
/// <returns></returns>
public Task<List<TEntity>> ToListAsync(Expression<Func<TEntity, bool>> whereExpression)
{
return Entities.Where(whereExpression).ToListAsync();
}
/// <summary>
/// 获取列表
/// </summary>
/// <param name="whereExpression"></param>
/// <param name="orderByExpression"></param>
/// <param name="orderByType"></param>
/// <returns></returns>
public List<TEntity> ToList(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, object>> orderByExpression = null, OrderByType orderByType = OrderByType.Asc)
{
return Entities.OrderByIF(orderByExpression != null, orderByExpression, orderByType).Where(whereExpression).ToList();
}
/// <summary>
/// 获取列表
/// </summary>
/// <param name="whereExpression"></param>
/// <param name="orderByExpression"></param>
/// <param name="orderByType"></param>
/// <returns></returns>
public Task<List<TEntity>> ToListAsync(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, object>> orderByExpression = null, OrderByType orderByType = OrderByType.Asc)
{
return Entities.OrderByIF(orderByExpression != null, orderByExpression, orderByType).Where(whereExpression).ToListAsync();
}
#endregion
#region
/// <summary>
/// 新增多条记录
/// </summary>
/// <param name="entities"></param>
/// <returns></returns>
public int Insert(TEntity[] entities)
{
return Context.Insertable(entities).ExecuteCommand();
}
/// <summary>
/// 新增多条记录
/// </summary>
/// <param name="entities"></param>
/// <returns></returns>
public Task<int> InsertAsync(TEntity[] entities)
{
return Context.Insertable(entities).ExecuteCommandAsync();
}
/// <summary>
/// 新增多条记录
/// </summary>
/// <param name="entities"></param>
/// <returns></returns>
public int Insert(IEnumerable<TEntity> entities)
{
return Context.Insertable(entities.ToArray()).ExecuteCommand();
}
/// <summary>
/// 新增多条记录
/// </summary>
/// <param name="entities"></param>
/// <returns></returns>
public Task<int> InsertAsync(IEnumerable<TEntity> entities)
{
if (entities != null && entities.Any())
{
return Context.Insertable(entities.ToArray()).ExecuteCommandAsync();
}
return Task.FromResult(0);
}
#endregion
#region
/// <summary>
/// 更新单条记录指定列
/// </summary>
/// <param name="entity"></param>
/// <param name="updateColumn"></param>
/// <returns></returns>
public int Update(TEntity entity, object updateColumn)
{
return Context.Updateable(entity).UpdateColumns(MergeUpdateColumns(updateColumn)).ExecuteCommand();
}
/// <summary>
/// 更新单条记录指定列
/// </summary>
/// <param name="entity"></param>
/// <param name="updateColumn"></param>
/// <returns></returns>
public Task<int> UpdateAsync(TEntity entity, object updateColumn)
{
return Context.Updateable(entity).UpdateColumns(MergeUpdateColumns(updateColumn)).ExecuteCommandAsync();
}
/// <summary>
/// 更新单条记录指定列
/// </summary>
/// <param name="entity"></param>
/// <param name="updateColumn"></param>
/// <param name="whereExpression"></param>
/// <returns></returns>
public int Update(TEntity entity, object updateColumn, Expression<Func<TEntity, bool>> whereExpression)
{
return Context.Updateable(entity).UpdateColumns(MergeUpdateColumns(updateColumn)).Where(whereExpression).ExecuteCommand();
}
/// <summary>
/// 更新单条记录指定列
/// </summary>
/// <param name="entity"></param>
/// <param name="updateColumn"></param>
/// <param name="whereExpression"></param>
/// <returns></returns>
public Task<int> UpdateAsync(TEntity entity, object updateColumn, Expression<Func<TEntity, bool>> whereExpression)
{
return Context.Updateable(entity).UpdateColumns(MergeUpdateColumns(updateColumn)).Where(whereExpression).ExecuteCommandAsync();
}
/// <summary>
/// 更新多条记录
/// </summary>
/// <param name="entities"></param>
/// <returns></returns>
public int Update(TEntity[] entities)
{
return Context.Updateable(entities).ExecuteCommand();
}
/// <summary>
/// 更新多条记录
/// </summary>
/// <param name="entities"></param>
/// <returns></returns>
public Task<int> UpdateAsync(TEntity[] entities)
{
return Context.Updateable(entities).ExecuteCommandAsync();
}
/// <summary>
/// 更新多条记录指定列
/// </summary>
/// <param name="entities"></param>
/// <param name="updateColumn"></param>
/// <returns></returns>
public int Update(TEntity[] entities, object updateColumn)
{
return Context.Updateable(entities).UpdateColumns(MergeUpdateColumns(updateColumn)).ExecuteCommand();
}
/// <summary>
/// 更新多条记录指定列
/// </summary>
/// <param name="entities"></param>
/// <param name="updateColumn"></param>
/// <returns></returns>
public Task<int> UpdateAsync(TEntity[] entities, object updateColumn)
{
return Context.Updateable(entities).UpdateColumns(MergeUpdateColumns(updateColumn)).ExecuteCommandAsync();
}
/// <summary>
/// 合并指定更新列和附加更新列
/// </summary>
/// <param name="updateColumn"></param>
/// <returns></returns>
private string[] MergeUpdateColumns(object updateColumn)
{
List<string> columnList = new List<string>();
if (updateColumn.GetType() == typeof(string))
{
columnList.Add((string)updateColumn);
}
else if (updateColumn.GetType() == typeof(string[]))
{
columnList.AddRange((string[])updateColumn);
}
return columnList.ToArray();
}
#endregion
#region
/// <summary>
/// 删除一条记录
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public int Delete(object key)
{
return Context.Deleteable<TEntity>().In(key).ExecuteCommand();
}
/// <summary>
/// 删除一条记录
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public Task<int> DeleteAsync(object key)
{
return Context.Deleteable<TEntity>().In(key).ExecuteCommandAsync();
}
/// <summary>
/// 删除多条记录
/// </summary>
/// <param name="keys"></param>
/// <returns></returns>
public int Delete(object[] keys)
{
return Context.Deleteable<TEntity>().In(keys).ExecuteCommand();
}
/// <summary>
/// 删除多条记录
/// </summary>
/// <param name="keys"></param>
/// <returns></returns>
public Task<int> DeleteAsync(object[] keys)
{
return Context.Deleteable<TEntity>().In(keys).ExecuteCommandAsync();
}
#endregion
#region
/// <summary>
/// 开启事务
/// </summary>
public void BeginTran()
{
Context.Ado.BeginTran();
}
/// <summary>
/// 提交事务
/// </summary>
public void CommitTran()
{
Context.Ado.CommitTran();
}
/// <summary>
/// 回滚事务
/// </summary>
public void RollbackTran()
{
Context.Ado.RollbackTran();
}
#endregion
}
}

View File

@@ -1,388 +0,0 @@
using Common.DTOS;
using NetTaste;
using RestSharp;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Service
{
public class APIService
{
private static string Token = "Bearer u-cVaGe.hv160U5.zqxeyRKx11ifIR04iPjoayYQC00EXF";
#region
/// <summary>
/// 初始化多维表格
/// </summary>
/// <param name="FormName">表格名称</param>
public static FeishuResponse<CreateBitableAppData> InitForm(string FormName)
{
try
{
var client = new RestClient("https://open.feishu.cn/open-apis/bitable/v1/apps");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", Token);
request.AddParameter("application/json", ParameterType.RequestBody);
request.AddJsonBody(new
{
folder_token = "",
name = FormName
});
IRestResponse response = client.Execute(request);
return Newtonsoft.Json.JsonConvert.DeserializeObject<FeishuResponse<CreateBitableAppData>>(response.Content);
}
catch (Exception ex)
{
throw;
}
}
/// <summary>
/// 创建表
/// </summary>
/// <param name="FormName">多维表格ID</param>
/// <param name="FormName">表名称</param>
public static FeishuResponse<CreateTableData> AddTable(string FormID, string TableName)
{
try
{
var client = new RestClient($"https://open.feishu.cn/open-apis/bitable/v1/apps/{FormID}/tables");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", Token);
request.AddJsonBody(new
{
table = new
{
default_view_name = "默认的表格视图",
name = TableName,
fields = new object[]
{
new
{
field_name = "索引字段",
type = 1
},
new
{
field_name = "单选",
type = 3,
ui_type = "SingleSelect",
property = new
{
options = new[]
{
new { color = 0, name = "Enabled" },
new { color = 1, name = "Disabled" },
new { color = 2, name = "Draft" }
}
}
}
}
}
});
request.AddParameter("application/json", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
return Newtonsoft.Json.JsonConvert.DeserializeObject<FeishuResponse<CreateTableData>>(response.Content);
}
catch (Exception ex)
{
throw;
}
}
/// <summary>
/// 删除表
/// </summary>
/// <param name="FormID">多维表格ID</param>
/// <param name="TabelID">表ID</param>
public static FeishuResponse<object> DeleteTable(string FormID, string TabelID)
{
var client = new RestClient($"https://open.feishu.cn/open-apis/bitable/v1/apps/{FormID}/tables/{TabelID}");
client.Timeout = -1;
var request = new RestRequest(Method.DELETE);
request.AddHeader("Authorization", Token);
IRestResponse response = client.Execute(request);
return Newtonsoft.Json.JsonConvert.DeserializeObject<FeishuResponse<object>>(response.Content);
}
/// <summary>
/// 获得表列表
/// </summary>
/// <param name="FormName">多维表格ID</param>
/// <param name="FormName">分页数</param>
/// <param name="FormName">表ID</param>
public static FeishuResponse<QueryTableListData> GetTableList(string FormID, int PageSize, string TabelID = "")
{
RestClient client;
if (TabelID == "")
{
client = new RestClient($"https://open.feishu.cn/open-apis/bitable/v1/apps/{FormID}/tables?page_size={PageSize}");
}
else
{
client = new RestClient($"https://open.feishu.cn/open-apis/bitable/v1/apps/{FormID}/tables?page_size={PageSize}&page_token={TabelID}");
}
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", Token);
IRestResponse response = client.Execute(request);
return Newtonsoft.Json.JsonConvert.DeserializeObject<FeishuResponse<QueryTableListData>>(response.Content);
}
#endregion
#region
/// <summary>
/// 添加一行记录
/// </summary>
/// <param name="FormID">多维表格ID</param>
/// <param name="TabelID">表ID</param>
public static FeishuResponse<CreateRecordData> AddRecord(string FormID, string TableID)
{
try
{
var client = new RestClient($"https://open.feishu.cn/open-apis/bitable/v1/apps/{FormID}/tables/{TableID}/records");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", Token);
request.AddJsonBody(new
{
fields = new
{
= "333",
= "拜访潜在客户"
}
});
request.AddParameter("application/json", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
return Newtonsoft.Json.JsonConvert.DeserializeObject<FeishuResponse<CreateRecordData>>(response.Content);
}
catch (Exception ex)
{
throw;
}
}
/// <summary>
/// 更新一行记录
/// </summary>
/// <param name="FormID">多维表格ID</param>
/// <param name="TabelID">表ID</param>
/// <param name="TabelID">记录ID</param>
public static FeishuResponse<CreateRecordData> UpdateRecord(string FormID, string TableID, string RecordID)
{
try
{
var client = new RestClient($"https://open.feishu.cn/open-apis/bitable/v1/apps/{FormID}/tables/{TableID}/records/{RecordID}");
client.Timeout = -1;
var request = new RestRequest(Method.PUT);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", Token);
request.AddJsonBody(new
{
fields = new
{
= "99",
= "mike"
}
});
request.AddParameter("application/json", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
return Newtonsoft.Json.JsonConvert.DeserializeObject<FeishuResponse<CreateRecordData>>(response.Content);
}
catch (Exception ex)
{
throw;
}
}
/// <summary>
/// 删除记录
/// </summary>
/// <param name="FormID">多维表格ID</param>
/// <param name="TabelID">表ID</param>
/// <param name="TabelID">记录ID</param>
public static FeishuResponse<object> DeleteRecord(string FormID, string TableID, string RecordID)
{
try
{
var client = new RestClient($"https://open.feishu.cn/open-apis/bitable/v1/apps/{FormID}/tables/{TableID}/records/{RecordID}");
client.Timeout = -1;
var request = new RestRequest(Method.DELETE);
request.AddHeader("Authorization", Token);
var body = "";
request.AddParameter("", body, ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
return Newtonsoft.Json.JsonConvert.DeserializeObject<FeishuResponse<object>>(response.Content);
}
catch (Exception ex)
{
throw;
}
}
/// <summary>
/// 获取记录
/// </summary>
/// <param name="FormID">多维表格ID</param>
/// <param name="TabelID">表ID</param>
/// <param name="TabelID">分页数量</param>
public static FeishuResponse<QueryRecordListData> GetRecord(string FormID, string TableID, int PageSize)
{
try
{
var client = new RestClient($"https://open.feishu.cn/open-apis/bitable/v1/apps/{FormID}/tables/{TableID}/records/search?page_size={PageSize}");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", "Bearer u-dSpI1fRVp2jWi4Nyz0lIkal5i0n504UVMUaaYNy021um");
var body = "{}";
request.AddParameter("application/json", body, ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
return Newtonsoft.Json.JsonConvert.DeserializeObject<FeishuResponse<QueryRecordListData>>(response.Content);
}
catch (Exception ex)
{
throw;
}
}
#endregion
#region
/// <summary>
/// 添加多行记录
/// </summary>
/// <param name="FormID">多维表格ID</param>
/// <param name="TabelID">表ID</param>
public static FeishuResponse<CreateRecordsRequest> AddMutipulRecords(string FormID, string TableID)
{
try
{
var client = new RestClient($"https://open.feishu.cn/open-apis/bitable/v1/apps/{FormID}/tables/{TableID}/records/batch_create");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", Token);
request.AddJsonBody(new
{
records = new[]
{
new
{
fields = new
{
= "333",
= "拜访潜在客户1"
}
},
new
{
fields = new
{
= "333",
= "拜访潜在客户2"
}
}
}
});
request.AddParameter("application/json", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
return Newtonsoft.Json.JsonConvert.DeserializeObject<FeishuResponse<CreateRecordsRequest>>(response.Content);
}
catch (Exception ex)
{
throw;
}
}
/// <summary>
/// 更新多行记录
/// </summary>
/// <param name="FormID">多维表格ID</param>
/// <param name="TabelID">表ID</param>
/// <param name="FieldIDs">记录ID列表</param>
public static FeishuResponse<CreateRecordsRequest> UpdateRecords(string FormID, string TableID, string[] FieldIDs)
{
try
{
var client = new RestClient($"https://open.feishu.cn/open-apis/bitable/v1/apps/{FormID}/tables/{TableID}/records/batch_update");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", Token);
request.AddJsonBody(new
{
records = new[]
{
new
{
record_id=FieldIDs[0],
fields = new
{
= "333",
= "改拜访潜在客户1"
}
},
new
{
record_id=FieldIDs[1],
fields = new
{
= "333",
= "改拜访潜在客户2"
}
}
}
});
request.AddParameter("application/json", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
return Newtonsoft.Json.JsonConvert.DeserializeObject<FeishuResponse<CreateRecordsRequest>>(response.Content);
}
catch (Exception ex)
{
throw;
}
}
/// <summary>
/// 删除多行记录
/// </summary>
/// <param name="FormID">多维表格ID</param>
/// <param name="TabelID">表ID</param>
/// <param name="FieldIDs">记录ID列表</param>
public static FeishuResponse<object> DeleteRecords(string FormID, string TableID, string[] FieldIDs)
{
try
{
var client = new RestClient($"https://open.feishu.cn/open-apis/bitable/v1/apps/{FormID}/tables/{TableID}/records/batch_delete");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", Token);
request.AddJsonBody(new
{
records = FieldIDs
});
request.AddParameter("application/json", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
return Newtonsoft.Json.JsonConvert.DeserializeObject<FeishuResponse<object>>(response.Content);
}
catch (Exception ex)
{
throw;
}
}
#endregion
}
}