diff --git a/src/CncCollector/Api/CollectorApiServer.cs b/src/CncCollector/Api/CollectorApiServer.cs index 7cb5be4..592859d 100644 --- a/src/CncCollector/Api/CollectorApiServer.cs +++ b/src/CncCollector/Api/CollectorApiServer.cs @@ -122,6 +122,24 @@ namespace CncCollector.Api _engine.Refresh(); SendSuccess(ctx, new { message = "配置已刷新" }); } + else if (path == "/api/collector/daily-summary" && method == "POST") + { + // 读取请求体中的日期参数,默认今天 + string body = ReadRequestBody(ctx); + DateTime summaryDate = DateTime.Today; + if (!string.IsNullOrEmpty(body)) + { + try + { + var obj = Newtonsoft.Json.Linq.JObject.Parse(body); + var dateStr = obj["date"]?.ToString(); + if (!string.IsNullOrEmpty(dateStr)) summaryDate = DateTime.Parse(dateStr); + } + catch { } + } + _engine.RunDailySummary(summaryDate); + SendSuccess(ctx, new { message = $"日终汇总已执行(日期={summaryDate:yyyy-MM-dd})" }); + } else { SendJson(ctx, 404, new { code = 40400, message = "未知端点", data = (object)null }); @@ -159,5 +177,20 @@ namespace CncCollector.Api } catch { } } + + /// + /// 读取请求体 + /// + private string ReadRequestBody(HttpListenerContext ctx) + { + try + { + using (var reader = new System.IO.StreamReader(ctx.Request.InputStream, Encoding.UTF8)) + { + return reader.ReadToEnd(); + } + } + catch { return ""; } + } } } diff --git a/src/CncCollector/Core/CollectRecordWriter.cs b/src/CncCollector/Core/CollectRecordWriter.cs index d30f20e..2423575 100644 --- a/src/CncCollector/Core/CollectRecordWriter.cs +++ b/src/CncCollector/Core/CollectRecordWriter.cs @@ -30,7 +30,7 @@ namespace CncCollector.Core /// 错误信息(失败时) public static void WriteBatch(string businessConnStr, string logConnStr, List records, string rawJson, int collectAddressId, - DateTime requestTime, long? responseDurationMs, bool isSuccess, string errorMessage) + DateTime requestTime, long? responseDurationMs, bool isSuccess, string errorMessage, int? statusCode = null) { var now = DateTime.Now; @@ -49,7 +49,7 @@ namespace CncCollector.Core ResponseTime = now, ResponseDuration = responseDurationMs, IsSuccess = isSuccess ? 1 : 0, - StatusCode = isSuccess ? (int?)200 : null, + StatusCode = statusCode ?? (isSuccess ? (int?)200 : null), RawJson = rawJson ?? "", ErrorMessage = errorMessage ?? (string)null, CreatedAt = now diff --git a/src/CncCollector/Core/CollectWorker.cs b/src/CncCollector/Core/CollectWorker.cs index 03ddff5..c01131b 100644 --- a/src/CncCollector/Core/CollectWorker.cs +++ b/src/CncCollector/Core/CollectWorker.cs @@ -140,6 +140,7 @@ namespace CncCollector.Core bool success = false; string errorMsg = null; long durationMs = 0; + int? httpStatusCode = null; int retryCount = _config.CollectRetryCount; for (int attempt = 0; attempt <= retryCount; attempt++) @@ -158,6 +159,7 @@ namespace CncCollector.Core var response = client.GetAsync(_address.Url).Result; sw.Stop(); durationMs = sw.ElapsedMilliseconds; + httpStatusCode = (int)response.StatusCode; if (response.IsSuccessStatusCode) { @@ -187,7 +189,7 @@ namespace CncCollector.Core // 写入失败记录 CollectRecordWriter.WriteBatch(_businessConnStr, _logConnStr, null, rawJson, - _address.Id, requestTime, durationMs, false, errorMsg); + _address.Id, requestTime, durationMs, false, errorMsg, httpStatusCode); CollectRecordWriter.RecordFailure(_businessConnStr, _address.Id, errorMsg); // 连续失败超过阈值,触发告警 @@ -207,7 +209,7 @@ namespace CncCollector.Core try { - ParseAndSave(rawJson, requestTime, durationMs); + ParseAndSave(rawJson, requestTime, durationMs, httpStatusCode ?? 200); } catch (Exception ex) { @@ -262,7 +264,7 @@ namespace CncCollector.Core /// /// 解析采集到的 JSON 数据并写入数据库 /// - private void ParseAndSave(string rawJson, DateTime requestTime, long durationMs) + private void ParseAndSave(string rawJson, DateTime requestTime, long durationMs, int statusCode = 200) { var collectTime = DateTime.Now; @@ -380,7 +382,7 @@ namespace CncCollector.Core // 4. 批量写入 CollectRecordWriter.WriteBatch(_businessConnStr, _logConnStr, records, rawJson, - _address.Id, requestTime, durationMs, true, null); + _address.Id, requestTime, durationMs, true, null, statusCode); _log.Info($"采集完成: {_address.Name} → {records.Count}台设备, {durationMs}ms"); } diff --git a/src/CncCollector/Core/CollectorEngine.cs b/src/CncCollector/Core/CollectorEngine.cs index 282be33..b3d6424 100644 --- a/src/CncCollector/Core/CollectorEngine.cs +++ b/src/CncCollector/Core/CollectorEngine.cs @@ -160,6 +160,16 @@ namespace CncCollector.Core return status; } + /// + /// 手动执行日终汇总(指定日期) + /// + /// 要汇总的日期 + public void RunDailySummary(DateTime summaryDate) + { + _log.Info($"手动触发日终汇总(日期={summaryDate:yyyy-MM-dd})"); + _dailySummary.Execute(summaryDate); + } + /// /// 加载启用的采集地址并启动工作线程 /// diff --git a/src/CncSimulator/Core/SimulatorServer.cs b/src/CncSimulator/Core/SimulatorServer.cs index 8118579..c41695e 100644 --- a/src/CncSimulator/Core/SimulatorServer.cs +++ b/src/CncSimulator/Core/SimulatorServer.cs @@ -166,7 +166,7 @@ namespace CncSimulator.Core } } - /// 启动HTTP服务和定时器 + /// 启动数据模拟定时器(HttpListener由构造函数或Shutdown后由外部重建) public void Start() { if (_isRunning) return; @@ -180,13 +180,14 @@ namespace CncSimulator.Core _tickTimer.Elapsed += OnTick; _tickTimer.Start(); - // 启动HttpListener - _listener = new HttpListener(); - _listener.Prefixes.Add($"http://+:{_config.Port}/"); - _listener.Start(); - - // 异步接收请求 - _listener.BeginGetContext(OnRequest, null); + // 如果HttpListener未运行,则启动 + if (_listener == null || !_listener.IsListening) + { + _listener = new HttpListener(); + _listener.Prefixes.Add($"http://+:{_config.Port}/"); + _listener.Start(); + _listener.BeginGetContext(OnRequest, null); + } Console.WriteLine($" [✓] {_config.Name}: http://localhost:{_config.Port}/ (管理: http://localhost:{_config.Port}/admin)"); } diff --git a/src/CncWebApi/Controllers/DashboardController.cs b/src/CncWebApi/Controllers/DashboardController.cs index 6e867f5..9457b85 100644 --- a/src/CncWebApi/Controllers/DashboardController.cs +++ b/src/CncWebApi/Controllers/DashboardController.cs @@ -158,6 +158,17 @@ namespace CncWebApi.Controllers return ForwardToCollector("/api/collector/refresh"); } + /// + /// 手动触发日终汇总 + /// POST /api/admin/collector/daily-summary + /// + [HttpPost] + [Route("~/api/admin/collector/daily-summary")] + public IHttpActionResult RunDailySummary() + { + return ForwardToCollector("/api/collector/daily-summary"); + } + /// /// 转发请求到采集服务 ///