智慧消防系统开发实战:从毕设源码到设备告警工单闭环

发布时间:2026/9/16 14:15:56
智慧消防系统开发实战:从毕设源码到设备告警工单闭环 简介面向计算机毕业设计场景的智慧消防微信小程序完整项目包基于微信小程序开发工具与Java后端及MySQL实现适合毕业设计、课程设计以及小程序全栈学习。项目区分管理员与用户两类角色管理员可管理个人中心、用户信息、新闻信息、商品信息、报修信息及报修结果、房屋信息、留言板、系统设置等用户支持注册登录、在线报修和商品购买。资源共有1252个文件约39.79MB类型覆盖vue/java/js等前后端源码wxml/wxss小程序页面文件png/svg界面素材sql数据库脚本mp4演示录像以及bat安装运行脚本目录结构清晰便于按模块查阅和二次开发。已有125人学习下载压缩包内附说明文档与演示视频可协助快速理解业务流程、完成环境部署对完成智慧消防课题或积累小程序开发经验很有价值。1. 源码包不等于能跑智慧消防系统真正要交付的是闭环拿到这种毕设源码包第一件事不是解压跑起来而是冷静认出它的真实身份一套带演示录像和说明文档的课程设计作品不是可以直接部署的生产系统。智慧消防这个题目在校招和课设里出现频率极高本质要解决的是三件事的闭环设备把状态报上来告警能流转到人巡查结果能回到系统。打开源码后最常见的三个坑也正对应这三件事——接口域名还是写死的局域网地址点检记录写进内存重启就丢演示录像里的扫码流程在代码里根本找不到对应页面。所以这篇只讲一件事把设备、告警、工单和权限这条路走通其余特效都是加分项。2. 先立数据模型设备、告警、工单三张表的设计与接口协议2.1 消防设备台账为什么必须设计成二维码可扫码——device 表结构智慧消防和普通的管理系统最大的差别在设备端。烟感、温感、水压表、手报按钮这些都是物理存在巡查员要拿着手机到现场扫设备码才能确认“我来过、设备正常”。所以设备表的核心不是 id而是device_code这个二维码里承载的业务标识。CREATE TABLE device ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, device_code VARCHAR(32) NOT NULL COMMENT 设备编号二维码扫码的结果, device_name VARCHAR(64) NOT NULL COMMENT 设备名称如3号楼3层东侧烟感, device_type TINYINT NOT NULL DEFAULT 1 COMMENT 1烟感 2温感 3水压 4手报 5消防栓, building VARCHAR(32) NOT NULL COMMENT 楼栋, floor VARCHAR(16) NOT NULL COMMENT 楼层, room VARCHAR(32) DEFAULT COMMENT 房间或点位, status TINYINT NOT NULL DEFAULT 0 COMMENT 0离线 1在线 2故障, install_time DATETIME DEFAULT NULL, last_report_time DATETIME DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_device_code (device_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT消防设备台账;这个结构里device_code建了唯一索引扫码后直接用字符串匹配不需要关心二维码内部是 JSON 还是纯编号。building、floor、room分开存而不是拼成一个地址字段是为了后面做楼层平面图和按楼栋统计时不需要写字符串解析。status用 TINYINT 而不是字符串演示录像里切换“在线/离线”筛选时下拉框绑定的就是这组枚举值。2.2 告警状态机从上报、派单到归档的流转设计告警不是一条单纯的数据插入它要经历产生、确认、派单、处置、归档五个环节。很多毕设源码只在告警表里放一个status字段处理完就改成 2这会导致演示时说不清“谁在什么时候做了什么”。CREATE TABLE alarm ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, device_id BIGINT UNSIGNED NOT NULL, alarm_type TINYINT NOT NULL COMMENT 1烟感 2温感 3水压 4手报, alarm_level TINYINT NOT NULL DEFAULT 1 COMMENT 1一般 2严重 3紧急, content VARCHAR(255) NOT NULL COMMENT 告警描述, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待确认 1已派单 2处置中 3已归档, assignee_id BIGINT UNSIGNED DEFAULT NULL COMMENT 处置人关联用户表, create_time DATETIME NOT NULL, handle_time DATETIME DEFAULT NULL, remark VARCHAR(255) DEFAULT , PRIMARY KEY (id), KEY idx_status_time (status, create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT消防告警记录;注意status的注释不是简单粗暴的“0未处理 1已处理”而是拆成四个可操作的阶段。idx_status_time这个联合索引专门服务告警列表页最常见的查询打开小程序先按状态分组显示再按时间倒序排列。处置人字段放在告警表而不是单独的工单表是课设体量下的合理简化如果导师要求必须体现“派单”就再建一张work_order表关联alarm_id两张表的关系是 1 对 1。2.3 接口协议里的时间、经纬度与权限字段设备端和后端约定接口协议时最容易出问题的是三个字段时间、位置、权限。时间统一用yyyy-MM-dd HH:mm:ss的字符串传输小程序端可以直接渲染省掉new Date的兼容性处理。经纬度传到后端用 DECIMAL(10,6)别用前端算好的距离值电子围栏的判定必须在后端做否则抓包改个坐标就能绕过巡检。权限字段推荐用role字符串而不是 level 数字。原因很直接演示答辩时评委可能随口问“这个 user 为什么进不了管理页”这时回答“代码判断 role 等于 admin”比解释“level 大于等于 2”直观得多。接口统一返回这个外壳{ code: 0, message: success, data: {} }code为 0 表示成功非 0 是业务错误码。前端请求封装里只判断code不判断 HTTP 200因为登录过期、设备离线这类业务异常在 HTTP 层也是 200。2.4 演示项目最不该省的维度角色与权限很多毕设源码为了演示流畅把权限做成了摆设——小程序端靠隐藏按钮控制入口后端接口不做校验。抓包工具一开就能直接调管理接口删除数据答辩现场非常尴尬。最少要分三个角色管理员、值班员、巡查员。管理员看全部告警和统计数据值班员负责确认告警并派单巡查员只能看到分配给自己的点检任务。表设计上不需要单独建权限表在用户表加一个role字段后端写一个拦截器校验请求路径前缀/admin/**只允许管理员访问。3. 小程序端三个高频改造点加载页、导航栏高度与点检表单3.1 修改刚进入的加载页面避免启动白屏毕设源码自带的加载页往往是一张静态图过两秒自动跳转。真正的智慧消防小程序启动时要拉取设备汇总、今日告警、未完成工单三个数据如果都在onLoad里串行请求点击图标后至少白屏两秒。常见做法是先渲染一个带进度状态的启动页数据到位后再switchTab进主页面。Page({ data: { progress: 0, loadingText: 正在同步消防设备状态... }, onLoad() { this.tickProgress() this.fetchBootstrapData() }, tickProgress() { this.timer setInterval(() { if (this.data.progress 90) { clearInterval(this.timer) return } this.setData({ progress: this.data.progress 10 }) }, 100) }, fetchBootstrapData() { wx.request({ url: ${app.globalData.baseUrl}/api/fire/bootstrap, success: (res) { if (res.data.code 0) { app.globalData.summary res.data.data wx.switchTab({ url: /pages/overview/index }) } } }) } })进度条最多走到 90%剩下 10% 留给接口返回后瞬间拉满避免用户看到进度条卡在 100% 页面还没跳。这种做法比“固定延时跳转”可靠——接口慢时不会误跳接口快时不用白等。3.2 顶部导航栏高度适配胶囊按钮和状态栏的换算微信小程序顶部导航栏高度不是固定的。刘海屏、灵动岛、普通安卓机状态栏高度都不一样。写死44px或48px的页面换台手机就上下错位。只要是自定义导航栏都靠wx.getMenuButtonBoundingClientRect取值来算高度。const getNavBarInfo () { const menuRect wx.getMenuButtonBoundingClientRect() const systemInfo wx.getSystemInfoSync() const statusBarHeight systemInfo.statusBarHeight const navBarHeight (menuRect.top - statusBarHeight) * 2 menuRect.height return { statusBarHeight, navBarHeight, menuRect } }这段代码的核心逻辑胶囊按钮右上角那个圆角矩形垂直居中于导航栏所以导航栏总高度等于「胶囊到状态栏的距离 × 2 胶囊自身高度」。拿到结果后在app.globalData里存一份所有页面的自定义导航栏组件统一读取。注意wx.getSystemInfoSync在部分新版本基础库上已标记废弃但兼容性仍最稳毕设项目用这个没毛病。3.3 用 radio-group 实现点检单处理单选框回填点检任务是小程序端最核心的交互界面。巡查员到现场后逐项确认“设备外观正常、指示灯正常、压力表读数正常”每一项对应一个单选框组。这里容易踩的坑是回填任务详情接口返回的是 JSON 数组而不是每个选项的布尔值。radio-group bindchangeonCheckChange label wx:for{{checkPoints}} wx:keyid classcheck-item {{item.result 1 ? checked : }} radio value{{item.id}} checked{{item.result 1}} color#e64340 / text{{item.name}}/text /label /radio-groupcheckPoints中每项包含id、name、result三个字段result为 0 表示异常为 1 表示正常。后端返回的历史点检记录直接赋值给data.checkPointsradio 的checked属性会自动回填不需要额外写 setData 逻辑。提交时遍历数组收集结果这一步通常和拍照上传绑在同一个按钮上。3.4 长按拖拽排序与图片上传巡查点检顺序有时需要现场调整比如先查烟感再查手报。微信小程序没有原生长按拖拽组件常见方案是movable-area配合movable-view实现但代码量大课设更推荐用现成思路长按进入排序模式监听touchmove计算交换位置。onItemLongPress(e) { this.setData({ dragIndex: e.currentTarget.dataset.index, isDragging: true }) }, onItemTouchMove(e) { if (!this.data.isDragging) return const touch e.touches[0] const items this.data.checkPoints const currentIndex this.data.dragIndex // 根据 touch.clientY 与各项累计高度判断移动到哪个位置 }实现里最关键的参数是touch.clientY和每个check-item的offsetTop对比超过中间线就交换数组顺序。拖完记得把isDragging置回 false否则松手后还会继续响应 touchmove屏幕会跟着手指乱跳。图片上传用wx.chooseMedia一次最多选 9 张上传前压缩到宽度 1280 以内能省不少流量和上传时间。3.5 uniapp 与原生小程序HBuilderX 跑同一套代码的取舍如果源码是用 uniapp 写的项目结构里会多出pages.json而不是app.jsonHBuilderX 里可以直接运行到微信开发者工具。这种技术选型的好处是一套代码同时出小程序和 App但要注意 uniapp 的 radio-group 事件名是change原生小程序是bindchange改造页面时经常在这里翻车。我一般会先看根目录有没有manifest.json有就是 uniapp 工程直接 HBuilderX 打开。原生的微信小程序项目没有这个文件用微信开发者工具导入。两者在演示上没本质差别选哪个取决于源码本身不必为了技术栈高低去重写。4. 后端接口与联调登录、定位、告警推送与微信支付 v34.1 微信登录换取 token演示项目常见的绕过方案小程序调用wx.login拿到 code后端拿 code 到微信接口换 openid再签发自己的 token。这套流程本身五分钟能写完但很多毕设源码为了演示省事写了个“一键登录”按钮直接带一个user_id参数请求接口跳过整个登录流程。PostMapping(/api/auth/login) public Result login(RequestBody LoginDTO dto) { String openid wechatService.code2Session(dto.getCode()); if (openid null) { return Result.fail(登录凭证已失效); } User user userMapper.selectByOpenid(openid); if (user null) { user userMapper.createDefaultUser(openid, dto.getRole()); } String token JwtUtil.createToken(user.getId(), user.getRole()); return Result.ok(token); }这段代码里有三个点值得展开。第一code2Session请求微信接口必须后端发起不能在前端直接把 code 发给微信服务器否则appsecret会暴露。第二首次登录自动建用户的写法适合演示场景但生产上应该弹窗让用户补全姓名和手机号。第三token 里只放id和role不要把整个 user 对象塞进去JWT 是有体积上限的。注意最新的微信基础库已经收紧头像昵称获取方式wx.getUserProfile返回的头像昵称不再是真实信息。毕设里如果需要显示用户姓名建议用个人中心里的昵称输入框后端存到用户表不要尝试调getUserInfo。4.2 定位上报与电子围栏判定消防巡查必须要求巡查员在设备附近才能提交点检否则坐在宿舍里刷任务也能完成。后端的判定逻辑不能只比对“距离小于 50 米”要考虑设备坐标的存储和小程序端定位误差。SELECT * FROM device WHERE ( 6371000 * acos( cos(radians(#{lat})) * cos(radians(latitude)) * cos(radians(longitude) - radians(#{lng})) sin(radians(#{lat})) * sin(radians(latitude)) ) ) 50;这条 SQL 直接查出 50 米内的所有设备Haversine 公式算的是球面距离单位是米。业务逻辑上先提交定位后端返回距离最近的三台设备小程序端让巡查员点选“我就在这台设备前”而不是硬性限制成必须匹配唯一设备因为室内 GPS 漂移十几米是常态。4.3 告警订阅消息与定时轮询的配合小程序不像 App 能常驻后台收推送订阅消息每条都要用户点击授权同意且一次性订阅只能收到一次推送。智慧消防这个场景里告警推送的可行做法是用户在小程序里点击“开启告警提醒”时请求订阅授权后端在设备上报告警时调用subscribeMessage.send发送。轮询作为兜底。小程序onShow和下拉刷新时拉取最近告警列表两套机制配合既能实时提醒又不至于在用户没授权订阅时完全失联。演示录像里最常看到的问题是开发者工具里订阅消息功能默认关闭必须到详情设置的「本地设置」里勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”还要在公众平台里配置request合法域名。4.4 扩展功能小程序微信支付 v3 对接的关键参数不是每个智慧消防项目都要做支付但如果设备年检、消防培训课程收费这个模块出现在源码里对接的就是微信支付 v3。先看三个关键参数是否配齐mchid商户号、serial_no商户证书序列号、api_v3_keyAPI v3 密钥。v3 和 v2 最大的区别是签名方式。v3 用 RSA-SHA256请求头里要带Authorization: WECHATPAY2-SHA256-RSA2048签名串由请求方法、路径、时间戳、随机串、请求体拼接而成。毕设里最常见的错误是时间戳和随机串每次请求都重新生成但验签用的商户私钥没加载对。把这三个值放到配置文件里appid和mchid分开放不要写死在代码里。支付成功后微信会往回调地址推通知注意回调地址必须用 HTTPS 域名本地调试时用内网穿透工具转到 8080 端口。5. 演示录像怎么录抓包验接口、反编译验代码、场景串流程5.1 录像脚本编排看板→告警→处置三段式演示录像不是操作录屏是按脚本表演的系统验收。从多个毕业设计答辩现场回来看得分高的录像都遵循同一个结构先用看板页的统计数字交代系统全貌再触发一条真实告警展示流转最后以巡查员扫码提交点检收尾。三段各留 15 秒给评委看清页面跳转不要一镜到底乱点。触发告警不要用假按钮。常见做法是后端提供一个测试接口POST /api/fire/alarm/test传设备编号和告警类型模拟真实上报链路。录像里能看到这条请求进 Redis 或数据库告警列表 5 秒内出现新纪录比前端写死一条数据有说服力得多。5.2 用抓包工具核对每个请求是否走通录完像后我会用抓包工具把小程序流量完整过一遍。启动小程序操作点检、告警处理、工单归档三个核心流程然后逐个检查请求路径和响应体。重点看三处code是否都是 0、时间字段格式是否统一、经纬度是不是真实值。抓包前需要做两个准备HTTP 请求在调试工具里能直接看到但 HTTPS 的要装证书并且要把证书安装到系统信任区否则小程序请求会报证书校验失败。另一个是关闭小程序的域名校验这个在开发者工具里勾选“不校验合法域名”即可不影响代码逻辑。5.3 反编译检查代码里有没有该删的东西毕设源码包里带反编译出来的代码不奇怪关键是自己要会用反编译工具检查有没有“不能见人”的东西。把小程序包解包后第一眼看app.js里的globalData第二眼看配置文件里的appid第三眼看接口地址。如果发现源码里带真实的appsecret、商户私钥、数据库密码答辩前必须换掉。尤其注意小程序端代码里出现mchid和私钥内容的这是把服务端配置写到前端的大忌。反编译的代码结构可以在答辩时间接展示自己对微信小程序运行机制的理解但没必要主动提评委如果问了就说“我用反编译工具分析过官方示例小程序学习它的分包结构”。5.4 答辩前必改的三个演示破绽改完这三处再去录最终的演示录像第一全局搜索代码里所有http://localhost、http://192.168、10.0.2.2这类地址统一换成已备案的 HTTPS 域名或演示用的线上接口第二检查数据库脚本里有没有DROP DATABASE有的话去掉避免评委在自行运行项目时误操作第三把测试账号的密码从123456改成可展示的强密码并在说明文档里标注出来。这三处是答辩现场翻车概率最高的位置改完再绿录屏基本就不会被追问“这个系统是不是只能演示”。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询