4. 农业监控数据模拟与Mock服务搭建
在农业远程监控Web看板的开发前期,真实大棚的传感器数据往往拿不到:硬件还在安装、网关尚未联调、或者现场只有一两个棚能出数据。如果前端和看板逻辑一直等真实数据,开发进度就会被卡住。本节要解决的就是这个问题——用Mock.js和json-server搭建一套“像真的”模拟数据服务,让看板在没有真实硬件的情况下也能完整跑通分页、时间筛选和异常告警展示。
先明确本节要模拟的四类核心数据对象。第一类是环境数据:空气温度(℃)、空气湿度(%RH)、土壤EC值(mS/cm)、光照强度(lux),它们按时间序列产生,是看板曲线和仪表盘的主要来源。第二类是设备状态:传感器编号、所属大棚、在线/离线状态、最后上报时间、电量。第三类是分页与筛选参数:page、pageSize、startTime、endTime、greenhouseId。第四类是异常值:用于验证看板告警颜色、阈值判断和异常标记是否生效。这四类对象决定了接口的字段结构和返回格式,先定字段再写服务,能避免后期反复改前端。
用Mock.js生成结构化随机数据
Mock.js适合在纯前端阶段快速造数据,核心是利用它的模板语法生成符合业务范围的随机值。下面这段示例定义了一个生成单条环境记录的模板,参数为合理假设的教学示例,实际项目可按传感器量程调整:
// mock/env.js
const Mock = require('mockjs');
const Random = Mock.Random;
function makeEnvRecord(greenhouseId, time) {
return {
id: Random.guid(),
greenhouseId,
timestamp: time,
temperature: +Random.float(18, 32, 1, 1), // ℃
humidity: +Random.float(45, 85, 1, 1), // %RH
soilEc: +Random.float(0.8, 2.6, 2, 2), // mS/cm
light: Random.integer(8000, 65000), // lux
deviceOnline: Random.boolean(9, 1, true) // 90%在线
};
}
这里有两个细节值得注意。一是数值范围要贴近真实农业场景:番茄大棚白天温度常在20~30℃,湿度60%~80%,土壤EC在1.0~2.5 mS/cm之间比较常见,光照晴天可达数万lux。二是deviceOnline用Random.boolean(9,1,true)让在线概率约为90%,这样看板的在线率统计才有波动感,而不是永远100%。生成时间序列时,用循环按固定间隔(如每5分钟一条)推进时间戳,就能得到一条连续的曲线数据。
用json-server提供可分页、可筛选的REST接口
Mock.js擅长造数据,但分页和按时间范围筛选如果全靠手写路由会比较繁琐。json-server的优势在于:给定一个JSON数据文件,它自动提供GET /envRecords接口,并原生支持_page、_limit分页参数,以及字段等值筛选。对于时间范围这种“大于等于、小于等于”的条件,可以通过自定义中间件或路由实现。下面是一个可运行的配置示例:
// db.json 结构(节选)
{
"envRecords": [
{ "id": "e1", "greenhouseId": "GH-01",
"timestamp": "2024-06-01T08:00:00",
"temperature": 24.6, "humidity": 68.2,
"soilEc": 1.42, "light": 32000, "deviceOnline": true }
],
"devices": [
{ "id": "D-01", "greenhouseId": "GH-01",
"type": "温湿度传感器", "online": true,
"lastReport": "2024-06-01T08:00:00", "battery": 87 }
]
}
// server.js
const jsonServer = require('json-server');
const server = jsonServer.create();
const router = jsonServer.router('db.json');
const middlewares = jsonServer.defaults();
server.use(middlewares);
server.get('/envRecords', (req, res) => {
const { startTime, endTime, greenhouseId } = req.query;
let data = router.db.get('envRecords').value();
if (greenhouseId) data = data.filter(r => r.greenhouseId === greenhouseId);
if (startTime) data = data.filter(r => r.timestamp >= startTime);
if (endTime) data = data.filter(r => r.timestamp <= endTime);
const page = +req.query._page || 1;
const limit = +req.query._limit || 20;
res.json({
total: data.length,
page, pageSize: limit,
list: data.slice((page - 1) * limit, page * limit)
});
});
server.use(router);
server.listen(3001, () => console.log('Mock服务运行在 3001'));
启动后,前端请求GET /envRecords?greenhouseId=GH-01&startTime=2024-06-01T00:00:00&endTime=2024-06-01T12:00:00&_page=1&_limit=20,就能拿到带total、page、list的标准分页响应。看板的分页组件直接读total算总页数,时间选择器把起止时间拼进查询串,筛选逻辑就闭环了。注意时间字符串统一用ISO格式,保证字符串比较与时间先后一致,这是筛选正确的前提。
异常值注入:让告警逻辑真正被验证
只造正常数据,看板的红色告警、阈值判断永远触发不了。异常值注入就是在生成数据时按一定比例写入越界值,例如温度超过35℃、湿度低于30%、土壤EC高于3.0 mS/cm、光照低于2000 lux,或把deviceOnline置为false。建议用“异常标记”字段区分,而不是让异常值混在正常数据里无法追溯:
function injectAnomaly(record, rate = 0.08) {
if (Math.random() < rate) {
const type = Random.pick(['tempHigh', 'humLow', 'ecHigh', 'lightLow', 'offline']);
if (type === 'tempHigh') record.temperature = +Random.float(35, 42, 1, 1);
if (type === 'humLow') record.humidity = +Random.float(15, 29, 1, 1);
if (type === 'ecHigh') record.soilEc = +Random.float(3.1, 4.5, 2, 2);
if (type === 'lightLow') record.light = Random.integer(200, 1900);
if (type === 'offline') record.deviceOnline = false;
record.anomalyType = type;
}
return record;
}
注入比例建议控制在5%~10%,既能稳定触发告警,又不至于满屏红色失去真实感。看板侧据此验证:温度超过35℃是否变红、离线设备是否灰显、异常记录是否出现在告警列表。这样前端告警逻辑在真实硬件到位前就完成了验证。
接口约定与联调要点
Mock服务不是随便返回一堆数据就完事,它要和真实后端保持同一套接口契约,否则将来切换真实接口时前端要返工。建议在项目里维护一份接口约定表,明确字段名、类型、单位、分页参数名和响应外层结构。例如温度字段统一叫temperature、单位℃,时间统一ISO字符串,分页统一用total/page/pageSize/list。前端把请求地址抽成配置项,开发阶段指向http://localhost:3001,上线时只改一个baseURL即可。
联调时重点观察三件事:一是翻页后total是否稳定、最后一页条数是否正确;二是时间范围边界是否包含端点,跨天查询是否漏数据;三是异常记录是否在看板上被正确标红并进入告警列表。如果发现筛选后总数不对,优先检查时间字符串格式是否统一、比较时是否混用了本地时间与UTC。把这些验证做扎实,Mock服务就真正起到了“替身演员”的作用,而不是一个只能看不能用的摆设。
db.json,保证数据量、异常比例和时间跨度可控。需要复现某个告警场景时,固定随机种子或直接手写几条异常记录,比反复刷新碰运气更可靠。