1. 项目概述
作为一名经历过多次校园信息化项目开发的工程师,我最近完成了一个基于SSM+Vue架构的智能楼宇能源管理系统。这个系统最初是为某高校宿舍楼设计的,目的是解决传统楼宇管理中普遍存在的"能源黑箱"问题——管理人员不清楚每栋楼的用电情况,住户也不了解自己的能耗数据,导致大量不必要的能源浪费。
系统采用前后端分离架构,后端使用Spring+SpringMVC+MyBatis框架组合,前端基于Vue.js+ElementUI构建。经过三个月的开发和测试,系统已经能够实现楼宇用电的实时监控、异常预警和智能分析,预计可为中小型楼宇节省10%-15%的能源消耗。下面我将详细分享这个项目的技术实现和开发经验。
2. 系统架构设计
2.1 技术选型考量
在项目启动阶段,我们面临的首要问题就是技术栈的选择。经过对项目需求和团队技术储备的综合评估,最终确定了以下技术方案:
后端技术栈:
- Spring Framework 4.3:提供IoC容器和AOP支持,简化企业级应用开发
- SpringMVC:处理Web请求和响应,实现RESTful API
- MyBatis 3.4:ORM框架,简化数据库操作
- MySQL 5.7:关系型数据库,存储业务数据
- Redis 3.2:缓存热点数据,提升系统响应速度
选择这套组合主要基于以下考虑:
- SSM框架在Java Web开发中成熟稳定,社区资源丰富,遇到问题容易找到解决方案
- 项目规模适中,不需要Spring Boot的自动化配置带来的便利
- 团队对MyBatis比较熟悉,相比Hibernate能更灵活地控制SQL
前端技术栈:
- Vue.js 2.6:渐进式JavaScript框架,构建用户界面
- ElementUI 2.13:基于Vue的UI组件库,快速搭建管理后台
- ECharts 4.7:数据可视化库,展示能耗趋势
- Axios 0.19:HTTP客户端,处理API请求
- Vue-Socket.io 3.0:WebSocket客户端,实现实时通信
前端选择Vue.js而非React或Angular的主要原因是:
- Vue的学习曲线平缓,适合团队成员快速上手
- ElementUI提供了丰富的后台管理组件,能显著提高开发效率
- ECharts与Vue的集成方案成熟,能满足数据可视化需求
2.2 系统架构图
整个系统采用典型的三层架构设计:
code复制[表现层] Vue前端 → [业务逻辑层] SpringMVC → [数据访问层] MyBatis → [数据存储层] MySQL
↑
[实时通信层] WebSocket ←→ [缓存层] Redis
这种架构的优势在于:
- 层次清晰,各层职责明确,便于团队协作
- 前后端完全分离,可以独立开发和部署
- 引入Redis缓存后,能有效减轻数据库压力
- WebSocket实现真正的实时通信,满足能耗预警需求
3. 核心功能实现
3.1 电表数据实时采集
电表数据采集是系统的核心功能之一。我们设计了两种数据采集方式:
1. API对接智能电表
java复制// 电表数据采集服务接口
public interface MeterDataService {
/**
* 从智能电表API获取实时数据
* @param meterId 电表ID
* @return 当前读数(kWh)
*/
BigDecimal fetchRealtimeData(String meterId);
/**
* 批量获取电表数据
* @param meterIds 电表ID列表
* @return 电表数据映射表
*/
Map<String, BigDecimal> batchFetchData(List<String> meterIds);
}
// 实现类使用RestTemplate调用电表厂商API
@Service
public class MeterDataServiceImpl implements MeterDataService {
@Autowired
private RestTemplate restTemplate;
@Value("${meter.api.url}")
private String apiUrl;
@Override
public BigDecimal fetchRealtimeData(String meterId) {
String url = apiUrl + "/meters/" + meterId + "/reading";
MeterReading reading = restTemplate.getForObject(url, MeterReading.class);
return reading.getValue();
}
}
2. 手动录入界面
对于尚未智能化的传统电表,我们开发了数据录入界面,支持Excel批量导入:
vue复制<template>
<el-upload
action="/api/meter-data/import"
:before-upload="beforeUpload"
:on-success="handleSuccess"
accept=".xlsx,.xls"
>
<el-button type="primary">点击上传电表数据Excel</el-button>
</el-upload>
</template>
<script>
export default {
methods: {
beforeUpload(file) {
const isExcel = file.type === 'application/vnd.ms-excel' ||
file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
if (!isExcel) {
this.$message.error('只能上传Excel文件!');
}
return isExcel;
},
handleSuccess(response) {
if (response.code === 200) {
this.$message.success('导入成功');
this.$emit('refresh');
}
}
}
}
</script>
数据采集优化技巧:
- 使用Spring的
@Scheduled注解实现定时任务,每15分钟自动采集一次数据 - 对API调用添加重试机制,网络异常时自动重试3次
- 批量获取接口采用异步方式,提高采集效率
- 手动录入的数据需要经过双重校验,确保准确性
3.2 能耗超标实时预警
能耗预警是系统的另一核心功能。我们设计了多级预警机制:
- 阈值设置:支持按楼栋类型设置不同的用电量阈值
- 实时比对:新采集的数据会立即与阈值比对
- 多渠道通知:发现超标后通过系统消息、短信、邮件通知相关人员
后端预警逻辑实现:
java复制@Service
public class EnergyAlertServiceImpl implements EnergyAlertService {
@Autowired
private MeterDataMapper meterDataMapper;
@Autowired
private AlertRuleMapper alertRuleMapper;
@Autowired
private NotificationService notificationService;
@Override
@Transactional
public void checkAlert(String meterId, BigDecimal currentValue) {
// 1. 获取电表所属楼栋类型
BuildingType buildingType = meterDataMapper.getBuildingTypeByMeter(meterId);
// 2. 查询对应的预警规则
AlertRule rule = alertRuleMapper.findByBuildingType(buildingType);
// 3. 比对当前值与阈值
if (currentValue.compareTo(rule.getThreshold()) > 0) {
// 4. 记录预警
AlertRecord record = new AlertRecord();
record.setMeterId(meterId);
record.setExceedValue(currentValue.subtract(rule.getThreshold()));
alertRecordMapper.insert(record);
// 5. 发送通知
List<User> toNotify = userMapper.findUsersToNotify(meterId);
notificationService.sendAlert(toNotify, record);
}
}
}
前端实时展示:
使用WebSocket实现预警信息的实时推送:
javascript复制// 前端建立WebSocket连接
this.socket = io.connect('/alert', {
reconnection: true,
reconnectionDelay: 5000
});
this.socket.on('alert', (data) => {
this.$notify({
title: '能耗预警',
message: `电表${data.meterId}用电量超标,当前值:${data.currentValue}kWh`,
type: 'warning',
duration: 0 // 不自动关闭
});
});
预警系统优化经验:
- 预警阈值支持按工作日/节假日分别设置
- 采用滑动窗口算法,避免瞬时波动导致的误报
- 重要预警需要确认机制,防止通知遗漏
- 预警信息包含具体超标数值和建议措施
4. 数据库设计与优化
4.1 主要表结构设计
系统数据库包含20余张表,以下是核心表的设计:
1. 电表数据表(meter_data)
sql复制CREATE TABLE `meter_data` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`meter_id` varchar(32) NOT NULL COMMENT '电表编号',
`reading` decimal(10,2) NOT NULL COMMENT '当前读数(kWh)',
`reading_time` datetime NOT NULL COMMENT '抄表时间',
`collect_method` tinyint(1) NOT NULL COMMENT '采集方式(0-自动 1-手动)',
`operator` varchar(32) DEFAULT NULL COMMENT '操作人',
PRIMARY KEY (`id`),
INDEX `idx_meter_time` (`meter_id`, `reading_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2. 预警记录表(alert_record)
sql复制CREATE TABLE `alert_record` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`meter_id` varchar(32) NOT NULL,
`exceed_value` decimal(10,2) NOT NULL COMMENT '超出阈值量',
`alert_time` datetime NOT NULL,
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0-未处理 1-已确认',
`confirm_user` varchar(32) DEFAULT NULL,
`confirm_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `idx_meter_status` (`meter_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4.2 查询性能优化
随着数据量增长,电费统计查询逐渐变慢。我们通过以下措施优化性能:
1. 添加合适的索引
sql复制-- 为电费统计表添加联合索引
ALTER TABLE `electric_charge`
ADD INDEX `idx_building_month` (`building_id`, `charge_month`);
-- 为用户电表关系表添加索引
ALTER TABLE `user_meter_relation`
ADD INDEX `idx_user_meter` (`user_id`, `meter_id`);
2. 使用MyBatis二级缓存
xml复制<!-- 在mapper.xml中配置缓存 -->
<mapper namespace="com.energy.mapper.ElectricChargeMapper">
<cache eviction="LRU" flushInterval="3600000" size="1024"/>
<select id="findByBuildingAndMonth" resultType="ElectricCharge" useCache="true">
SELECT * FROM electric_charge
WHERE building_id = #{buildingId} AND charge_month = #{month}
</select>
</mapper>
3. 引入Redis缓存热点数据
java复制@Service
public class ElectricChargeServiceImpl implements ElectricChargeService {
@Autowired
private ElectricChargeMapper chargeMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String CACHE_PREFIX = "charge:";
@Override
public List<ElectricCharge> findByBuilding(String buildingId, String month) {
String cacheKey = CACHE_PREFIX + buildingId + ":" + month;
// 先查缓存
List<ElectricCharge> charges = (List<ElectricCharge>) redisTemplate.opsForValue().get(cacheKey);
if (charges != null) {
return charges;
}
// 缓存没有则查数据库
charges = chargeMapper.findByBuildingAndMonth(buildingId, month);
// 存入缓存,过期时间1小时
redisTemplate.opsForValue().set(cacheKey, charges, 1, TimeUnit.HOURS);
return charges;
}
}
数据库优化心得:
- 大表一定要根据查询条件建立合适的索引
- 定期使用EXPLAIN分析慢查询,针对性优化
- 缓存能显著提升性能,但要处理好缓存一致性问题
- 历史数据可以考虑归档,保持主表精简
5. 前端关键技术实现
5.1 数据可视化展示
使用ECharts实现用电数据的可视化展示:
vue复制<template>
<div class="chart-container">
<div ref="chart" style="width: 100%; height: 400px;"></div>
</div>
</template>
<script>
import * as echarts from 'echarts';
export default {
props: ['meterId'],
data() {
return {
chart: null
};
},
mounted() {
this.initChart();
this.loadData();
},
methods: {
initChart() {
this.chart = echarts.init(this.$refs.chart);
this.chart.setOption({
tooltip: {
trigger: 'axis',
formatter: params => {
const date = params[0].axisValue;
const value = params[0].data;
return `${date}<br/>用电量: ${value}kWh`;
}
},
xAxis: {
type: 'category',
data: []
},
yAxis: {
type: 'value',
name: '用电量(kWh)'
},
series: [{
data: [],
type: 'line',
smooth: true,
lineStyle: {
width: 3,
color: '#409EFF'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(64, 158, 255, 0.5)' },
{ offset: 1, color: 'rgba(64, 158, 255, 0.1)' }
])
}
}]
});
},
async loadData() {
const res = await this.$http.get(`/api/meter-data/history/${this.meterId}?days=7`);
const dates = res.data.map(item => item.readingTime);
const values = res.data.map(item => item.reading);
this.chart.setOption({
xAxis: { data: dates },
series: [{ data: values }]
});
}
}
};
</script>
5.2 权限控制实现
系统有三种角色:超级管理员、楼栋管理员和普通用户。前端通过以下方式实现权限控制:
1. 路由权限控制
javascript复制// 路由配置
const routes = [
{
path: '/',
component: Layout,
children: [
{
path: 'dashboard',
component: Dashboard,
meta: { roles: ['admin', 'building_admin', 'user'] }
},
{
path: 'building',
component: BuildingManage,
meta: { roles: ['admin', 'building_admin'] }
},
{
path: 'user',
component: UserManage,
meta: { roles: ['admin'] }
}
]
}
];
// 路由守卫
router.beforeEach((to, from, next) => {
const userRole = store.getters.role;
if (to.meta.roles && !to.meta.roles.includes(userRole)) {
next('/403'); // 无权限跳转到403页面
} else {
next();
}
});
2. 按钮级权限控制
vue复制<template>
<el-button
v-if="hasPermission('building:add')"
type="primary"
@click="handleAdd"
>
新增楼栋
</el-button>
</template>
<script>
export default {
methods: {
hasPermission(permission) {
const permissions = this.$store.getters.permissions;
return permissions.includes(permission);
}
}
}
</script>
前端开发经验分享:
- ElementUI的按需加载能显著减小打包体积
- 使用Vuex管理全局状态比组件间传值更清晰
- 合理使用keep-alive缓存组件提升性能
- 封装通用的表格、表单组件能提高开发效率
6. 系统部署与运维
6.1 部署环境准备
服务器要求:
- CPU: 4核以上
- 内存: 8GB以上
- 磁盘: 100GB以上(根据数据量调整)
- 操作系统: CentOS 7.x
软件安装:
bash复制# 安装JDK
yum install -y java-1.8.0-openjdk-devel
# 安装MySQL
wget https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm
rpm -ivh mysql57-community-release-el7-11.noarch.rpm
yum install -y mysql-community-server
# 安装Redis
yum install -y epel-release
yum install -y redis
# 安装Tomcat
tar -xzf apache-tomcat-7.0.108.tar.gz -C /usr/local/
6.2 应用部署步骤
1. 数据库初始化
sql复制CREATE DATABASE energy DEFAULT CHARACTER SET utf8mb4;
GRANT ALL PRIVILEGES ON energy.* TO 'energy'@'%' IDENTIFIED BY 'Energy@123';
FLUSH PRIVILEGES;
2. 应用打包部署
bash复制# 后端打包
mvn clean package -Dmaven.test.skip=true
# 前端打包
npm run build
# 部署到Tomcat
cp energy-web/target/energy.war /usr/local/tomcat/webapps/
cp -r energy-front/dist/* /usr/local/nginx/html/energy/
3. Nginx配置
nginx复制server {
listen 80;
server_name energy.example.com;
location / {
root /usr/local/nginx/html/energy;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://localhost:8080/energy;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /socket.io {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
部署注意事项:
- 生产环境务必使用HTTPS,确保数据传输安全
- 数据库密码要足够复杂,不要使用默认密码
- 定期备份数据库,防止数据丢失
- 使用PM2等工具管理Node进程,确保前端服务稳定运行
7. 项目总结与改进方向
经过三个月的开发和测试,系统已经稳定运行在某高校的5栋宿舍楼,实现了以下目标:
- 电表数据采集自动化,告别人工抄表
- 能耗超标实时预警,响应时间<30秒
- 电费计算准确率100%,无人工干预
- 公共能耗分摊透明化,减少纠纷
实际运行效果:
- 管理员平均每天处理预警次数:从15次降至3次
- 电费核算工作时间:从3天缩短到2小时
- 整体能耗下降:12.7%(与去年同期相比)
遇到的典型问题及解决方案:
-
问题:初期电表数据采集不稳定,经常漏采
解决:增加重试机制和异常告警,采集成功率提升至99.9% -
问题:月末电费计算时数据库负载高
解决:优化SQL语句,增加适当索引,引入Redis缓存 -
问题:部分老旧电表API响应慢
解决:采用异步采集方式,超时设置为30秒
未来改进方向:
- 接入更多类型的能源数据(水、气等)
- 引入机器学习算法,实现能耗预测
- 开发移动端应用,方便用户随时查看
- 增加能耗排名功能,激励节能行为
这个项目让我深刻体会到,一个好的能源管理系统不仅能提高管理效率,更能培养用户的节能意识。在开发过程中,我们不断调整方案以适应实际需求,最终交出了一份令人满意的答卷。对于想要开发类似系统的同学,我的建议是:先做好需求分析,设计好数据库结构,这些前期工作会为后续开发节省大量时间。