1. 项目概述
充电桩共享服务管理系统是一个基于Spring Boot框架开发的B/S架构应用,旨在为新能源汽车用户提供便捷的充电桩查询、预约和使用服务。随着新能源汽车的普及,充电基础设施的管理需求日益增长,传统的人工管理方式已无法满足高效运营的需求。本系统通过信息化手段实现了充电桩资源的统一管理和优化配置,解决了用户找桩难、充电排队时间长等痛点问题。
作为一名有10年开发经验的Java全栈工程师,我在设计这个系统时特别注重了以下几个关键点:
- 采用前后端分离架构,保证系统的可扩展性和维护性
- 实现精细化的权限控制,确保不同角色的用户只能访问其权限范围内的功能
- 引入智能调度算法,优化充电桩资源分配
- 设计完善的支付结算模块,支持多种支付方式
2. 系统架构设计
2.1 技术栈选型
后端技术栈:
- 核心框架:Spring Boot 2.7.3(提供自动配置和快速开发能力)
- ORM框架:MyBatis-Plus 3.5.1(简化数据库操作)
- 安全框架:Spring Security(处理认证和授权)
- 缓存:Redis 6.2(提升系统性能)
- 消息队列:RabbitMQ 3.9(处理异步任务)
- 数据库:MySQL 8.0(关系型数据库存储核心数据)
前端技术栈:
- 核心框架:Vue 3.2(构建响应式用户界面)
- UI组件库:Element Plus 2.2(提供丰富的UI组件)
- 状态管理:Pinia 2.0(替代Vuex的状态管理方案)
- 图表库:ECharts 5.3(数据可视化展示)
- 构建工具:Vite 3.0(前端构建工具)
技术选型考量:选择Spring Boot+Vue的组合主要考虑到社区活跃度高、学习曲线平缓、生态完善。MyBatis-Plus相比JPA在复杂查询场景下更灵活,而Vue3的组合式API让前端代码更易维护。
2.2 系统架构模式
系统采用经典的MVC分层架构,具体分层如下:
-
表现层:
- 负责接收HTTP请求并返回响应
- 使用Spring MVC处理请求路由
- 统一异常处理和参数校验
-
业务逻辑层:
- 实现核心业务逻辑
- 事务管理(@Transactional)
- 业务规则校验
- 服务组合和编排
-
数据访问层:
- 数据库CRUD操作
- MyBatis-Plus动态SQL生成
- 多数据源配置(主从分离)
-
基础设施层:
- 缓存管理(Redis)
- 消息队列(RabbitMQ)
- 文件存储(MinIO)
- 定时任务(Quartz)
3. 核心功能模块实现
3.1 用户认证与授权
用户认证采用JWT(JSON Web Token)方案,具体实现流程:
- 用户登录时,服务端验证用户名密码
- 验证通过后生成JWT令牌(包含用户ID、角色等信息)
- 客户端存储JWT并在后续请求中携带
- 服务端通过过滤器验证JWT有效性
java复制// JWT生成示例代码
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", ((CustomUserDetails)userDetails).getUserId());
claims.put("roles", userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()));
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + jwtExpiration))
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
权限控制采用RBAC(基于角色的访问控制)模型,数据库设计包含以下表:
- sys_user(用户表)
- sys_role(角色表)
- sys_menu(菜单权限表)
- sys_user_role(用户角色关联表)
- sys_role_menu(角色菜单关联表)
3.2 充电桩管理模块
充电桩管理是系统的核心功能,主要包含以下子模块:
-
充电桩信息管理:
- 充电桩基本信息CRUD
- 状态监控(空闲/使用中/故障)
- 地理位置信息(基于高德地图API)
-
预约管理:
- 用户预约充电桩
- 预约超时自动取消
- 预约冲突检测
-
使用记录:
- 充电开始/结束时间记录
- 充电量统计
- 费用计算
数据库表设计关键字段:
sql复制CREATE TABLE `charging_pile` (
`id` bigint NOT NULL AUTO_INCREMENT,
`code` varchar(32) NOT NULL COMMENT '充电桩编号',
`type` tinyint NOT NULL COMMENT '类型:1-快充 2-慢充',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '状态:0-空闲 1-使用中 2-故障',
`power` decimal(10,2) NOT NULL COMMENT '功率(kW)',
`price` decimal(10,2) NOT NULL COMMENT '单价(元/度)',
`location` point NOT NULL COMMENT '地理位置',
`address` varchar(255) NOT NULL COMMENT '详细地址',
`create_time` datetime NOT NULL,
`update_time` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code` (`code`),
SPATIAL KEY `idx_location` (`location`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.3 支付结算模块
支付流程设计:
- 用户结束充电后系统生成订单
- 订单包含充电时长、电量、费用等信息
- 用户选择支付方式(微信/支付宝/余额)
- 调用第三方支付接口完成支付
- 支付成功后更新订单状态
java复制// 订单创建逻辑示例
public Order createOrder(Long pileId, Long userId) {
ChargingPile pile = pileService.getById(pileId);
if (pile == null || pile.getStatus() != 1) {
throw new BusinessException("充电桩不可用");
}
User user = userService.getById(userId);
if (user == null) {
throw new BusinessException("用户不存在");
}
// 计算费用
LocalDateTime startTime = chargingRecord.getStartTime();
LocalDateTime endTime = LocalDateTime.now();
long minutes = Duration.between(startTime, endTime).toMinutes();
BigDecimal amount = pile.getPrice().multiply(
new BigDecimal(minutes * pile.getPower() / 60));
Order order = new Order();
order.setOrderNo(IdUtil.getSnowflakeNextIdStr());
order.setUserId(userId);
order.setPileId(pileId);
order.setStartTime(startTime);
order.setEndTime(endTime);
order.setAmount(amount);
order.setStatus(0); // 待支付
orderMapper.insert(order);
// 更新充电桩状态
pile.setStatus(0); // 空闲
pileService.updateById(pile);
return order;
}
4. 系统特色功能实现
4.1 智能调度算法
为解决充电桩资源分配问题,系统实现了基于贪心算法的智能调度策略:
- 当用户发起预约请求时,系统会:
- 获取用户当前位置(经纬度)
- 查询周围3公里内可用充电桩
- 根据距离、充电桩类型、价格等因素计算权重
- 返回权重最高的充电桩推荐给用户
java复制public List<ChargingPileVO> recommendPiles(Double lng, Double lat, Integer type) {
// 查询附近充电桩
List<ChargingPile> piles = pileMapper.selectNearby(
lng, lat, 3000, type);
// 计算权重并排序
return piles.stream()
.map(pile -> {
ChargingPileVO vo = new ChargingPileVO();
BeanUtils.copyProperties(pile, vo);
// 计算距离权重(越近权重越高)
double distance = GeoUtil.getDistance(
lng, lat, pile.getLng(), pile.getLat());
vo.setDistance(distance);
double distanceScore = 1 / (distance + 0.1) * 40;
// 价格权重(越便宜权重越高)
double priceScore = (2 - pile.getPrice().doubleValue()) * 30;
// 评价权重
double ratingScore = pile.getRating() * 3;
vo.setScore(distanceScore + priceScore + ratingScore);
return vo;
})
.sorted(Comparator.comparing(ChargingPileVO::getScore).reversed())
.limit(5)
.collect(Collectors.toList());
}
4.2 实时监控大屏
基于WebSocket和ECharts实现的实时监控功能:
- 后端使用Spring WebSocket维护连接
- 前端使用SockJS和StompJS建立连接
- 定时推送系统关键指标:
- 在线充电桩数量
- 当前充电订单数
- 今日营收统计
- 故障告警信息
javascript复制// 前端WebSocket连接示例
const socket = new SockJS('/api/ws');
const stompClient = Stomp.over(socket);
stompClient.connect({}, () => {
// 订阅监控主题
stompClient.subscribe('/topic/monitor', (message) => {
const data = JSON.parse(message.body);
// 更新ECharts图表
updateDashboard(data);
});
});
// ECharts图表初始化
const chart = echarts.init(document.getElementById('chart'));
const option = {
tooltip: { trigger: 'axis' },
legend: { data: ['在线数量', '使用中数量'] },
xAxis: { type: 'category', data: [] },
yAxis: { type: 'value' },
series: [
{ name: '在线数量', type: 'line', data: [] },
{ name: '使用中数量', type: 'line', data: [] }
]
};
chart.setOption(option);
5. 系统部署与运维
5.1 生产环境部署方案
服务器配置建议:
- 应用服务器:2核4G(最少),推荐4核8G
- 数据库服务器:4核8G(主从配置)
- Redis服务器:2核4G
- 带宽:5Mbps以上
部署流程:
-
数据库初始化:
bash复制
mysql -uroot -p < schema.sql mysql -uroot -p < data.sql -
后端应用打包部署:
bash复制mvn clean package -DskipTests nohup java -jar charging-system.jar --spring.profiles.active=prod > log.out 2>&1 & -
前端应用构建部署:
bash复制npm install npm run build # 将dist目录内容部署到Nginx
Nginx配置示例:
nginx复制server {
listen 80;
server_name charging.example.com;
location / {
root /var/www/charging-web;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /ws {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
5.2 性能优化实践
-
数据库优化:
- 为常用查询字段建立索引
- 使用EXPLAIN分析慢查询
- 合理设计表结构,避免过度冗余
-
缓存策略:
- 热点数据缓存(充电桩状态、用户信息)
- 使用Redis分布式锁防止并发问题
- 多级缓存设计(Caffeine + Redis)
-
JVM调优:
bash复制# 启动参数示例 java -Xms1g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \ -jar charging-system.jar -
前端优化:
- 组件懒加载
- 路由懒加载
- 图片压缩
- 启用Gzip压缩
6. 开发经验与避坑指南
6.1 常见问题解决方案
问题1:MyBatis-Plus主键策略冲突
现象:使用雪花ID生成器时,插入数据报主键冲突。
解决方案:
java复制@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 防止全表更新与删除
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
@Bean
public IdentifierGenerator idGenerator() {
return new CustomIdGenerator(); // 自定义ID生成器
}
}
问题2:Vue3响应式丢失
现象:使用reactive定义的对象,解构后失去响应性。
正确做法:
javascript复制// 错误示例
const state = reactive({ count: 0 });
let { count } = state; // 解构后count不是响应式的
// 正确做法1:直接访问
state.count++;
// 正确做法2:使用toRefs
const { count } = toRefs(state);
count.value++;
6.2 开发效率提升技巧
-
代码生成器:
使用MyBatis-Plus代码生成器快速生成基础CRUD代码:java复制FastAutoGenerator.create(dataSourceConfig) .globalConfig(builder -> builder.author("dev").outputDir("src/main/java")) .packageConfig(builder -> builder.parent("com.example.charging")) .strategyConfig(builder -> builder.addInclude("sys_user", "charging_pile")) .templateConfig(builder -> builder.disable(TemplateType.CONTROLLER)) .execute(); -
接口调试:
使用SpringDoc OpenAPI 3自动生成API文档:xml复制<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.6.11</version> </dependency>访问http://localhost:8080/swagger-ui.html即可查看和测试接口
-
前端Mock数据:
使用Mock.js模拟后端接口:javascript复制import Mock from 'mockjs' Mock.mock('/api/user/list', 'get', { 'list|10': [{ 'id|+1': 1, 'name': '@cname', 'phone': /^1[385][0-9]{9}$/ }] })
7. 项目扩展方向
-
物联网集成:
- 通过MQTT协议与充电桩硬件直接通信
- 实时获取充电桩电压、电流等详细参数
- 远程控制充电桩启停
-
数据分析平台:
- 使用Flink实时计算充电热点区域
- 基于历史数据预测充电需求
- 生成运营报表和用户画像
-
小程序端开发:
- 开发微信小程序版本
- 集成微信支付
- 利用LBS实现附近充电桩查询
-
会员体系:
- 积分奖励机制
- 会员等级制度
- 优惠券发放系统
在实际开发中,我特别建议重视日志系统的建设。完善的日志可以帮助快速定位问题,建议按功能模块划分日志文件,并使用ELK(Elasticsearch+Logstash+Kibana)搭建日志分析平台。以下是一个日志配置的示例:
xml复制<!-- logback-spring.xml -->
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/charging-system.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/charging-system.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.example.charging.mapper" level="DEBUG" additivity="false">
<appender-ref ref="FILE"/>
</logger>
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</configuration>
这个充电桩共享服务管理系统从技术选型到架构设计都经过了精心考量,在实际运行中表现稳定。开发过程中积累的经验告诉我,好的系统不仅要有完善的功能,还需要考虑性能、可维护性和扩展性。特别是在处理高并发场景时,合理的缓存策略和数据库设计至关重要。