using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Net.NetworkInformation; using System.Windows.Forms; using System.Net; using System.Security.Cryptography; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Diagnostics; using Microsoft.VisualBasic.Logging; using System.IO.Compression; using SharpCompress.Archives.Rar; using SharpCompress.Common; using SharpCompress.Archives; using System.Diagnostics.Eventing.Reader; using System.ServiceProcess; using static System.Windows.Forms.VisualStyles.VisualStyleElement; using System.Security.Policy; using static System.Windows.Forms.VisualStyles.VisualStyleElement.ToolBar; using System.Data.SqlTypes; using Serilog; using System.Xml.Linq; using System.IO; using Microsoft.Win32; using System.Web; namespace Start { public partial class Main : Form { private bool isCanDBConfig = true; private bool isPortTestOk = false; private bool isServiceTestOk = false; private bool isDBNameTestOK = false; private bool isRuntimeTestOk = false; public Main() { InitializeComponent(); this.MaximizeBox = false; var physicalAddressList = NetworkInterface.GetAllNetworkInterfaces().Select(t => t.GetPhysicalAddress().ToString()); this.machineTextBox.Text = physicalAddressList.FirstOrDefault()?.ToString(); //this.KeySecreteTextBox.Text = Md5($"{this.machineTextBox.Text}_XINGCANG"); } int apiPort = 7100; int vuePort = 9527; private async void connectButton_Click(object sender, EventArgs e) { string connectionString = $"Server={serverTextBox.Text};User Id={usernameTextBox.Text};Password={passwordTextBox.Text};"; using (SqlConnection connection = new SqlConnection(connectionString)) { try { await Task.Run(() => { connection.Open(); }); WinformLog("数据库连接测试成功!", Color.Green); isCanDBConfig = false; serverTextBox.Enabled = isCanDBConfig; usernameTextBox.Enabled = isCanDBConfig; passwordTextBox.Enabled = isCanDBConfig; connectButton.Enabled = false; selectPathBtn.Enabled = true; //portBtn.Enabled = true; //testDBBtn.Enabled = true; //testServicebtn.Enabled = true; } catch (Exception ex) { WinformLog($"数据库连接测试失败:{ex.Message}", Color.Red); } } } private void portBtn_Click(object sender, EventArgs e) { if (int.TryParse(nginxPortTBox.Text, out vuePort) == false || int.TryParse(apiPortTBox.Text, out apiPort) == false) { WinformLog($"请输入合法的端口", Color.Red); return; } if (IsPortInUse(vuePort)) { WinformLog($"服务设置的前端端口被占用,请选择其他端口", Color.Red); return; } if (IsPortInUse(apiPort)) { WinformLog($"服务设置的后端端口被占用,请选择其他端口", Color.Red); return; } WinformLog("端口测试成功!", Color.Green); isPortTestOk = true; } private void testServicebtn_Click(object sender, EventArgs e) { if (ServiceController.GetServices().Any(t => t.ServiceName == serviceNameTBox.Text)) { WinformLog($"设置的后端服务名称已存在,请更换", Color.Red); return; } if (ServiceController.GetServices().Any(t => t.ServiceName == nginxServiceNameTbox.Text)) { WinformLog($"设置的nginx服务名称已存在,请更换", Color.Red); return; } WinformLog($"测试服务名称成功", Color.Green); isServiceTestOk = true; } private void testNetcoreRuntime() { string runtimeListCommand = "dotnet --list-runtimes"; Process process = new Process(); process.StartInfo.FileName = "cmd.exe"; process.StartInfo.Arguments = "/c " + runtimeListCommand; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.Start(); string output = process.StandardOutput.ReadToEnd(); process.WaitForExit(); bool isAspNetCoreRuntimeInstalled = false; bool isNETCoreRuntimeInstalled = false; var lineList = output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).ToList(); foreach (string line in lineList) { if (line.Contains("Microsoft.AspNetCore.App") && !line.Contains("Preview")) { string versionString = line.Split(' ')[1]; Version version = new Version(versionString); if (version.Major > 6 || (version.Major == 6 && version.Minor >= 0)) { // 大于等于 6.0.0 的 .NET 运行时已安装 isAspNetCoreRuntimeInstalled = true; continue; } } if (line.Contains("Microsoft.NETCore.App") && !line.Contains("Preview")) { string versionString = line.Split(' ')[1]; Version version = new Version(versionString); if (version.Major > 6 || (version.Major == 6 && version.Minor >= 0)) { // 大于等于 6.0.0 的 .NET 运行时已安装 isNETCoreRuntimeInstalled = true; continue; } } } if (isAspNetCoreRuntimeInstalled && isNETCoreRuntimeInstalled) { // 执行逻辑,表示大于等于 6.0.0 的 .NET 运行时已安装 WinformLog("当前系统>= 6.0.0 NetCore 运行时已安装", Color.Green); isRuntimeTestOk = true; } else { WinformLog("当前系统>= 6.0.0 NetCore 运行时未安装,请安装后,再运行该程序", Color.Red); connectButton.Enabled = false; //await Task.Run(() => // { // WinformLog("现在为您启动安装Net core 运行时...", Color.Orange); // string runtimePath = Path.Combine(AppContext.BaseDirectory, "Resource/NetCoreRuntime/dotnet-hosting-6.0.0-win.exe"); // var processInfo = new ProcessStartInfo // { // FileName = runtimePath, // 安装程序的文件名 // Arguments = $"/S /v /qn REINSTALLMODE=vomus", // UseShellExecute = false // }; // var otherProcess = new Process { StartInfo = processInfo }; // otherProcess.Start(); // otherProcess.WaitForExit(); // WinformLog("Net core 6.0.0 运行时安装完成", Color.Green); // }); } } private void testDBBtn_Click(object sender, EventArgs e) { if (string.IsNullOrWhiteSpace(dbNameTBox.Text)) { WinformLog($"请输入部署的数据库名称", Color.Red); return; } string connectionString = $"Server={serverTextBox.Text};User Id={usernameTextBox.Text};Password={passwordTextBox.Text};"; string sql = $"SELECT COUNT(*) FROM sys.databases WHERE name = '{dbNameTBox.Text.Trim()}';"; // 创建连接对象和命令对象 using (SqlConnection connection = new SqlConnection(connectionString)) using (SqlCommand command = new SqlCommand(sql, connection)) { // 打开连接 connection.Open(); // 执行查询,返回结果行数 int rowCount = (int)command.ExecuteScalar(); // 判断结果是否大于0,即是否存在该数据库 if (rowCount > 0) { WinformLog($"{dbNameTBox.Text.Trim()}该数据库已存在。", Color.Red); return; } } WinformLog($"数据库名称测试成功", Color.Green); isDBNameTestOK = true; } private void confimDeployBtn_Click(object sender, EventArgs e) { portBtn_Click(null, null); testServicebtn_Click(null, null); testDBBtn_Click(null, null); if (string.IsNullOrWhiteSpace(this.folderPathTbox.Text)) { WinformLog($"请选择部署路径,测试的端口,数据库名,服务名都成功才可以进行确认", Color.Red); return; } if (isDBNameTestOK == false || isPortTestOk == false || isServiceTestOk == false) { WinformLog($"测试的端口,数据库名,服务名都成功才可以进行确认", Color.Red); return; } WinformLog($"部署配置确认成功", Color.Green); activeBtn.Enabled = true; confimDeployBtn.Enabled = false; selectPathBtn.Enabled = false; //portBtn.Enabled = false; //testDBBtn.Enabled = false; //testServicebtn.Enabled = false; nginxPortTBox.Enabled = false; apiPortTBox.Enabled = false; dbNameTBox.Enabled = false; serviceNameTBox.Enabled = false; serviceDisplayNameTBox.Enabled = false; nginxServiceNameTbox.Enabled = false; nginxServiceDisplayNameTbox.Enabled = false; } private static bool IsPortInUse(int port) { IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); IPEndPoint[] tcpEndPoints = ipProperties.GetActiveTcpListeners(); foreach (IPEndPoint endPoint in tcpEndPoints) { if (endPoint.Port == port) { return true; } } return false; } public static string Md5(string target) { using (MD5 md5 = MD5.Create()) { // MD5非线程安全 byte[] bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(target)); StringBuilder sb = new StringBuilder(32); for (int i = 0; i < bytes.Length; ++i) sb.Append(bytes[i].ToString("x2")); return sb.ToString(); } } private void WinformLog(string message, Color color) { if (InvokeRequired) { Invoke(new Action(() => WinformLog(message, color))); return; } logTBox.SelectionColor = color; logTBox.AppendText($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}: {message}" + Environment.NewLine); Serilog.Log.Information(message); } private async Task ProcessStandardInputAsync(Process process, string cmd, string workDirectory = "") { if (!string.IsNullOrWhiteSpace(workDirectory)) { process.StartInfo.WorkingDirectory = workDirectory; } await process.StandardInput.WriteLineAsync(cmd); await process.StandardInput.FlushAsync(); //while (!process.StandardOutput.EndOfStream) //{ // var output = await process.StandardOutput.ReadLineAsync(); // if (string.IsNullOrWhiteSpace(output)) // { // break; // } //} } private async void activeBtn_Click(object sender, EventArgs e) { if (Md5($"{machineTextBox.Text}_XINGCANG") != KeySecreteTextBox.Text.Trim()) { WinformLog($"秘钥不正确,请联系供应商", Color.Red); return; } this.activeBtn.Enabled = false; var serviceName = serviceNameTBox.Text; var serviceDisplayName = serviceDisplayNameTBox.Text; var nginxServiceName = nginxServiceNameTbox.Text; var nginxServiceDesName = nginxServiceDisplayNameTbox.Text; var deployFoder = this.folderPathTbox.Text; string nginxExtractName = "EIImageViewerWeb"; string apiExtractName = "EIImageViewerService"; string dataExtraName = "EIImageViewerData"; string nginxStartPath = Path.Combine(deployFoder, @$"{nginxExtractName}\"); var nginxConfigPath = Path.Combine(nginxStartPath, @$"conf\nginx.conf"); var nginxEXEPath = Path.Combine(nginxStartPath, "nginx.exe"); string nginxServiceEXEPath = Path.Combine(nginxStartPath, "nginxService.exe"); string nginxServiceXMLPath = Path.Combine(nginxStartPath, "nginxService.xml"); var apiBinPath = Path.Combine(deployFoder, "EIImageViewerService/EI_Image_Viewer.exe"); var apiJsonConfigPath = Path.Combine(deployFoder, "EIImageViewerService/appsettings.CertificateApply.json"); string nginxRarFilePath = Path.Combine(AppContext.BaseDirectory, $@"Resource\EIImageViewerWeb.rar"); string apiRarFilePath = Path.Combine(AppContext.BaseDirectory, "Resource/EIImageViewerService.rar"); string dataRarFilePath = Path.Combine(AppContext.BaseDirectory, "Resource/EIImageViewerData.rar"); #region 创建进程执行命令 var startInfo = new ProcessStartInfo(); startInfo.FileName = "cmd.exe"; // 指定要启动的应用程序 startInfo.RedirectStandardInput = true; startInfo.UseShellExecute = false; startInfo.CreateNoWindow = true; // 创建新进程并启动 var process = new Process(); process.StartInfo = startInfo; process.Start(); #endregion await Task.Run(async () => { #region 解压nginx 和前端部署的文件 WinformLog($"开始解压 nginx 及前端部署压缩文件...", Color.Green); string extractPath = Path.Combine(deployFoder, nginxExtractName); if (!Directory.Exists(extractPath)) { Directory.CreateDirectory(extractPath); } using (var archive = RarArchive.Open(nginxRarFilePath)) { foreach (var entry in archive.Entries) { if (!entry.IsDirectory) { entry.WriteToDirectory(extractPath, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true }); } else { string directoryPath = Path.Combine(extractPath, entry.Key); Directory.CreateDirectory(directoryPath); } } } WinformLog($"nginx 及前端部署压缩文件已成功解压缩到: {extractPath}", Color.Green); #endregion #region 修改 nginx 配置文件 启动nginx if (!File.Exists(nginxConfigPath)) { WinformLog("预设路径不存在nginx", Color.Red); } WinformLog("准备写入nginx配置...", Color.Green); var nginxConfig = File.ReadAllText(nginxConfigPath); nginxConfig = nginxConfig.Replace("9520", vuePort.ToString()); nginxConfig = nginxConfig.Replace("7100", apiPort.ToString()); File.WriteAllText(nginxConfigPath, nginxConfig); WinformLog("nginx部署配置写入完成", Color.Green); #region nginxservice WinformLog("准备nginx自启动服务配置文件...", Color.Green); var nginxServiceConfig = File.ReadAllText(nginxServiceXMLPath); nginxServiceConfig = nginxServiceConfig.Replace("{nginxName}", nginxServiceName); nginxServiceConfig = nginxServiceConfig.Replace("{nginxDesName}", nginxServiceDesName); nginxServiceConfig = nginxServiceConfig.Replace("{nginxPath}", nginxEXEPath); nginxServiceConfig = nginxServiceConfig.Replace("{nginxStartPath}", nginxStartPath); File.WriteAllText(nginxServiceXMLPath, nginxServiceConfig); WinformLog("nginx自启动服务配置写入完成", Color.Green); #endregion WinformLog($"准备创建自启动nginx服务...", Color.Green); var nginxCreateStr = $@"{nginxServiceEXEPath} install "; // 执行 sc create 命令来创建服务 await ProcessStandardInputAsync(process, nginxCreateStr); WinformLog($"服务{nginxServiceName}创建执行结束", Color.Green); await Task.Delay(2000); #region nginx 服务 WinformLog($"启动部署的{nginxServiceName}服务...", Color.Green); await ProcessStandardInputAsync(process, $"sc start {nginxServiceName}"); ServiceController scNginx = new ServiceController(nginxServiceName); if (scNginx.Status != ServiceControllerStatus.Running) { scNginx.Refresh(); await Task.Delay(3000); scNginx.Refresh(); await Task.Delay(2000); } if (scNginx.Status == ServiceControllerStatus.Running) { WinformLog($"nginx服务启动成功", Color.Green); } else { WinformLog($"nginx服务启动失败", Color.Red); } #endregion #region 命令行方式启动 废弃 //// 创建ProcessStartInfo对象,指定要启动的可执行文件及其参数 //ProcessStartInfo psi = new ProcessStartInfo(nginxEXEPath); //// 指定工作目录,即进入nginx.exe所在的目录 //psi.WorkingDirectory = nginxStartPath; //WinformLog(" 开始启动 nginx 服务...", Color.Green); //// 启动可执行文件 //Process.Start(psi); //if (Process.GetProcesses().Any(t => t.ProcessName.Contains("nginx") && t.MainModule.FileName.Contains(nginxStartPath))) //{ // WinformLog(" nginx 服务启动成功", Color.Green); //} //else //{ // WinformLog(" nginx 服务启动失败", Color.Red); // return; //} #endregion #endregion }); await Task.Run(() => { #region 解压后端部署的文件 WinformLog($"开始解压后端部署压缩文件...", Color.Green); string apiExtractPath = Path.Combine(deployFoder, apiExtractName); if (!Directory.Exists(apiExtractPath)) { Directory.CreateDirectory(apiExtractPath); } using (var archive = RarArchive.Open(apiRarFilePath)) { foreach (var entry in archive.Entries) { if (!entry.IsDirectory) { entry.WriteToDirectory(apiExtractPath, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true }); } else { string directoryPath = Path.Combine(apiExtractPath, entry.Key); Directory.CreateDirectory(directoryPath); } } } WinformLog($"后端部署压缩文件已成功解压缩到: {apiExtractPath}", Color.Green); #endregion }); await Task.Run(() => { #region 解压模板文件 WinformLog($"开始解压部署依赖模板压缩文件...", Color.Green); string extractPath = Path.Combine(deployFoder, dataExtraName); if (!Directory.Exists(extractPath)) { Directory.CreateDirectory(extractPath); } using (var archive = RarArchive.Open(dataRarFilePath)) { foreach (var entry in archive.Entries) { if (!entry.IsDirectory) { entry.WriteToDirectory(extractPath, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true }); } } } WinformLog($"依赖模板压缩文件已成功解压缩到: {extractPath}", Color.Green); #endregion #region 文档等资源文件 var otherFileFolder = Path.Combine(AppContext.BaseDirectory, $@"Resource"); if (Directory.Exists(otherFileFolder)) { // 获取源目录下所有文件 string[] files = Directory.GetFiles(otherFileFolder); foreach (string file in files) { // 筛选掉文件夹 if (!File.GetAttributes(file).HasFlag(FileAttributes.Directory)) { string extension = Path.GetExtension(file); if (extension != ".zip" && extension != ".rar" && extension != ".7z" && extension != ".tar" && extension != ".gz") { // 获取文件名 string fileName = Path.GetFileName(file); // 拷贝文件到目标目录 File.Copy(file, Path.Combine(deployFoder, fileName), true); } } } } WinformLog($"文档资源释放成功", Color.Green); #endregion }); await Task.Run(() => { #region 激活 try { string directoryPath = @"C:\ProgramData\.xingcang"; if (!Directory.Exists(directoryPath)) { Directory.CreateDirectory(directoryPath); } var configObj = new { key = this.machineTextBox.Text, value = this.KeySecreteTextBox.Text.Trim(), user = usernameTextBox.Text, server = serverTextBox.Text, password = passwordTextBox.Text, dbName = dbNameTBox.Text, deployFolder = deployFoder, nginxStartPath = nginxStartPath, serviceName = serviceName, nginxServiceName = nginxServiceName, nginxServiceEXEPath = nginxServiceEXEPath }; File.WriteAllText(Path.Combine(directoryPath, "config.json"), JsonConvert.SerializeObject(configObj)); WinformLog("写入激活秘钥完成", Color.Green); } catch (Exception ex) { WinformLog("写入激活秘钥失败,请用管理员方式启动该程序,保证程序可以", Color.Red); } #endregion }); await Task.Run(async () => { #region 创建服务 WinformLog($"开始创建服务{serviceName}...", Color.Green); var createTestStr = $"sc create {serviceName} binPath= \"{apiBinPath} --urls=\"http://127.0.0.1:{apiPort}\" --env CertificateApply\" DisplayName= \"{serviceDisplayName}\" start= auto"; var createStr = $@"sc create {serviceName} binPath= ""{apiBinPath} --urls=""http://127.0.0.1:{apiPort}"" --env CertificateApply"" DisplayName= ""{serviceDisplayName}"" start= auto"; // 执行 sc create 命令来创建服务 await ProcessStandardInputAsync(process, createStr); WinformLog($"服务{serviceName}创建执行结束", Color.Green); #endregion #region 初始化数据库脚本 WinformLog($"执行初始化数据库脚本...", Color.Green); //执行数据库脚本 await ProcessStandardInputAsync(process, $@" SQLCMD -v dbName = ""{dbNameTBox.Text}"" -i ""{AppContext.BaseDirectory}Resource\Data\dbo.sql"" "); await Task.Delay(3000); await ProcessStandardInputAsync(process, $@" SQLCMD -v dbName = ""{dbNameTBox.Text}"" -i ""{AppContext.BaseDirectory}Resource\Data\data.sql"" "); WinformLog($"初始化数据库脚本执行结束", Color.Green); #endregion #region 部署网站配置文件写入 WinformLog("开始写入部署网站服务配置文件...", Color.Green); var appsettingsJson = File.ReadAllText(apiJsonConfigPath); // 解析 JSON 字符串 var jObject = JObject.Parse(appsettingsJson); // 获取 UpdateConfig 属性所在的节点 var updateConfigNode = jObject["ConnectionStrings"]; var connectionString = $"Server={serverTextBox.Text};Database={dbNameTBox.Text};User ID={usernameTextBox.Text};Password={passwordTextBox.Text};TrustServerCertificate=true"; updateConfigNode["RemoteNew"] = connectionString; try { using (var connection = new SqlConnection(connectionString)) { connection.Open(); File.WriteAllText(apiJsonConfigPath, jObject.ToString()); } } catch (SqlException ex) { WinformLog($"连接字符串错误", Color.Red); return; } WinformLog("部署网站服务配置文件写入并测试OK", Color.Green); #endregion }); await Task.Run(async () => { #region 启动后端服务 WinformLog($"启动部署的后端服务...", Color.Green); await ProcessStandardInputAsync(process, $"sc start {serviceName}"); // 关闭进程流并等待进程退出 process.StandardInput.Close(); process.WaitForExit(); ServiceController sc = new ServiceController(serviceName); if (sc.Status != ServiceControllerStatus.Running) { sc.Refresh(); await Task.Delay(3000); sc.Refresh(); await Task.Delay(2000); } if (sc.Status == ServiceControllerStatus.Running) { WinformLog($"后端服务启动成功", Color.Green); WinformLog($"部署工作已完成", Color.Green); try { WinformLog($"现在准备打开浏览器...", Color.Green); await Task.Delay(2000); // Use ProcessStartInfo class var start = new ProcessStartInfo($"http://127.0.0.1:{vuePort}") { UseShellExecute = true, Verb = "open" }; var urlProcess= Process.Start(start); urlProcess.WaitForExit(); // 等待进程结束 } catch (Exception ex) { // Handle exception MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } else { WinformLog($"后端服务启动失败,您可手动启动{serviceDisplayName}尝试,如若手动尝试还是无法启动,请联系技术人员确认部署环境", Color.Red); } #endregion await Task.Delay(10000); Application.Exit(); }); } private void selectPathBtn_Click(object sender, EventArgs e) { FolderBrowserDialog dialog = new FolderBrowserDialog(); if (dialog.ShowDialog() == DialogResult.OK) { folderPathTbox.Text = dialog.SelectedPath; confimDeployBtn.Enabled = true; WinformLog($"部署路径选择成功", Color.Green); } } private void btnCopy_Click(object sender, EventArgs e) { Clipboard.SetText(machineTextBox.Text); WinformLog("机器码拷贝到剪切板成功!", Color.Green); } private void Main_Load(object sender, EventArgs e) { Serilog.Log.Logger = new LoggerConfiguration() .WriteTo.File("logs\\log.txt", rollingInterval: RollingInterval.Day) .CreateLogger(); // 检查是否安装了 SQL Server bool isSqlServerInstalled = false; RegistryKey key = null; try { key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SQL Server"); if (key != null) isSqlServerInstalled = true; } finally { key?.Close(); } if (isSqlServerInstalled) { // 获取 SQL Server 实例列表 key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL"); if (key != null) { foreach (var instanceName in key.GetValueNames()) { WinformLog($"当前系统SQL Server 已安装实例: {instanceName}", Color.Green); } } } else { WinformLog("SQL Server 未安装,请安装后再运行该部署程序", Color.Red); } key?.Close(); //// Check if any version of SQL Server is installed //RegistryKey sqlServerKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Microsoft SQL Server"); //if (sqlServerKey != null) //{ // WinformLog("当前系统SQL Server 已安装", Color.Green); //} //else //{ // WinformLog("SQL Server 未安装,请安装后再运行该部署程序", Color.Red); //} testNetcoreRuntime(); this.TopMost = true; this.Activate(); } } }