using System; using System.Linq; using System.Net.Http; using Dapper; using MySqlConnector; const string BizConn = "Server=localhost;Database=cnc_business;Uid=root;Pwd=root;Charset=utf8mb4;SslMode=None;"; const string LogConn = "Server=localhost;Database=cnc_log;Uid=root;Pwd=root;Charset=utf8mb4;SslMode=None;"; int pass = 0, fail = 0; void Assert(string name, bool cond, string detail = "") { if (cond) { Console.WriteLine($" ✅ {name} {detail}"); pass++; } else { Console.WriteLine($" ❌ {name} {detail}"); fail++; } } Console.WriteLine("===== 错误模拟验证 =====\n"); // ====== 测试1: HTTP请求失败 ====== Console.WriteLine("--- 测试1: HTTP请求失败 → log_collect_raw + 本地日志 ---"); try { using var http = new HttpClient(); http.DefaultRequestHeaders.Add("X-Api-Key", "collector_api_key_2026"); http.PostAsync("http://localhost:5800/api/collector/refresh", null).Wait(); Console.WriteLine(" 已触发配置刷新(将重新采集)"); System.Threading.Thread.Sleep(40000); // 等两个采集周期 using var c = new MySqlConnection(LogConn); var err = c.QueryFirstOrDefault<(long Id, string Msg, DateTime T)>( "SELECT id, error_message, request_time FROM log_collect_raw WHERE is_success=0 AND error_message IS NOT NULL AND error_message!='' ORDER BY id DESC LIMIT 1"); bool hasRec = err.Id > 0; bool notVague = err.Msg != "发生一个或多个错误。" && !(err.Msg??"").StartsWith("发生一个或多个错误"); bool hasType = (err.Msg??"").Contains("Exception") || (err.Msg??"").Contains("Error"); Assert("失败记录存在", hasRec); Assert("非模糊消息(不是'发生一个或多个错误')", notVague); Assert("包含异常类型名", hasType); if (hasRec) Console.WriteLine($" 错误: {err.Msg?.Substring(0, Math.Min(200, err.Msg.Length))}"); Assert("本地日志文件存在", System.IO.File.Exists(@"C:\CncCollector\logs\collector.log")); Assert("错误日志文件存在", System.IO.File.Exists(@"C:\CncCollector\logs\collector_error.log")); } catch (Exception ex) { Assert("测试1执行", false, ex.Message); } // ====== 测试2: JSON解析失败 ====== Console.WriteLine("\n--- 测试2: JSON解析失败 → log_collect_raw ---"); try { using var c = new MySqlConnection(LogConn); var before = c.ExecuteScalar("SELECT COUNT(*) FROM log_collect_raw WHERE error_message LIKE '%[JsonReaderException]%'"); // 写一条模拟JSON解析失败的记录 c.Execute(@"INSERT INTO log_collect_raw (collect_address_id,request_time,response_time,response_duration,is_success,status_code,raw_json,error_message,created_at) VALUES (1,NOW(),NOW(),50,0,NULL,'{broken json',@E,NOW())", new { E = "[JsonReaderException] 无效的JSON格式: Unexpected character ← [ParseAndSave异常] JSON解析失败" }); var after = c.ExecuteScalar("SELECT COUNT(*) FROM log_collect_raw WHERE error_message LIKE '%[JsonReaderException]%'"); Assert("JSON解析失败已记录", after > before); // 清理 c.Execute("DELETE FROM log_collect_raw WHERE error_message LIKE '%[JsonReaderException]%'"); } catch (Exception ex) { Assert("测试2执行", false, ex.Message); } // ====== 测试3: ProductionTracker失败 → cnc_alert ====== Console.WriteLine("\n--- 测试3: ProductionTracker失败 → cnc_alert ---"); try { using var c = new MySqlConnection(BizConn); var before = c.ExecuteScalar("SELECT COUNT(*) FROM cnc_alert WHERE alert_type='production_error'"); c.Execute(@"INSERT INTO cnc_alert (alert_type,title,detail,is_resolved,created_at) VALUES ('production_error','产量跟踪处理异常(模拟)','机床999产量跟踪失败: MySqlException: Connection timeout',0,NOW())"); var after = c.ExecuteScalar("SELECT COUNT(*) FROM cnc_alert WHERE alert_type='production_error'"); Assert("产量跟踪告警已创建", after > before); var alert = c.QueryFirstOrDefault<(string T, string D)>( "SELECT title, detail FROM cnc_alert WHERE title LIKE '%模拟%' ORDER BY id DESC LIMIT 1"); Assert("告警标题含'产量跟踪'", alert.T.Contains("产量跟踪")); Assert("告警详情非空", !string.IsNullOrEmpty(alert.D)); Console.WriteLine($" 告警: {alert.T}: {alert.D}"); c.Execute("DELETE FROM cnc_alert WHERE title LIKE '%模拟%'"); } catch (Exception ex) { Assert("测试3执行", false, ex.Message); } // ====== 测试4: DailySummaryJob失败 → cnc_alert ====== Console.WriteLine("\n--- 测试4: DailySummaryJob失败 → cnc_alert ---"); try { using var c = new MySqlConnection(BizConn); var before = c.ExecuteScalar("SELECT COUNT(*) FROM cnc_alert WHERE alert_type='summary_error'"); c.Execute(@"INSERT INTO cnc_alert (alert_type,title,detail,is_resolved,created_at) VALUES ('summary_error','日终汇总失败(模拟-2026-05-07)','MySqlException: Connection timeout during daily summary transaction',0,NOW())"); var after = c.ExecuteScalar("SELECT COUNT(*) FROM cnc_alert WHERE alert_type='summary_error'"); Assert("日终汇总告警已创建", after > before); var alert = c.QueryFirstOrDefault<(string T, string D)>( "SELECT title, detail FROM cnc_alert WHERE title LIKE '%模拟%' ORDER BY id DESC LIMIT 1"); Assert("告警标题含'日终汇总'", alert.T.Contains("日终汇总")); Console.WriteLine($" 告警: {alert.T}: {alert.D}"); c.Execute("DELETE FROM cnc_alert WHERE title LIKE '%模拟%'"); } catch (Exception ex) { Assert("测试4执行", false, ex.Message); } Console.WriteLine($"\n===== 结果: {pass}通过, {fail}失败 =====");