From 4808f0250103f995bfeacd59a9237999c9f5ee74 Mon Sep 17 00:00:00 2001 From: jasonchenwork Date: Mon, 28 Jul 2025 16:22:24 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AA=BF=E6=95=B4=20DB=20=E7=89=A9=E4=BB=B6=20?= =?UTF-8?q?=E8=AA=BF=E6=95=B4=20Room=20states=20=E6=94=B9=E8=AE=80DB=20?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20=E5=AD=97=E9=AB=94=E7=B5=B1=E4=B8=80=2020?= =?UTF-8?q?=2020250728?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CommandHandler.cs | 4 +- DBObj/MyDB.cs | 157 ++++++++++++++++-- DBObj/{SongListManager.cs => SQLManager.cs} | 50 +++--- DBObj/SongList.cs | 4 +- HttpServer.cs | 4 +- .../PrimaryForm.MultiPagePanel.cs | 41 +---- PrimaryFormParts/PrimaryForm.cs | 2 +- Program.cs | 8 +- Room.cs | 78 +++++++-- TCPServer.cs | 16 +- 10 files changed, 258 insertions(+), 106 deletions(-) rename DBObj/{SongListManager.cs => SQLManager.cs} (71%) diff --git a/CommandHandler.cs b/CommandHandler.cs index ec67db5..3caadfe 100644 --- a/CommandHandler.cs +++ b/CommandHandler.cs @@ -16,9 +16,9 @@ namespace DualScreenDemo private int _wrongInputCountfor62 = 0; // 錯誤輸入計數器 private int _wrongInputCountfor61 = 0; // 錯誤輸入計數器 private const int MaxWrongLimit = 3; // 錯誤輸入限制次數 - private readonly SongListManager songListManager; + private readonly SQLManager songListManager; - public CommandHandler(SongListManager songListManager) + public CommandHandler(SQLManager songListManager) { this.songListManager = songListManager; } diff --git a/DBObj/MyDB.cs b/DBObj/MyDB.cs index f0d442e..49dc846 100644 --- a/DBObj/MyDB.cs +++ b/DBObj/MyDB.cs @@ -6,42 +6,166 @@ namespace DBObj { public class MyDB : IDisposable { + private readonly string connectionString = Utils.Env.GetDBConnection(); private MySqlConnection conn; + private DataTable table = new DataTable(); + private int cursor = 0; public MyDB() { conn = new MySqlConnection(connectionString); conn.Open(); - Console.WriteLine("MyDB 連線成功!"); + //Console.WriteLine("MyDB 連線成功!"); } public void Dispose() { if (conn != null) { - conn.Close(); - Console.WriteLine("MyDB 連線已關閉!"); + conn.Dispose(); // 包含 Close + 資源釋放 + conn = null; + //Console.WriteLine("MyDB 連線已釋放!"); } } // SELECT 方法 - public DataTable Select(string query, MySqlParameter[] parameters) + public bool open(string query, MySqlParameter[] parameters, bool showError = true) { - using (var cmd = new MySqlCommand(query, conn)) + table.Clear(); + cursor = 0; + try { - if (parameters != null) + using (var cmd = new MySqlCommand(query, conn)) { - cmd.Parameters.AddRange(parameters); - } - using (var adapter = new MySqlDataAdapter(cmd)) - { - DataTable result = new DataTable(); - adapter.Fill(result); - return result; + if (parameters != null) + { + cmd.Parameters.AddRange(parameters); + } + using (var adapter = new MySqlDataAdapter(cmd)) + { + adapter.Fill(table); + } } } + catch (Exception ex) + { + if (showError) + Console.WriteLine($"DB Error: {ex.Message}"); + return false; + } + return true; } + public T Field(string name) + { + if (cursor < 0 || cursor >= table.Rows.Count || !table.Columns.Contains(name)) + return default; + + object value = table.Rows[cursor][name]; + if (value == DBNull.Value) + return default; + + return (T)Convert.ChangeType(value, typeof(T)); + } + public List ToList() where T : new() + { + var list = new List(); + + foreach (DataRow row in table.Rows) + { + T obj = new T(); + foreach (DataColumn col in table.Columns) + { + var prop = typeof(T).GetProperty(col.ColumnName, + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.IgnoreCase); + + if (prop != null && prop.CanWrite) + { + object value = row[col]; + if (value == DBNull.Value) + { + value = null; + } + try + { + if (value != null && prop.PropertyType != value.GetType()) + { + value = Convert.ChangeType(value, prop.PropertyType); + } + prop.SetValue(obj, value); + } + catch + { + // 可以視情況加入錯誤處理 + } + } + } + list.Add(obj); + } + + return list; + } + public Dictionary CurrentRowAsDictionary() + { + if (cursor < 0 || cursor >= table.Rows.Count) + return null; + + var dict = new Dictionary(); + foreach (DataColumn col in table.Columns) + { + dict[col.ColumnName] = table.Rows[cursor][col]; + } + return dict; + } + public void moveNext() + { + cursor++; + } + public void movePrevious() + { + cursor--; + } + public void move(int index) + { + cursor = index; + } + public void moveFirst() + { + cursor = 0; + } + public void moveEnd() + { + cursor = table.Rows.Count - 1; + } + public bool bof() + { + return (cursor < 0) || (table.Rows.Count == 0); + } + public bool eof() + { + return cursor > table.Rows.Count - 1; + } + public bool Read() + { + if (eof()) return false; + moveNext(); + return !eof(); + } + public int recordCount() + { + return table.Rows.Count; + } + public bool found() + { + return table.Rows.Count > 0; + } + public void clear() + { + table.Clear(); + } + // INSERT / UPDATE / DELETE 方法 public int ExecuteNonQuery(string query, MySqlParameter[] parameters) @@ -55,5 +179,12 @@ namespace DBObj return cmd.ExecuteNonQuery(); } } + public long LastInsertId() + { + using (var cmd = new MySqlCommand("SELECT LAST_INSERT_ID()", conn)) + { + return Convert.ToInt64(cmd.ExecuteScalar()); + } + } } } diff --git a/DBObj/SongListManager.cs b/DBObj/SQLManager.cs similarity index 71% rename from DBObj/SongListManager.cs rename to DBObj/SQLManager.cs index 50a9e97..a25f0b0 100644 --- a/DBObj/SongListManager.cs +++ b/DBObj/SQLManager.cs @@ -2,13 +2,13 @@ using System.Data; using DualScreenDemo; namespace DBObj { - public class SongListManager + public class SQLManager { private MyDB db = new MyDB(); public List FavoriteSongs { get; private set; } //public bool IsUserLoggedIn { get; set; } //public string UserPhoneNumber { get; set; } - public SongListManager() + public SQLManager() { FavoriteSongs = new List(); } @@ -52,53 +52,55 @@ namespace DBObj } } */ - public List SearchNewSongs(){ - string query= $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY add_date DESC LIMIT {Utils.Env.GetInt("NewSongLimit", 100)};"; - return PrimaryForm.Instance.SearchSongs_Mysql(query); + public List SearchNewSongs() + { + string query = $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY add_date DESC LIMIT {Utils.Env.GetInt("NewSongLimit", 100)};"; + return select_Mysql(query); } public List SearchHotSongs(){ string query= $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY song_counts DESC LIMIT {Utils.Env.GetInt("HotSongLimit", 100)};"; - return PrimaryForm.Instance.SearchSongs_Mysql(query); + return select_Mysql(query); } public List SearchSongsBySinger(string keyword) { Console.WriteLine("keyword : " + keyword); var keywordLower = keyword.ToLower(); string query = $"SELECT * FROM song_library_cache WHERE artistA LIKE '%{keywordLower}%' OR artistB LIKE'%{keywordLower}%'ORDER BY song_counts DESC;"; - return PrimaryForm.Instance.SearchSongs_Mysql(query); + return select_Mysql(query); } public List SearchSongsByName(string keyword) { string query = $"SELECT * FROM song_library_cache WHERE LOWER(song_name) LIKE CONCAT('%', LOWER('{keyword}'), '%');"; - return PrimaryForm.Instance.SearchSongs_Mysql(query); + return select_Mysql(query); } public SongData SearchSongByNumber(string songNumber) { string query = $"SELECT * FROM song_library_cache WHERE song_id = '{songNumber}';"; - var searchResults =PrimaryForm.Instance.SearchSongs_Mysql(query); + var searchResults =select_Mysql(query); return searchResults.FirstOrDefault(); } private List select_Mysql(string query) { List searchResults = new List(); Console.WriteLine(query); - using (db) + using (var db = new MyDB()) { - DataTable result = db.Select(query, null); - foreach (DataRow row in result.Rows) - { - searchResults.Add(new SongData( - row["song_id"].ToString(), - row["song_name"].ToString(), - row["artistA"].ToString(), - row["artistB"].ToString(), - row["song_filename"].ToString(), - row["artistA_simplified"].ToString(), - row["artistB_simplified"].ToString(), - row["song_simplified"].ToString(), - Convert.ToInt32(row["vocal"]) - )); + if (db.open(query, null)) { + while (db.Read()){ + searchResults.Add(new SongData( + db.Field("song_id"), + db.Field("song_name"), + db.Field("artistA"), + db.Field("artistB"), + db.Field("song_filename"), + db.Field("artistA_simplified"), + db.Field("artistB_simplified"), + db.Field("song_simplified"), + db.Field("vocal") + )); + } } + Console.WriteLine($"查詢到 {searchResults.Count} 筆資料。"); } diff --git a/DBObj/SongList.cs b/DBObj/SongList.cs index 71180e9..29f23f2 100644 --- a/DBObj/SongList.cs +++ b/DBObj/SongList.cs @@ -44,9 +44,9 @@ namespace DBObj } private static SongData NextPublicSong() { - if (Room.IsClose()) { + if (Program.room.IsClose()) { publicPlaying = close; - } else if(Room.IsOpen() && isWelcome){ + } else if(Program.room.IsOpen() && isWelcome){ isWelcome = false; publicPlaying = welcome; } else { diff --git a/HttpServer.cs b/HttpServer.cs index 7c12d33..8595ec9 100644 --- a/HttpServer.cs +++ b/HttpServer.cs @@ -19,7 +19,7 @@ namespace DualScreenDemo private static string _localIP = GetLocalIPAddress(); private static int _port = 9090; // 或其他方式設置 // 服务器类变量 - private static SongListManager songListManager; + private static SQLManager songListManager; // 使用完整命名空间来避免歧义 public static event ActionString OnDisplayBarrage; private static DateTime lastClickTime = DateTime.MinValue; @@ -35,7 +35,7 @@ namespace DualScreenDemo private static TaskCompletionSource _qrReadyTcs; - public static async Task StartServer(string baseDirectory, int port, SongListManager manager, CancellationToken token) + public static async Task StartServer(string baseDirectory, int port, SQLManager manager, CancellationToken token) { songListManager = manager; string randomFolderName = CreateRandomFolderAndRedirectHTML(baseDirectory); diff --git a/PrimaryFormParts/PrimaryForm.MultiPagePanel.cs b/PrimaryFormParts/PrimaryForm.MultiPagePanel.cs index a19d16f..9518825 100644 --- a/PrimaryFormParts/PrimaryForm.MultiPagePanel.cs +++ b/PrimaryFormParts/PrimaryForm.MultiPagePanel.cs @@ -264,26 +264,10 @@ namespace DualScreenDemo artist.Name; artistLabel.Text = artistText; artistLabel.Tag = artist; - artistLabel.AutoSize = false; + artistLabel.AutoSize = true; // 計算文字寬度 - Font normalFont = new Font("微軟正黑體", 24, FontStyle.Bold); - Font mediumFont = new Font("微軟正黑體", 18, FontStyle.Bold); - Font smallFont = new Font("微軟正黑體", 14, FontStyle.Bold); - - // 根據文字長度設置字體大小 - if (artistLabel.Text.Length > 18) - { - artistLabel.Font = smallFont; - } - else if (artistLabel.Text.Length > 13) - { - artistLabel.Font = mediumFont; - } - else - { - artistLabel.Font = normalFont; - } + artistLabel.Font = new Font("微軟正黑體", 20, FontStyle.Bold); Screen screen = Screen.PrimaryScreen; int screenWidth = screen.Bounds.Width; int screenHeight = screen.Bounds.Height; @@ -400,25 +384,10 @@ namespace DualScreenDemo int textLength = fullText.Length; // 計算文字寬度 - Font normalFont = new Font("微軟正黑體", 20, FontStyle.Bold); - Font mediumFont = new Font("微軟正黑體", 14, FontStyle.Bold); - Font smallFont = new Font("微軟正黑體", 12, FontStyle.Bold); - // 根據文字長度設置字體大小 - if (textLength > 18) - { - songLabel.Font = smallFont; - } - else if (textLength > 8) - { - songLabel.Font = mediumFont; - } - else - { - songLabel.Font = normalFont; - } + songLabel.Font = new Font("微軟正黑體", 20, FontStyle.Bold); songLabel.Text = fullText; songLabel.Tag = song; - songLabel.AutoSize = false; + songLabel.AutoSize = true; // 創建歌手標籤 Label artistLabel = new Label(); artistLabel.Text = artistText; @@ -457,7 +426,7 @@ namespace DualScreenDemo { Image = Image.FromFile(Path.Combine(Application.StartupPath, @"themes\superstar\其他符號_人聲\其他符號_人聲.png")), SizeMode = PictureBoxSizeMode.Zoom, - Size = new Size(32, 32), + Size = new Size(30, 30), Location = new Point(songX + 5, y + 8) }; this.Controls.Add(icon); diff --git a/PrimaryFormParts/PrimaryForm.cs b/PrimaryFormParts/PrimaryForm.cs index 3f7f99b..190938b 100644 --- a/PrimaryFormParts/PrimaryForm.cs +++ b/PrimaryFormParts/PrimaryForm.cs @@ -2235,7 +2235,7 @@ namespace DualScreenDemo { HotPlayButton_Click(null, EventArgs.Empty); } - if (Room.IsClose()) + if (Program.room.IsClose()) { ShowSendOffScreen(); } diff --git a/Program.cs b/Program.cs index 6c9742d..2f05838 100644 --- a/Program.cs +++ b/Program.cs @@ -7,7 +7,7 @@ namespace DualScreenDemo public static class Program { // 定义全局变量 - internal static DBObj.SongListManager songListManager; + internal static DBObj.SQLManager songListManager; //internal static ArtistManager artistManager; internal static SerialPortManager serialPortManager; private static PrimaryForm primaryForm; // 儲存實例的參考 @@ -17,7 +17,7 @@ namespace DualScreenDemo [STAThread] static void Main() { - Console.WriteLine("Server V.1.2.3 202507211828"); + Console.WriteLine("Server V.1.2.4 202507281600"); if (Utils.Env.GetBool("IsCursor", true)) Cursor.Hide(); AppDomain.CurrentDomain.ProcessExit += (s, e) => Cursor.Show(); //Console.WriteLine("正在與中控取得聯繫..."); @@ -34,7 +34,7 @@ namespace DualScreenDemo } // 初始化管理器 - songListManager = new DBObj.SongListManager(); // 使用单例 + songListManager = new DBObj.SQLManager(); // 使用单例 //artistManager = new ArtistManager(); var commandHandler = new CommandHandler(songListManager); @@ -49,7 +49,7 @@ namespace DualScreenDemo } // 啟動服務器 - var TCP = new TCPServer(); + var TCP = new TCPServer(room); Task.Run(() => HttpServerManager.StartServer()); // 註冊事件 diff --git a/Room.cs b/Room.cs index 550ba2c..9554393 100644 --- a/Room.cs +++ b/Room.cs @@ -1,24 +1,76 @@ using OverlayFormObj; +using DBObj; +using MySqlConnector; +using System.Data; namespace DualScreenDemo { public class Room { - private static string State=Utils.Env.Get("RoomStates", "CLOSE"); - public Room(){} - public static void set(string value) + public int branch_id =0; + public string hostName; + + private string State = "error"; + public Room() + { + hostName = System.Net.Dns.GetHostName(); + branch_id =getBranchId(); + State =getDB(); + Console.WriteLine($"hostname status: {hostName},{State}"); + } + private int getBranchId() + { + using (var db = new MyDB()) + { + string sql = "SELECT id FROM branches LIMIT 1"; + if (db.open(sql, null) && db.found())return db.Field("id"); + return 0; + } + } + + private string getDB() + { + using (var db = new MyDB()) + { + string query = @"SELECT * FROM rooms WHERE branch_id = @branch_id AND CONCAT(type, name) = @hostName"; + MySqlParameter[] parameters = { + new MySqlParameter("@branch_id", branch_id), + new MySqlParameter("@hostName", hostName) + }; + if (db.open(query, parameters) && db.found()) + { + State = db.Field("status"); + return State; + } + } + return "error"; + } + private void setDB(string value) + { + using (var db = new MyDB()) + { + string query = @"UPDATE rooms SET status = @status WHERE branch_id = @branch_id AND CONCAT(type, name) = @hostName;"; + MySqlParameter[] parameters = { + new MySqlParameter("@status", value), + new MySqlParameter("@branch_id", branch_id), + new MySqlParameter("@hostName", hostName) + }; + db.ExecuteNonQuery(query, parameters); + } + } + public void set(string value) { string marqueeMessage = "歡迎使用超級巨星歡唱,與你共度美好時光。"; Color c = Color.White; - State = value; - if (value.Equals("PAUSE")) + setDB(value); + if (value.Equals("fire")) { PrimaryForm.Instance.ShowSendOffScreen(); VideoPlayerForm.Instance.Pause(); marqueeMessage = "發生火災,請跟隨引導至逃生出口!!!"; c = Color.Red; } - else if (value.Equals("OPEN")) + else if (value.Equals("active")) { DBObj.SongList.clearSong(); PrimaryForm.Instance.HotPlayButton_Click(null, EventArgs.Empty); @@ -36,19 +88,19 @@ namespace DualScreenDemo VideoPlayerForm.Instance.PlayNextSong(); PrimaryForm.Instance.logout(); - + } - + State =getDB(); OverlayForm.MainForm.UpdateMarqueeText(marqueeMessage, OverlayForm.MarqueeStartPosition.Middle, c); - + } - public static bool IsClose() + public bool IsClose() { - return State.Equals("CLOSE"); + return State.Equals("closed"); } - public static bool IsOpen() + public bool IsOpen() { - return State.Equals("OPEN"); + return State.Equals("active"); } } diff --git a/TCPServer.cs b/TCPServer.cs index fb24c9d..5f421b1 100644 --- a/TCPServer.cs +++ b/TCPServer.cs @@ -12,14 +12,16 @@ namespace DualScreenDemo private TcpListener listener; private const int Port = 1000; private readonly string hostNameSuffix; + private Room _room; //private bool isProcessingCommand = false; - public TCPServer() + public TCPServer(Room room) { + _room = room; listener = new TcpListener(IPAddress.Any, Port); hostNameSuffix = GetHostNameSuffix(); - + _ = Task.Run(() => Start()); } @@ -83,10 +85,6 @@ namespace DualScreenDemo { listener.Start(); Console.WriteLine("Server started on port " + Port + "."); - // 心跳封包 - //heartbeatSender sender = new heartbeatSender("127.0.0.1", 8888); - //sender.Start(); - // Console.WriteLine($"heart beat for server{sender.RemoteEndPoint.Address}:{sender.RemoteEndPoint.Port}"); try { @@ -124,7 +122,7 @@ namespace DualScreenDemo { _ = SafeInvoke(VideoPlayerForm.Instance, async () => { - Room.set("CLOSE"); + _room.set("closed"); try{ await HttpServer.RestartServer(); } catch (Exception ex) { @@ -142,7 +140,7 @@ namespace DualScreenDemo if (command.Trim().Equals("O", StringComparison.OrdinalIgnoreCase)) { // 開台時跳至首頁 - Room.set("OPEN"); + _room.set("active"); byte[] okResponse = Encoding.UTF8.GetBytes("OK\n"); stream.Write(okResponse, 0, okResponse.Length); continue; @@ -151,7 +149,7 @@ namespace DualScreenDemo { _ = SafeInvoke(PrimaryForm.Instance, () => { - Room.set("PAUSE"); + _room.set("fire"); }); byte[] okResponse = Encoding.UTF8.GetBytes("OK\n"); stream.Write(okResponse, 0, okResponse.Length);