江西206单人单电脑Apifox全流程与压测方案
日期:2026-08-23
条件:1名执行人员、1台Windows电脑、Apifox
服务地址:http://192.168.0.11:8080,执行前再次确认
说明:原多点位网络测试方案保留,本方案用于先复现业务接口表现,不能单独定位具体网络区段
1. 本次分成两项
- 全流程测试:按真实设备调用顺序,串行跑通消费机、绑盘/450取餐和称重台流程。
- Apifox压测:将已通过的请求复制到压力场景,观察客户端耗时、错误率和服务端
runtime。
全流程未通过前不进行压测。生产环境禁止压测真实扣款、批量绑盘和批量就餐数据写入。
2. Apifox环境变量
在Apifox新建环境“江西206-现场测试”,只在本地保存敏感参数:
| 变量 | 说明 |
|---|---|
baseUrl |
http://192.168.0.11:8080 |
consume_code |
测试消费机编号 |
bind_code |
测试绑盘机编号 |
weigh_code |
测试称重台编号 |
staff_uuid |
测试人员UUID |
card_id |
测试卡号,只允许测试账户 |
plate_code |
可清理的测试餐盘编号 |
left_dishes_uuid |
当餐测试菜品UUID |
right_dishes_uuid |
双称设备右侧菜品UUID,单称为空 |
api_account/api_pwd/api_key |
消费接口认证参数,不写入公共文档 |
msgid |
每笔消费动态生成 |
create_time |
每次请求动态生成 |
将以下脚本放到集合级前置脚本,或分别放到zhctPushMeal和orderPay请求前:
const d = new Date();
const pad = n => String(n).padStart(2, '0');
const createTime = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
pm.environment.set('create_time', createTime);
pm.environment.set('msgid', `${pm.environment.get('consume_code')}${Date.now()}`);
3. 关键流程接口总表
| 顺序 | 场景 | 接口 | 类型 | 主要作用 | 是否适合生产压测 |
|---|---|---|---|---|---|
| 1 | 公共 | POST /p/api/equipmentStart |
写状态 | 设备启动并读取运行配置 | 仅低频 |
| 2 | 公共 | POST /p/api/heartbeat |
轻量写状态 | 设备心跳、读取餐次等状态 | 适合低压安全探测 |
| 3 | 菜谱 | POST /p/api/getMeal |
查询 | 获取当餐菜谱,卡顿页面的关键接口 | 适合压测 |
| 4 | 称重台 | POST /api/weigh/pushDishes |
写数据 | 给称重台左/右称排菜 | 仅测试设备低频 |
| 5 | 称重台 | POST /api/weigh/pushWeight |
写数据 | 上传左右称当前重量 | 仅测试设备受控压测 |
| 6 | 绑盘 | POST /p/api/selectPlate |
查询 | 查询餐盘是否已绑定及绑定信息 | 适合测试餐盘低压 |
| 7 | 绑盘 | POST /p/api/bindPlateInfo |
创建绑定/订单 | 绑定人员和餐盘并返回人员、余额、营养数据 | 不批量压生产 |
| 8 | 450取餐 | POST /p/api/zhctPushMeal |
创建就餐数据 | 按餐盘绑定关系写入菜品重量和订单金额 | 不压生产 |
| 9 | 营养 | POST /p/api/currentMealNutrition |
查询 | 验证当餐累计金额和营养变化 | 适合测试人员低压 |
| 10 | 消费机 | POST /api/consume/orderPay |
扣款/创建订单 | 消费机在线或离线订单结算 | 严禁生产压测 |
4. 全流程测试
4.1 流程A:基础连接和菜谱
按顺序执行:
POST /p/api/equipmentStart- Body:
x-www-form-urlencoded - 参数:
code={{weigh_code}} - 断言:HTTP 200、
body.code=0、记录trace_id。
- Body:
POST /p/api/heartbeat- 参数:
code={{weigh_code}} - 断言:HTTP 200、
body.code=0。
- 参数:
POST /p/api/getMeal- Body:
form-data - 参数:
category=true、keyword=、code={{weigh_code}} - 断言:HTTP 200、
body.code=0、data包含当餐菜品。
- Body:
该流程用于确认设备启动、心跳和卡顿页面的菜谱请求是否正常。
4.2 流程B:称重台排菜和重量上报
POST /api/weigh/pushDishes- Body:
x-www-form-urlencoded - 参数:
code={{weigh_code}}、left_dishes_uuid={{left_dishes_uuid}}、right_dishes_uuid={{right_dishes_uuid}} - 断言:
body.code=0、message包含“排菜成功”。
- Body:
POST /api/weigh/pushWeight- 参数:
code={{weigh_code}}、left_dishes_weight=5000、right_dishes_weight=0 - 断言:
body.code=0、message包含“上传成功”。
- 参数:
- 再次调用
pushWeight,将测试重量改为较小值,用于模拟取餐后的重量变化。
注意:pushWeight控制器实际读取的是设备编号和左右重量;菜品UUID来自前一步排菜状态。必须使用专用测试设备编号,避免改动现场真实称重台状态。
4.3 流程C:绑盘到450取餐
POST /p/api/selectPlate- 参数:
plate_code={{plate_code}} - 目标:确认测试餐盘当前状态。
- 参数:
POST /p/api/bindPlateInfo- 参数:
staff_uuid={{staff_uuid}}、plate_code={{plate_code}}、equipment_code={{bind_code}}、dishes_uuids=["{{left_dishes_uuid}}"] - 断言:
body.code=0,返回人员、余额、绑定ID和营养数据。
- 参数:
POST /p/api/zhctPushMeal- 参数:
code={{plate_code}}、equipment_code={{weigh_code}} dish_info为JSON字符串,包含当前时间和测试菜品重量。- 断言:
body.code=0、message包含“推送成功”。
- 参数:
POST /p/api/currentMealNutrition- 参数:
staff_uuid={{staff_uuid}} - 断言:
body.code=0,累计金额/营养变化与测试重量一致。
- 参数:
dish_info示意:
{"create_date":"{{create_time}}","details":[{"uuid":"{{left_dishes_uuid}}","weight":"50"}]}
该流程会创建绑盘订单和就餐数据。每轮必须使用可清理测试餐盘,并在下一轮前确认餐盘绑定和订单数据已清理或已完成业务闭环。
4.4 流程D:消费机消费
POST /p/api/equipmentStart- 参数:
code={{consume_code}}。
- 参数:
POST /p/api/heartbeat- 参数:
code={{consume_code}}。
- 参数:
POST /api/consume/orderPay- Body:JSON。
- 使用测试
card_id或测试staff_uuid,金额建议0.01元且必须能够退款。 - 每次请求由前置脚本生成唯一
msgid和create_time。 - 认证字段从Apifox私有环境变量读取。
- 断言:HTTP 200、
body.code=0、返回order_no和trace_id。
- 用相同
msgid立即再提交一次。- 断言:在线订单应被防重拦截,不能生成第二笔订单。
消费成功后必须核对订单、余额变化并完成退款。未确认测试账户和退款方案前,不执行本流程。
5. 全流程记录表
Apifox每个请求保存以下结果:
| 流程 | 接口 | 开始时间 | HTTP状态 | Apifox总耗时 | body.code | trace_id | 服务端runtime | 结果 |
|---|---|---|---|---|---|---|---|---|
流程通过条件:
- 所有请求HTTP状态为200,没有连接超时。
- 成功用例
body.code=0,防重等负向用例符合预期。 - 数据库只产生预期的一笔绑定、就餐或消费记录。
- Apifox总耗时与服务端
runtime能通过trace_id对应。
6. Apifox压测分组
6.1 场景P0:单请求基线
每个接口连续执行20次,记录p50、p95、p99、最大耗时和错误率。先确认单请求稳定,再进入并发测试。
6.2 场景P1:安全查询与心跳混合场景
推荐在生产闭餐时段执行:
| 接口 | 权重 |
|---|---|
/p/api/heartbeat |
50% |
/p/api/getMeal |
30% |
/p/api/currentMealNutrition |
20% |
阶梯:1 -> 5 -> 10 -> 20 -> 50 -> 100 VU。每档爬坡1分钟、保压5分钟;只有上一档错误率和服务器指标正常才进入下一档。VU只是工具并发数,最终结论以实际RPS为准。
6.3 场景P2:称重台重量上报
只允许使用专用测试weigh_code:
- 接口:
POST /api/weigh/pushWeight - 阶梯:
1 -> 5 -> 10 -> 20 VU - 每档保压3分钟。
- 参数在合理范围内变化,禁止使用现场真实称重台编号。
该场景会持续写设备重量和历史记录,测试后需要核对并清理测试数据。
6.4 场景P3:状态型业务接口
bindPlateInfo和zhctPushMeal需要大量独立测试餐盘、人员和清理脚本;orderPay还涉及真实扣款、防重和退款。因此:
- 生产环境只做串行全流程验证,不做并发压测。
- 必须压测时,在隔离测试环境准备参数化CSV数据集,每个VU使用独立
plate_code/msgid/card_id。 orderPay每次必须生成唯一msgid,并验证订单总数、扣款总额和退款总额。
7. Apifox压力测试配置
每个压力场景至少配置:
- 连接超时:2秒。
- 请求超时:10秒。
- 禁止自动无限重试;重试必须单独统计。
- 保存HTTP状态、业务
code、trace_id和响应时间。 - 监控执行电脑CPU、内存和网络;Apifox电脑CPU持续超过80%时,结果可能是压测机瓶颈。
- 同步采集服务端CPU、内存、IO、网络、Nginx/PHP-FPM和应用日志。
推荐停止条件:
- 任一档错误率超过1%。
- p99超过3秒或出现连接超时。
- 生产业务受到影响。
- Apifox电脑CPU持续超过80%。
- 服务端CPU、IO、连接池或数据库锁等待异常。
- 出现非预期真实订单、扣款或重复数据。
8. 如何判断结果
| 结果 | 判断 |
|---|---|
Apifox耗时高,服务端runtime低 |
优先看网络传输、连接建立、响应回程或Apifox电脑排队 |
Apifox耗时和服务端runtime都高 |
优先看应用、PHP-FPM、数据库和服务器资源 |
| 单接口稳定、混合场景异常 | 看连接数、接口资源竞争和服务端队列 |
| 低VU正常、高VU错误且Apifox CPU高 | 压测电脑可能先达到瓶颈,不能直接判服务器容量不足 |
getMeal单独出现长尾 |
重点查菜谱查询逻辑、响应体大小和对应SQL |
pushWeight正常、zhctPushMeal慢 |
网络基础请求未必异常,重点查就餐写入和下游同步逻辑 |
同一msgid生成多笔消费订单 |
防重失效,立即停止消费场景 |
9. 一个人的实际执行顺序
- 准备测试设备、人员、卡、餐盘、菜品和清理方案。
- 在Apifox导入项目OpenAPI,建立私有环境变量。
- 串行执行流程A、B、C、D,保存每个
trace_id。 - 查询服务端日志,对齐Apifox总耗时和服务端
runtime。 - 执行P0单请求基线。
- 闭餐时段执行P1安全混合压测。
- 使用专用测试称重台执行P2。
- 不在生产并发执行P3;确有需要时另建隔离环境和数据集。
- 输出p50/p95/p99、RPS、错误率、客户端与服务端耗时差及异常trace_id。
10. 本方案边界
单电脑Apifox可以判断接口功能、长尾、并发下错误率,以及客户端与服务端处理耗时是否一致;但它不能精确判断故障位于哪台交换机、哪段跨地区链路,也不能排除压测电脑自身成为瓶颈。若复现出稳定异常,再启用原多点位网络测试方案进行分段定位。