using Tiobon.Core.OPS.Tool.OPS.Tool.Helper; using Tiobon.Core.OPS.Tool.OPS.Tool.Model; using Tiobon.Core.OPS.Tool.OPS.Tool.src; using Renci.SshNet; using Renci.SshNet.Sftp; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace Tiobon.Core.OPS.Tool.OPS.Tool.View { public partial class Frm_MultiEnvironment : Form { bool isLinux = Const.config.system == "Linux"; #region 初始化 public Frm_MultiEnvironment() { InitializeComponent(); this.Text = $"多环境管理 - {Const.config.name} {Const.config.ip}"; RefreshView(); } private void RefreshView(bool invoke = false) { if (invoke) { try { BeginInvoke(new EventHandler(delegate { listView_MultiEnv.Items.Clear(); Const.MultiEnvs.Where(o => o.Ip == Const.config.ip).ForEach(o => { ListViewItem item = new ListViewItem(); item.SubItems[0].Text = o.Name; item.SubItems.Add(o.FileName); item.SubItems.Add(o.DefaultPort); item.SubItems.Add(o.DefaultMask); listView_MultiEnv.Items.Add(item); }); })); } catch { } } else { listView_MultiEnv.Items.Clear(); Const.MultiEnvs.Where(o => o.Ip == Const.config.ip).ForEach(o => { ListViewItem item = new ListViewItem(); item.SubItems[0].Text = o.Name; item.SubItems.Add(o.FileName); item.SubItems.Add(o.DefaultPort); item.SubItems.Add(o.DefaultMask); listView_MultiEnv.Items.Add(item); }); } } #endregion #region 新增、编辑、删除 private void tsmi_Create_Click(object sender, EventArgs e) { using (Frm_MultiItem f = new Frm_MultiItem()) { f.ShowDialog(); } RefreshView(); } private void tsmi_Edit_Click(object sender, EventArgs e) { Edit(); } private void listView_MultiEnv_MouseDoubleClick(object sender, MouseEventArgs e) { Edit(); } private void Edit() { if (this.listView_MultiEnv.SelectedItems.Count <= 0) return; using (Frm_MultiItem f = new Frm_MultiItem($"{this.listView_MultiEnv.SelectedItems[0].Text}")) { f.ShowDialog(); } RefreshView(); } private void tsmi_Delete_Click(object sender, EventArgs e) { if (this.listView_MultiEnv.SelectedItems.Count <= 0) return; if (MessageBox.Show("是否确认删除?", "提示", MessageBoxButtons.YesNo) != DialogResult.Yes) { return; } var item = Const.MultiEnvs.Where(o => o.Ip == Const.config.ip && o.Name == this.listView_MultiEnv.SelectedItems[0].Text).FirstOrDefault(); if (!(item is null)) { Const.MultiEnvs.Remove(item); Const.SaveMultiEnv(); RefreshView(); } } #endregion #region 重启 private void tsmi_Restart_Click(object sender, EventArgs e) { if (this.listView_MultiEnv.SelectedItems.Count <= 0) return; if (this.listView_MultiEnv.SelectedItems.Count <= 0) return; var name = this.listView_MultiEnv.SelectedItems[0].Text; if (MessageBox.Show($"是否确认重启[{name}]?", "提示", MessageBoxButtons.YesNo) != DialogResult.Yes) { return; } var item = Const.MultiEnvs.Where(o => o.Ip == Const.config.ip && o.Name == name).FirstOrDefault(); var exist = Exists($"/home/{Const.config.ssh_user}/ihdis/{item.FileName}"); if (!exist) { MessageBox.Show($"环境[{name}]未部署,不执行此操作", "提示"); return; } try { LoadingHelper.ShowLoading("正在重启服务,请稍等...", this, (obj) => { var b_suc = ExecSsh($"cd /home/{Const.config.ssh_user}/ihdis/{item.FileName};sudo docker-compose restart;", out string result); Thread.Sleep(2000); ExecSsh($"cd /home/{Const.config.ssh_user}/ihdis/{item.FileName};sudo docker-compose restart rtm", out _); Thread.Sleep(5000); ExecSsh($"cd /home/{Const.config.ssh_user}/ihdis/{item.FileName};sudo docker-compose restart job", out _); Thread.Sleep(2000); ExecSsh($"cd /home/{Const.config.ssh_user}/ihdis/{item.FileName};sudo docker-compose restart hfs", out _); Thread.Sleep(2000); ExecSsh($"cd /home/{Const.config.ssh_user}/ihdis/{item.FileName};sudo docker-compose restart iot", out _); Thread.Sleep(2000); ExecSsh($"cd /home/{Const.config.ssh_user}/ihdis/{item.FileName};sudo docker-compose restart ts", out _); Thread.Sleep(2000); ExecSsh($"cd /home/{Const.config.ssh_user}/ihdis/{item.FileName};sudo docker-compose restart webapi", out _); Thread.Sleep(10000); ExecSsh($"cd /home/{Const.config.ssh_user}/ihdis/{item.FileName};sudo docker-compose restart nginx", out _); if (b_suc) { MessageBox.Show($"环境[{name}]重启成功!", "提示"); } else { MessageBox.Show($"环境[{name}]重启失败:\r\n{result}", "提示"); } }); } catch { } } #endregion #region 停止 private void tsmi_Stop_Click(object sender, EventArgs e) { if (this.listView_MultiEnv.SelectedItems.Count <= 0) return; var name = this.listView_MultiEnv.SelectedItems[0].Text; if (MessageBox.Show($"是否确认停止[{name}]?", "提示", MessageBoxButtons.YesNo) != DialogResult.Yes) { return; } var item = Const.MultiEnvs.Where(o => o.Ip == Const.config.ip && o.Name == name).FirstOrDefault(); var exist = Exists($"/home/{Const.config.ssh_user}/ihdis/{item.FileName}"); if (!exist) { MessageBox.Show($"环境[{name}]未部署,不执行此操作", "提示"); return; } try { LoadingHelper.ShowLoading("正在停止服务,请稍等...", this, (obj) => { var b_suc = ExecSsh($"cd /home/{Const.config.ssh_user}/ihdis/{item.FileName};sudo docker-compose stop;", out string result); if (b_suc) { MessageBox.Show($"环境[{name}]停止成功!", "提示"); } else { MessageBox.Show($"环境[{name}]停止失败:\r\n{result}", "提示"); } }); } catch { } } #endregion #region 前端 private void tsmi_Web_Click(object sender, EventArgs e) { if (this.listView_MultiEnv.SelectedItems.Count <= 0) return; try { var item = Const.MultiEnvs.Where(o => o.Ip == Const.config.ip && o.Name == this.listView_MultiEnv.SelectedItems[0].Text).FirstOrDefault(); var exist = Exists($"/home/{Const.config.ssh_user}/ihdis/{item.FileName}"); if (!exist) { MessageBox.Show($"环境[{item.Name}]未部署,不执行此操作", "提示"); return; } System.Diagnostics.Process.Start($"http://{item.Ip}:{item.DefaultPort}00"); } catch { } } #endregion #region 数据库 private void tsmi_Database_Click(object sender, EventArgs e) { if (this.listView_MultiEnv.SelectedItems.Count <= 0) return; try { var item = Const.MultiEnvs.Where(o => o.Ip == Const.config.ip && o.Name == this.listView_MultiEnv.SelectedItems[0].Text).FirstOrDefault(); var exist = Exists($"/home/{Const.config.ssh_user}/ihdis/{item.FileName}"); if (!exist) { MessageBox.Show($"环境[{item.Name}]未部署,不执行此操作", "提示"); return; } string heidisql = @"C:\Program Files\HeidiSQL\heidisql.exe"; if (!File.Exists(heidisql)) { MessageBox.Show($"未能检测到 [HeidiSQL],请先安装!\r\n默认检测路径为:{heidisql}", "提示"); } else { System.Diagnostics.Process.Start(heidisql, $" -d {item.Ip}({item.FileName}) -n {0} -h {item.Ip} -u root -p jlmed#123 -P {item.DefaultPort}90"); } } catch { } } #endregion #region 数据库初始化 private void tsmi_DBInit_Click(object sender, EventArgs e) { if (this.listView_MultiEnv.SelectedItems.Count <= 0) return; var name = this.listView_MultiEnv.SelectedItems[0].Text; if (MessageBox.Show($"此操作存在极高风险\r\n1、会重置数据密码为jlmed#123\r\n2、现有数据将丢失,数据库重置为初始状态\r\n是否确认初始化数据库[{name}]?", "提示", MessageBoxButtons.YesNo) != DialogResult.Yes) { return; } var item = Const.MultiEnvs.Where(o => o.Ip == Const.config.ip && o.Name == name).FirstOrDefault(); var exist = Exists($"/home/{Const.config.ssh_user}/ihdis/{item.FileName}"); if (!exist) { MessageBox.Show($"环境[{name}]未部署,不执行此操作", "提示"); return; } if (MessageBox.Show($"是否已经联系过研发,确认必须执行此操作?", "提示", MessageBoxButtons.YesNo) != DialogResult.Yes) { return; } try { LoadingHelper.ShowLoading("正在初始化数据库,请稍等...", this, (obj) => { var script = $"cd /home/{Const.config.ssh_user}/ihdis/{item.FileName};sudo docker-compose exec -T mysql mysql -uroot -hlocalhost mysql --default-character-set=utf8 < conf/mysql/hdis_db.sql;"; Const.write_log($"初始化数据库:{script}"); var b_suc = ExecSsh(script, out string result); if (!b_suc) { Const.write_log($"初始化数据库:{result}"); MessageBox.Show($"初始化数据库:{result}", "提示"); return; } Const.write_log($"初始化数据库成功"); System.Threading.Thread.Sleep(10000); script = $"cd /home/{Const.config.ssh_user}/ihdis/{item.FileName};sudo docker-compose exec -T mysql mysql -uroot -hlocalhost hdis --default-character-set=utf8 < conf/mysql/hdis_table.sql;"; Const.write_log($"初始化数据库表:{script}"); b_suc = ExecSsh(script, out result); if (!b_suc) { Const.write_log($"初始化数据库表失败:{result}"); MessageBox.Show($"初始化数据库表失败:\r\n{result}", "提示"); return; } Const.write_log($"初始化数据库表成功"); System.Threading.Thread.Sleep(10000); script = $"cd /home/{Const.config.ssh_user}/ihdis/{item.FileName};sudo docker-compose exec -T mysql mysql -uroot -hlocalhost mysql --default-character-set=utf8 < conf/mysql/hdis_user.sql;"; Const.write_log($"初始化用户信息:{script}"); b_suc = ExecSsh(script, out result); if (!b_suc) { Const.write_log($"初始化用户信息失败:{result}"); MessageBox.Show($"初始化用户信息失败:\r\n{result}", "提示"); return; } Const.write_log($"初始化用户信息成功"); System.Threading.Thread.Sleep(10000); script = $"cd /home/{Const.config.ssh_user}/ihdis/{item.FileName};sudo docker-compose exec -T mysql mysql -uroot -hlocalhost -e \"ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'jlmed#123'; flush privileges; \";"; Const.write_log($"初始化用户密码:{script}"); b_suc = ExecSsh(script, out result); if (!b_suc) { Const.write_log($"初始化用户密码失败:{result}"); MessageBox.Show($"初始化用户密码失败:\r\n{result}", "提示"); return; } Const.write_log($"初始化用户密码成功"); System.Threading.Thread.Sleep(10000); script = $"cd /home/{Const.config.ssh_user}/ihdis/{item.FileName};sudo docker-compose restart;"; Const.write_log($"重启服务:{script}"); b_suc = ExecSsh(script, out result); if (!b_suc) { Const.write_log($"重启服务失败:{result}"); MessageBox.Show($"重启服务失败:\r\n{result}", "提示"); return; } Const.write_log($"重启服务成功"); }); } catch { } } #endregion #region 同步 private void ll_Sync_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { try { LoadingHelper.ShowLoading("正在同步数据,请稍等...", this, (obj) => { if (isLinux) { var hospitals = ListDirectory($"/home/{Const.config.ssh_user}/ihdis").Select(o => o.Name).ToList(); hospitals.ForEach(hospitalName => { //服务器端数据 if (!Exists($"/home/{Const.config.ssh_user}/ihdis/{hospitalName}/.env")) { Const.write_log($"发现 文件夹 {hospitalName} 不存在 .env 文件,跳过同步"); return; } var fname = GetTempFileName(".env"); bool b_suc = DownloadFile($"/home/{Const.config.ssh_user}/ihdis/{hospitalName}/.env", fname); var content = File.ReadAllText(fname); var datas = content.Split(new string[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries).ToList(); var hospital = new MultiEnvInfo { Name = hospitalName, FileName = hospitalName, Ip = Const.config.ip, DefaultPort = datas.Where(d => d.StartsWith("IHDIS_PORT=")).FirstOrDefault()?.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries)[1].Trim(), DefaultMask = datas.Where(d => d.StartsWith("mask=")).FirstOrDefault()?.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries)[1].Trim(), }; //是否存在 var item = Const.MultiEnvs.Where(o => o.Ip == Const.config.ip && o.FileName == hospitalName).FirstOrDefault(); if (item == null) { if (b_suc) { Const.MultiEnvs.Add(hospital); Const.write_log($"发现新环境 {hospitalName} 端口 {hospital.DefaultPort} 掩码 {hospital.DefaultMask}"); } } else { if (item.DefaultPort != hospital.DefaultPort || item.DefaultMask != hospital.DefaultMask) { Const.write_log($"更新环境 {hospitalName} 端口号 {item.DefaultPort} 掩码 {hospital.DefaultMask}"); item.DefaultPort = hospital.DefaultPort; } else { Const.write_log($"医院 {hospitalName} 端口号一致 {hospital.DefaultPort} 掩码一致 {DefaultMask}"); } } }); //本地数据 var notExsit = new List(); Const.MultiEnvs.Where(o => o.Ip == Const.config.ip).ForEach(hospital => { if (!hospitals.Contains(hospital.FileName)) { notExsit.Add(hospital); } }); notExsit.ForEach(hospital => { Const.MultiEnvs.Remove(hospital); //环境不存在 Const.write_log($"环境已经不存在 {hospital.FileName}"); }); Const.SaveMultiEnv(); RefreshView(true); } else { var hospitals = new List(); DirectoryInfo directory = new DirectoryInfo(Const.config.install_dir); FileSystemInfo[] filesArray = directory.GetFileSystemInfos(); //foreach (var filesItem in filesArray) filesArray.ForEach(filesItem => { //是否是一个文件夹 if (filesItem.Attributes == FileAttributes.Directory) { var hospitalName = filesItem.Name; //服务器端数据 if (!File.Exists($"{filesItem.FullName}/.env")) { Const.write_log($"发现 文件夹 {hospitalName} 不存在 .env 文件,跳过同步"); return; } hospitals.Add(hospitalName); var content = File.ReadAllText($"{filesItem.FullName}/.env"); var datas = content.Split(new string[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries).ToList(); var hospital = new MultiEnvInfo { Name = hospitalName, FileName = hospitalName, Ip = Const.config.ip, DefaultPort = datas.Where(d => d.StartsWith("IHDIS_PORT=")).FirstOrDefault()?.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries)[1].Trim(), DefaultMask = datas.Where(d => d.StartsWith("mask=")).FirstOrDefault()?.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries)[1].Trim(), }; //是否存在 var item = Const.MultiEnvs.Where(o => o.Ip == Const.config.ip && o.FileName == hospitalName).FirstOrDefault(); if (item == null) { Const.MultiEnvs.Add(hospital); Const.write_log($"发现新环境 {hospitalName} 端口 {hospital.DefaultPort} 掩码 {hospital.DefaultMask}"); } else { if (item.DefaultPort != hospital.DefaultPort || item.DefaultMask != hospital.DefaultMask) { Const.write_log($"更新环境 {hospitalName} 端口号 {item.DefaultPort} 掩码 {hospital.DefaultMask}"); item.DefaultPort = hospital.DefaultPort; } else { Const.write_log($"医院 {hospitalName} 端口号一致 {hospital.DefaultPort} 掩码一致 {DefaultMask}"); } } } }); //本地数据 var notExsit = new List(); Const.MultiEnvs.Where(o => o.Ip == Const.config.ip).ForEach(hospital => { if (!hospitals.Contains(hospital.FileName)) notExsit.Add(hospital); }); notExsit.ForEach(hospital => { Const.MultiEnvs.Remove(hospital); //环境不存在 Const.write_log($"环境已经不存在 {hospital.FileName}"); }); Const.SaveMultiEnv(); RefreshView(true); } }); } catch { } } #endregion #region 辅助方法 private bool ExecSsh(string command, out string result) { bool b_suc = false; result = string.Empty; try { using (SshClient ssh = new SshClient(Const.config.ip, Convert.ToInt32(Const.config.ssh_port), "root", Const.config.supasswd)) { ssh.Connect(); var cmd = ssh.RunCommand(command); if (cmd.ExitStatus != 0) { result = cmd.Error; } else { b_suc = true; result = $"{cmd.Result}{cmd.Error}"; } } } catch (Exception ex) { result = ex.Message; } Const.write_log(result); return b_suc; } private bool Exists(string path) { bool b_suc = false; try { using (SftpClient sftp = new SftpClient(Const.config.ip, Convert.ToInt32(Const.config.ssh_port), "root", Const.config.supasswd)) { sftp.Connect(); b_suc = sftp.Exists(path); } } catch (Exception ex) { Const.write_log(ex.ToString()); } return b_suc; } /// /// 获取当前目录的文件夹列表 /// /// /// /// private List ListDirectory(string remotePath) { List list = new List(); try { using (SftpClient sftp = new SftpClient(Const.config.ip, Convert.ToInt32(Const.config.ssh_port), "root", Const.config.supasswd)) { sftp.Connect(); list = sftp.ListDirectory(remotePath).Where(f => f.IsDirectory && f.Name != "." && f.Name != "..").ToList(); } } catch (Exception ex) { Const.write_log($"获取文件夹失败:{remotePath} {ex.Message}"); } return list; } /// /// 下载单个文件 /// /// /// /// public static bool DownloadFile(string remoteFile, string localFile) { bool b_suc = false; try { using (SftpClient sftp = new SftpClient(Const.config.ip, Convert.ToInt32(Const.config.ssh_port), "root", Const.config.supasswd)) { sftp.Connect(); if (sftp.Exists(remoteFile)) { Const.write_log($"下载文件 {remoteFile}"); if (File.Exists(localFile)) { File.Delete(localFile); System.Threading.Thread.Sleep(50); } var sftFile = sftp.ListDirectory(remoteFile.Substring(0, remoteFile.LastIndexOf('/'))).Where(o => o.FullName == remoteFile).FirstOrDefault(); using (var file = File.OpenWrite(localFile)) { sftp.DownloadFile(remoteFile, file); Const.write_log($"成功 {remoteFile} =>> {localFile} len {file.Length} byte"); b_suc = true; } } else { Const.write_log($"文件不存在 {remoteFile}"); } } } catch (Exception ex) { Const.write_log($"下载文件失败:{remoteFile} {ex.Message}"); } return b_suc; } /// /// 获取临时文件名 /// /// /// /// private string GetTempFileName(string fname, bool random = true) { return $"{System.Environment.GetEnvironmentVariable("TEMP")}\\{fname}" + (random ? $"_{DateTime.Now.Ticks}" : ""); } #endregion } }