# 端到端联动测试方案

**版本:** v1.0
**创建日期:** 2026-05-03
**适用范围:** CNC机床数据采集系统 — 模拟器 + 采集服务 + 管理后台 + 数据库 全链路联动

---

## 一、测试环境准备（前置条件）

| 组件 | 启动方式 | 验证方法 |
|------|---------|---------|
| **模拟器 CncSimulator** | `src\CncSimulator\bin\CncSimulator.exe` | 浏览器打开 `http://localhost:{port}/admin` 看到管理页面 |
| **采集服务 CncCollector** | `src\CncCollector\bin\CncCollector.exe` | `Invoke-WebRequest http://localhost:5800/api/collector/status` 返回 `isRunning:true` |
| **Web API (IIS)** | AppPool `haoliang` 已运行 | `Invoke-WebRequest http://127.0.0.1/api/admin/login` 返回200 |
| **数据库 MariaDB** | 服务已启动 | `mysql -u root -proot -e "SELECT COUNT(*) FROM cnc_business.cnc_machine"` |

---

## 二、测试域A：模拟器测试

### A1. 模拟器基本功能

| # | 测试点 | 操作 | 期望结果 | 验证方法 |
|---|--------|------|---------|---------|
| A1.1 | 启动与数据输出 | 访问 `http://localhost:{port}/` | 返回JSON数组，包含在线设备数据 | PowerShell `Invoke-WebRequest`，验证JSON解析成功、数组>0 |
| A1.2 | 设备数据结构 | 解析返回JSON | 每台设备包含：`deviceCode, programName, partCount, runStatus, operateMode, spindleSpeedSet/Actual, feedSpeedSet/Actual, spindleLoad, machiningStatus` | 逐字段非空检查 |
| A1.3 | 管理页面 | 浏览器打开 `http://localhost:{port}/admin` | 显示HTML管理页面，设备列表、状态、操作按钮可见 | Playwright截图 |
| A1.4 | 状态API | GET `/admin/api/status` | 返回 `{name, port, isRunning, totalDevices, onlineDevices, ...}` | 字段非空 |

### A2. 模拟器管理API

| # | 测试点 | 操作 | 期望结果 |
|---|--------|------|---------|
| A2.1 | 停止模拟 | POST `/admin/api/stop` → 查status | `isRunning:false`，GET `/` 不再返回更新数据 |
| A2.2 | 启动模拟 | POST `/admin/api/start` → 查status | `isRunning:true`，数据恢复变化 |
| A2.3 | 修改数据频率 | POST `/admin/api/interval {value:5}` | status显示 `dataChangeInterval:5`，连续2次GET间隔5秒内数据变化 |
| A2.4 | 切换剧本模式 | POST `/admin/api/mode {mode:"manual"}` | status显示 `scenarioMode:"manual"`，设备不再自动切换场景 |
| A2.5 | 手动触发事件 | POST `/admin/api/event {deviceId:"xxx", eventType:"program_change"}` | 设备NC程序名变更，partCount可能重置 |
| A2.6 | 添加设备 | POST `/admin/api/add-device {deviceCode:"TEST-01", desc:"测试设备"}` | status设备数+1，新设备出现在GET `/` 响应中 |
| A2.7 | 移除设备 | POST `/admin/api/remove-device {deviceCode:"TEST-01"}` | status设备数-1，GET `/` 不再包含该设备 |

### A3. 网络异常模拟

| # | 测试点 | 操作 | 期望结果 |
|---|--------|------|---------|
| A3.1 | HTTP 500 | POST `/admin/api/network {type:"http500"}` → 采集服务请求 | 模拟器返回500，采集服务记录失败 |
| A3.2 | 超时 | POST `/admin/api/network {type:"timeout"}` | 模拟器延迟60秒响应，采集服务超时 |
| A3.3 | 空数据 | POST `/admin/api/network {type:"empty"}` | 模拟器返回 `[]`，采集服务应记录空响应 |
| A3.4 | 畸形JSON | POST `/admin/api/network {type:"malformed"}` | 模拟器返回 `{broken`，采集服务应记录解析失败 |
| A3.5 | 拒绝连接 | POST `/admin/api/network {type:"refuse"}` | 端口不可达，采集服务应记录连接失败 |
| A3.6 | 恢复正常 | POST `/admin/api/network {type:"normal"}` | 模拟器恢复正常数据输出 |

### A4. 模拟器数据统计

| # | 测试点 | 操作 | 期望结果 |
|---|--------|------|---------|
| A4.1 | 统计API | GET `/admin/api/stats` | `totalDevices, onlineDevices, totalParts, partsByDevice` 非空且合理 |
| A4.2 | 事件历史 | GET `/admin/api/event-history` | 返回事件数组，每条含 `timestamp, deviceCode, eventType, oldProgram, newProgram` |
| A4.3 | 完整汇总 | GET `/admin/api/full-summary` | 包含启动时间、运行时长、请求数、成功/失败数、每设备每程序零件数 |
| A4.4 | 错误日志 | 先设网络异常→恢复→GET `/admin/api/error-log` | 有对应错误记录，含 `errorType, description, affectedDevices` |
| A4.5 | 请求日志 | GET `/admin/api/logs` | 返回最近100条采集日志，每条含 `timestamp, deviceCount, keyData, fullJson` |

---

## 三、测试域B：采集服务测试

### B1. 采集引擎启停

| # | 测试点 | 操作 | 期望结果 | 验证方法 |
|---|--------|------|---------|---------|
| B1.1 | 引擎启动 | POST `/api/collector/start` (带X-Api-Key) | 返回 `code:0`，status显示 `isRunning:true` | 调5800 status确认 |
| B1.2 | 重复启动 | 再次POST start | 静默返回成功（幂等） | status仍为running |
| B1.3 | 引擎停止 | POST `/api/collector/stop` | `isRunning:false`，心跳表最新记录status=stopped | 查DB |
| B1.4 | 启动后心跳立即写入 | POST start → 立即查心跳表 | 最新记录status=running，uptime_seconds=0 | `SELECT * FROM cnc_log.log_collector_heartbeat ORDER BY id DESC LIMIT 1` |

### B2. 数据采集流程

| # | 测试点 | 操作 | 期望结果 | 验证方法 |
|---|--------|------|---------|---------|
| B2.1 | 正常采集 | 模拟器运行中，采集服务运行中，等待1-2个采集间隔 | `cnc_collect_record` 有新数据写入 | 查DB：`SELECT COUNT(*) FROM cnc_collect_record WHERE collect_time > NOW() - INTERVAL 1 MINUTE` |
| B2.2 | 原始JSON存储 | 同上 | `log_collect_raw` 有新记录，`raw_json` 字段非空 | 查DB |
| B2.3 | 采集记录字段完整 | 查最新采集记录 | `machine_id, collect_time, program_name, part_count, device_status, run_status, spindle_speed_set, spindle_speed_actual` 均非空 | 逐字段检查 |
| B2.4 | 设备状态更新 | 采集1-2轮后查 `cnc_machine` | `last_ping_time, last_collect_time, last_program_name, last_part_count, last_device_status, last_run_status` 更新 | 查DB |

### B3. 生产段跟踪（ProductionTracker）

| # | 测试点 | 操作 | 期望结果 | 验证方法 |
|---|--------|------|---------|---------|
| B3.1 | 新程序名创建段 | 模拟器切换NC程序（触发program_change事件） | `cnc_production_segment` 新增一条记录，`start_time` 有值，`end_time` 为NULL（活跃段） | 查DB |
| B3.2 | 程序切换结账 | 再次切换程序 | 上一段 `end_time` 填充，`quantity = end_part_count - start_part_count` | 查DB |
| B3.3 | 同程序多次出现 | 先O0001→O0002→O0001 | 两段O0001分别记录（分段记录） | 查DB，同日同机器有2条program_name=O0001 |

### B4. 心跳与监控

| # | 测试点 | 操作 | 期望结果 | 验证方法 |
|---|--------|------|---------|---------|
| B4.1 | 定时心跳 | 等待2个心跳间隔（20秒） | 心跳表新增≥2条running记录 | 查DB |
| B4.2 | 停止后心跳 | POST stop | 最新心跳status=stopped | 查DB |
| B4.3 | 配置轮询 | 修改DB中运行时配置，等待1个轮询周期（30秒） | 日志输出配置刷新信息 | 查看采集服务控制台/log |

### B5. 异常处理

| # | 测试点 | 操作 | 期望结果 | 验证方法 |
|---|--------|------|---------|---------|
| B5.1 | 采集地址不可达 | 停止模拟器 → 等待采集 | `log_collect_raw` 记录失败，`is_success=0`，`error_message` 非空 | 查DB |
| B5.2 | 采集地址恢复 | 重启模拟器 | 采集恢复，`is_success=1` | 查DB |
| B5.3 | 空数据处理 | 模拟器返回空数组 | 采集不崩溃，日志记录空响应 | 查采集服务日志 |
| B5.4 | 畸形JSON处理 | 模拟器返回畸形JSON | 采集不崩溃，记录解析错误 | 查采集服务日志 |
| B5.5 | 连续失败告警 | 模拟器持续返回500，超过阈值（默认5次） | 告警触发 | 查 `cnc_alert` 或日志 |

### B6. 配置刷新

| # | 测试点 | 操作 | 期望结果 | 验证方法 |
|---|--------|------|---------|---------|
| B6.1 | 手动刷新 | POST `/api/collector/refresh` | 返回成功，日志输出配置刷新 | 查日志 |
| B6.2 | 新增采集地址 | DB中新增一条 `cnc_collect_address` → refresh | status中workerCount+1 | 调status确认 |
| B6.3 | 删除采集地址 | DB中删除/禁用一条地址 → refresh | status中workerCount-1 | 调status确认 |

---

## 四、测试域C：管理后台功能测试

> 每个测试点对应的维度编号参照 `docs/06-测试规范.md` 5.0维度总表。

### C1. 登录（/login）

| # | 测试点 | 期望结果 | 维度 |
|---|--------|---------|------|
| C1.1 | 正确账密登录（admin/admin123） | 跳转到仪表盘，localStorage有token | 1 |
| C1.2 | 错误密码 | 提示错误，停留在登录页 | 20 |
| C1.3 | 无Token访问管理后台 | 重定向到/login | 20 |
| C1.4 | 有Token访问/login | 重定向到/dashboard | 20 |

### C2. 仪表盘（/dashboard）

| # | 测试点 | 期望结果 | 维度 |
|---|--------|---------|------|
| C2.1 | 8个统计卡片有值 | 每个卡片数值≠空≠占位符 | 3 |
| C2.2 | 采集服务状态显示 | 显示"运行中"或"已停止"，对应按钮可操作 | 23 |
| C2.3 | 机床状态分布图表 | 饼图/柱状图有渲染内容（非空白） | 3.1 |
| C2.4 | 产量趋势图表 | 折线图有数据点 | 3.1 |
| C2.5 | 机床/工人排行表格 | 行数>0，每列有值 | 4,5 |
| C2.6 | 最新告警列表 | ≥0条（有则检查字段非空） | 4,5 |
| C2.7 | 日期筛选 | 切换日期→趋势/排行数据变化 | 12 |
| C2.8 | API vs 页面对账 | 调API → 对比卡片数值一致 | 17 |
| C2.9 | 启动采集按钮 | 点击→状态切换为"运行中"→按钮变为"停止采集" | 23 |
| C2.10 | 停止采集按钮 | 点击→状态切换为"已停止"→按钮变为"启动采集" | 23 |
| C2.11 | 刷新配置按钮 | 点击→成功提示 | 23 |
| C2.12 | 表格布局铺满 | 排行表格宽度=100%容器宽度 | 21 |
| C2.13 | 告警列表可点击跳转 | 点击告警→跳转告警中心 | 22 |

### C3. 设备管理（/machine + /machine/:id）

| # | 测试点 | 期望结果 | 维度 |
|---|--------|---------|------|
| C3.1 | 机床列表加载 | 表格有数据，每列有值 | 4,5 |
| C3.2 | 车间/品牌/状态下拉筛选 | 选项≥1，选择后筛选生效 | 8,9,10 |
| C3.3 | 关键词搜索 | 输入→查询→结果包含关键词 | 11 |
| C3.4 | 分页 | 切换页码，数据变化 | 14 |
| C3.5 | 新增机床 | 填写表单→提交→列表刷新→新机床出现 | 15,16 |
| C3.6 | 编辑机床 | 修改→保存→列表更新 | 15,16 |
| C3.7 | 删除机床 | 确认→行消失 | 23 |
| C3.8 | 启停机床 | 切换→状态标签变更 | 23 |
| C3.9 | 点击设备名称跳转详情 | 跳转到详情页，URL含id，详情页有数据 | 22 |
| C3.10 | 详情页-实时采集状态 | 状态信息展示 | 2 |
| C3.11 | 详情页-今日产量 | 表格有数据 | 4,5 |
| C3.12 | 详情页-产量趋势 | 图表有渲染 | 3.1 |
| C3.13 | 详情页-采集记录 | 表格有数据 | 4,5 |
| C3.14 | 表格布局铺满 | 表格宽度=100%容器宽度 | 21 |
| C3.15 | 空数据状态 | 不可能的筛选→行数=0+不白屏 | 19 |
| C3.16 | 重置按钮 | 筛选恢复默认 | 13 |

### C4. 品牌模板（/brand + /brand/create + /brand/:id/edit）

| # | 测试点 | 期望结果 | 维度 |
|---|--------|---------|------|
| C4.1 | 品牌列表 | 表格有数据 | 4,5 |
| C4.2 | 新增品牌 | 填写→提交→成功 | 15,16 |
| C4.3 | 编辑品牌（含字段映射） | 修改字段映射→保存→刷新后保留 | 15,16 |
| C4.4 | 复制品牌 | 点复制→新品牌出现（名称含"副本"） | 23 |
| C4.5 | 删除品牌 | 确认→行消失 | 23 |
| C4.6 | 启停品牌 | 切换→状态变更 | 23 |
| C4.7 | 标准字段列表 | 下拉有选项 | 8 |
| C4.8 | 点击品牌名称跳转编辑 | 跳转到编辑页，字段预填 | 22 |
| C4.9 | 表格布局铺满 | 表格宽度=100%容器宽度 | 21 |

### C5. 采集地址（/collect-address + /collect-address/:id）

| # | 测试点 | 期望结果 | 维度 |
|---|--------|---------|------|
| C5.1 | 地址列表 | 表格有数据 | 4,5 |
| C5.2 | 新增地址 | 填写URL、品牌→提交→成功 | 15,16 |
| C5.3 | 编辑地址 | 修改→保存 | 15,16 |
| C5.4 | 删除地址 | 确认→行消失 | 23 |
| C5.5 | 启停地址 | 切换→状态变更 | 23 |
| C5.6 | 点击地址名称跳转详情 | 跳转详情页 | 22 |
| C5.7 | 详情-关联机床列表 | 有数据 | 4 |
| C5.8 | 详情-采集记录 | 有数据 | 4 |
| C5.9 | 详情-原始JSON | JSON展示区有内容 | 2 |
| C5.10 | 表格布局铺满 | 表格宽度=100%容器宽度 | 21 |
| C5.11 | 重置按钮 | 筛选恢复默认 | 13 |

### C6. 员工管理（/worker + /worker/:id）

| # | 测试点 | 期望结果 | 维度 |
|---|--------|---------|------|
| C6.1 | 员工列表 | 表格有数据 | 4,5 |
| C6.2 | 新增员工 | 填写→提交→成功 | 15,16 |
| C6.3 | 编辑员工 | 修改→保存 | 15,16 |
| C6.4 | 删除员工 | 确认→行消失 | 23 |
| C6.5 | 启停员工 | 切换→状态变更 | 23 |
| C6.6 | 点击员工名称跳转详情 | 跳转详情页 | 22 |
| C6.7 | 详情-绑定机床 | 列表有数据或可添加 | 4 |
| C6.8 | 详情-今日产量 | 表格有数据 | 4 |
| C6.9 | 详情-产量趋势 | 图表有渲染 | 3.1 |
| C6.10 | 表格布局铺满 | 表格宽度=100%容器宽度 | 21 |
| C6.11 | 重置按钮 | 筛选恢复默认 | 13 |

### C7. 产量报表（/production）

| # | 测试点 | 期望结果 | 维度 |
|---|--------|---------|------|
| C7.1 | 日汇总卡片有值 | totalQuantity等≠空 | 3 |
| C7.2 | 日产量列表 | 表格有数据，运行时间/切削时间/日状态有值 | 4,5,6 |
| C7.3 | 车间/机床/工人下拉 | 选项≥1，筛选生效 | 8,9,10 |
| C7.4 | 日期范围筛选 | 切换→数据变化 | 12 |
| C7.5 | 程序名文本筛选 | 输入→匹配 | 11 |
| C7.6 | 重置按钮 | 筛选恢复默认 | 13 |
| C7.7 | 修正弹窗 | 打开→填写→提交→列表刷新→修正标记出现 | 15,16 |
| C7.8 | 修正历史弹窗 | 打开→有历史记录 | 15 |
| C7.9 | 分页 | 切换页码→数据变化 | 14 |
| C7.10 | 导出 | 触发下载 | 23 |
| C7.11 | 汇总卡片 vs API对账 | 调API→对比卡片值一致 | 17 |
| C7.12 | 表格布局铺满 | 表格宽度=100%容器宽度 | 21 |
| C7.13 | 下拉选项值有效性 | 每项文本非空、value可选中 | 18 |

### C8. 告警中心（/alert）

| # | 测试点 | 期望结果 | 维度 |
|---|--------|---------|------|
| C8.1 | 告警统计 | 卡片有值（总数/未处理/已处理） | 3 |
| C8.2 | 告警列表 | 表格有数据 | 4,5 |
| C8.3 | 类型/状态筛选 | 选项≥1，筛选生效 | 8,9,10 |
| C8.4 | 处理单条告警 | 点处理→状态变为已处理 | 23 |
| C8.5 | 批量处理 | 勾选→批量处理→状态批量变更 | 23 |
| C8.6 | 日期筛选 | 切换→数据变化 | 12 |
| C8.7 | 表格布局铺满 | 表格宽度=100%容器宽度 | 21 |
| C8.8 | 重置按钮 | 筛选恢复默认 | 13 |

### C9. 系统设置（/settings）

| # | 测试点 | 期望结果 | 维度 |
|---|--------|---------|------|
| C9.1 | 配置项列表 | 表格有数据 | 4 |
| C9.2 | 编辑配置项 | 修改→保存→值更新 | 15,16 |
| C9.3 | 重置Token | 点击→确认→新Token生成 | 23 |
| C9.4 | 修改密码 | 填写→提交→成功提示 | 15,16 |
| C9.5 | 车间管理-列表 | 表格有数据 | 4 |
| C9.6 | 车间管理-新增 | 填写→提交→成功 | 15,16 |
| C9.7 | 车间管理-编辑 | 修改→保存 | 15,16 |
| C9.8 | 车间管理-删除 | 确认→行消失 | 23 |
| C9.9 | 车间管理-启停 | 切换→状态变更 | 23 |

### C10. 操作日志（/log）

| # | 测试点 | 期望结果 | 维度 |
|---|--------|---------|------|
| C10.1 | 产量修正日志 | 表格有数据 | 4,5 |
| C10.2 | 系统运行日志 | 表格有数据 | 4,5 |
| C10.3 | 日期筛选 | 切换→数据变化 | 12 |
| C10.4 | 导出 | 触发下载 | 23 |
| C10.5 | 表格布局铺满 | 表格宽度=100%容器宽度 | 21 |

### C11. 大屏配置（/screen-config）

| # | 测试点 | 期望结果 | 维度 |
|---|--------|---------|------|
| C11.1 | 卡片配置列表 | 表格有数据 | 4 |
| C11.2 | 新增卡片配置 | 填写→提交→成功 | 15,16 |
| C11.3 | 编辑卡片配置 | 修改→保存 | 15,16 |
| C11.4 | 删除卡片配置 | 确认→行消失 | 23 |
| C11.5 | 启停卡片 | 切换→状态变更 | 23 |
| C11.6 | 筛选配置列表 | 表格有数据 | 4 |
| C11.7 | 新增筛选项 | 填写→提交→成功 | 15,16 |
| C11.8 | 编辑筛选项 | 修改→保存 | 15,16 |
| C11.9 | 删除筛选项 | 确认→行消失 | 23 |

---

## 五、测试域D：端到端联动测试

> 核心验证：数据从模拟器→采集服务→数据库→管理后台的完整链路。

### D1. 完整数据链路验证

| # | 测试场景 | 操作步骤 | 期望结果 | 验证方法 |
|---|---------|---------|---------|---------|
| D1.1 | 正常采集全链路 | ①模拟器运行 ②采集服务启动 ③等待2个采集周期 | 模拟器输出→采集服务获取→解析→写DB→管理后台可查 | ①查模拟器admin/api/logs有请求记录 ②查collect_record有新数据 ③管理后台设备列表last_collect_time更新 |
| D1.2 | 机床状态实时反映 | 模拟器设备在线→采集→查管理后台 | 机床is_online=1，last_device_status非空 | 查cnc_machine表 + 管理后台设备列表 |
| D1.3 | NC程序切换→生产段记录 | ①记录当前程序名和partCount ②触发program_change事件 ③等待2个采集周期 | 旧段结账，新段创建，quantity计算正确 | 查cnc_production_segment |
| D1.4 | 产量汇总 | 采集运行≥5分钟（完成若干段）→查仪表盘/产量报表 | 日汇总卡片有值，日产量列表有记录，数量与段记录一致 | 对比cnc_daily_production与cnc_production_segment |

### D2. 采集服务启停联动

| # | 测试场景 | 操作步骤 | 期望结果 | 验证方法 |
|---|---------|---------|---------|---------|
| D2.1 | 管理后台启动采集 | ①仪表盘点"启动采集" ②等待1秒 ③查仪表盘状态 | 状态切换为"运行中"，uptime递增 | Playwright + API |
| D2.2 | 停止→数据停止更新 | ①停止采集 ②等待1分钟 ③查collect_record | 停止后无新采集记录 | `SELECT MAX(collect_time) FROM cnc_collect_record` 应早于停止时间 |
| D2.3 | 重启→数据恢复 | ①重新启动采集 ②等待1个采集周期 | 新记录写入，心跳恢复running | 查DB |

### D3. 模拟器异常联动

| # | 测试场景 | 操作步骤 | 期望结果 | 验证方法 |
|---|---------|---------|---------|---------|
| D3.1 | 模拟器挂了→采集失败→告警 | ①停止模拟器 ②等待采集重试3次+告警阈值 | 采集记录is_success=0，告警表新增collect_fail记录 | 查log_collect_raw + cnc_alert |
| D3.2 | 模拟器恢复→采集恢复 | ①重启模拟器 ②等待采集 | is_success=1，fail_count归零 | 查DB |
| D3.3 | 空数据→采集处理 | 模拟器设empty模式 | 采集不崩溃，log记录空响应 | 查日志 |
| D3.4 | 超时→采集超时 | 模拟器设timeout模式 | 采集超时，记录失败 | 查log_collect_raw |

### D4. 配置变更联动

| # | 测试场景 | 操作步骤 | 期望结果 | 验证方法 |
|---|---------|---------|---------|---------|
| D4.1 | 新增采集地址→刷新 | ①管理后台新增采集地址 ②仪表盘点"刷新配置" ③查采集服务status | workerCount+1 | 调5800 status API |
| D4.2 | 禁用地址→刷新 | ①禁用某采集地址 ②刷新 | workerCount-1 | 调status |
| D4.3 | 修改采集间隔 | ①DB修改collect_interval ②等配置轮询（30秒） ③观察采集频率变化 | 采集间隔变更 | 对比两次collect_time差值 |

### D5. 日终汇总联动

| # | 测试场景 | 操作步骤 | 期望结果 | 验证方法 |
|---|---------|---------|---------|---------|
| D5.1 | 手动触发日终汇总 | ①确保有当日生产段数据 ②修改配置的汇总时间为当前时间+1分钟 ③等待触发 | cnc_daily_production有当日记录，total_quantity与段记录quantity之和一致 | 查DB |

### D6. 数据一致性交叉验证

| # | 验证项 | SQL/方法 | 通过标准 |
|---|--------|---------|---------|
| D6.1 | 生产段 vs 采集记录 | `SELECT ps.machine_id, ps.program_name, ps.quantity, COUNT(cr.id) FROM cnc_production_segment ps LEFT JOIN cnc_collect_record cr ON cr.machine_id=ps.machine_id AND cr.collect_time BETWEEN ps.start_time AND IFNULL(ps.end_time, NOW()) GROUP BY ps.id` | 每段至少有1条采集记录 |
| D6.2 | 日产量 vs 生产段 | `SELECT dp.machine_id, dp.production_date, dp.total_quantity, SUM(ps.quantity) FROM cnc_daily_production dp JOIN cnc_production_segment ps ON ps.machine_id=dp.machine_id AND ps.production_date=dp.production_date GROUP BY dp.id` | total_quantity = SUM(quantity) |
| D6.3 | 工人日汇总 vs 日产量 | `SELECT ws.worker_id, ws.total_quantity, SUM(dp.total_quantity) FROM cnc_worker_daily_summary ws JOIN cnc_worker_machine wm ON wm.worker_id=ws.worker_id JOIN cnc_daily_production dp ON dp.machine_id=wm.machine_id AND dp.production_date=ws.production_date GROUP BY ws.id` | 数值一致 |
| D6.4 | 模拟器零件数 vs DB零件数 | 对比模拟器 `/admin/api/full-summary` 的 totalParts 与 DB `cnc_daily_production.total_quantity` | 允许差异（采集周期延迟），但趋势一致 |
| D6.5 | 心跳 uptime 连续性 | `SELECT id, status, uptime_seconds, created_at FROM cnc_log.log_collector_heartbeat WHERE service_id='collector-service' ORDER BY id DESC LIMIT 20` | running记录uptime递增，无跳变 |
| D6.6 | 原始JSON vs 解析后数据 | 取一条log_collect_raw.raw_json，解析后与cnc_collect_record对应记录对比 | 字段值一致 |

---

## 六、测试通过标准

| 类别 | 标准 |
|------|------|
| **A 模拟器** | 所有A1-A4测试点通过，管理API全部可操作，异常模式全部可设置和恢复 |
| **B 采集服务** | 所有B1-B6测试点通过，数据写入DB正确，异常不崩溃 |
| **C 管理后台** | 所有C1-C11模块按维度总表(23维度)逐项通过，CRUD操作闭环 |
| **D 联动测试** | D1-D6全部通过，数据链路完整，交叉验证一致 |
| **零容忍** | 无白屏、无500错误、无数据丢失、无空字段（设计允许NULL的除外） |

---

## 七、测试执行顺序

```
1. 启动所有组件（模拟器→采集服务→确认IIS运行）
2. 域A：模拟器独立测试（确认数据源正常）
3. 域B：采集服务独立测试（确认采集流程正确）
4. 域C：管理后台逐页面测试（确认展示正确）
5. 域D：联动测试（按D1→D2→D3→D4→D5→D6顺序）
6. 域D6：数据一致性交叉验证（全链路数据对账）
```

---

## 八、测试点统计

| 测试域 | 测试点数 |
|--------|---------|
| A 模拟器 | 21 |
| B 采集服务 | 22 |
| C 管理后台 | 65 |
| D 联动测试 | 21 |
| **合计** | **129** |
