在野外生态研究中,蜱虫密度的动态变化直接影响传染病风险评估。DeTick是一套面向蜱虫诱捕器的监测方案,核心目标是把分散在林区的低成本传感器数据汇总到中心服务,并支持实时查询与预警。使用Node.js来实现DeTick,可以充分利用其非阻塞I/O与丰富npm生态,在树莓派或小型云主机上稳定运行。本文将围绕设备接入、数据处理与告警三个维度,详细说明如何用Node.js从零搭建一套可用的蜱虫监测系统。

一、系统架构与设备接入设计
DeTick的物理层由若干诱捕器组成,每个诱捕器配备温湿度探头和红外计数模块,通过MQTT或HTTP短轮询把数据发往服务端。Node.js在这里承担网关角色,接收成百上千个设备的POST请求。由于蜱虫监测不需要毫秒级响应,我们采用Express框架暴露REST接口,配合jsonwebtoken做设备级鉴权,避免非法节点灌入脏数据。
设备接入首先要解决身份问题。每个诱捕器烧录时写入唯一deviceId和密钥,上线后先用密钥换token,后续上报携带该token。服务端中间件校验签名,失败时直接返回401。这样即便有人嗅探到接口地址,也无法伪造蜱虫计数。下面的代码展示了鉴权中间件的简化实现:
const jwt = require('jsonwebtoken');
function authMiddleware(req, res, next) {
const token = req.headers['x-device-token'];
if (!token) {
return res.status(401).json({ error: '缺少设备令牌' });
}
try {
const payload = jwt.verify(token, 'SECRET_KEY_FROM_ENV');
req.deviceId = payload.deviceId;
next();
} catch (e) {
return res.status(401).json({ error: '令牌无效' });
}
}
module.exports = authMiddleware;
除了鉴权,接入层还要考虑限流。蜱虫数据虽不频繁,但固件bug可能导致某设备狂发请求。我们可以用express-rate-limit针对每个deviceId做滑动窗口限制,例如每分钟最多三十次。这种保护让Node进程在弱网环境下也不会被单点打挂,保障其他诱捕器的数据准时入库。
二、数据校验与时序存储逻辑
原始上报体通常包含温度、湿度、诱捕数和信号强度。Node接收到后不能信任客户端,必须在服务端用Joi或手写函数做类型与范围校验。例如蜱虫计数应为非负整数,温度介于零下四十到六十之间。过滤掉异常包后,再转换成时序点写入数据库。选择时序库而非关系表,是因为监测数据天然带时间戳且按设备维度滚动。
下面示例用InfluxDB的Node客户端写入一条记录。我们把deviceId作为tag,其余指标作为field,方便后期按林区聚合。注意代码中JSON字段提取要做容错,防止undefined导致写入失败:
const { InfluxDB, Point } = require('@influxdata/influxdb-client');
const client = new InfluxDB({ url: 'http://127.0.0.1:8086', token: 'TOKEN' });
const writeApi = client.getWriteApi('org', 'detick-bucket');
function saveReading(deviceId, body) {
const point = new Point('tick_trap')
.tag('device', deviceId)
.floatField('temperature', Number(body.temperature))
.floatField('humidity', Number(body.humidity))
.intField('catch_count', parseInt(body.catch_count, 10))
.timestamp(new Date(body.ts));
writeApi.writePoint(point);
}
module.exports = saveReading;
存储之外,还要处理数据补全。林区网络断开时设备应本地缓存,恢复后补传。服务端依据ts时间戳乱序写入即可,InfluxDB本身支持乱序点。我们在接口层允许ts早于当前一小时,但拒绝超过三天的迟报,以免历史脏数据冲乱近期曲线。这一策略用代码里的简单判断就能完成,不需要复杂调度。
三、阈值告警与可视化接口
蜱虫密度突增往往预示风险。DeTick在Node端启动一个定时任务,每十分钟拉取各设备最近一小时的catch_count求和,若超阈值则触发告警。告警可走邮件或钉钉机器人,重点是告警内容带上设备位置和趋势,方便巡护人员出动。定时逻辑用node-cron实现,不阻塞主事件循环。
同时,系统需向前端提供聚合查询接口。前端地图页拉取某矩形区域内设备的最新计数,Node用Flux查询语句按地理围栏过滤。下面代码演示了查询封装,返回精简后的JSON给浏览器:
const { InfluxDB } = require('@influxdata/influxdb-client');
async function getLatestByRegion(bounds) {
const queryApi = new InfluxDB({ url: 'http://127.0.0.1:8086', token: 'TOKEN' })
.getQueryApi('org');
const flux = `from(bucket: "detick-bucket")
|> range(start: -1h)
|> filter(fn: (r) => r._measurement == "tick_trap")
|> last()`;
const rows = [];
await queryApi.collectRows(flux, (row) => rows.push(row));
return rows.filter(r =>
r.lon >= bounds.minLon && r.lon <= bounds.maxLon &&
r.lat >= bounds.minLat && r.lat <= bounds.maxLat);
}
module.exports = getLatestByRegion;
可视化不只是接口,还包括缓存。地图页刷新频繁,直接打时序库会增加负载。我们用Node内的Map做六十秒短缓存,相同区域请求命中缓存直接返回。这种轻量设计让单台两核云服务器也能支撑乡镇级蜱虫监测网的日常访问,而无需引入Redis等额外组件。