智能回潮率检测仪系统 · 面试 TOP10
智能回潮率检测仪系统 · 面试 TOP10
Q1:这个项目你扮演了什么角色?整体架构是什么样的?
这个项目是我独立架构和开发的,从零搭起来的。整体分三块:
- Electron 桌面端 — 主进程里内嵌了一个 Express API 服务(端口3000),通过 Preload 脚本桥接 IPC,让渲染进程可以调用主进程的 API。用统一 Service Manager 管理所有的硬件通信、MQTT/UDP、WebSocket 推送这些服务的生命周期,启动时有序初始化,退出时优雅关闭。
- Node.js 云端后端 — 用的 Express + Prisma + MySQL,提供设备注册认证、心跳保活、数据同步、版本管理这些 RESTful API。
- Vue3 管理后台 — Element Plus 写的,管理员可以在上面查看设备状态、同步数据、发布版本等。
设备端和云端通过 HTTP + JWT Token 通信,设备端每5秒发一次心跳,云端根据心跳判断设备在线/离线。
Q2:Prisma 在 Electron 环境打包有什么坑?你怎么解决的?
Electron 打包时有个业界级难题:Prisma Client 默认装在
node_modules里,但electron-builder打包时会把node_modules里的大部分文件过滤掉,导致 Prisma Client 在生产环境找不到,应用直接崩溃。我的解决方案是:
- 修改
schema.prisma的output路径,把生成的 Client 从node_modules迁出到src/generated/client- 在
package.json里用"db": "link:src/generated/client"做个本地链接- 这样
electron-builder就会把它当成源码一起打包,生产环境就能正常用了数据存储用的是 SQLite,打包成单文件数据库,用户不需要安装任何数据库环境。
Q3:硬件通信是怎么做的?MQTT 和 UDP 分别用来干什么?
用了两种协议各司其职:
MQTT — 用来发控制指令。我在 Electron 里内置了 Aedes MQTT Broker(就是个轻量级的 MQTT 服务器),硬件物联网关作为 Client 连到这个 Broker,后端的 Node.js 也作为 Client 连上来。这样两端通过 MQTT 的 Topic 发布/订阅来通信,比如下发"开始测量"的指令、接收设备状态变化。因为 MQTT 是长连接+发布订阅模式,实时性很好,状态响应 <100ms。
UDP — 用来收高频传感器数据。传感器的 Q 值和 M 值需要 100ms 采集一次,如果用 MQTT 的话报文头开销太大。UDP 无连接、轻量,更适合这种高频小数据包的传输。
Q4:实时数据采集为什么重构?怎么做的?
之前用的是请求-响应模式,每次发一个请求等一个响应,但经常出现请求发出去后没收到响应,前端就一直卡在"测量中"状态。
后来我改成持续采集模式:系统启动后就不停地发 UDP 请求(每100ms一次),不管前端有没有在测量,数据一直在收。收到有效数据就存到一个数组里,限制最多保留最新的100个。前端需要实时数据时,直接从数组里拿最新的值就行,不需要等待请求响应。
这样改造后,时序问题彻底解决了,再也没有因为某次请求没响应就卡死的情况。
Q5:回潮率是怎么计算的?公式的原理是什么?
回潮率就是纺织品里水分的百分比。检测原理是传感器发出电磁波穿透纱线,根据介电常数变化推算出水分含量。
公式是多元回归模型,我用了 Decimal.js 来保证精度。支持两种模式:
- 多面公式 — 11个系数 + 6个派生变量,比如 Q 值平均值、标准差、有效探测比这些,从多个角度综合计算
- 单面公式 — 4个系数,用单个 Q 值和直径计算
每个品种可以在管理后台配置自己的公式参数(k0~k10),系统自动从数据库读取配置来算,前后端保持一致的配置来源。
Q6:测量稳定性你怎么保障的?产线设备老卡死怎么办?
我做了三层兜底:
- WebSocket 心跳 + 自动重连 — 每30秒发一次 ping,10秒没收到 pong 就判定断线,自动重连最多10次,间隔5秒
- 测量超时自动复位 — 前端开始测量时启动60秒计时器,60秒没收到完成事件,自动重置状态回空闲,不会永久卡死
- 全局异常捕获 — 监听 Node.js 的
unhandledRejection和uncaughtException,严重错误时自动重启应用这三层下来,产线设备基本不会再出现卡死需要人工重启的情况。
Q7:SQLite 离线数据怎么同步到云端?
实现很直接:所有数据先写本地 SQLite,不管有没有网。后台有个定时器每5分钟尝试一次同步。
同步时先把 SQLite 数据库文件复制一份(防止读写锁冲突),然后把整个文件通过 HTTP 上传到云端。云端解析 SQLite 文件,把新记录插入 MySQL,返回已同步的记录 ID。客户端收到后把本地记录标记为已同步。
另外还有几个触发机制:网络断开后重新连上时会立即触发同步,也可以在管理后台手动点"同步到云端"按钮强制同步。
Q8:Vue3 + Electron 通信你用的什么方案?和传统 Web 开发有什么不同?
Electron 分主进程和渲染进程,渲染进程不能直接调用 Node.js API,所以要用 IPC 通信。
我在 Preload 脚本里用
contextBridge暴露了一些 API,比如:
startUdp()→ 通过ipcRenderer.invoke调用主进程的udp:starthandleronUdpData()→ 通过ipcRenderer.on监听主进程发来的数据事件这样渲染进程调用
window.probeApi.startUdp()就能触发主进程启动 UDP 接收,主进程收到数据后通过事件推给渲染进程。和传统 Web 最大的区别就是:不能直接在浏览器里调系统 API,所有系统级别的操作(硬件通信、文件读写、数据库)都得走 IPC 到主进程去做。
Q9:项目里用了哪些设计模式?
用了不少:
- 单例模式 — Service Manager、各个 Service 都是单例,避免重复创建
- 观察者模式 — Service Manager 作为事件总线,各个 Service 发布事件,前端通过 WebSocket 订阅
- 分层架构 — 后端 Express 用了 Route → Controller → Service 三层,职责分离
- 策略模式 — 公式计算支持多面和单面两种策略,根据配置动态切换
- 滑动窗口 — 实时数据采集用固定长度数组,新数据进来旧数据出去
Q10:这个项目你觉得最难的点是什么?
最难的是 Prisma 在 Electron 下的打包问题。当时查了很多资料,官方文档也没有完美的方案。后来我参考了几个开源项目的做法,试了好几种方式才找到解决方案——把 generated client 迁出 node_modules 作为源码打包。这个问题解决了整个项目的数据持久化基础。
另一个是实时数据采集的稳定性。产线环境网络不稳定、设备偶尔无响应,一开始经常卡死。我后来从架构上把请求-响应模式改成了持续采集模式,再加了三层兜底机制,才彻底解决。

浙公网安备 33010602011771号