還在苦撐場(chǎng)站運(yùn)營(yíng)?試試 100Charge:若依 + SpringBoot + Netty 加持的超級(jí)數(shù)字化平臺(tái)
隨著新能源汽車保有量持續(xù)上漲,充電基礎(chǔ)設(shè)施的建設(shè)速度已成為產(chǎn)業(yè)能否健康發(fā)展的關(guān)鍵指標(biāo)。很多中小充電場(chǎng)站負(fù)責(zé)人都面臨同一個(gè)難題:設(shè)備接入要靠專業(yè)團(tuán)隊(duì)、系統(tǒng)改造成本高、用戶找樁困難、訂單數(shù)據(jù)散落難統(tǒng)計(jì)……這些問(wèn)題使得原本應(yīng)該“自動(dòng)化、高效率”的充電運(yùn)營(yíng)變得極度繁瑣。
100Charge 正是為解決這些真實(shí)痛點(diǎn)而生。平臺(tái)基于 RuoYi(若依)框架 + SpringBoot + Netty + Redis + PostgreSQL 搭建,秉持 “輕部署、低成本、快速上線、全業(yè)務(wù)覆蓋” 的理念,為開發(fā)者與場(chǎng)站運(yùn)營(yíng)者提供一個(gè)可擴(kuò)展、可學(xué)習(xí)、可實(shí)際落地的充電運(yùn)營(yíng)整體解決方案。
本文將從核心技術(shù)基礎(chǔ)、平臺(tái)架構(gòu)設(shè)計(jì)、業(yè)務(wù)功能邏輯到關(guān)鍵代碼實(shí)現(xiàn),完整拆解 100Charge 背后的設(shè)計(jì)與實(shí)現(xiàn)過(guò)程,助你輕松把握充電運(yùn)營(yíng)系統(tǒng)的核心要點(diǎn)。
核心技術(shù)棧:不是堆技術(shù),而是貼合場(chǎng)景的最佳組合
100Charge 的技術(shù)組合并不是隨意羅列,而是圍繞三個(gè)核心需求優(yōu)化所得:
- 設(shè)備高并發(fā)實(shí)時(shí)通信
- 業(yè)務(wù)數(shù)據(jù)一致性與可靠性
- 用戶端毫秒級(jí)體驗(yàn)
以下從四個(gè)維度解讀其技術(shù)架構(gòu)設(shè)計(jì)思路。
RuoYi(若依)——降低后臺(tái)系統(tǒng)開發(fā)與運(yùn)維成本
若依框架在國(guó)內(nèi)中小企業(yè)級(jí)項(xiàng)目中幾乎是“快開快用”的代名詞,它為 100Charge 帶來(lái)的價(jià)值體現(xiàn)在:
- 成熟權(quán)限體系 “角色—菜單—接口” 權(quán)限模型開箱即用,不必重復(fù)造輪子。 如:
場(chǎng)站管理員:僅能查看本場(chǎng)站設(shè)備與訂單
總部用戶:可管理所有場(chǎng)站與設(shè)備
- 完善的前端組件體系 借助若依自帶的 Vue 組件庫(kù),可以迅速構(gòu)建管理端頁(yè)面,如:
- 設(shè)備狀態(tài)表格
- 場(chǎng)站運(yùn)營(yíng)看板
- 訂單統(tǒng)計(jì)圖表
- 活躍的社區(qū)支持 中小場(chǎng)站無(wú)需重新組建技術(shù)團(tuán)隊(duì),日常維護(hù)門檻極低。
Netty —— 充電樁實(shí)時(shí)通信的核心引擎
100Charge 的通信層完全基于 Netty 構(gòu)建,適配充電樁的實(shí)時(shí)數(shù)據(jù)上報(bào)與指令下發(fā)。
為何一定要用 Netty?
- 高性能的 異步非阻塞通信
- 可同時(shí)接入 上千臺(tái)設(shè)備
- 延遲控制在 100ms 內(nèi)
- 可實(shí)現(xiàn) 自定義私有協(xié)議 (解決不同廠家充電樁協(xié)議格式不統(tǒng)一的問(wèn)題)
這層通信能力決定了系統(tǒng)是否能做到“用戶掃碼立即啟動(dòng)”、“設(shè)備故障秒級(jí)上報(bào)”。
PostgreSQL + Redis —— 數(shù)據(jù)一致性 + 高性能查詢的最佳組合
充電訂單涉及金額、時(shí)長(zhǎng)、實(shí)時(shí)電量,要求極高的數(shù)據(jù)一致性,因此 100Charge 使用:
PostgreSQL(存核心數(shù)據(jù))
- 存儲(chǔ)結(jié)構(gòu)化業(yè)務(wù)數(shù)據(jù)(訂單、用戶余額、計(jì)費(fèi)規(guī)則等)
- 支持 ACID 事務(wù) → 避免 “扣費(fèi)成功但訂單失敗” 的情況
Redis(緩存高頻數(shù)據(jù))
- 場(chǎng)站空閑樁數(shù)量
- 當(dāng)前電價(jià)與計(jì)費(fèi)規(guī)則
- 用戶附近站點(diǎn)緩存
Redis 將用戶端訪問(wèn)速度提升至 微秒級(jí)。
MinIO + Elasticsearch(增強(qiáng)版)——文件與海量日志存儲(chǔ)能力提升
- MinIO: 存儲(chǔ)場(chǎng)站實(shí)景圖、故障截圖、證件文件等
- ES(可選): 用于日志與運(yùn)營(yíng)數(shù)據(jù)分析,例如:
- 最近 7 天峰值充電時(shí)間段
- 某用戶月度充電行為分析
平臺(tái)架構(gòu):真正能夠落地的模塊化分層結(jié)構(gòu)
100Charge 采用 分層架構(gòu) + 模塊化設(shè)計(jì),從代碼層、數(shù)據(jù)層到前端層都清晰可控。
/com/icoderoad/charge
├── netty # 設(shè)備通信
├── station # 場(chǎng)站信息
├── device # 充電樁管理
├── order # 訂單業(yè)務(wù)
├── billing # 計(jì)費(fèi)策略
├── user # 用戶賬戶
├── common # 公共組件
└── storage #MinIO 文件處理平臺(tái)總體結(jié)構(gòu)如下:
層級(jí) | 組件 | 職責(zé) |
通信層 | Netty | 設(shè)備狀態(tài)上報(bào)、啟停指令下發(fā) |
服務(wù)層 | SpringBoot | 場(chǎng)站、設(shè)備、訂單、計(jì)費(fèi)、小程序用戶服務(wù) |
緩存層 | Redis | 高頻業(yè)務(wù)查詢緩存 |
數(shù)據(jù)層 | PostgreSQL / ES | 核心數(shù)據(jù) + 海量日志 |
文件層 | MinIO | 圖片、截圖、證件文件 |
前端層 | 小程序、若依后臺(tái) | 用戶端 + 管理端 |
功能設(shè)計(jì):覆蓋充電運(yùn)營(yíng)的全業(yè)務(wù)鏈路
場(chǎng)站與設(shè)備管理
- 多場(chǎng)站聚合管理
- 實(shí)時(shí)監(jiān)控設(shè)備狀態(tài)(空閑、充電中、故障)
- 功率、電量、啟動(dòng)限制參數(shù)配置
- 故障自動(dòng)告警
靈活計(jì)費(fèi)能力
支持:
- 時(shí)段電價(jià)
- 節(jié)假日電價(jià)
- 新用戶活動(dòng)價(jià)
- 服務(wù)費(fèi)可配置(全免 / 部分免)
- 停車費(fèi)聯(lián)動(dòng)優(yōu)惠
非常貼合中小場(chǎng)站靈活運(yùn)營(yíng)的需求。
微信小程序用戶端
用戶無(wú)需下載 App,即可完成:
- 附近找樁
- 掃碼啟動(dòng)
- 實(shí)時(shí)查看電量、電費(fèi)
- 自動(dòng)停止
- 微信支付 / 余額支付
- 訂單查詢與開票
整個(gè)鏈路不超過(guò) 3 步。
訂單與財(cái)務(wù)體系
- 訂單明細(xì)全記錄(電量、時(shí)長(zhǎng)、費(fèi)用、設(shè)備號(hào))
- 按場(chǎng)站、時(shí)間、用戶篩選
- 導(dǎo)出 Excel 用于財(cái)務(wù)對(duì)賬
- 余額充值 / 贈(zèng)送 / 扣費(fèi)全流程記錄
關(guān)鍵代碼實(shí)現(xiàn)(已按 com.icoderoad 規(guī)范優(yōu)化)
以下為關(guān)鍵模塊的完整代碼示例(已做語(yǔ)義優(yōu)化)。
Maven 依賴(pom.xml)
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- 若依父工程 -->
<parent>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-parent</artifactId>
<version>3.8.5</version>
<relativePath/>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>100charge-server</artifactId>
<name>100Charge Server</name>
<properties>
<netty.version>4.1.90.Final</netty.version>
<postgresql.version>42.5.4</postgresql.version>
</properties>
<dependencies>
<!-- 若依基礎(chǔ)依賴 -->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-framework</artifactId>
</dependency>
<!-- Netty -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- PostgreSQL -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>application.yml(核心配置)
server:
port: 8088
servlet:
context-path: /100charge
spring:
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/100charge_db
username: postgres
password: 123456
redis:
host: localhost
port: 6379
database: 1
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 1
netty:
port: 8888
boss-thread-count: 1
worker-thread-count: 4Netty 服務(wù)端啟動(dòng)類
package com.icoderoad.charge.netty;
@Slf4j
@Component
public class NettyServer {
@Value("${netty.port}")
private int port;
@PostConstruct
public void start() {
EventLoopGroup boss = new NioEventLoopGroup(1);
EventLoopGroup worker = new NioEventLoopGroup(4);
try {
ServerBootstrap bootstrap = new ServerBootstrap()
.group(boss, worker)
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new ChargerDataDecoder());
ch.pipeline().addLast(new ChargerDataEncoder());
ch.pipeline().addLast(new ChargerBusinessHandler());
}
});
bootstrap.bind(port).sync();
log.info("Netty server started at {}", port);
} catch (Exception e) {
log.error("Netty start error", e);
}
}
}設(shè)備上報(bào)業(yè)務(wù)處理器
package com.icoderoad.charge.netty;
@Slf4j
public class ChargerBusinessHandler extends SimpleChannelInboundHandler<String> {
private final IChargerDeviceService deviceService =
SpringUtils.getBean(IChargerDeviceService.class);
private final RedisTemplate<String, Object> redis =
SpringUtils.getBean(RedisTemplate.class);
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
log.info("Device report: {}", msg);
String[] arr = msg.split("\\|");
if (arr.length != 3) {
ctx.writeAndFlush("ERROR\n");
return;
}
String deviceId = arr[0];
Integer status = Integer.valueOf(arr[1]);
Double power = Double.valueOf(arr[2]);
ChargerDevice dev = new ChargerDevice();
dev.setDeviceId(deviceId);
dev.setStatus(status);
dev.setRealTimePower(power);
deviceService.updateChargerDevice(dev);
String key = "CHARGER_STATUS:" + deviceId;
redis.opsForValue().set(key, dev, 1, TimeUnit.HOURS);
ctx.writeAndFlush("OK\n");
}
}結(jié)語(yǔ):100Charge 不是 Demo,而是可真正運(yùn)營(yíng)的生產(chǎn)級(jí)方案
中小場(chǎng)站缺的不是“更復(fù)雜的系統(tǒng)”,而是:
- 易部署
- 又穩(wěn)定
- 又能快速上線
- 又能低成本維護(hù)
而 100Charge 正是基于這個(gè)核心需求構(gòu)建。 你既可以把它用作生產(chǎn)運(yùn)營(yíng)系統(tǒng),也可以把它當(dāng)成充電行業(yè)學(xué)習(xí)的最佳實(shí)踐架構(gòu)。

























