using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Net; using System.Text; using Microsoft.Win32; using Lskj.Business; using Lskj.Business.Impl; using Lskj.Control.Model; using Lskj.Core; using Lskj.Data; using Lskj.Main.Model; using Lskj.Model; using Lskj.Util; namespace Lskj.Main.Hosting { public sealed partial class LegacyLoginRuntime { private const string RegistryFilePath = @"AA_LS_Erp V2.0\File"; private const string SystemResourcesIniName = "SystemResources.ini"; private const string SystemResourcesSection = "SystemResources"; private const string DirectHistoryPrefix = "DownloadAESStr_"; private const string DirectLastHostKey = "LocalLastIP"; private const string DirectLastPortKey = "LocalLastPort"; private const string DirectLastNameKey = "LocalLastPortName"; private const string ConnectionModeKey = "FrmConfigFlag"; private const int DirectRequestTimeoutMilliseconds = 5000; public DataSet LoadConnectionSettings() { var result = new DataSet("LoginConnectionSettings"); DataTable settings = CreateSettingsTable(); DataTable savedConnections = CreateSavedConnectionsTable(); string directHost = IniHelper.Read( SystemResourcesIniName, DirectLastHostKey); string directPort = IniHelper.Read( SystemResourcesIniName, DirectLastPortKey); string directName = IniHelper.Read( SystemResourcesIniName, DirectLastNameKey); bool directMode = string.Equals( IniHelper.Read(SystemResourcesIniName, ConnectionModeKey), "1", StringComparison.Ordinal); settings.Rows.Add( directMode ? "Direct" : "Database", ReadRegistryValue("ServerName"), ReadRegistryValue("datastr"), directHost, directPort, directName); Dictionary entries = IniHelper.GetSectionKeys( SystemResourcesIniName, SystemResourcesSection); foreach (KeyValuePair entry in entries) { if (!entry.Key.StartsWith( DirectHistoryPrefix, StringComparison.OrdinalIgnoreCase)) { continue; } string endpoint = entry.Key.Substring( DirectHistoryPrefix.Length); string host; int port; if (!TrySplitEndpoint(endpoint, out host, out port)) continue; string displayName = ReadHistoryDisplayName(entry.Value); if (string.IsNullOrWhiteSpace(displayName)) continue; savedConnections.Rows.Add( entry.Key, displayName, host, port); } result.Tables.Add(settings); result.Tables.Add(savedConnections); return result; } public string ApplyDatabaseConnection( string serverName, string databaseName) { try { ConnectionCandidate candidate = CreateDatabaseCandidate( serverName, databaseName); return ApplyConnection( candidate, delegate { CommitDatabaseConnection(); }); } catch (Exception exception) { LogSanitized(exception); return ToSafeConnectionError(exception); } } public string ApplyDirectConnection( string host, int port, string accountBookName) { try { Uri resourceUri = BuildDirectResourceUri(host, port); string encryptedPayload = DownloadDirectPayload(resourceUri); string decryptedPayload = AESUtil.Decrypt(encryptedPayload); string[] parts = ParseDirectPayload(decryptedPayload); ConnectionCandidate candidate = CreateDirectCandidate( parts, host, port, accountBookName, encryptedPayload); return ApplyConnection( candidate, delegate { CommitDirectConnection(candidate); }); } catch (Exception exception) { LogSanitized(exception); return ToSafeConnectionError(exception); } } private string ApplyConnection( ConnectionCandidate candidate, Action commit) { LegacyConnectionStateSnapshot snapshot = LegacyConnectionStateSnapshot.Capture(this); try { ApplyCandidate(candidate); ValidateDatabase(candidate); ValidateDelphi(candidate); SystemInfo.RefreshSystemParam(); BaseResources.Localization(LocalizationType.CHS); commit(); _ledgerTable = null; SelectedLedgerName = candidate.AccountBookName; ERPInfo.Instance.AccountBook = candidate.AccountBookName; _connectionInitialized = true; return string.Empty; } catch (Exception exception) { snapshot.Restore(this); LogSanitized(exception); return ToSafeConnectionError(exception); } } private static ConnectionCandidate CreateDatabaseCandidate( string serverName, string databaseName) { serverName = (serverName ?? string.Empty).Trim(); databaseName = (databaseName ?? string.Empty).Trim(); if (string.IsNullOrWhiteSpace(serverName)) throw new ArgumentException("请输入数据库服务器地址。"); if (string.IsNullOrWhiteSpace(databaseName)) throw new ArgumentException("请输入数据库名称。"); return new ConnectionCandidate { ServerName = serverName, DatabaseName = databaseName, AccountBookName = DBConfig.Instance.DataBook ?? string.Empty, ServerType = DBConfig.Instance.ServerType ?? "SqlServer", ConnectionTemplate = string.Empty, DelphiConnectionTemplate = string.Empty }; } private static ConnectionCandidate CreateDirectCandidate( string[] parts, string host, int port, string accountBookName, string encryptedPayload) { accountBookName = (accountBookName ?? string.Empty).Trim(); if (string.IsNullOrWhiteSpace(accountBookName)) throw new ArgumentException("请输入直连账套名称。"); string connectionTemplate = "Server={0};Database={1};Persist Security Info=True;" + "User ID=" + parts[2] + ";Password=" + parts[3] + ";" + "Connection Timeout=5;MultipleActiveResultSets=true"; string delphiTemplate = "Provider=SQLOLEDB.1;Server={0};Database={1};" + "Persist Security Info=True;User ID=" + parts[2] + ";Password=" + parts[3] + ";Connection Timeout=5"; return new ConnectionCandidate { ServerName = parts[0], DatabaseName = parts[1], AccountBookName = accountBookName, ServerType = DBConfig.Instance.ServerType ?? "SqlServer", ConnectionTemplate = AESUtil.Encrypt(connectionTemplate), DelphiConnectionTemplate = AESUtil.Encrypt(delphiTemplate), DirectHost = (host ?? string.Empty).Trim().TrimEnd('/'), DirectPort = port, DirectEncryptedPayload = encryptedPayload }; } private static void ApplyCandidate(ConnectionCandidate candidate) { DBConfig.Instance.ServerName = candidate.ServerName; DBConfig.Instance.DataBase = candidate.DatabaseName; DBConfig.Instance.DataBook = candidate.AccountBookName; DBConfig.Instance.ServerType = candidate.ServerType; DBConfig.Instance.Connection = candidate.ConnectionTemplate; DBConfig.Instance.dephiConnection = candidate.DelphiConnectionTemplate; } private static void ValidateDatabase(ConnectionCandidate candidate) { if (!DBConfig.Instance.CreateConnection( candidate.ConnectionTemplate)) { throw new LegacyConnectionSettingsException( "无法连接数据库,请检查服务器和数据库配置。"); } } private static void ValidateDelphi(ConnectionCandidate candidate) { if (string.Equals( candidate.ServerType, "达梦数据库", StringComparison.Ordinal)) { return; } string connection = DBConfig.Instance.GetDelphiConnection( candidate.DelphiConnectionTemplate); if (DelphiHelper.Delphi_Init(new StringBuilder(connection)) == 0) { throw new LegacyConnectionSettingsException( "数据库已连接,但 Delphi 组件初始化失败。"); } } private static void CommitDatabaseConnection() { DBConfig.Instance.WriteConfig(); IniHelper.Write( SystemResourcesIniName, ConnectionModeKey, "0"); } private static void CommitDirectConnection( ConnectionCandidate candidate) { string endpoint = candidate.DirectHost + ":" + candidate.DirectPort; string historyValue = candidate.AccountBookName + "^" + candidate.DirectEncryptedPayload; IniHelper.Write( SystemResourcesIniName, DirectHistoryPrefix + endpoint, historyValue); IniHelper.Write( SystemResourcesIniName, DirectLastHostKey, candidate.DirectHost); IniHelper.Write( SystemResourcesIniName, DirectLastPortKey, candidate.DirectPort.ToString()); IniHelper.Write( SystemResourcesIniName, DirectLastNameKey, candidate.AccountBookName); IniHelper.Write( SystemResourcesIniName, ConnectionModeKey, "1"); DBConfig.Instance.WriteConfig( DirectLastHostKey, candidate.DirectHost); DBConfig.Instance.WriteConfig( DirectLastPortKey, candidate.DirectPort.ToString()); DBConfig.Instance.WriteConfig( DirectLastNameKey, candidate.AccountBookName); } private static Uri BuildDirectResourceUri(string host, int port) { host = (host ?? string.Empty).Trim(); if (string.IsNullOrWhiteSpace(host)) throw new ArgumentException("请输入直连服务器地址。"); if (port < 1 || port > 65535) throw new ArgumentOutOfRangeException( "port", "直连端口必须介于 1 和 65535 之间。"); if (!host.StartsWith("http://", StringComparison.OrdinalIgnoreCase) && !host.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { host = "http://" + host; } Uri baseUri; if (!Uri.TryCreate(host, UriKind.Absolute, out baseUri) || (baseUri.Scheme != Uri.UriSchemeHttp && baseUri.Scheme != Uri.UriSchemeHttps)) { throw new ArgumentException("直连服务器地址格式无效。"); } var builder = new UriBuilder(baseUri) { Port = port, Path = "/SystemResources.txt", Query = string.Empty, Fragment = string.Empty }; return builder.Uri; } private static string DownloadDirectPayload(Uri resourceUri) { var request = (HttpWebRequest)WebRequest.Create(resourceUri); request.Method = "GET"; request.Timeout = DirectRequestTimeoutMilliseconds; request.ReadWriteTimeout = DirectRequestTimeoutMilliseconds; using (var response = (HttpWebResponse)request.GetResponse()) using (Stream stream = response.GetResponseStream()) { if (stream == null) { throw new InvalidDataException( "直连服务器未返回连接配置。"); } using (var reader = new StreamReader( stream, Encoding.UTF8, true)) { string payload = reader.ReadToEnd().Trim(); if (string.IsNullOrWhiteSpace(payload)) { throw new InvalidDataException( "直连服务器返回的连接配置为空。"); } return payload; } } } private static string[] ParseDirectPayload(string decryptedPayload) { string[] parts = (decryptedPayload ?? string.Empty).Split('^'); if (parts.Length != 5) { throw new InvalidDataException( "直连服务器返回的连接配置格式无效。"); } for (int index = 0; index < parts.Length; index++) { parts[index] = (parts[index] ?? string.Empty).Trim(); if (string.IsNullOrWhiteSpace(parts[index])) { throw new InvalidDataException( "直连服务器返回的连接配置不完整。"); } } return parts; } private static string ToSafeConnectionError(Exception exception) { if (exception is ArgumentException || exception is InvalidDataException || exception is LegacyConnectionSettingsException) { return exception.Message; } if (exception is WebException) { return "无法读取直连配置,请检查地址、端口和网络连接。"; } return "连接设置失败,请检查配置和网络连接。"; } private static void LogSanitized(Exception exception) { string exceptionType = exception == null ? "Unknown" : exception.GetType().FullName; LogHelper.Instance.WriteLog( "WPF 登录连接设置失败。异常类型:" + exceptionType); } private static DataTable CreateSettingsTable() { var table = new DataTable("Settings"); table.Columns.Add("Mode", typeof(string)); table.Columns.Add("ServerName", typeof(string)); table.Columns.Add("DatabaseName", typeof(string)); table.Columns.Add("DirectHost", typeof(string)); table.Columns.Add("DirectPort", typeof(string)); table.Columns.Add("DirectAccountBookName", typeof(string)); return table; } private static DataTable CreateSavedConnectionsTable() { var table = new DataTable("SavedDirectConnections"); table.Columns.Add("Key", typeof(string)); table.Columns.Add("DisplayName", typeof(string)); table.Columns.Add("Host", typeof(string)); table.Columns.Add("Port", typeof(int)); return table; } private static string ReadRegistryValue(string valueName) { try { using (RegistryKey key = Registry.CurrentUser.OpenSubKey( RegistryFilePath, false)) { object value = key == null ? null : key.GetValue(valueName, string.Empty); return value == null ? string.Empty : value.ToString(); } } catch { return string.Empty; } } private static bool TrySplitEndpoint( string endpoint, out string host, out int port) { host = string.Empty; port = 0; if (string.IsNullOrWhiteSpace(endpoint)) return false; int separatorIndex = endpoint.LastIndexOf(':'); if (separatorIndex <= 0 || separatorIndex >= endpoint.Length - 1) { return false; } host = endpoint.Substring(0, separatorIndex).Trim(); return !string.IsNullOrWhiteSpace(host) && int.TryParse(endpoint.Substring(separatorIndex + 1), out port) && port >= 1 && port <= 65535; } private static string ReadHistoryDisplayName(string value) { if (string.IsNullOrWhiteSpace(value)) return string.Empty; int separatorIndex = value.IndexOf('^'); return separatorIndex < 0 ? string.Empty : value.Substring(0, separatorIndex).Trim(); } private sealed class ConnectionCandidate { public string ServerName; public string DatabaseName; public string AccountBookName; public string ServerType; public string ConnectionTemplate; public string DelphiConnectionTemplate; public string DirectHost; public int DirectPort; public string DirectEncryptedPayload; } private sealed class LegacyConnectionStateSnapshot { private string _serverName; private string _databaseName; private string _dataBook; private string _loginName; private string _serverType; private string _connectionTemplate; private string _delphiConnectionTemplate; private string _selectedLedgerName; private string _accountBook; private bool _connectionInitialized; public static LegacyConnectionStateSnapshot Capture( LegacyLoginRuntime runtime) { return new LegacyConnectionStateSnapshot { _serverName = DBConfig.Instance.ServerName, _databaseName = DBConfig.Instance.DataBase, _dataBook = DBConfig.Instance.DataBook, _loginName = DBConfig.Instance.LoginName, _serverType = DBConfig.Instance.ServerType, _connectionTemplate = DBConfig.Instance.Connection, _delphiConnectionTemplate = DBConfig.Instance.dephiConnection, _selectedLedgerName = runtime.SelectedLedgerName, _accountBook = ERPInfo.Instance.AccountBook, _connectionInitialized = runtime._connectionInitialized }; } public void Restore(LegacyLoginRuntime runtime) { DBConfig.Instance.ServerName = _serverName; DBConfig.Instance.DataBase = _databaseName; DBConfig.Instance.DataBook = _dataBook; DBConfig.Instance.LoginName = _loginName; DBConfig.Instance.ServerType = _serverType; DBConfig.Instance.Connection = _connectionTemplate; DBConfig.Instance.dephiConnection = _delphiConnectionTemplate; runtime.SelectedLedgerName = _selectedLedgerName; runtime._ledgerTable = null; runtime._connectionInitialized = _connectionInitialized; ERPInfo.Instance.AccountBook = _accountBook; try { if (!string.IsNullOrWhiteSpace(_serverName) && !string.IsNullOrWhiteSpace(_databaseName)) { DBConfig.Instance.CreateConnection( _connectionTemplate); if (!string.Equals( _serverType, "达梦数据库", StringComparison.Ordinal)) { DelphiHelper.Delphi_Init(new StringBuilder( DBConfig.Instance.GetDelphiConnection( _delphiConnectionTemplate))); } SystemInfo.RefreshSystemParam(); } } catch (Exception restoreException) { LogSanitized(restoreException); } } } private sealed class LegacyConnectionSettingsException : Exception { public LegacyConnectionSettingsException(string message) : base(message) { } } } }