拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于Spring Boot的智慧工地监控管理系统毕业设计实战指南

每年这个时候都会有一大批计算机专业的学生卡在毕业设计选题上。如果你正在纠结做什么题目又希望它既有技术含量、又有工程落地感、还能在答辩时讲出东西来那么基于Spring Boot的智慧工地监控管理系统这个方向我建议你认真考虑。这个题目听起来很唬人实际上拆开来看它就是一个典型的物联网云平台业务管理系统的组合拳前端有可视化大屏和移动端后端有Spring Boot撑起业务逻辑底层通过MQTT或Netty接入了工地上各种传感器和监控设备再配上MySQL存放业务数据、Redis处理缓存和高频访问数据。一套下来Spring Boot、Java、物联网、云数据、智慧工地这些热搜词全部覆盖评委想挑毛病都难。作为过来人我想把整个项目的设计思路、技术选型、核心模块实现和部署答辩经验完整地拆给你。这篇文章不是那种只给目录的凑数文而是你照着做就能少走三个月弯路的实战复盘。1. 智慧工地到底在智哪里先捋清业务边界很多同学拿到这个题目第一反应是我要做一个大而全的系统于是把人员管理、考勤、工资、物料、进度、质量、安全全部塞进去。这是毕设最致命的错误之一。智慧工地不是一个ERP系统它的核心价值在于**感知现场、预警风险、调度资源**这十二个字而不是替工地做全流程行政管理。我建议你把业务范围收敛为五大核心领域这也是当前工程现场管理公认最痛的点监管维度核心痛点系统对应功能人员工地人员流动性大实名制难落实危险区域闯入难管控实名制登记、人脸识别考勤、电子围栏、人员定位轨迹回放设备塔吊、升降机等大型设备操作不规范维保逾期塔吊运行状态监测、吊重/力矩实时上报、维保到期提醒环境扬尘、噪音、温湿度超标安全隐患无法及时发现环境传感器数据采集、超标自动告警、喷淋联动视频监控点位分散事后查证难无法主动预警视频监控接入、AI识别未戴安全帽/烟雾火焰、周界入侵检测调度物料进场、车辆进出、人员调配靠人工协调效率低车辆道闸联动、物料计划跟踪、人员/设备调度看板对应到毕业设计里你不必把每个点都做成生产级但至少要有一条完整的数据链路跑通物理设备或模拟器采集数据 → 网络传输 → 服务端接收解析 → 业务处理 → 存储与展示 → 异常告警与资源调度。评审老师最看重的就是这个闭环而不是你写了多少行增删改查。补充一个我对题目关键词云数据的理解这里的云不是一定要你用华为云、阿里云的PaaS服务而是强调数据能够汇聚到服务端云端并支持远程监控与调度。换句话说你的系统要具备现场设备端 云端服务端 远端Web/App端的分离形态这就在架构层面体现了云数据的思想。后面我讲的部署方案也是按这个思路走的。2. 技术选型的底层逻辑为什么Spring Boot是这类系统的最优解选技术栈不能凭感觉更不能因为学校教过什么就用什么。你需要能在答辩时说清楚为什么是它。我给出当时自己选用这套组合的思考过程供你参考。2.1 后端框架Spring Boot的生态红利智慧工地系统的本质是一个接收物联网设备高频上报数据 支撑Web端低延迟查询 处理调度业务逻辑的中间枢纽。这种场景下Spring Boot几乎是当前最平衡的选择内置Tomcat服务器一条命令就能跑起来不像SSH那样要手动配置各种XML大幅降低环境搭建成本自动配置机制让集成MyBatis、Redis、WebSocket、MQTT等组件时只需要引入依赖和少量配置对毕设这样的小团队和短周期非常友好起步依赖Starter体系天然适合微服务化演进——就算你的毕设是单体结构用spring-boot-starter-web写出的代码未来要拆成NacosGatewaySentinel的微服务体系也毫无违和感答辩时可以顺带提一句演进思路加分。2.2 为什么不用Netty而选MQTT做设备接入物联网设备接入有两种主流方式一是直接用Netty做TCP长连接自定义协议二是用MQTT协议。我实测下来毕设场景强烈建议主推MQTT 扩展NettyMQTT基于发布/订阅模型一个设备上报消息可以被多个订阅端消费。比如塔吊传感器上报的数据告警服务要订阅、监控大屏要订阅、数据存储服务也要订阅这一条消息就同时驱动了三个模块非常契合智慧工地的场景。EMQX等开源Broker自带WebSocket端口浏览器端可以直接通过WebSocket订阅设备消息这比你在后端用WebSocket再转一层轻松太多。至于Netty我建议你在文档里写一句系统支持自定义TCP协议接入对MQTT形成补充用于体现你的技术深度但核心链路不要用Netty实现否则会因为粘包拆包、心跳保活、协议解析等细节拖垮进度。2.3 前端与可视化方案智慧工地天然需要一个大屏展示现场态势。我当时的组合是Vue3 ECharts DataV。ECharts的实时折线图、地图散点图用来展示设备数据和人员定位DataV可以直接用现成的边框装饰不用自己写CSS特效。如果你对前端不太熟也有一个偷懒方案用Spring Boot的模板引擎Thymeleaf直接渲染页面再用ECharts的ajax接口获取数据。这样就不用维护前后端分离的两套工程但答辩效果会稍弱。我的建议是既然标题里写了Java驱动和云监控还是老实做一套前后端分离哪怕页面简单一点至少架构上说得通。2.4 整体技术栈速览层次选型用途说明后端框架Spring Boot 2.7.x / 3.x业务接口、数据汇聚、任务调度ORMMyBatis Plus单表CRUD不用写SQL节省大量时间缓存Redis设备实时状态缓存、验证码、分布式Session数据库MySQL 8.x业务数据持久化消息接入EMQXMQTT Broker接收设备上报消息支持WebSocket订阅实时通信WebSocket后端推送将告警和监控数据实时推送到Web页面定时任务Spring Quartz / XXL-Job设备离线检测、维保到期提醒、趋势统计前端Vue3 Element Plus ECharts管理后台与可视化大屏部署云服务器 Docker ComposeMySQL、Redis、EMQX、应用容器化一键部署这套技术栈覆盖了Java基础、框架运用、数据库设计、中间件使用、物联网协议、云部署六大维度每一个都能在你的简历和答辩PPT里占据一席之地。3. 从零搭建工程骨架环境、依赖与数据库设计确定技术栈之后动手第一步不是写代码而是把工程环境、依赖管理和数据库表结构一次定好。这个阶段返工成本最低也最容易暴雷。3.1 环境版本匹配实测版本选择是第一个大坑我这里直接给你我验证过的一套JDK1.8 或 17如果Spring Boot用3.x则必须17建议用3.x因为答辩时面试官大概率会问新特性Maven3.8MySQL8.0Redis7.xEMQX5.xNode.js18前端构建用开发工具IDEA 2023 或 2024强烈建议把所有中间件装到Docker里。我见过太多同学在Windows上折腾MySQL安装失败、Redis连接不上折腾两天还没开始写代码。用Docker Compose一条指令全搞定而且部署到云服务器时也是同一套配置不用改任何东西。下面是我当时用的docker-compose.yml直接保存即可用version: 3.8 services: mysql: image: mysql:8.0 container_name: smart-site-mysql ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: root123456 MYSQL_DATABASE: smart_construction volumes: - ./mysql-data:/var/lib/mysql command: --default-authentication-pluginmysql_native_password redis: image: redis:7-alpine container_name: smart-site-redis ports: - 6379:6379 emqx: image: emqx/emqx:5.8.0 container_name: smart-site-emqx ports: - 1883:1883 - 8083:8083 - 8084:8084 - 18083:18083 environment: EMQX_DASHBOARD__DEFAULT_PASSWORD: admin123提示EMQX的8083端口是WebSocket接入端口Web页面联调时要用到18083是Dashboard管理后台第一次登录记得改密码。3.2 Spring Boot 工程初始化与核心依赖通过Spring Initializr创建工程时我建议直接选中这些依赖Spring Web、Validation、MyBatis Plus注意Initializr里可能没有需要手动加坐标、Redis、MySQL Driver、Lombok、WebSocket、Quartz。核心依赖坐标如下dependencies !-- Web 基础 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis Plus 多数据源与扩展 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.7/version /dependency !-- Redis -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency !-- MySQL -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency !-- MQTT 客户端 -- dependency groupIdorg.eclipse.paho/groupId artifactIdorg.eclipse.paho.client.mqttv3/artifactId version1.2.5/version /dependency !-- WebSocket -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-websocket/artifactId /dependency !-- 工具类 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies注意MyBatis Plus和Spring Boot 3.x的兼容问题——MP 3.5.7以上版本才完整支持Boot 3如果你用的是Boot 3.2不要用旧版MP否则启动时会报各种奇怪的ClassNotFound。3.3 配置文件多环境设计工程内创建三个配置文件这是生产级项目的标准做法在毕设中却很加分# application.yml spring: profiles: active: dev# application-dev.yml本地开发 server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/smart_construction?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: root123456 redis: host: localhost port: 6379 data: redis: repositories: enabled: false mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0# application-prod.yml云端部署 server: port: 8080 spring: datasource: url: jdbc:mysql://你的云服务器IP:3306/smart_construction?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: smart_user password: 生产环境强密码 redis: host: 你的云服务器IP port: 6379 password: redis密码多环境配置最大的好处是本地联调时用dev日志里能看到完整SQL输出部署到云服务器时指定--spring.profiles.activeprod不会把本地配置带到生产环境也不会在日志里泄露SQL和密码。3.4 数据库核心表设计评审必问数据库设计是你论文里最重要的图之一。我用三张核心表举例你把它们画成ER图放论文里评审看到就知道你懂业务。设备信息表device_info——所有接入系统的物理/虚拟设备都注册在这里CREATE TABLE device_info ( id bigint(20) NOT NULL AUTO_INCREMENT, device_code varchar(64) NOT NULL COMMENT 设备编码全局唯一, device_name varchar(128) NOT NULL, device_type tinyint(4) DEFAULT NULL COMMENT 1塔吊 2升降机 3环境监测 4门禁 5视频, location varchar(255) DEFAULT NULL COMMENT 安装位置如A区塔吊1号, status tinyint(4) DEFAULT 0 COMMENT 在线状态0离线 1在线, last_report_time datetime DEFAULT NULL COMMENT 最后上报时间, latitude decimal(10,6) DEFAULT NULL COMMENT 纬度, longitude decimal(10,6) DEFAULT NULL COMMENT 经度, ext_info json DEFAULT NULL COMMENT 扩展属性, deleted tinyint(1) DEFAULT 0, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_device_code (device_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT设备信息表;设备上报原始数据表device_data_raw——物联网系统的血液按时间流水存储CREATE TABLE device_data_raw ( id bigint(20) NOT NULL AUTO_INCREMENT, device_code varchar(64) NOT NULL, message_type varchar(32) DEFAULT NULL COMMENT 消息类型heartbeat/data/alarm, payload json DEFAULT NULL COMMENT 原始报文如{weight:2.5,angle:38}, report_time datetime DEFAULT NULL, PRIMARY KEY (id), KEY idx_device_time (device_code, report_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT设备上报原始数据;告警记录表alarm_record——所有异常事件的落地记录CREATE TABLE alarm_record ( id bigint(20) NOT NULL AUTO_INCREMENT, alarm_type varchar(32) NOT NULL COMMENT 告警类型weight_overload/fall_in_zone/env_over_limit/tamper, alarm_level tinyint(4) DEFAULT 2 COMMENT 1紧急 2重要 3一般, device_code varchar(64) DEFAULT NULL, alarm_content varchar(500) DEFAULT NULL, lat decimal(10,6) DEFAULT NULL, lng decimal(10,6) DEFAULT NULL, status tinyint(4) DEFAULT 0 COMMENT 0未处理 1已确认 2已复位, handle_user varchar(64) DEFAULT NULL, handle_time datetime DEFAULT NULL, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT告警记录表;这三张表对应了设备元数据、数据流过、异常事件三个核心概念你整个系统的所有功能几乎都围绕它们展开。其他表用户表、角色权限表、维保记录表、调度工单表根据你选的具体功能再补。4. 设备接入层设计让工地上的传感器先活起来毕设答辩中最容易被追问的就是你的设备数据从哪来。真实工地当然不会真的把硬件寄给你所以你面临的第一道坎是在没有物理硬件的情况下怎么完整跑通数据链路。4.1 三种数据来源方案对比方案优点缺点建议纯模拟线程生成随机数实现最快演示稳定太假答辩容易被追问物理设备怎么接入不推荐单独用MQTT模拟客户端程序完整走一遍设备接入流程能展示通信链路需要额外写一个模拟器工程毕设首选真实硬件ESP32传感器演示效果好真实性最强成本高、调试周期长有余力可以加我当时采用的是模拟客户端 预留真实设备接口的组合方案写一个Java模拟器定时通过MQTT上报塔吊的吊重、力矩、高度、回转角度、风速等数据同时把MQTT消息结构和通信协议完整梳理成接口文档。答辩时打开模拟器评委能看到数据从模拟器到EMQX再到后端存储的全过程这比纯随机数有说服力得多。4.2 MQTT Topic 结构设计Topic设计是一个很容易被忽略但非常重要的细节。我在第一版时把所有设备都发到同一个Topic结果后端根本分辨不了数据类型后来才改成结构化设计smart-site/{projectId}/{deviceType}/{deviceCode}/data smart-site/{projectId}/{deviceType}/{deviceCode}/alarm smart-site/{projectId}/{deviceType}/{deviceCode}/heartbeatprojectId项目编号支持多工地扩展对应你数据库里的项目表deviceType设备类型如crane、elevator、environment、gatedeviceCode设备唯一编码和数据库表device_info.device_code对应。后端订阅时可以使用通配符smart-site////data smart-site////alarm smart-site////heartbeat这种设计的好处是新增设备不用改任何代码实现真正的即插即用同时多工地场景下可以通过projectId做数据隔离逻辑非常清晰。4.3 MQTT消息体格式定义我定义了一套统一的JSON协议包含基础设施字段和业务载荷两部分{ deviceCode: CRANE-001, projectId: P20240001, timestamp: 2024-12-20 14:30:00, msgType: data, data: { loadWeight: 2.5, ratedCapacity: 6.0, loadRatio: 0.42, height: 56, rotationAngle: 120, windSpeed: 12.5, towerArmAngle: 38 } }这里要特别注意loadRatio负载率吊重/额定起重这个字段。它是后端做超载判断的核心依据告警触发阈值通常设为0.9——超过额定载荷的90%就要预警超过100%立即产生紧急告警并联动设备限制操作。模拟器生成数据时要把loadRatio算好了再上报方便后续逻辑处理。4.4 后端MQTT订阅与数据落库后端通过Eclipse Paho客户端订阅MQTT主题收到消息后解析、校验、落库同时推送到Redis和WebSocket。核心监听代码逻辑如下Component Slf4j public class MqttMessageListener { Autowired private IDeviceDataService deviceDataService; Autowired private RedisTemplateString, Object redisTemplate; Autowired private WebSocketPushService webSocketPushService; Autowired private AlarmHandleService alarmHandleService; Autowired private StringRedisTemplate stringRedisTemplate; public void handleMessage(String topic, MqttMessage message) { String payload new String(message.getPayload(), StandardCharsets.UTF_8); // 1.解析主题得到deviceType String[] topicPart topic.split(/); String deviceType topicPart[2]; String deviceCode topicPart[3]; // 2.解析业务消息 DeviceDataDto dataDto JsonUtils.parseObject(payload, DeviceDataDto.class); // 3.按设备类型分发处理每种设备有独立的业务逻辑 switch (deviceType) { case crane - handleCraneData(dataDto); case environment - handleEnvironmentData(dataDto); case gate - handleGateAccess(dataDto); default - log.warn(未知设备类型: {}, deviceType); } } private void handleCraneData(DeviceDataDto dto) { // 1.实时写入Redis供监控大屏快速读取 stringRedisTemplate.opsForValue().set( device:realtime: dto.getDeviceCode(), JSON.toJSONString(dto), 30, TimeUnit.SECONDS ); // 2.原始数据异步落库 deviceDataService.saveRawData(dto); // 3.超载判断与告警推送 BigDecimal loadRatio dto.getData().getBigDecimal(loadRatio); if (loadRatio ! null loadRatio.compareTo(new BigDecimal(0.9)) 0) { alarmHandleService.triggerAlarm(weight_overload, dto.getDeviceCode(), 负载率 loadRatio 超阈值, 1); } // 4.告警推送到WebSocket前端 webSocketPushService.pushMessage(/topic/realtime, JSON.toJSONString(dto)); } }这一步是整个系统的心脏。消息从MQTT进来经过解析判断落到Redis、MySQL再通过WebSocket推到前端页面一条完整的数据高速公路就修通了。你说这算不算云数据当然算——数据从现场设备汇聚到云端服务端再分发到各业务模块这就是典型的物联网云平台架构。5. 五大核心监管功能的实战拆解骨架搭好、数据链路通了接下来就是把业务功能一个个填进去。我挑三个最有代表性的功能来讲其余的按同样的套路照搬。5.1 视频监控接入RTSP拉流与Web端播放视频监控是监控管理里的重头戏也是最容易让评委眼前一亮的模块。如果条件有限无法接入真实摄像头可以用Hikvision的模拟RTSP流或者直接用本地视频文件推流。技术路线上我当时的方案是FFmpeg做RTSP拉流转HLS Web端video.js播放 后端做录像回放管理。后端提供一个接口根据设备编号动态生成HLS流的访问地址RestController RequestMapping(/api/video) public class VideoController { GetMapping(/live/{deviceCode}) public ApiResultString getLiveStream(PathVariable String deviceCode) { // 根据设备id查关联的RTSP地址 DeviceInfo device deviceService.getByCode(deviceCode); if (device null || StringUtils.isEmpty(device.getRtspUrl())) { return ApiResult.error(设备不存在或未配置视频流地址); } // 实际项目中这里会调用流媒体服务的接口生成HLS地址 String hlsUrl http://server/live/ deviceCode .m3u8; return ApiResult.success(hlsUrl); } GetMapping(/history/{deviceCode}) public ApiResultListVideoRecordVo getHistoryRecords( PathVariable String deviceCode, RequestParam String startTime, RequestParam String endTime) { // 按时间段查询录像记录 ListVideoRecordVo records videoRecordService.queryByTimeRange(deviceCode, startTime, endTime); return ApiResult.success(records); } }如果你不想自己搭建流媒体服务器另一条更省事的路线是使用萤石云/乐橙云的设备接入API——摄像机把流推到云端你的后端只调云平台API拉取播放地址这也是云监控概念的直接体现。答辩时可以讲两种方式的选择权衡显得有深度。5.2 电子围栏与人员定位前端地理围栏 后端射线法判定人员越界告警是智慧工地安全管理的核心需求。通常做法是在工地地图上画出若干个区域如塔吊吊装区材料堆放区办公区每个区域有对应的允许权限人员佩戴的定位标签或手机GPS实时上报位置后端做命中判断。电子围栏的区域数据用GeoJSON格式存储{ type: FeatureCollection, features: [{ type: Feature, properties: { id: 1001, name: 塔吊吊装区, level: danger, allowedRoles: [crane_operator, manager] }, geometry: { type: Polygon, coordinates: [[ [113.916, 22.5564], [113.922, 22.5566], [113.924, 22.5612], [113.918, 22.5610], [113.916, 22.5564] ]] } }] }后端判定一个点是否在多边形内我用的就是经典的射线法算法。这里把核心代码贴出来你直接拿去用public class GeoFenceUtil { /** * 射线法判断点是否在多边形内 */ public static boolean isPointInPolygon(double px, double py, ListPoint polygon) { int n polygon.size(); boolean inside false; int j n - 1; for (int i 0; i n; i) { Point pi polygon.get(i); Point pj polygon.get(j); if ((pi.getY() py) ! (pj.getY() py) px (pj.getX() - pi.getX()) * (py - pi.getY()) / (pj.getY() - pi.getY()) pi.getX()) { inside !inside; } j i; } return inside; } }配合定位服务模拟器可以定时模拟一个在工地平面图上移动的坐标点当监测到非授权人员进入危险区域时后端生成告警记录并推送到管理端同时可以联动短信或语音播报提醒——这一步就把AI安防的雏形做出来了。5.3 环境监测与自动联动扬尘超标自动喷淋工地扬尘、噪音、温湿度、风速、PM2.5/PM10等数据通过环境监测设备实时上报。光展示还不够智慧体现在规则引擎的联动控制上。后端服务里维护一张联动规则表比如触发条件动作PM2.5浓度 75μg/m³ 持续3分钟开启喷淋系统间隔30分钟自动关闭噪音 70dB 持续时间大于5分钟推送告警到项目部记录事件风速 8级17.2m/s塔吊停机告警限制吊装作业这里要注意一个常见坑传感器抖动导致误触发。解决方案是连续N次采样均超阈值才触发告警可以在代码里加一个计数窗口public class EnvironmentMonitorService { // 使用Caffeine本地缓存或Redis做滑动窗口计数 Autowired private RedisTemplateString, Object redisTemplate; private static final int PM25_THRESHOLD 75; private static final int MAX_CONTINUOUS_COUNT 3; public void processEnvData(EnvironmentDataDto dto) { double pm25 dto.getPm25(); if (pm25 PM25_THRESHOLD) { // 窗口内计数1 String key env:pm25:count: dto.getDeviceCode(); Long count redisTemplate.opsForValue().increment(key); redisTemplate.expire(key, 5, TimeUnit.MINUTES); if (count ! null count MAX_CONTINUOUS_COUNT) { // 触发喷淋联动同时落告警 sendSprayCommand(dto.getDeviceCode(), 开); alarmHandleService.triggerAlarm(env_over_limit, dto.getDeviceCode(), PM2.5持续超标, 2); // 重置计数避免重复触发 redisTemplate.delete(key); } } else { // 浓度正常清空计数窗口 redisTemplate.delete(env:pm25:count: dto.getDeviceCode()); } } }用Redis做计数窗口是我踩过坑之后总结出来的如果直接用本地变量计数集群部署时会失效用Redis才能保证统计状态跨实例一致而且天然支持过期自动清窗。6. 资源调度模块从人管到算法管资源调度是标题里的重头戏也是最容易写进论文创新点的地方。在实际工地中资源调度的对象包括塔吊、升降机、车辆、人员、物料。受限于毕设体量我建议你聚焦塔吊作业调度这个最核心的场景把它做深做透。6.1 塔吊群防碰撞算法多机协同的空间冲突检测大型工地通常多台塔吊同时工作塔臂交叉区域容易发生碰撞。这是实际工程中一个非常专业的安全问题同时也是算法亮点。我们可以简化模型每台塔吊的基本参数有大臂长度L、当前回转角度θ、塔吊中心坐标(X,Y)、吊钩高度H。两台塔吊之间的碰撞风险取决于两塔吊中心距离与臂长之和的关系两塔吊当前回转覆盖区域是否重叠通过根关节的最小距离是否小于安全阈值。我设计了一个相邻塔吊距离计算模型把两个塔吊的回转区域抽象为两个扇形或圆形投影计算当前时刻两塔臂上的关键采样点等距取5个点求所有采样点之间的最小欧氏距离低于安全阈值则告警。public class CraneCollisionDetector { /** * 计算两台塔吊当前姿态下的最小距离 */ public double calculateMinDistance(CraneStatus craneA, CraneStatus craneB) { double minDist Double.MAX_VALUE; ListPoint pointsA sampleCraneArm(craneA, 5); // 在A塔臂上取5个采样点 ListPoint pointsB sampleCraneArm(craneB, 5); for (Point pa : pointsA) { for (Point pb : pointsB) { double dist calcDistance(pa, pb); if (dist minDist) { minDist dist; } } } return minDist; } private ListPoint sampleCraneArm(CraneStatus crane, int count) { ListPoint points new ArrayList(); double armLen crane.getArmLength(); for (int i 0; i count; i) { double radius armLen * i / (count - 1); double angleRad Math.toRadians(crane.getRotationAngle()); double x crane.getCenterX() radius * Math.cos(angleRad); double y crane.getCenterY() radius * Math.sin(angleRad); points.add(new Point(x, y)); } return points; } }当最小距离低于安全阈值时系统自动发出碰撞预警并通过WebSocket推送到调度大屏同时给司机端模拟App发送避让指令优先让大臂转角较小的塔吊先回转、起重小车回收至安全位置。这些指令就是在发挥调度系统的作用。6.2 基于任务优先级的人员与车辆调度模型另一个便于实现、逻辑清晰的调度场景是当塔吊维修、混凝土浇筑、材料进场等任务同时发生时系统如何安排先后顺序、分配车辆和人员。这里我引入一个简单的优先级调度算法Priority Scheduling每项任务有紧急程度Pri、预计时长T、所需资源R如需要一辆运输车、两名工人。系统按综合得分排序综合得分 紧急程度Pri * 0.6 (1 / 预计时长T) * 0.4规则上再做两层约束资源互斥一辆运输车同一时间只能服务一个任务设备依赖塔吊吊装任务必须在对应塔吊空闲且维保有效状态下才能排单。代码模块里维护一个调度队列每5分钟重新校准一次public class ResourceScheduler { // 待调度队列 private PriorityQueueScheduleTask taskQueue; public void submitTask(ScheduleTask task) { // 计算动态优先级并加入队列 task.setPriorityScore(calculatePriority(task)); taskQueue.offer(task); } public void dispatchLoop() { while (!taskQueue.isEmpty()) { ScheduleTask task taskQueue.poll(); // 检查资源是否满足 boolean allocSuccess allocateResources(task); if (allocSuccess) { sendDispatchCommand(task); } else { // 资源不足挂起等待下一次调度周期 task.setWaitingCount(task.getWaitingCount() 1); taskQueue.offer(task); } } } }如果你的毕设时间紧张这个模块不用做得太重关键在于把你使用的调度策略讲清楚配合ECharts画一个调度甘特图页面效果和答辩效果就都有了。6.3 调度看板让数据可视化讲清故事调度系统做得好不好最终要看管理者的使用体验。我在调度看板里设计了三个层次的信息展示工地地图层基于地图高德/百度或ECharts geo展示实时的人员位置、设备位置、告警点位设备状态层通过仪表盘展示塔吊负载率分布、设备在线率、今日上下行消息数任务调度层待办任务列表 甘特图 调度指令下发记录。这三层信息分别对应全局把握、设备健康、行动决策看板不是炫技而是让管理者一眼知道现场发生了什么、下一步该干什么。7. 部署上线与毕业答辩的双重准备代码写完了只是开始部署和答辩才是决定成绩的关键。很多人辛辛苦苦写好代码结果在部署环节翻车或者答辩时一问三不知。我把自己总结的避坑经验完整分享给你。7.1 云服务器选型与部署架构如果你没有自己的云服务器可以用轻量应用服务器2核4G就够学生认证通常有优惠。系统部署后让评委扫码访问这个演示效果比本地演示高一个档次。推荐部署架构┌──────────────┐ │ 浏览器访问 │ └──────┬───────┘ │ HTTPS ┌──────▼───────┐ │ Nginx 反向代理 │ 端口80/443 │ 静态文件SSL │ └──────┬───────┘ │ ┌──────▼───────┐ │ Spring Boot应用 │ 端口8080 └──────┬───────┘ │ ┌────────────────┼────────────────┐ │ │ │ ┌────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ │ MySQL │ │ Redis │ │ EMQX │ └──────────┘ └───────────┘ └───────────┘这个架构图我故意没有画成时序或流程类图仅用于部署拓扑的文字示意你可以直接用表格或文字把它描述清楚。如果你的内存比较紧张可以在同服务器上把所有中间件用Docker跑起来但Spring Boot应用建议直接打jar包用systemd托管避免Docker内日志管理麻烦。部署的核心步骤# 1. 打包后端 mvn clean package -DskipTests # 2. 上传jar包并启动 scp target/smart-construction-0.0.1-SNAPSHOT.jar root你的服务器IP:/opt/app/ ssh root你的服务器IP cd /opt/app nohup java -jar smart-construction-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod app.log 21 # 3. 构建前端并部署到Nginx npm run build # 将dist目录上传到 /usr/share/nginx/html/注意云服务器的安全组一定要放通端口常见问题就是代码没问题但外部访问不了检查一下安全组规则和防火墙八成是端口没放行。7.2 性能与安全别让答辩变成批斗会评委大概率会问系统抗并发能力怎么样有没有安全考虑。提前做几个小动作能让你的回答有底气Redis缓存热点数据设备实时状态查询走缓存不直接打MySQL。用spring-boot-starter-data-redis做缓存管理接口响应时间能从几百毫秒降到几十毫秒。接口鉴权使用JWT做登录状态校验写一个简单的拦截器放行/api/auth/**其他接口都需要校验Token。日志规范用logback-spring.xml按天滚动、按级别拆分方便定位问题也让评委觉得你有工程素养。数据备份写一个每日凌晨的MySQL定时备份脚本mysqldump crontab一劳永逸。7.3 答辩时高频追问与应答思路这部分比代码本身更值钱。我结合自身答辩经历和后来指导学弟学妹的情况整理了几组高频问题为什么用MQTT而不是HTTP答HTTP是同步请求/响应模型适合低频的客户端主动查询但工地设备是高频主动上报秒级甚至毫秒级用HTTP会带来大量无意义的连接建连开销和服务器资源浪费。MQTT基于发布/订阅服务端被动接收、一消息多消费天然适配这种设备主动上报、多个下游系统同时消费的场景。数据量大了怎么办分库分表吗答从三个层面处理。一是MySQL按时间归档设备原始数据在业务上保留90天超过的转历史表二是实时性要求高的数据走Redis三是未来数据量达到千万级时可以引入ClickHouse等列式存储来承载时序数据或者用Kafka削峰填谷。这样既回答了现有方案又展示了自己的知识边界。和市面上已有的智慧工地如广联达有什么区别答市面上产品的优势是标准化程度高但缺陷是私有化部署成本高、数据不出工地二次开发难。我的设计采用开源技术栈具备代码可控和数据自主的优势关键在于针对本项目的高危设备联动规则和调度模型做深度定制。这个回答既承认商业产品价值又突出了毕设的个性化。设备的定位数据不准、丢失怎么处理答物联网场景里数据质量是一个真实挑战。我在模拟器设计了随机丢包率后端通过设备心跳超时检测判断设备离线超过30秒没心跳就置离线状态告警记录里也保留原始定位快照便于事后核验。定位漂移则通过在围栏判定前做一次坐标过滤排除明显跳变来处理。7.4 一条龙清单答辩前夜检查表[ ] 数据库SQL脚本是否有初始化数据没有数据的大屏会显得很空[ ] Java模拟器和真实后端之间的Topic是否一致[ ] WebSocket推送是否会被浏览器跨域拦截检查CORS配置[ ] Redis是否设置了内存淘汰策略默认配置可能在长时间运行后把内存写爆[ ] Nginx的websocket升级Upgrade头是否需要额外配置[ ] 演示用的账号、权限是否提前写好PPT上展示用的页面是否正常最后再分享几个我踩过的坑我在写这个系统的过程中最大的教训是把一个塔吊超载告警的功能做了整整两个星期原因是模拟器的负载率计算逻辑有误导致后端永远触发不了告警。排查到最后发现是模拟器发送的JSON里loadRatio字段被我写成了loadRate后端解析全是null告警逻辑一直跳不过空值判断。这个经历让我养成了一个习惯先定好接口文档模拟器和后端共用一套数据结构定义谁都不许私下改字段名。另一个让我印象深刻的坑是Docker容器时区和宿主机不一致导致告警时间显示比实际时间早了8个小时。解决办法是在docker-compose.yml里给每个容器加上TZ: Asia/Shanghai环境变量。这种问题看起来很低级但排查起来相当折磨人。关于Maven依赖冲突尤其要注意mybatis-plus-boot-starter和mybatis-spring-boot-starter不能同时存在否则启动时会出现Invalid bound statement (not found)的经典报错。三年前我在答辩前夜花了一个通宵才定位到是这个问题。通过这个项目我最大的体会是毕业设计不只是为了拿学分而是把大学四年学的东西串成一个完整的工程。从数据库设计、后端开发、前端交互到中间件使用、云部署每一步都在逼你去查文档、看源码、踩坑总结。这套实战经验在你春招秋招时就是最真实的谈资比刷一百道Spring Boot八股文都有说服力。万事开头难动手第一步就是把这个骨架在本地跑起来后面的事情会越做越顺。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门