From 4366cf9347ea9a577742de33f52e236a13df85e1 Mon Sep 17 00:00:00 2001 From: haoliang <821644@qq.com> Date: Thu, 7 May 2026 02:06:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BB=AA=E8=A1=A8=E7=9B=98?= =?UTF-8?q?=E4=B8=89=E4=B8=AAbug=EF=BC=9A=E9=87=87=E9=9B=86=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E7=B2=BE=E7=A1=AE=E5=8C=BA=E5=88=865=E7=A7=8D+?= =?UTF-8?q?=E6=9A=82=E5=81=9C=E6=81=A2=E5=A4=8D=E3=80=81=E4=BA=A7=E9=87=8F?= =?UTF-8?q?=E7=BB=93=E7=AE=97=E4=BF=9D=E7=95=99=E5=AE=9E=E6=97=B6=E5=80=BC?= =?UTF-8?q?=E3=80=81=E8=BD=A6=E9=97=B4=E5=B9=B3=E5=9D=87=E4=BA=A7=E9=87=8F?= =?UTF-8?q?=E6=8E=92=E9=99=A4=E5=81=9C=E7=94=A8=E6=9C=BA=E5=BA=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/dashboard/DashboardPage.vue | 45 ++++++++++--------- src/CncCollector/Core/ProductionTracker.cs | 5 ++- .../Impl/Dashboard/DashboardRepository.cs | 2 +- src/CncService/Impl/DashboardService.cs | 26 ++++++++++- .../Controllers/DashboardController.cs | 8 +++- 5 files changed, 59 insertions(+), 27 deletions(-) diff --git a/frontend/src/views/dashboard/DashboardPage.vue b/frontend/src/views/dashboard/DashboardPage.vue index 46046df..13d537b 100644 --- a/frontend/src/views/dashboard/DashboardPage.vue +++ b/frontend/src/views/dashboard/DashboardPage.vue @@ -33,7 +33,7 @@
采集服务 - +
@@ -42,13 +42,14 @@ {{ collectorStatusText }}
-
运行 {{ formatUptime(collectorStatus.uptimeSeconds) }}
+
运行 {{ formatUptime(collectorStatus.uptimeSeconds) }}
- 启动采集 - 停止采集 - 刷新配置 -
+ 需手动启动服务 + 恢复采集 + 暂停采集 + 刷新配置 + @@ -402,25 +403,25 @@ function formatNumber(val: number | undefined | null): string { return Number(val).toFixed(2) } -// 采集服务状态:综合心跳 + Windows服务状态 +// 采集服务状态:5种精确状态 const collectorTagType = computed(() => { - const { serviceStatus, status } = collectorStatus.value - if (serviceStatus === 'Running' && status === 'running') return 'success' - if (serviceStatus === 'Running' && status !== 'running') return 'warning' // 进程在但心跳超时 - if (serviceStatus === 'NotInstalled') return 'danger' - if (serviceStatus === 'StartFailed') return 'danger' + const { status } = collectorStatus.value + if (status === 'running') return 'success' + if (status === 'paused') return 'info' + if (status === 'timeout') return 'warning' + if (status === 'stopped') return 'info' + if (status === 'not_installed') return 'danger' return 'warning' }) const collectorStatusText = computed(() => { - const { serviceStatus, status } = collectorStatus.value - if (serviceStatus === 'Running' && status === 'running') return '运行中' - if (serviceStatus === 'Running' && status !== 'running') return '心跳超时' - if (serviceStatus === 'NotInstalled') return '未安装' - if (serviceStatus === 'Stopped') return '已停止' - if (serviceStatus === 'Starting') return '启动中' - if (serviceStatus === 'StartFailed') return '启动失败' - return serviceStatus || '-' + const { status } = collectorStatus.value + if (status === 'running') return '运行中' + if (status === 'paused') return '已暂停' + if (status === 'timeout') return '心跳超时' + if (status === 'stopped') return '已停止' + if (status === 'not_installed') return '未安装' + return status || '-' }) function alertTypeTag(type: string): string { @@ -443,7 +444,7 @@ function initWorkshopChart() { trigger: 'axis', formatter: (params: any) => { const d = workshopData.value[params[0].dataIndex] - return `${d.workshopName}
${unitLabel}产量: ${params[0].value} ${unit}
总产量: ${d.quantity} 件
机床数: ${d.machineCount} 台` + return `${d.workshopName}
${unitLabel}产量: ${Number(params[0].value).toFixed(2)} ${unit}
总产量: ${d.quantity} 件
机床数: ${d.machineCount} 台` }, }, grid: { left: 60, right: 20, top: 20, bottom: 30 }, @@ -452,7 +453,7 @@ function initWorkshopChart() { series: [{ type: 'bar', data: workshopData.value.map(i => i.avgQuantity), itemStyle: { color: '#67C23A', borderRadius: [4, 4, 0, 0] }, barWidth: '40%', - label: { show: true, position: 'top', formatter: `{c} ${unit}`, fontSize: 12 }, + label: { show: true, position: 'top', formatter: (p: any) => `${Number(p.value).toFixed(2)} ${unit}`, fontSize: 12 }, }], }) } diff --git a/src/CncCollector/Core/ProductionTracker.cs b/src/CncCollector/Core/ProductionTracker.cs index 2a19f4b..ed0fd7f 100644 --- a/src/CncCollector/Core/ProductionTracker.cs +++ b/src/CncCollector/Core/ProductionTracker.cs @@ -119,8 +119,11 @@ namespace CncCollector.Core using (var conn = new MySqlConnection(_connectionString)) { // 结账所有活跃段:is_settled=0 且 end_time IS NULL + // 正确计算quantity:保留当前end_part_count,用end-start计算产量 conn.Execute(@"UPDATE cnc_production_segment - SET end_time = NOW(), end_part_count = start_part_count, quantity = 0, + SET end_time = NOW(), + end_part_count = COALESCE(end_part_count, start_part_count), + quantity = GREATEST(0, COALESCE(end_part_count, start_part_count) - start_part_count), close_reason = @Reason, is_settled = 1, updated_at = NOW() WHERE is_settled = 0 AND end_time IS NULL", new { Reason = SegmentCloseReason.ServiceStop }); diff --git a/src/CncRepository/Impl/Dashboard/DashboardRepository.cs b/src/CncRepository/Impl/Dashboard/DashboardRepository.cs index d13d674..abee519 100644 --- a/src/CncRepository/Impl/Dashboard/DashboardRepository.cs +++ b/src/CncRepository/Impl/Dashboard/DashboardRepository.cs @@ -94,7 +94,7 @@ namespace CncRepository.Impl.Dashboard ELSE COALESCE(SUM(ad.day_quantity), 0) / (DATEDIFF(@EndDate, @StartDate) + 1) / NULLIF(COUNT(DISTINCT m.id), 0) END AS AvgQuantity FROM cnc_workshop w - LEFT JOIN cnc_machine m ON m.workshop_id = w.id + LEFT JOIN cnc_machine m ON m.workshop_id = w.id AND m.is_enabled = 1 LEFT JOIN ( SELECT machine_id, production_date, day_quantity FROM ( SELECT dp.machine_id, dp.production_date, SUM(dp.total_quantity) AS day_quantity diff --git a/src/CncService/Impl/DashboardService.cs b/src/CncService/Impl/DashboardService.cs index f7dd673..cab35c4 100644 --- a/src/CncService/Impl/DashboardService.cs +++ b/src/CncService/Impl/DashboardService.cs @@ -111,8 +111,30 @@ namespace CncService.Impl serviceStatusText = svc.ToString(); } - // 组合状态:NotInstalled -> 停止,其他根据心跳决定 - string status = (serviceStatusText == "NotInstalled") ? "stopped" : (heartbeatRunning ? "running" : "stopped"); + // 组合状态:精确区分5种情况 + string status; + if (serviceStatusText == "NotInstalled") + { + status = "not_installed"; + } + else if (serviceStatusText == "Stopped" || serviceStatusText == "StartFailed") + { + status = "stopped"; + } + else if (heartbeatRunning) + { + status = "running"; + } + else if (latest != null && latest.Status == "stopped") + { + // 引擎主动停止(暂停),心跳status='stopped' + status = "paused"; + } + else + { + // 服务在运行但心跳超时 + status = "timeout"; + } return new { status, diff --git a/src/CncWebApi/Controllers/DashboardController.cs b/src/CncWebApi/Controllers/DashboardController.cs index 34f6195..4d27066 100644 --- a/src/CncWebApi/Controllers/DashboardController.cs +++ b/src/CncWebApi/Controllers/DashboardController.cs @@ -137,15 +137,21 @@ namespace CncWebApi.Controllers try { dynamic statusObj = _dashboardService.GetCollectorStatus(); + string status = statusObj?.status as string; string serviceStatus = statusObj?.serviceStatus as string; if (!string.IsNullOrEmpty(serviceStatus) && string.Equals(serviceStatus, "NotInstalled", StringComparison.OrdinalIgnoreCase)) { return Ok(ApiResponse.Fail(40001, "采集服务未安装,请先在服务器上运行 install.ps1 安装服务")); } - if (!string.IsNullOrEmpty(serviceStatus) && string.Equals(serviceStatus, "Running", StringComparison.OrdinalIgnoreCase)) + if (status == "running") { return Ok(ApiResponse.Fail(40002, "采集服务已在运行中,无需再次启动")); } + if (status == "stopped") + { + return Ok(ApiResponse.Fail(40003, "采集服务已停止,请手动启动Windows服务")); + } + // paused 或 timeout 状态:转发到CncCollector恢复引擎 } catch { /* ignore status fetch errors and fallback to forwarding */ }