feat: expose legacy login connection settings

This commit is contained in:
2026-07-28 22:35:30 +08:00
parent 4d3128bbfe
commit f3994c6c6e
3 changed files with 597 additions and 1 deletions
@@ -43,7 +43,7 @@ namespace Lskj.Main.Hosting
/// 向外部 WPF 登录页提供旧账套、用户、认证和会话状态写入能力。 /// 向外部 WPF 登录页提供旧账套、用户、认证和会话状态写入能力。
/// 该类型不创建 WinForms 控件,也不引用 WPF。 /// 该类型不创建 WinForms 控件,也不引用 WPF。
/// </summary> /// </summary>
public sealed class LegacyLoginRuntime public sealed partial class LegacyLoginRuntime
{ {
private DataTable _ledgerTable; 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)
{
}
}
}
}
+1
View File
@@ -278,6 +278,7 @@
<Compile Include="Model\winApi.cs" /> <Compile Include="Model\winApi.cs" />
<Compile Include="Hosting\IExternalMainShell.cs" /> <Compile Include="Hosting\IExternalMainShell.cs" />
<Compile Include="Hosting\LegacyApplicationHost.cs" /> <Compile Include="Hosting\LegacyApplicationHost.cs" />
<Compile Include="Hosting\LegacyLoginRuntime.ConnectionSettings.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Control\AwaitScreenControl.resx"> <EmbeddedResource Include="Control\AwaitScreenControl.resx">