using Prism.Commands; using Prism.Events; using Prism.Mvvm; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Windows; using System.Windows.Input; using System.Windows.Media; using Txgy.EWS.Client.Common; using Txgy.EWS.Client.Common.MessageEvents; using Txgy.EWS.Client.Entity; using Microsoft.Win32; using Forms = System.Windows.Forms; namespace Txgy.EWS.Client.SysModule.ViewModels { public class SystemSettingsViewModel : BindableBase { private const string AdvancedSettingsTargetView = "SystemSettingsAdvanced"; private const string ResourcesDirectoryName = "resources"; private const string ResourcesRelativePrefix = "\\resources\\"; private static readonly string[] AdvancedSettingsFallbackTargetViews = { "MenuManagementView", "RoleManagementView", "UserManagementView" }; private readonly IEventAggregator _eventAggregator; private string _statusMessage; private Brush _statusBrush = Brushes.Gray; private bool _canEditSystemSettings; public SystemSettingsViewModel(IEventAggregator eventAggregator) { _eventAggregator = eventAggregator; CanEditAdvancedSettings = HasAdvancedSettingsPermission(); ReloadFromConfig(); } public string ConfigPath => BusinessConfigManager.ConfigPath; public string StatusMessage { get => _statusMessage; set => SetProperty(ref _statusMessage, value); } public Brush StatusBrush { get => _statusBrush; set => SetProperty(ref _statusBrush, value); } public bool CanEditAdvancedSettings { get; } public Visibility AdvancedSettingsVisibility => CanEditAdvancedSettings ? Visibility.Visible : Visibility.Collapsed; public bool CanEditSystemSettings { get => _canEditSystemSettings; set { if (SetProperty(ref _canEditSystemSettings, value)) { RaisePropertyChanged(nameof(SystemSettingsReadOnly)); RaisePropertyChanged(nameof(SystemSettingsEditButtonText)); } } } public bool SystemSettingsReadOnly => !CanEditSystemSettings; public string SystemSettingsEditButtonText => CanEditSystemSettings ? "保存" : "高级修改"; public string ApiDomain { get; set; } public string AlarmSetting { get; set; } public string AlarmLevelConfig { get; set; } public string ReportEventLevelSetting { get; set; } public string WorkAreaFilePath { get; set; } public string StationsCsvFilePath { get; set; } public string CadDwgFilePath { get; set; } public string DwgJsonSetting { get; set; } public string WavesMseedFilePath { get; set; } public string WavesTxtFilePath { get; set; } public string LocalSqLiteDb { get; set; } public string DwgSettings { get; set; } public string CadSettingsFileName { get; set; } public bool IsDesign { get; set; } public bool IsRealtime { get; set; } public string RefreshInterval { get; set; } public string DataLookbackHours { get; set; } public string DataCacheTimeLenMins { get; set; } public string TencentMySql { get; set; } public string RealtimeResultTable { get; set; } public string RealtimeWaveDataTable { get; set; } public string RealtimeFocalmechanismTable { get; set; } public string PostResultTable { get; set; } public string PostWaveDataTable { get; set; } public string PostFocalmechanismTable { get; set; } public string CommpanyName { get; set; } public string SystemNameCn { get; set; } public string SystemNameEn { get; set; } public string WorkAreaName { get; set; } public string SystemShortName { get; set; } public string DailyReportStartTime { get; set; } public string DailyReportPlanOffsetX { get; set; } public string DailyReportPlanOffsetY { get; set; } public string BaseX { get; set; } public string BaseY { get; set; } public string BaseZ { get; set; } public ICommand ReloadCommand => new DelegateCommand(ReloadFromConfig); public ICommand ApplyCommand => new DelegateCommand(ApplyConfig); public ICommand BrowseResourceFileCommand => new DelegateCommand(BrowseResourceFile); public ICommand BrowseLocalFileCommand => new DelegateCommand(BrowseLocalFile); public ICommand BrowseFolderCommand => new DelegateCommand(BrowseFolder); public ICommand ToggleSystemSettingsEditCommand => new DelegateCommand(ToggleSystemSettingsEdit); private void ReloadFromConfig() { try { Load(BusinessConfigManager.Read()); SetStatus("已读取最新配置", Brushes.Gray); } catch (Exception ex) { SetStatus($"读取配置失败:{ex.Message}", Brushes.Red); } } private void ApplyConfig() { TryApplyConfig(); } private bool TryApplyConfig() { try { var previousConfig = BusinessConfigManager.Current; var config = BuildConfig(previousConfig); var savedConfig = BusinessConfigManager.Save(config); var shouldReloadWarningData = ShouldReloadWarningData(previousConfig, savedConfig); GlobalConfig.ApplyBusinessConfig(savedConfig); _eventAggregator.GetEvent().Publish(savedConfig); if (shouldReloadWarningData) { _eventAggregator.GetEvent().Publish(); } Load(savedConfig); SetStatus($"配置已应用:{DateTime.Now:HH:mm:ss}", Brushes.Green); return true; } catch (Exception ex) { SetStatus($"应用失败:{ex.Message}", Brushes.Red); return false; } } private void ToggleSystemSettingsEdit() { if (CanEditSystemSettings) { if (TryApplyConfig()) { CanEditSystemSettings = false; SetStatus($"系统参数已保存:{DateTime.Now:HH:mm:ss}", Brushes.Green); } return; } var result = MessageBox.Show( "系统参数会影响系统名称显示和缓存目录,是否确认修改系统参数?", "高级修改", MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result != MessageBoxResult.Yes) { return; } CanEditSystemSettings = true; SetStatus("系统参数已解锁", Brushes.Gray); } private void BrowseResourceFile(string targetName) { var resourcesDirectory = GetResourcesDirectory(); if (!Directory.Exists(resourcesDirectory)) { SetStatus($"resources目录不存在:{resourcesDirectory}", Brushes.Red); return; } var dialog = new OpenFileDialog { CheckFileExists = true, Multiselect = false, Title = "选择resources目录下的文件", Filter = GetFileFilter(targetName), InitialDirectory = GetInitialDirectory(GetPathValue(targetName), true) }; if (dialog.ShowDialog() != true) { return; } if (!TryGetResourcesRelativePath(dialog.FileName, out var relativePath)) { SetStatus("请选择resources目录下的文件", Brushes.Red); return; } SetPathValue(targetName, relativePath); SetStatus($"已选择:{relativePath}", Brushes.Gray); } private void BrowseLocalFile(string targetName) { var dialog = new OpenFileDialog { CheckFileExists = true, Multiselect = false, Title = "选择文件", Filter = GetFileFilter(targetName), InitialDirectory = GetInitialDirectory(GetPathValue(targetName), false) }; if (dialog.ShowDialog() != true) { return; } SetPathValue(targetName, dialog.FileName); SetStatus($"已选择:{dialog.FileName}", Brushes.Gray); } private void BrowseFolder(string targetName) { using (var dialog = new Forms.FolderBrowserDialog()) { dialog.Description = "选择目录"; dialog.SelectedPath = GetInitialDirectory(GetPathValue(targetName), false); if (dialog.ShowDialog() != Forms.DialogResult.OK || string.IsNullOrWhiteSpace(dialog.SelectedPath)) { return; } SetPathValue(targetName, dialog.SelectedPath); SetStatus($"已选择:{dialog.SelectedPath}", Brushes.Gray); } } private string GetFileFilter(string targetName) { switch (targetName) { case nameof(StationsCsvFilePath): return "CSV文件 (*.csv)|*.csv|所有文件 (*.*)|*.*"; case nameof(CadDwgFilePath): return "DWG文件 (*.dwg)|*.dwg|所有文件 (*.*)|*.*"; case nameof(LocalSqLiteDb): return "SQLite数据库 (*.db;*.sqlite;*.sqlite3)|*.db;*.sqlite;*.sqlite3|所有文件 (*.*)|*.*"; case nameof(WorkAreaFilePath): case nameof(DwgJsonSetting): case nameof(AlarmSetting): case nameof(AlarmLevelConfig): case nameof(ReportEventLevelSetting): return "JSON文件 (*.json)|*.json|所有文件 (*.*)|*.*"; default: return "所有文件 (*.*)|*.*"; } } private string GetInitialDirectory(string currentPath, bool resourcesOnly) { if (resourcesOnly) { var resourceFilePath = ResolveAppRelativePath(currentPath); if (!string.IsNullOrWhiteSpace(resourceFilePath) && File.Exists(resourceFilePath)) { return Path.GetDirectoryName(resourceFilePath); } return GetResourcesDirectory(); } if (!string.IsNullOrWhiteSpace(currentPath)) { var path = ResolveAppRelativePath(currentPath); if (Directory.Exists(path)) { return path; } if (File.Exists(path)) { return Path.GetDirectoryName(path); } } return AppDomain.CurrentDomain.BaseDirectory; } private string ResolveAppRelativePath(string path) { if (string.IsNullOrWhiteSpace(path)) { return null; } if (Path.IsPathRooted(path) && !path.StartsWith("\\", StringComparison.Ordinal)) { return path; } return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path.TrimStart('\\', '/')); } private string GetResourcesDirectory() { return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ResourcesDirectoryName); } private bool TryGetResourcesRelativePath(string selectedFile, out string relativePath) { relativePath = null; var resourcesDirectory = Path.GetFullPath(GetResourcesDirectory()) .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar; var filePath = Path.GetFullPath(selectedFile); if (!filePath.StartsWith(resourcesDirectory, StringComparison.OrdinalIgnoreCase)) { return false; } var relativePart = filePath.Substring(resourcesDirectory.Length) .Replace(Path.DirectorySeparatorChar, '\\') .Replace(Path.AltDirectorySeparatorChar, '\\'); relativePath = ResourcesRelativePrefix + relativePart; return true; } private string GetPathValue(string targetName) { switch (targetName) { case nameof(AlarmSetting): return AlarmSetting; case nameof(AlarmLevelConfig): return AlarmLevelConfig; case nameof(ReportEventLevelSetting): return ReportEventLevelSetting; case nameof(WorkAreaFilePath): return WorkAreaFilePath; case nameof(StationsCsvFilePath): return StationsCsvFilePath; case nameof(CadDwgFilePath): return CadDwgFilePath; case nameof(DwgJsonSetting): return DwgJsonSetting; case nameof(WavesMseedFilePath): return WavesMseedFilePath; case nameof(WavesTxtFilePath): return WavesTxtFilePath; case nameof(LocalSqLiteDb): return LocalSqLiteDb; case nameof(DwgSettings): return DwgSettings; case nameof(CadSettingsFileName): return CadSettingsFileName; default: throw new InvalidOperationException($"未知的路径参数:{targetName}"); } } private void SetPathValue(string targetName, string value) { switch (targetName) { case nameof(AlarmSetting): AlarmSetting = value; break; case nameof(AlarmLevelConfig): AlarmLevelConfig = value; break; case nameof(ReportEventLevelSetting): ReportEventLevelSetting = value; break; case nameof(WorkAreaFilePath): WorkAreaFilePath = value; break; case nameof(StationsCsvFilePath): StationsCsvFilePath = value; break; case nameof(CadDwgFilePath): CadDwgFilePath = value; break; case nameof(DwgJsonSetting): DwgJsonSetting = value; break; case nameof(WavesMseedFilePath): WavesMseedFilePath = value; break; case nameof(WavesTxtFilePath): WavesTxtFilePath = value; break; case nameof(LocalSqLiteDb): LocalSqLiteDb = value; break; case nameof(DwgSettings): DwgSettings = value; break; case nameof(CadSettingsFileName): CadSettingsFileName = value; break; default: throw new InvalidOperationException($"未知的路径参数:{targetName}"); } RaisePropertyChanged(targetName); } private bool ShouldReloadWarningData(BusinessConfig previousConfig, BusinessConfig savedConfig) { if (previousConfig?.Runtime == null || savedConfig?.Runtime == null) { return false; } return previousConfig.Runtime.DataLookbackHours != savedConfig.Runtime.DataLookbackHours; } private void Load(BusinessConfig config) { ApiDomain = config.Endpoints.ApiDomain; AlarmSetting = config.Paths.AlarmSetting; AlarmLevelConfig = config.Paths.AlarmLevelConfig; ReportEventLevelSetting = config.Paths.ReportEventLevelSetting; WorkAreaFilePath = config.Paths.WorkAreaFilePath; StationsCsvFilePath = config.Paths.StationsCsvFilePath; CadDwgFilePath = config.Paths.CadDwgFilePath; DwgJsonSetting = config.Paths.DwgJsonSetting; WavesMseedFilePath = config.Paths.WavesMseedFilePath; WavesTxtFilePath = config.Paths.WavesTxtFilePath; LocalSqLiteDb = config.Paths.LocalSqLiteDb; DwgSettings = config.Paths.DwgSettings; CadSettingsFileName = config.Paths.CadSettingsFileName; IsDesign = config.Runtime.IsDesign; IsRealtime = config.Runtime.IsRealtime; RefreshInterval = config.Runtime.RefreshInterval.ToString(CultureInfo.InvariantCulture); DataLookbackHours = config.Runtime.DataLookbackHours.ToString("0.##", CultureInfo.InvariantCulture); DataCacheTimeLenMins = config.Runtime.DataCacheTimeLenMins.ToString(CultureInfo.InvariantCulture); TencentMySql = GetConnection(config, "TencetnMySQL"); RealtimeResultTable = config.Database.Tables.RealtimeResultTable; RealtimeWaveDataTable = config.Database.Tables.RealtimeWaveDataTable; RealtimeFocalmechanismTable = config.Database.Tables.RealtimeFocalmechanismTable; PostResultTable = config.Database.Tables.PostResultTable; PostWaveDataTable = config.Database.Tables.PostWaveDataTable; PostFocalmechanismTable = config.Database.Tables.PostFocalmechanismTable; CommpanyName = config.Branding.CommpanyName; SystemNameCn = config.Branding.SystemNameCn; SystemNameEn = config.Branding.SystemNameEn; WorkAreaName = config.Branding.WorkAreaName; SystemShortName = config.Branding.SystemShortName; DailyReportStartTime = config.Report.DailyReportStartTime; DailyReportPlanOffsetX = config.Report.DailyReportPlanOffsetX.ToString(CultureInfo.InvariantCulture); DailyReportPlanOffsetY = config.Report.DailyReportPlanOffsetY.ToString(CultureInfo.InvariantCulture); BaseX = config.Coordinates.BaseX.ToString(CultureInfo.InvariantCulture); BaseY = config.Coordinates.BaseY.ToString(CultureInfo.InvariantCulture); BaseZ = config.Coordinates.BaseZ.ToString(CultureInfo.InvariantCulture); RaiseAllSettingPropertiesChanged(); } private BusinessConfig BuildConfig(BusinessConfig currentConfig) { DateTime.Parse(Require(DailyReportStartTime, "日报起始时间")); var dataLookbackHours = ParseDouble(DataLookbackHours, "数据时长"); DataCacheTimeLenMins = ((int)Math.Round(dataLookbackHours * 60.0)).ToString(CultureInfo.InvariantCulture); var endpointSettings = BuildEndpointSettings(currentConfig); var databaseSettings = BuildDatabaseSettings(currentConfig); return new BusinessConfig { Endpoints = endpointSettings, Paths = new PathSettings { AlarmSetting = Require(AlarmSetting, "报警配置"), AlarmLevelConfig = Require(AlarmLevelConfig, "报警等级配置"), ReportEventLevelSetting = Require(ReportEventLevelSetting, "报表事件分级"), WorkAreaFilePath = Require(WorkAreaFilePath, "工区配置"), StationsCsvFilePath = Require(StationsCsvFilePath, "台站CSV"), CadDwgFilePath = Require(CadDwgFilePath, "CAD DWG"), DwgJsonSetting = Require(DwgJsonSetting, "DWG图层设置"), WavesMseedFilePath = Require(WavesMseedFilePath, "Mseed缓存目录"), WavesTxtFilePath = Require(WavesTxtFilePath, "Txt缓存目录"), LocalSqLiteDb = CanEditAdvancedSettings ? Require(LocalSqLiteDb, "本地 SQLite") : currentConfig.Paths.LocalSqLiteDb, DwgSettings = CanEditAdvancedSettings ? EmptyToNull(DwgSettings) : currentConfig.Paths.DwgSettings, CadSettingsFileName = CanEditAdvancedSettings ? EmptyToNull(CadSettingsFileName) : currentConfig.Paths.CadSettingsFileName }, Runtime = new RuntimeSettings { IsDesign = CanEditAdvancedSettings ? IsDesign : currentConfig.Runtime.IsDesign, IsRealtime = CanEditAdvancedSettings ? IsRealtime : currentConfig.Runtime.IsRealtime, RefreshInterval = ParseInt(RefreshInterval, "刷新间隔"), DataLookbackHours = dataLookbackHours, DataCacheTimeLenMins = ParseInt(DataCacheTimeLenMins, "数据缓存时长") }, Database = databaseSettings, Branding = new BrandingSettings { CommpanyName = Require(CommpanyName, "公司名称"), SystemNameCn = Require(SystemNameCn, "系统中文名"), SystemNameEn = Require(SystemNameEn, "系统英文名"), WorkAreaName = Require(WorkAreaName, "工区名称"), SystemShortName = Require(SystemShortName, "系统简称") }, Report = new ReportSettings { DailyReportStartTime = DailyReportStartTime.Trim(), DailyReportPlanOffsetX = ParseDouble(DailyReportPlanOffsetX, "平面图横向偏移"), DailyReportPlanOffsetY = ParseDouble(DailyReportPlanOffsetY, "平面图纵向偏移") }, Coordinates = new CoordinateSettings { BaseX = ParseDouble(BaseX, "BaseX"), BaseY = ParseDouble(BaseY, "BaseY"), BaseZ = ParseDouble(BaseZ, "BaseZ") } }; } private EndpointSettings BuildEndpointSettings(BusinessConfig currentConfig) { return new EndpointSettings { ApiDomain = CanEditAdvancedSettings ? Require(ApiDomain, "API 域名") : currentConfig.Endpoints.ApiDomain }; } private DatabaseSettings BuildDatabaseSettings(BusinessConfig currentConfig) { if (!CanEditAdvancedSettings) { return new DatabaseSettings { Connections = new Dictionary { { "TencetnMySQL", GetConnection(currentConfig, "TencetnMySQL") } }, Tables = CopyTableSettings(currentConfig.Database.Tables) }; } return new DatabaseSettings { Connections = new Dictionary { { "TencetnMySQL", Require(TencentMySql, "腾讯 MySQL") } }, Tables = new TableSettings { RealtimeResultTable = Require(RealtimeResultTable, "实时结果表"), RealtimeWaveDataTable = Require(RealtimeWaveDataTable, "实时波形表"), RealtimeFocalmechanismTable = Require(RealtimeFocalmechanismTable, "实时震源机制表"), PostResultTable = Require(PostResultTable, "后处理结果表"), PostWaveDataTable = Require(PostWaveDataTable, "后处理波形表"), PostFocalmechanismTable = Require(PostFocalmechanismTable, "后处理震源机制表") } }; } private TableSettings CopyTableSettings(TableSettings source) { return new TableSettings { RealtimeResultTable = source.RealtimeResultTable, RealtimeWaveDataTable = source.RealtimeWaveDataTable, RealtimeFocalmechanismTable = source.RealtimeFocalmechanismTable, PostResultTable = source.PostResultTable, PostWaveDataTable = source.PostWaveDataTable, PostFocalmechanismTable = source.PostFocalmechanismTable }; } private string GetConnection(BusinessConfig config, string name) { if (config.Database.Connections != null && config.Database.Connections.TryGetValue(name, out var value)) { return value; } return string.Empty; } private bool HasAdvancedSettingsPermission() { var menus = GlobalData.CurrentUserInfo?.Menus; if (menus == null) { return false; } return ContainsTargetView(menus, AdvancedSettingsTargetView) || AdvancedSettingsFallbackTargetViews.Any(targetView => ContainsTargetView(menus, targetView)); } private bool ContainsTargetView(IEnumerable menus, string targetView) { return menus.Any(menu => string.Equals(menu.TargetView, targetView, StringComparison.OrdinalIgnoreCase)); } private string Require(string value, string name) { if (string.IsNullOrWhiteSpace(value)) { throw new InvalidOperationException($"{name}不能为空"); } return value.Trim(); } private string EmptyToNull(string value) { return string.IsNullOrWhiteSpace(value) ? null : value.Trim(); } private int ParseInt(string value, string name) { if (!int.TryParse(Require(value, name), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { throw new InvalidOperationException($"{name}必须是整数"); } return result; } private double ParseDouble(string value, string name) { if (!double.TryParse(Require(value, name), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { throw new InvalidOperationException($"{name}必须是数字"); } return result; } private void SetStatus(string message, Brush brush) { StatusMessage = message; StatusBrush = brush; } private void RaiseAllSettingPropertiesChanged() { RaisePropertyChanged(nameof(ApiDomain)); RaisePropertyChanged(nameof(AlarmSetting)); RaisePropertyChanged(nameof(AlarmLevelConfig)); RaisePropertyChanged(nameof(ReportEventLevelSetting)); RaisePropertyChanged(nameof(WorkAreaFilePath)); RaisePropertyChanged(nameof(StationsCsvFilePath)); RaisePropertyChanged(nameof(CadDwgFilePath)); RaisePropertyChanged(nameof(DwgJsonSetting)); RaisePropertyChanged(nameof(WavesMseedFilePath)); RaisePropertyChanged(nameof(WavesTxtFilePath)); RaisePropertyChanged(nameof(LocalSqLiteDb)); RaisePropertyChanged(nameof(DwgSettings)); RaisePropertyChanged(nameof(CadSettingsFileName)); RaisePropertyChanged(nameof(IsDesign)); RaisePropertyChanged(nameof(IsRealtime)); RaisePropertyChanged(nameof(RefreshInterval)); RaisePropertyChanged(nameof(DataLookbackHours)); RaisePropertyChanged(nameof(DataCacheTimeLenMins)); RaisePropertyChanged(nameof(TencentMySql)); RaisePropertyChanged(nameof(RealtimeResultTable)); RaisePropertyChanged(nameof(RealtimeWaveDataTable)); RaisePropertyChanged(nameof(RealtimeFocalmechanismTable)); RaisePropertyChanged(nameof(PostResultTable)); RaisePropertyChanged(nameof(PostWaveDataTable)); RaisePropertyChanged(nameof(PostFocalmechanismTable)); RaisePropertyChanged(nameof(CommpanyName)); RaisePropertyChanged(nameof(SystemNameCn)); RaisePropertyChanged(nameof(SystemNameEn)); RaisePropertyChanged(nameof(WorkAreaName)); RaisePropertyChanged(nameof(SystemShortName)); RaisePropertyChanged(nameof(DailyReportStartTime)); RaisePropertyChanged(nameof(DailyReportPlanOffsetX)); RaisePropertyChanged(nameof(DailyReportPlanOffsetY)); RaisePropertyChanged(nameof(BaseX)); RaisePropertyChanged(nameof(BaseY)); RaisePropertyChanged(nameof(BaseZ)); } } }