资讯动态

基于Web的城市能源管理系统:从需求到验收的完整实现方案

发布时间:2026/10/17 11:49:44 来源:尧图企业网站定制
简介这是一套面向城市能源管理场景的Web系统源码适合计算机、物联网及能源相关专业学生与开发者用于课程设计、毕业设计或技术学习。系统以Bootstrap框架搭建响应式界面涵盖登录验证、能源监测仪表盘、异常预警、设备入库检修查询等核心模块可帮助读者理解从数据采集到可视化展示的完整业务链路。资源包共170个文件约3MB包含18个html页面、18个js脚本、13个css样式及14个scss、14个less预编译文件另有64张png与14张jpg界面素材、字体图标文件及1个db数据库文件前端结构与样式资源较为完整。目前已有899人学习下载。通过该源码读者可掌握AdminLTE后台布局、图表化能耗监控、预警响应逻辑与设备管理流程的实现方式并借鉴其目录组织与组件复用思路快速搭建同类能源管理项目原型。1. 从一次能耗对账说起这套城市能源管理系统到底要解决什么某园区后勤负责人每月最头疼的事是拿着三块电表、两块水表、一块气表的读数和财务系统里的账单逐条对。数据对不上是常态差在哪、什么时候差的、哪栋楼超了配额全靠翻 Excel 和打电话问。这不是个例而是大多数城市片区、园区、校园在能源管理上的真实状态计量设备装了不少数据却散在各处没有统一口径更没有能看的趋势和能追的责任。这套基于 Web 的城市能源管理系统要解决的就是这件事——把分散的电、水、气计量点接进来做统一采集、实时监控、分项统计、超额预警和报表导出让管理者在一个浏览器里看清整个片区的能耗账。它适合做物联网/软件工程方向的毕设或课设也适合作为中小型园区能源数字化改造的参考方案。整套方案围绕采集层、数据层、业务层、展示层四层展开下面从需求、架构、模块实现一路讲到验收答辩。2. 需求拆解与技术选型四类角色、五个核心功能怎么定2.1 先分清谁在用再决定做什么功能能源管理系统最容易翻车的地方是一上来就堆功能结果做出来的东西谁都不用。我一般会先把角色列清楚再倒推功能边界。这个项目里通常有四类角色角色核心诉求对应功能系统管理员管设备、管账号、管权限设备接入、用户管理、角色配置能源管理员看全局能耗、做统计分析实时监控、分项统计、报表导出片区/楼栋负责人盯自己管辖范围的用量分区看板、配额对比、预警接收运维人员处理设备异常和告警告警列表、设备状态、工单记录角色定完功能边界就清楚了。核心功能收敛为五块数据采集与接入、实时监控看板、分项统计分析、超额预警、报表导出。这五块覆盖了从数据进来到结论出去的完整链路也是答辩时最容易被追问的部分每一块都要能说清输入、处理和输出。2.2 技术选型为什么是这套组合选型的原则是毕设能跑起来、从业者能改得动。后端用 Spring Boot前端用 Vue 加 ECharts数据库 MySQL 存业务数据、Redis 做实时数据缓存采集侧用 MQTT 协议对接计量设备。这套组合的理由很实在Spring Boot 生态成熟权限、定时任务、接口文档都有现成方案不用从零造轮子Vue 加 ECharts 做能耗看板折线、柱状、饼图开箱即用改配置就能出图MySQL 存历史数据和业务表事务和关联查询够用Redis 扛实时数据的读写避免高频写入把 MySQL 拖垮MQTT 是计量设备的主流上报协议轻量、支持断线重连。提示如果设备侧只支持 Modbus可以在采集服务里加一层协议转换把 Modbus 读到的寄存器值转成 MQTT 消息再上报业务层不用改。选型不是越新越好。有同学想上时序数据库结果部署和调优成本远超项目周期最后数据量根本用不上。这个项目的量级——几百到几千个计量点、分钟级上报——MySQL 加 Redis 完全扛得住把精力放在业务逻辑和看板上更划算。3. 总体架构设计四层结构与数据流向3.1 四层架构的职责划分整套系统按采集层、数据层、业务层、展示层划分每层只干自己的事层与层之间通过明确定义的接口通信。采集层负责和计量设备打交道通过 MQTT 订阅设备上报的原始数据做初步的格式校验和单位统一然后投递到消息队列或直接写缓存。数据层分两块Redis 存最新的实时读数和短周期的滑动窗口数据MySQL 存历史明细、设备档案、用户权限、告警记录和报表数据。业务层是核心包含设备管理、数据统计、预警规则引擎、报表生成四个服务模块。展示层就是 Web 前端按角色渲染不同的看板和操作界面。数据流向是单向的设备上报 → 采集层解析 → 数据层落库/缓存 → 业务层计算 → 展示层呈现。反向的控制指令比如远程下发采集频率走单独的通道不和数据上报混在一起避免逻辑纠缠。3.2 数据库表设计五张核心表撑起业务表设计不用贪多把核心实体理清就行。这个项目通常需要五张核心表-- 设备档案表记录每个计量点的基本信息 CREATE TABLE device ( id BIGINT PRIMARY KEY AUTO_INCREMENT, device_code VARCHAR(64) NOT NULL UNIQUE COMMENT 设备编号, device_name VARCHAR(128) COMMENT 设备名称, energy_type TINYINT COMMENT 能源类型 1电 2水 3气, location VARCHAR(255) COMMENT 安装位置, status TINYINT DEFAULT 1 COMMENT 状态 1在线 0离线, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 能耗记录表存历史明细按时间和设备建索引 CREATE TABLE energy_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, device_code VARCHAR(64) NOT NULL, energy_type TINYINT, value DECIMAL(12,3) COMMENT 读数, unit VARCHAR(16) COMMENT 单位, collect_time DATETIME COMMENT 采集时间, INDEX idx_device_time (device_code, collect_time) ); -- 预警规则表定义什么条件下触发告警 CREATE TABLE alert_rule ( id BIGINT PRIMARY KEY AUTO_INCREMENT, rule_name VARCHAR(128), device_code VARCHAR(64), threshold DECIMAL(12,3) COMMENT 阈值, compare_type TINYINT COMMENT 1大于 2小于, period VARCHAR(32) COMMENT 统计周期 day/month, enabled TINYINT DEFAULT 1 ); -- 告警记录表 CREATE TABLE alert_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, rule_id BIGINT, device_code VARCHAR(64), alert_value DECIMAL(12,3), alert_time DATETIME, handled TINYINT DEFAULT 0 COMMENT 是否已处理 ); -- 用户与角色表简化 CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(64) UNIQUE, password VARCHAR(128), role VARCHAR(32) COMMENT ADMIN/ENERGY_MANAGER/AREA_MANAGER/OPS, area_code VARCHAR(64) COMMENT 管辖片区片区负责人用 );energy_record的联合索引(device_code, collect_time)是关键统计查询几乎都按某设备某时间段来查没有这个索引数据量一上来查询就会明显变慢。alert_rule里的period字段决定了阈值是按天还是按月统计规则引擎读这个字段决定聚合方式。3.3 实时数据缓存策略实时看板要求秒级刷新如果每次都查 MySQL压力会很大。做法是把最新读数写进 Redis用 Hash 结构按设备编号存# 设备最新读数field 是设备编号value 是 JSON HSET energy:realtime DEV001 {value:123.5,unit:kWh,time:2024-01-01 10:00:00} HSET energy:realtime DEV002 {value:45.2,unit:m3,time:2024-01-01 10:00:00}前端轮询或走 WebSocket 拉energy:realtime的数据响应在毫秒级。历史趋势才走 MySQL 查询。这样冷热数据分离既保证实时性又不让数据库背锅。4. 核心模块实现采集、统计、预警、报表四块怎么落地4.1 数据采集模块MQTT 订阅与解析采集模块的核心是订阅设备主题、解析报文、写库写缓存。用 Spring Boot 集成 MQTT 客户端关键代码如下Component public class EnergyCollector { Autowired private StringRedisTemplate redisTemplate; Autowired private EnergyRecordMapper recordMapper; // 订阅设备上报主题topic 形如 energy/device/{deviceCode} MqttListener(topic energy/device/) public void onMessage(String topic, String payload) { // 从主题里取设备编号 String deviceCode topic.substring(topic.lastIndexOf(/) 1); JSONObject json JSON.parseObject(payload); BigDecimal value json.getBigDecimal(value); String unit json.getString(unit); Date collectTime json.getDate(time); // 1. 写实时缓存供看板秒级读取 MapString, String realtime new HashMap(); realtime.put(value, value.toString()); realtime.put(unit, unit); realtime.put(time, DateUtil.formatDateTime(collectTime)); redisTemplate.opsForHash().putAll(energy:realtime: deviceCode, realtime); // 2. 异步写历史库避免阻塞采集线程 EnergyRecord record new EnergyRecord(); record.setDeviceCode(deviceCode); record.setValue(value); record.setUnit(unit); record.setCollectTime(collectTime); recordMapper.insertAsync(record); } }这段代码有两个设计点值得说。一是主题用通配符订阅所有设备设备编号从主题里解析新增设备不用改订阅配置。二是写库走异步采集线程只负责解析和投递落库交给线程池避免数据库慢查询把采集堵死。参数上value用BigDecimal而不是double是因为能耗读数涉及金额换算浮点误差会累积这个坑我在对账功能上踩过。4.2 统计分析模块分项聚合与趋势计算统计模块要回答三个问题某设备某时段用了多少、某片区某时段用了多少、同比环比变化多少。核心是按时段聚合查询-- 按天统计某设备某月的日用量 SELECT DATE(collect_time) AS day, MAX(value) - MIN(value) AS daily_usage FROM energy_record WHERE device_code DEV001 AND collect_time BETWEEN 2024-01-01 AND 2024-01-31 GROUP BY DATE(collect_time) ORDER BY day;这里用MAX - MIN算日用量前提是设备上报的是累计读数电表常见。如果设备上报的是瞬时值或增量值算法要换成SUM。这一点必须在设备接入时确认清楚否则统计结果会离谱。片区汇总则在设备档案里按location或area_code分组把设备用量加总。趋势计算用同比环比同比是本期和去年同期比环比是本期和上一期比。SQL 里用自关联或窗口函数都能实现数据量不大时在业务层算更直观。4.3 预警模块规则引擎与触发逻辑预警模块定时跑规则命中就写告警记录。用 Spring 的定时任务按小时或按天触发Scheduled(cron 0 0 * * * ?) // 每小时执行一次 public void checkAlertRules() { ListAlertRule rules alertRuleMapper.selectEnabled(); for (AlertRule rule : rules) { // 按规则周期取统计值 BigDecimal current statisticsService.getUsage( rule.getDeviceCode(), rule.getPeriod()); boolean hit rule.getCompareType() 1 ? current.compareTo(rule.getThreshold()) 0 : current.compareTo(rule.getThreshold()) 0; if (hit) { AlertRecord record new AlertRecord(); record.setRuleId(rule.getId()); record.setDeviceCode(rule.getDeviceCode()); record.setAlertValue(current); record.setAlertTime(new Date()); alertRecordMapper.insert(record); } } }规则引擎的关键是幂等——同一条规则在同一周期内不能重复告警。做法是在写告警前先查该规则该周期是否已有未处理记录有就跳过。否则每小时跑一次一条超额的规则能刷出几十条告警运维直接崩溃。4.4 报表导出模块Excel 生成与数据组装报表导出用 EasyExcel 或 POI把统计结果按模板写进 Excel。核心是把查询结果组装成行数据public void exportMonthlyReport(String areaCode, String month, HttpServletResponse response) { // 1. 查该片区所有设备 ListDevice devices deviceMapper.selectByArea(areaCode); ListReportRow rows new ArrayList(); for (Device device : devices) { BigDecimal usage statisticsService.getMonthlyUsage( device.getDeviceCode(), month); ReportRow row new ReportRow(); row.setDeviceName(device.getDeviceName()); row.setEnergyType(device.getEnergyType()); row.setUsage(usage); row.setUnit(device.getEnergyType() 1 ? kWh : m3); rows.add(row); } // 2. 写 Excel 并输出到响应流 response.setContentType(application/vnd.ms-excel); response.setHeader(Content-Disposition, attachment;filenamereport_ month .xlsx); EasyExcel.write(response.getOutputStream(), ReportRow.class) .sheet(能耗报表).doWrite(rows); }导出功能要注意两点一是文件名带月份避免多次导出覆盖二是大数据量时分批查询别一次性把几千条记录全查出来塞内存。报表模板通常固定表头用注解ExcelProperty标注字段和列名对应关系即可。5. 避坑与常见问题排查五个最容易翻车的地方5.1 设备时间戳不统一统计结果对不上现象是同一时段的用量看板和报表数字不一致。原因是设备上报的时间戳有的用本地时间、有的用 UTC还有的设备时钟本身就不准。解决方式是在采集层统一做时间归一化以服务器接收时间为准同时保留设备原始时间字段备查。如果设备支持 NTP 对时在接入时统一配置。5.2 累计读数回零导致用量为负现象是某天用量突然变成负数。原因是电表累计读数达到上限后回零或者设备重启后读数重置。解决方式是在统计逻辑里加判断当MAX - MIN为负时说明发生了回零用量按MAX (量程 - MIN)估算或者直接标记该时段数据异常、人工核对。这个坑不处理报表里出现负数答辩时很难解释。5.3 高频写入把数据库拖慢现象是设备数量上来后采集延迟越来越大看板刷新变卡。原因是每条上报都同步写 MySQL写入频率超过数据库承受能力。解决方式是采集层只写 Redis历史数据用批量异步落库比如攒够 100 条或每 5 秒批量插入一次。MySQL 的写入压力能降一个数量级。5.4 权限越权片区负责人看到全部数据现象是片区负责人登录后能看到其他片区的能耗。原因是接口只做了登录校验没做数据范围过滤。解决方式是在用户表里存area_code所有查询接口强制带上当前用户的片区条件管理员角色才放开全量。这个要在接口层统一拦截别指望每个查询都记得加条件。5.5 告警风暴一条规则刷屏现象是某个设备超额后告警列表瞬间多出几十条重复记录。原因是定时任务每次执行都写新告警没有去重。解决方式是在写告警前检查该规则该周期是否已有记录有则更新告警值而不新增同时给告警加已处理状态处理过的周期内不再重复触发。6. 验收与答辩怎么证明这套系统真的能用验收不是把功能点念一遍而是用数据证明系统跑通了完整链路。我一般会准备一套验收脚本按下面几个维度逐项过验收项验收方法合格标准数据采集模拟设备上报 100 条数据全部入库实时缓存可查实时监控打开看板观察刷新5 秒内反映最新读数分项统计查某设备某月日用量与手工计算误差小于 1%超额预警构造超阈值数据定时任务触发告警记录报表导出导出月度报表Excel 内容与页面一致权限控制用片区账号登录只能看到本片区数据答辩时最容易被问的三个问题提前准备好答案数据量大了怎么办答冷热分离、批量落库、加索引、设备协议不统一怎么办答采集层做协议适配、预警误报怎么处理答规则可配置、告警可人工确认。把这三个问题答清楚基本就稳了。演示环节有个技巧提前用脚本灌一批模拟数据让看板和报表都有内容别现场空库演示。我见过有同学答辩时数据库是空的看板一片空白评委问数据呢场面很尴尬。从那以后我每次做演示都强制先跑一遍数据初始化脚本确认每个页面都有真实数据再上场。这套方案从需求到验收是一条完整链路代码和表结构可以直接拿去改。需要完整方案文档和源码的可以按项目标题找对应的资源包里面包含数据库脚本、后端接口、前端页面和部署说明照着搭一遍就能跑起来。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑