feat: expose legacy login runtime for WPF host

This commit is contained in:
2026-07-28 14:19:28 +08:00
parent 6be6607a40
commit fa48419bd8
4 changed files with 562 additions and 5 deletions
@@ -0,0 +1,21 @@
using System.Windows.Forms;
namespace Lskj.Main.Hosting
{
/// <summary>
/// 旧启动流程与可选新主界面之间的中立扩展点。
/// 此接口只使用 .NET Framework 4.0/WinForms 基础类型,不引用 WPF 或 DevExpress 25.2。
/// </summary>
public interface IExternalMainShell
{
bool IsOpen { get; }
DialogResult ShowLoginDialog(object loginRuntime);
DialogResult ShowDialog();
void RequestRelogin();
void RequestExit(string message);
}
}
@@ -0,0 +1,439 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using DevExpress.LookAndFeel;
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
{
/// <summary>
/// 为独立的新启动程序公开原 Ls_ERP 初始化和登录流程。
/// </summary>
public static class LegacyApplicationHost
{
public static void Run(string[] args, IExternalMainShell externalMainShell)
{
if (externalMainShell == null)
throw new ArgumentNullException("externalMainShell");
if (Manager.ExternalMainShell != null)
throw new InvalidOperationException("旧程序已经由另一个外部主界面托管。");
Manager.ExternalMainShell = externalMainShell;
try
{
Program.RunFromExternalHost(args);
}
finally
{
Manager.ExternalMainShell = null;
}
}
}
/// <summary>
/// 向外部 WPF 登录页提供旧账套、用户、认证和会话状态写入能力。
/// 该类型不创建 WinForms 控件,也不引用 WPF。
/// </summary>
public sealed class LegacyLoginRuntime
{
private DataTable _ledgerTable;
public LegacyLoginRuntime()
{
ERPInfo.Instance.LoginResult = false;
}
public string SelectedLedgerName { get; private set; }
public string LoginName
{
get { return DBConfig.Instance.LoginName ?? string.Empty; }
}
public bool RememberPasswordEnabled
{
get { return !SystemInfo.Instance.DisableRememberPassword; }
}
public DataTable LoadLedgers()
{
_ledgerTable = MainImpl.GetLedgerList() ?? new DataTable("Ledgers");
SelectedLedgerName = ResolveSelectedLedgerName(_ledgerTable);
return _ledgerTable;
}
public DataTable LoadUsers()
{
DataTable users = SystemInfo.Instance.DisplayUserCode
? MainImpl.GetEmployeeListNew()
: MainImpl.GetEmployeeList();
return users ?? new DataTable("Users");
}
public DataTable SelectLedger(string ledgerName)
{
if (string.IsNullOrWhiteSpace(ledgerName))
throw new InvalidOperationException("请选择账套。");
if (_ledgerTable == null)
LoadLedgers();
DataRow ledger = _ledgerTable.Rows.Cast<DataRow>().FirstOrDefault(
row => string.Equals(
Value(row, "ShowName"),
ledgerName,
StringComparison.OrdinalIgnoreCase));
if (ledger == null)
throw new InvalidOperationException("未找到所选账套,请重新选择。");
string serverName = Value(ledger, "IP");
string database = Value(ledger, "DBName");
string dataBook = Value(ledger, "ShowName");
if (string.IsNullOrWhiteSpace(serverName) ||
string.IsNullOrWhiteSpace(database))
{
throw new InvalidOperationException(ResourceKeys.UnSetServerAddress);
}
string oldServerName = DBConfig.Instance.ServerName;
string oldDatabase = DBConfig.Instance.DataBase;
string oldDataBook = DBConfig.Instance.DataBook;
string oldSelectedLedgerName = SelectedLedgerName;
string oldAccountBook = ERPInfo.Instance.AccountBook;
try
{
DBConfig.Instance.ServerName = serverName;
DBConfig.Instance.DataBase = database;
DBConfig.Instance.DataBook = dataBook;
if (!DBConfig.Instance.CreateConnection(DBConfig.Instance.Connection))
throw new InvalidOperationException(ResourceKeys.ServerAddressFault);
if (!DBConfig.Instance.ServerType.Equals("达梦数据库") &&
DelphiHelper.Delphi_Init(new StringBuilder(
DBConfig.Instance.GetDelphiConnection(
DBConfig.Instance.dephiConnection))) == 0)
{
throw new InvalidOperationException(
ResourceKeys.UnConnectServer + "[By Delphi]");
}
SystemInfo.RefreshSystemParam();
DataTable users = LoadUsers();
SelectedLedgerName = dataBook;
ERPInfo.Instance.AccountBook = dataBook;
return users;
}
catch (Exception switchException)
{
DBConfig.Instance.ServerName = oldServerName;
DBConfig.Instance.DataBase = oldDatabase;
DBConfig.Instance.DataBook = oldDataBook;
SelectedLedgerName = oldSelectedLedgerName;
ERPInfo.Instance.AccountBook = oldAccountBook;
try
{
DBConfig.Instance.CreateConnection(DBConfig.Instance.Connection);
if (!DBConfig.Instance.ServerType.Equals("达梦数据库"))
{
DelphiHelper.Delphi_Init(new StringBuilder(
DBConfig.Instance.GetDelphiConnection(
DBConfig.Instance.dephiConnection)));
}
SystemInfo.RefreshSystemParam();
}
catch (Exception restoreException)
{
LogHelper.Instance.WriteError(restoreException);
}
LogHelper.Instance.WriteError(switchException);
throw;
}
}
public string LoadRememberedPassword(string loginName)
{
if (!RememberPasswordEnabled || string.IsNullOrWhiteSpace(loginName))
return null;
try
{
string encrypted = IniHelper.Read(
DBConfig.Instance.ServerName + loginName);
if (string.IsNullOrWhiteSpace(encrypted))
return null;
string[] parts = AESUtil.Decrypt(encrypted).Split('^');
bool remember;
if (parts.Length < 2 ||
!bool.TryParse(parts[0], out remember) ||
!remember)
{
return null;
}
return string.Join("^", parts.Skip(1).ToArray());
}
catch
{
return null;
}
}
public string Login(
string loginName,
string password,
bool rememberPassword)
{
ERPInfo.Instance.LoginResult = false;
if (string.IsNullOrWhiteSpace(loginName))
return ResourceKeys.UserNameNotNull;
if (password == null)
password = string.Empty;
try
{
DataTable users = MainImpl.GetEmployeeList();
string displayColumn = ResolveDisplayColumn(users);
List<DataRow> matches = users.Rows.Cast<DataRow>()
.Where(row =>
string.Equals(
Value(row, displayColumn),
loginName,
StringComparison.OrdinalIgnoreCase) ||
string.Equals(
Value(row, "UserCode"),
loginName,
StringComparison.OrdinalIgnoreCase))
.ToList();
if (matches.Count == 0)
return ResourceKeys.NameOrPasswordError;
if (matches.Count > 1)
return ResourceKeys.DuplicatedNameError;
DataRow user = matches[0];
string userId = Value(user, "UserId");
string userName = Value(user, "UserName");
string loginAccount = Value(user, "UserCode");
if (string.IsNullOrWhiteSpace(userId))
return ResourceKeys.UserNotFound;
ERPInfo.Instance.LoginResult = false;
if (SystemInfo.Instance.MacEnabled)
{
int macResult = MainImpl.CheckMacAddress(
ERPInfo.Instance.MacAddress,
userId);
MainImpl.LoginAfterRegister(
ERPInfo.Instance.MacAddress,
userId);
if (macResult == 0)
return ResourceKeys.LoginFault + ResourceKeys.UnKownMacAddress;
if (macResult == -1)
return ResourceKeys.LoginFault + ResourceKeys.UnKownMacAddressTable;
}
string keyError = VerifyEncryptionKey();
if (!string.IsNullOrEmpty(keyError))
return keyError;
if (!MainImpl.CheckingMacAddress(userId))
return ResourceKeys.MacAddressError +
",当前地址:" + ERPInfo.Instance.MacAddress;
int loginResult = MainImpl.Login(userId, password, false);
if (loginResult != 0)
return LoginError(loginResult);
CompleteLogin(
userId,
userName,
loginAccount,
loginName,
password,
rememberPassword);
ERPInfo.Instance.LoginResult = true;
LogUtil.WriteDebug("", "登录软件", "进入操作", "系统登录");
return string.Empty;
}
catch (Exception exception)
{
ERPInfo.Instance.LoginResult = false;
LogUtil.WriteError(ResourceKeys.UserLogin, exception);
return ResourceKeys.LoginFault;
}
}
private static string VerifyEncryptionKey()
{
if (!SystemInfo.Instance.IsEncrypt)
return string.Empty;
int[] keyHandles = ERPInfo.Instance.keyHandles = new int[8];
int[] keyNumbers = ERPInfo.Instance.keyNumber = new int[8];
SmartX1Api.SmartX1Find(DBConfig.Instance.ServerName, keyHandles, keyNumbers);
int keyHandle = keyHandles[0];
int pin1 = Convert.ToInt32("0x987F6BCD", 16);
int pin2 = Convert.ToInt32("0xE193C5B2", 16);
int pin3 = Convert.ToInt32("0xD507CC28", 16);
int pin4 = Convert.ToInt32("0x4B125AF6", 16);
return SmartX1Api.SmartX1Open(keyHandle, pin1, pin2, pin3, pin4) == 0
? string.Empty
: "U盾数据获取失败,请重试或联系管理员";
}
private void CompleteLogin(
string userId,
string userName,
string loginAccount,
string enteredLoginName,
string password,
bool rememberPassword)
{
if (!MainImpl.HasExistsColumn("p_employeetab", "MacAdress"))
BaseImpl.ExecSqlValue("alter table p_employeetab add MacAdress varchar(100)");
BaseImpl.ExecSqlValue(string.Format(
"update p_employeetab set MacAdress = '{0}' where employeeid='{1}'",
(ERPInfo.Instance.MacAddress ?? string.Empty).Replace("'", "''"),
userId.Replace("'", "''")));
if (!MainImpl.HasExistsColumn("p_employeetab", "ClientIp"))
BaseImpl.ExecSqlValue("alter table p_employeetab add ClientIp varchar(100)");
BaseImpl.ExecSqlValue(string.Format(
"update p_employeetab set ClientIp = '{0}' where employeeid='{1}'",
(ERPInfo.Instance.LoginIPV4 ?? string.Empty).Replace("'", "''"),
userId.Replace("'", "''")));
if (SystemInfo.Instance.AutoSystemDataFormat)
{
FormatSystemDatetime formatter = new FormatSystemDatetime();
formatter.SetDateTimeFormat();
}
string skinName = MainImpl.GetUserSkin(userId);
skinName = string.IsNullOrEmpty(skinName)
? ERPInfo.Instance.SkinName
: skinName;
UserLookAndFeel.Default.SetSkinStyle(skinName);
IniHelper.Write(
DBConfig.Instance.ServerName + enteredLoginName,
AESUtil.Encrypt(rememberPassword + "^" + password));
ERPInfo.Instance.SeriesId = SystemInfo.Instance.seriesid;
ERPInfo.Instance.UserLinkPhone = MainImpl.GetUserPhone(userId);
ERPInfo.Instance.UserId = userId;
ERPInfo.Instance.UserName = userName;
ERPInfo.Instance.Password = SHAHelper.EncryptPassword(password);
ERPInfo.Instance.SkinName = skinName;
string accountBook = string.IsNullOrWhiteSpace(SelectedLedgerName)
? DBConfig.Instance.DataBook
: SelectedLedgerName;
ERPInfo.Instance.AccountBook = accountBook;
ERPInfo.Instance.LoginAccount = loginAccount;
ERPInfo.Instance.InPassWord = password;
ERPInfo.Instance.PrimitiveBrowser = SystemInfo.Instance.PrimitiveBrowser;
DBConfig.Instance.LoginName = enteredLoginName;
DBConfig.Instance.NoticeUserID = userId;
DBConfig.Instance.NoticeUserName = userName;
DBConfig.Instance.DataBook = accountBook;
DBConfig.Instance.WriteConfig();
if (userName.Equals("管理员") &&
!string.IsNullOrWhiteSpace(loginAccount))
{
ERPInfo.Instance.Password = SqlHelper.ExecuteString(
"Password",
"select Password from P_EmployeeTab where LoginAccount='" +
loginAccount.Replace("'", "''") + "'");
}
ERPInfo.Instance.LanguageName = "中文";
LanguageTranslation.GetLanguageComparisonTable();
TrySetDelphiParameters();
PubUtil.ClearFilesInDirectory(PubUtil.ImageDownloadPath);
}
private static void TrySetDelphiParameters()
{
try
{
DelphiHelper.Delphi_SetParams(
Convert.ToInt32(ERPInfo.Instance.UserId),
string.IsNullOrWhiteSpace(ERPInfo.Instance.SubSysId)
? 0
: Convert.ToInt32(ERPInfo.Instance.SubSysId),
new StringBuilder(ERPInfo.Instance.UserName),
new StringBuilder(ERPInfo.Instance.WindowName),
new StringBuilder(ERPInfo.Instance.SkinName));
}
catch (Exception exception)
{
LogHelper.Instance.WriteError(exception);
}
}
private static string LoginError(int loginResult)
{
switch (loginResult)
{
case 1:
return ResourceKeys.NameOrPasswordError;
case 2:
return ResourceKeys.AlreadyLogin;
case -2:
return ResourceKeys.MultiFailedLogin;
default:
return ResourceKeys.LoginFault;
}
}
private static string ResolveSelectedLedgerName(DataTable ledgers)
{
DataRow selected = ledgers.Rows.Cast<DataRow>().FirstOrDefault(
row => string.Equals(
Value(row, "DBName"),
DBConfig.Instance.DataBase,
StringComparison.OrdinalIgnoreCase));
if (selected == null && !string.IsNullOrWhiteSpace(DBConfig.Instance.DataBook))
{
selected = ledgers.Rows.Cast<DataRow>().FirstOrDefault(
row => string.Equals(
Value(row, "ShowName"),
DBConfig.Instance.DataBook,
StringComparison.OrdinalIgnoreCase));
}
if (selected == null)
selected = ledgers.Rows.Cast<DataRow>().FirstOrDefault();
return selected == null ? string.Empty : Value(selected, "ShowName");
}
private static string ResolveDisplayColumn(DataTable users)
{
string configured = SystemInfo.Instance.LoginUsername;
return !string.IsNullOrWhiteSpace(configured) &&
users.Columns.Contains(configured)
? configured
: "UserName";
}
private static string Value(DataRow row, string columnName)
{
return row != null && row.Table.Columns.Contains(columnName)
? row[columnName] + string.Empty
: string.Empty;
}
}
}
+2
View File
@@ -276,6 +276,8 @@
<Compile Include="Model\BitMapHelper.cs" />
<Compile Include="Model\Manager.cs" />
<Compile Include="Model\winApi.cs" />
<Compile Include="Hosting\IExternalMainShell.cs" />
<Compile Include="Hosting\LegacyApplicationHost.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Control\AwaitScreenControl.resx">
+100 -5
View File
@@ -15,6 +15,7 @@ using Lskj.Control;
using Lskj.Control.Model;
using Lskj.Core;
using Lskj.Data;
using Lskj.Main.Hosting;
using Lskj.Model;
using Lskj.Util;
using Lskj.Web.Core.Util;
@@ -55,6 +56,10 @@ namespace Lskj.Main.Model
public static int isAwaitTime = 10;
public static int RefreshTime = 0;
/// <summary>
/// 由外部启动程序提供的可选主界面。默认为 null,旧 Ls_ERP.exe 仍打开 FrmMain。
/// </summary>
public static IExternalMainShell ExternalMainShell { get; internal set; }
/// <summary>
/// 默认下载地址
/// </summary>
private static string filePath = string.Empty;
@@ -180,7 +185,13 @@ namespace Lskj.Main.Model
bool isOpenBs = false;
DialogResult result;
if (ERPInfo.Instance.SubMenuCount > 1 && !skipMenu)
if (ExternalMainShell != null)
{
// WPF 主框架已经提供顶部子系统导航,不再打开旧 FrmSubSystem。
// 优先保留当前仍有权限的子系统,否则选择第一个可用子系统。
isStart = SelectExternalMainShellSubSystem();
}
else if (ERPInfo.Instance.SubMenuCount > 1 && !skipMenu)
{
// 进入子系统界面
result = RunSubSystemForm();
@@ -190,8 +201,19 @@ namespace Lskj.Main.Model
if (isStart)
{
// 直接进入主界面
RunMainForm();
if (ERPInfo.Instance.SubMenuCount > 1)
result = RunMainForm();
if (result == DialogResult.Retry)
{
StartForm();
return;
}
if (ExternalMainShell != null)
{
// 外部主界面关闭即结束本次 WPF 入口,不返回旧子系统选择页。
ExitSystem();
return;
}
else if (ERPInfo.Instance.SubMenuCount > 1)
{
ReStartMain();
}
@@ -653,6 +675,9 @@ namespace Lskj.Main.Model
/// <returns>DialogResult.</returns>
private static DialogResult RunLoginForm()
{
if (ExternalMainShell != null)
return ExternalMainShell.ShowLoginDialog(new LegacyLoginRuntime());
FrmLogin frmLogin = new FrmLogin();
return frmLogin.ShowDialog();
}
@@ -674,6 +699,50 @@ namespace Lskj.Main.Model
BsUrl = _frmSubSystem.BsUrl;
return dialogResult;
}
/// <summary>
/// 为外部 WPF 主框架选择登录后的初始子系统。
/// 旧 WinForms 入口仍由 FrmSubSystem 完成人工选择。
/// </summary>
private static bool SelectExternalMainShellSubSystem()
{
string where = string.IsNullOrEmpty(ERPInfo.Instance.SeriesId)
? string.Empty
: string.Format("and SeriesId={0}", ERPInfo.Instance.SeriesId);
DataTable subSystems = MainImpl.GetSubSystems(where);
if (subSystems == null || subSystems.Rows.Count == 0)
{
MessageUtil.Show(ResourceKeys.SubSystemUnEnabled);
return false;
}
DataRow selectedRow = subSystems.AsEnumerable().FirstOrDefault(row =>
IsEnabledSubSystem(row) &&
string.Equals(
row["SubSysId"] + string.Empty,
ERPInfo.Instance.SubSysId,
StringComparison.OrdinalIgnoreCase));
if (selectedRow == null)
selectedRow = subSystems.AsEnumerable().FirstOrDefault(IsEnabledSubSystem);
if (selectedRow == null)
{
MessageUtil.Show(ResourceKeys.SubSystemUnEnabled);
return false;
}
ERPInfo.Instance.SubMenuCount = subSystems.AsEnumerable().Count(IsEnabledSubSystem);
ERPInfo.Instance.SubSysId = selectedRow["SubSysId"] + string.Empty;
ERPInfo.Instance.SubSysName = selectedRow["SubSysName"] + string.Empty;
return true;
}
private static bool IsEnabledSubSystem(DataRow row)
{
return row != null &&
row.Table.Columns.Contains("UseEd") &&
row["UseEd"] != DBNull.Value &&
Convert.ToBoolean(row["UseEd"]);
}
/// <summary>
/// <para>说明:打开主程序</para>
/// <para>创建人:龚宇超</para>
@@ -686,6 +755,10 @@ namespace Lskj.Main.Model
/// <returns>DialogResult.</returns>
private static DialogResult RunMainForm()
{
IExternalMainShell externalMainShell = ExternalMainShell;
if (externalMainShell != null)
return externalMainShell.ShowDialog();
_frmMain = new FrmMain();
return _frmMain.ShowDialog();
}
@@ -909,6 +982,11 @@ namespace Lskj.Main.Model
}
}));
}
else if (ExternalMainShell != null && ExternalMainShell.IsOpen)
{
ExternalMainShell.RequestExit($"当前用户在另一电脑登录\r\nClientIp:{clientip}\r\nMacAdress:{loginMacAdress}\r\n当前系统即将关闭");
isClose = true;
}
else if (_frmMain != null && _frmMain.Visible == true)
{
_frmMain.Invoke(new Action(() =>
@@ -953,7 +1031,7 @@ namespace Lskj.Main.Model
}
//记录登出日志
LogUtil.WriteDebug("", "退出软件", "软件退出", "系统登录");
Process[] p = Process.GetProcessesByName("Ls_ERP");
Process[] p = GetApplicationProcesses();
foreach (Process item in p)
{
try
@@ -1107,6 +1185,12 @@ namespace Lskj.Main.Model
/// </summary>
public static void ReLogin()
{
IExternalMainShell externalMainShell = ExternalMainShell;
if (externalMainShell != null && externalMainShell.IsOpen)
{
externalMainShell.RequestRelogin();
return;
}
if (_frmMain != null)
{
if (ERPInfo.Instance.WatermarkForm != null)
@@ -1121,5 +1205,16 @@ namespace Lskj.Main.Model
StartForm();
}
/// <summary>
/// 返回当前启动路线需要退出的主进程。
/// 旧入口保持原有 Ls_ERP 进程名语义,外部宿主只退出当前 WPF 进程。
/// </summary>
internal static Process[] GetApplicationProcesses()
{
return ExternalMainShell == null
? Process.GetProcessesByName("Ls_ERP")
: new[] { Process.GetCurrentProcess() };
}
}
}
}