feat: expose legacy login connection settings
This commit is contained in:
@@ -43,7 +43,7 @@ namespace Lskj.Main.Hosting
|
||||
/// 向外部 WPF 登录页提供旧账套、用户、认证和会话状态写入能力。
|
||||
/// 该类型不创建 WinForms 控件,也不引用 WPF。
|
||||
/// </summary>
|
||||
public sealed class LegacyLoginRuntime
|
||||
public sealed partial class LegacyLoginRuntime
|
||||
{
|
||||
private DataTable _ledgerTable;
|
||||
|
||||
|
||||
@@ -0,0 +1,595 @@
|
||||
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.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<string, string> entries = IniHelper.GetSectionKeys(
|
||||
SystemResourcesIniName,
|
||||
SystemResourcesSection);
|
||||
foreach (KeyValuePair<string, string> 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();
|
||||
commit();
|
||||
|
||||
_ledgerTable = null;
|
||||
SelectedLedgerName = candidate.AccountBookName;
|
||||
ERPInfo.Instance.AccountBook = candidate.AccountBookName;
|
||||
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;
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -278,6 +278,7 @@
|
||||
<Compile Include="Model\winApi.cs" />
|
||||
<Compile Include="Hosting\IExternalMainShell.cs" />
|
||||
<Compile Include="Hosting\LegacyApplicationHost.cs" />
|
||||
<Compile Include="Hosting\LegacyLoginRuntime.ConnectionSettings.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="Control\AwaitScreenControl.resx">
|
||||
|
||||
Reference in New Issue
Block a user