修复端到端测试发现的3个Bug

Bug1(中): 采集服务失败时记录实际HTTP状态码
- CollectWorker新增httpStatusCode变量,捕获响应状态码
- CollectRecordWriter.WriteBatch新增statusCode参数,失败时写入实际状态码(如500)
- ParseAndSave传入statusCode参数

Bug2(低): 模拟器Stop→Start端口冲突
- SimulatorServer.Start()复用已有HttpListener,不再重复创建
- 仅在_listener为null或未监听时才创建新HttpListener

Bug3(低): 新增手动触发日终汇总API
- CollectorApiServer新增 POST /api/collector/daily-summary 端点
- CollectorEngine新增 RunDailySummary(date) 公开方法
- DashboardController新增 POST /api/admin/collector/daily-summary 转发
main
haoliang 4 days ago
parent 4b8657553b
commit c2c4d15453

@ -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 { }
}
/// <summary>
/// 读取请求体
/// </summary>
private string ReadRequestBody(HttpListenerContext ctx)
{
try
{
using (var reader = new System.IO.StreamReader(ctx.Request.InputStream, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
catch { return ""; }
}
}
}

@ -30,7 +30,7 @@ namespace CncCollector.Core
/// <param name="errorMessage">错误信息(失败时)</param>
public static void WriteBatch(string businessConnStr, string logConnStr,
List<CollectRecord> 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

@ -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
/// <summary>
/// 解析采集到的 JSON 数据并写入数据库
/// </summary>
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");
}

@ -160,6 +160,16 @@ namespace CncCollector.Core
return status;
}
/// <summary>
/// 手动执行日终汇总(指定日期)
/// </summary>
/// <param name="summaryDate">要汇总的日期</param>
public void RunDailySummary(DateTime summaryDate)
{
_log.Info($"手动触发日终汇总(日期={summaryDate:yyyy-MM-dd}");
_dailySummary.Execute(summaryDate);
}
/// <summary>
/// 加载启用的采集地址并启动工作线程
/// </summary>

@ -166,7 +166,7 @@ namespace CncSimulator.Core
}
}
/// <summary>启动HTTP服务和定时器</summary>
/// <summary>启动数据模拟定时器HttpListener由构造函数或Shutdown后由外部重建</summary>
public void Start()
{
if (_isRunning) return;
@ -180,13 +180,14 @@ namespace CncSimulator.Core
_tickTimer.Elapsed += OnTick;
_tickTimer.Start();
// 启动HttpListener
// 如果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)");
}

@ -158,6 +158,17 @@ namespace CncWebApi.Controllers
return ForwardToCollector("/api/collector/refresh");
}
/// <summary>
/// 手动触发日终汇总
/// POST /api/admin/collector/daily-summary
/// </summary>
[HttpPost]
[Route("~/api/admin/collector/daily-summary")]
public IHttpActionResult RunDailySummary()
{
return ForwardToCollector("/api/collector/daily-summary");
}
/// <summary>
/// 转发请求到采集服务
/// </summary>

Loading…
Cancel
Save